1 /* $OpenBSD: radiusctl.c,v 1.17 2024/11/21 13:43:10 claudio Exp $ */ 2 /* 3 * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 #include <sys/types.h> 18 #include <sys/cdefs.h> 19 #include <sys/socket.h> 20 #include <sys/time.h> 21 #include <sys/uio.h> 22 #include <sys/un.h> 23 #include <netinet/in.h> 24 #include <arpa/inet.h> 25 26 #include <err.h> 27 #include <errno.h> 28 #include <event.h> 29 #include <imsg.h> 30 #include <inttypes.h> 31 #include <md5.h> 32 #include <netdb.h> 33 #include <radius.h> 34 #include <stdbool.h> 35 #include <stddef.h> 36 #include <stdint.h> 37 #include <stdio.h> 38 #include <stdlib.h> 39 #include <string.h> 40 #include <sysexits.h> 41 #include <time.h> 42 #include <unistd.h> 43 44 #include "parser.h" 45 #include "radiusd.h" 46 #include "radiusd_ipcp.h" 47 #include "chap_ms.h" 48 #include "json.h" 49 50 #ifndef MAXIMUM 51 #define MAXIMUM(_a, _b) (((_a) > (_b))? (_a) : (_b)) 52 #endif 53 54 static int radius_test(struct parse_result *); 55 static void radius_dump(FILE *, RADIUS_PACKET *, bool, 56 const char *); 57 58 static int ipcp_handle_imsg(struct parse_result *, struct imsg *, 59 int); 60 static void ipcp_handle_show(struct radiusd_ipcp_db_dump *, 61 size_t, int); 62 static void ipcp_handle_dumps(struct radiusd_ipcp_db_dump *, 63 size_t, int); 64 static void ipcp_handle_dump(struct radiusd_ipcp_db_dump *, 65 size_t, int); 66 static void ipcp_handle_dump0(struct radiusd_ipcp_db_dump *, 67 size_t, struct timespec *, struct timespec *, 68 struct timespec *, int); 69 static void ipcp_handle_stat(struct radiusd_ipcp_statistics *); 70 static void ipcp_handle_jsons(struct radiusd_ipcp_db_dump *, 71 size_t, int); 72 static void ipcp_handle_json(struct radiusd_ipcp_db_dump *, 73 size_t, struct radiusd_ipcp_statistics *, int); 74 static void ipcp_handle_json0(struct radiusd_ipcp_db_dump *, 75 size_t, struct timespec *, struct timespec *, 76 struct timespec *, int); 77 78 static const char *radius_code_str(int code); 79 static const char *hexstr(const u_char *, int, char *, int); 80 static const char *sockaddr_str(struct sockaddr *, char *, size_t); 81 static const char *time_long_str(struct timespec *, char *, size_t); 82 static const char *time_short_str(struct timespec *, struct timespec *, 83 char *, size_t); 84 static const char *humanize_seconds(long, char *, size_t); 85 86 static void 87 usage(void) 88 { 89 extern char *__progname; 90 91 fprintf(stderr, "usage: %s command [argument ...]\n", __progname); 92 } 93 94 int 95 main(int argc, char *argv[]) 96 { 97 int ch, sock, done = 0; 98 ssize_t n; 99 struct parse_result *res; 100 struct sockaddr_un sun; 101 struct imsgbuf ibuf; 102 struct imsg imsg; 103 struct iovec iov[5]; 104 int niov = 0, cnt = 0; 105 char module_name[RADIUSD_MODULE_NAME_LEN + 1]; 106 107 while ((ch = getopt(argc, argv, "")) != -1) 108 switch (ch) { 109 default: 110 usage(); 111 return (EXIT_FAILURE); 112 } 113 argc -= optind; 114 argv += optind; 115 116 if (unveil(RADIUSD_SOCK, "rw") == -1) 117 err(EX_OSERR, "unveil"); 118 if (pledge("stdio unix rpath dns inet", NULL) == -1) 119 err(EX_OSERR, "pledge"); 120 121 res = parse(argc, argv); 122 if (res == NULL) 123 exit(EX_USAGE); 124 125 switch (res->action) { 126 default: 127 break; 128 case NONE: 129 exit(EXIT_SUCCESS); 130 break; 131 case TEST: 132 if (pledge("stdio dns inet", NULL) == -1) 133 err(EXIT_FAILURE, "pledge"); 134 exit(radius_test(res)); 135 break; 136 } 137 138 if (pledge("stdio unix rpath", NULL) == -1) 139 err(EX_OSERR, "pledge"); 140 141 memset(&sun, 0, sizeof(sun)); 142 sun.sun_family = AF_UNIX; 143 sun.sun_len = sizeof(sun); 144 strlcpy(sun.sun_path, RADIUSD_SOCK, sizeof(sun.sun_path)); 145 146 if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) 147 err(EX_OSERR, "socket"); 148 if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) 149 err(EX_OSERR, "connect"); 150 if (imsgbuf_init(&ibuf, sock) == -1) 151 err(EX_OSERR, "imsgbuf_init"); 152 153 res = parse(argc, argv); 154 if (res == NULL) 155 exit(EX_USAGE); 156 157 switch (res->action) { 158 case TEST: 159 case NONE: 160 abort(); 161 break; 162 case IPCP_SHOW: 163 case IPCP_DUMP: 164 case IPCP_MONITOR: 165 memset(module_name, 0, sizeof(module_name)); 166 strlcpy(module_name, "ipcp", 167 sizeof(module_name)); 168 iov[niov].iov_base = module_name; 169 iov[niov++].iov_len = RADIUSD_MODULE_NAME_LEN; 170 imsg_composev(&ibuf, (res->action == IPCP_MONITOR)? 171 IMSG_RADIUSD_MODULE_IPCP_MONITOR : 172 IMSG_RADIUSD_MODULE_IPCP_DUMP, 0, 0, -1, iov, niov); 173 break; 174 case IPCP_DELETE: 175 case IPCP_DISCONNECT: 176 memset(module_name, 0, sizeof(module_name)); 177 strlcpy(module_name, "ipcp", 178 sizeof(module_name)); 179 iov[niov].iov_base = module_name; 180 iov[niov++].iov_len = RADIUSD_MODULE_NAME_LEN; 181 iov[niov].iov_base = &res->session_seq; 182 iov[niov++].iov_len = sizeof(res->session_seq); 183 imsg_composev(&ibuf, 184 (res->action == IPCP_DELETE) 185 ? IMSG_RADIUSD_MODULE_IPCP_DELETE 186 : IMSG_RADIUSD_MODULE_IPCP_DISCONNECT, 0, 0, -1, iov, niov); 187 break; 188 } 189 if (imsgbuf_flush(&ibuf) == -1) 190 err(1, "ibuf_ctl: imsgbuf_flush error"); 191 while (!done) { 192 if (imsgbuf_read(&ibuf) != 1) 193 break; 194 for (;;) { 195 if ((n = imsg_get(&ibuf, &imsg)) <= 0) { 196 if (n != 0) 197 done = 1; 198 break; 199 } 200 switch (res->action) { 201 case IPCP_SHOW: 202 case IPCP_DUMP: 203 case IPCP_MONITOR: 204 case IPCP_DELETE: 205 case IPCP_DISCONNECT: 206 done = ipcp_handle_imsg(res, &imsg, cnt++); 207 break; 208 default: 209 break; 210 } 211 imsg_free(&imsg); 212 if (done) 213 break; 214 215 } 216 } 217 close(sock); 218 219 exit(EXIT_SUCCESS); 220 } 221 222 /*********************************************************************** 223 * "test" 224 ***********************************************************************/ 225 struct radius_test { 226 const struct parse_result *res; 227 int ecode; 228 229 RADIUS_PACKET *reqpkt; 230 int sock; 231 unsigned int tries; 232 struct event ev_send; 233 struct event ev_recv; 234 struct event ev_timedout; 235 }; 236 237 static void radius_test_send(int, short, void *); 238 static void radius_test_recv(int, short, void *); 239 static void radius_test_timedout(int, short, void *); 240 241 static int 242 radius_test(struct parse_result *res) 243 { 244 struct radius_test test = { .res = res }; 245 RADIUS_PACKET *reqpkt; 246 struct addrinfo hints, *ai; 247 int sock, retval; 248 struct sockaddr_storage sockaddr; 249 socklen_t sockaddrlen; 250 struct sockaddr_in *sin4; 251 struct sockaddr_in6 *sin6; 252 uint32_t u32val; 253 uint8_t id; 254 255 reqpkt = radius_new_request_packet(RADIUS_CODE_ACCESS_REQUEST); 256 if (reqpkt == NULL) 257 err(1, "radius_new_request_packet"); 258 id = arc4random(); 259 radius_set_id(reqpkt, id); 260 261 memset(&hints, 0, sizeof(hints)); 262 hints.ai_family = PF_UNSPEC; 263 hints.ai_socktype = SOCK_DGRAM; 264 265 retval = getaddrinfo(res->hostname, "radius", &hints, &ai); 266 if (retval) 267 errx(1, "%s %s", res->hostname, gai_strerror(retval)); 268 269 if (res->port != 0) 270 ((struct sockaddr_in *)ai->ai_addr)->sin_port = 271 htons(res->port); 272 273 sock = socket(ai->ai_family, ai->ai_socktype | SOCK_NONBLOCK, 274 ai->ai_protocol); 275 if (sock == -1) 276 err(1, "socket"); 277 278 /* Prepare NAS-IP{,V6}-ADDRESS attribute */ 279 if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) 280 err(1, "connect"); 281 sockaddrlen = sizeof(sockaddr); 282 if (getsockname(sock, (struct sockaddr *)&sockaddr, &sockaddrlen) == -1) 283 err(1, "getsockname"); 284 sin4 = (struct sockaddr_in *)&sockaddr; 285 sin6 = (struct sockaddr_in6 *)&sockaddr; 286 switch (sockaddr.ss_family) { 287 case AF_INET: 288 radius_put_ipv4_attr(reqpkt, RADIUS_TYPE_NAS_IP_ADDRESS, 289 sin4->sin_addr); 290 break; 291 case AF_INET6: 292 radius_put_raw_attr(reqpkt, RADIUS_TYPE_NAS_IPV6_ADDRESS, 293 sin6->sin6_addr.s6_addr, sizeof(sin6->sin6_addr.s6_addr)); 294 break; 295 } 296 297 /* User-Name and User-Password */ 298 radius_put_string_attr(reqpkt, RADIUS_TYPE_USER_NAME, 299 res->username); 300 301 switch (res->auth_method) { 302 case PAP: 303 if (res->password != NULL) 304 radius_put_user_password_attr(reqpkt, res->password, 305 res->secret); 306 break; 307 case CHAP: 308 { 309 u_char chal[16]; 310 u_char resp[1 + MD5_DIGEST_LENGTH]; /* "1 + " for CHAP Id */ 311 MD5_CTX md5ctx; 312 313 arc4random_buf(chal, sizeof(chal)); 314 arc4random_buf(resp, 1); /* CHAP Id is random */ 315 MD5Init(&md5ctx); 316 MD5Update(&md5ctx, resp, 1); 317 if (res->password != NULL) 318 MD5Update(&md5ctx, res->password, 319 strlen(res->password)); 320 MD5Update(&md5ctx, chal, sizeof(chal)); 321 MD5Final(resp + 1, &md5ctx); 322 radius_put_raw_attr(reqpkt, RADIUS_TYPE_CHAP_CHALLENGE, 323 chal, sizeof(chal)); 324 radius_put_raw_attr(reqpkt, RADIUS_TYPE_CHAP_PASSWORD, 325 resp, sizeof(resp)); 326 } 327 break; 328 case MSCHAPV2: 329 { 330 u_char pass[256], chal[16]; 331 u_int i, lpass; 332 struct _resp { 333 u_int8_t ident; 334 u_int8_t flags; 335 char peer_challenge[16]; 336 char reserved[8]; 337 char response[24]; 338 } __packed resp; 339 340 if (res->password == NULL) { 341 lpass = 0; 342 } else { 343 lpass = strlen(res->password); 344 if (lpass * 2 >= sizeof(pass)) 345 err(1, "password too long"); 346 for (i = 0; i < lpass; i++) { 347 pass[i * 2] = res->password[i]; 348 pass[i * 2 + 1] = 0; 349 } 350 } 351 352 memset(&resp, 0, sizeof(resp)); 353 resp.ident = arc4random(); 354 arc4random_buf(chal, sizeof(chal)); 355 arc4random_buf(resp.peer_challenge, 356 sizeof(resp.peer_challenge)); 357 358 mschap_nt_response(chal, resp.peer_challenge, 359 (char *)res->username, strlen(res->username), pass, 360 lpass * 2, resp.response); 361 362 radius_put_vs_raw_attr(reqpkt, RADIUS_VENDOR_MICROSOFT, 363 RADIUS_VTYPE_MS_CHAP_CHALLENGE, chal, sizeof(chal)); 364 radius_put_vs_raw_attr(reqpkt, RADIUS_VENDOR_MICROSOFT, 365 RADIUS_VTYPE_MS_CHAP2_RESPONSE, &resp, sizeof(resp)); 366 explicit_bzero(pass, sizeof(pass)); 367 } 368 break; 369 370 } 371 u32val = htonl(res->nas_port); 372 radius_put_raw_attr(reqpkt, RADIUS_TYPE_NAS_PORT, &u32val, 4); 373 374 if (res->msgauth) 375 radius_put_message_authenticator(reqpkt, res->secret); 376 377 event_init(); 378 379 test.ecode = EXIT_FAILURE; 380 test.res = res; 381 test.sock = sock; 382 test.reqpkt = reqpkt; 383 384 event_set(&test.ev_recv, sock, EV_READ|EV_PERSIST, 385 radius_test_recv, &test); 386 387 evtimer_set(&test.ev_send, radius_test_send, &test); 388 evtimer_set(&test.ev_timedout, radius_test_timedout, &test); 389 390 event_add(&test.ev_recv, NULL); 391 evtimer_add(&test.ev_timedout, &res->maxwait); 392 393 /* Send! */ 394 fprintf(stderr, "Sending:\n"); 395 radius_dump(stdout, reqpkt, false, res->secret); 396 radius_test_send(0, EV_TIMEOUT, &test); 397 398 event_dispatch(); 399 400 /* Release the resources */ 401 radius_delete_packet(reqpkt); 402 close(sock); 403 freeaddrinfo(ai); 404 405 explicit_bzero((char *)res->secret, strlen(res->secret)); 406 if (res->password) 407 explicit_bzero((char *)res->password, strlen(res->password)); 408 409 return (test.ecode); 410 } 411 412 static void 413 radius_test_send(int thing, short revents, void *arg) 414 { 415 struct radius_test *test = arg; 416 RADIUS_PACKET *reqpkt = test->reqpkt; 417 ssize_t rv; 418 419 retry: 420 rv = send(test->sock, 421 radius_get_data(reqpkt), radius_get_length(reqpkt), 0); 422 if (rv == -1) { 423 switch (errno) { 424 case EINTR: 425 case EAGAIN: 426 goto retry; 427 default: 428 break; 429 } 430 431 warn("send"); 432 } 433 434 if (++test->tries >= test->res->tries) 435 return; 436 437 evtimer_add(&test->ev_send, &test->res->interval); 438 } 439 440 static void 441 radius_test_recv(int sock, short revents, void *arg) 442 { 443 struct radius_test *test = arg; 444 RADIUS_PACKET *respkt; 445 RADIUS_PACKET *reqpkt = test->reqpkt; 446 447 retry: 448 respkt = radius_recv(sock, 0); 449 if (respkt == NULL) { 450 switch (errno) { 451 case EINTR: 452 case EAGAIN: 453 goto retry; 454 default: 455 break; 456 } 457 458 warn("recv"); 459 return; 460 } 461 462 radius_set_request_packet(respkt, reqpkt); 463 if (radius_get_id(respkt) == radius_get_id(reqpkt)) { 464 fprintf(stderr, "\nReceived:\n"); 465 radius_dump(stdout, respkt, true, test->res->secret); 466 467 event_del(&test->ev_recv); 468 evtimer_del(&test->ev_send); 469 evtimer_del(&test->ev_timedout); 470 test->ecode = EXIT_SUCCESS; 471 } 472 473 radius_delete_packet(respkt); 474 } 475 476 static void 477 radius_test_timedout(int thing, short revents, void *arg) 478 { 479 struct radius_test *test = arg; 480 481 event_del(&test->ev_recv); 482 } 483 484 static void 485 radius_dump(FILE *out, RADIUS_PACKET *pkt, bool resp, const char *secret) 486 { 487 size_t len; 488 char buf[256], buf1[256]; 489 uint32_t u32val; 490 struct in_addr ipv4; 491 492 fprintf(out, 493 " Id = %d\n" 494 " Code = %s(%d)\n", 495 (int)radius_get_id(pkt), radius_code_str((int)radius_get_code(pkt)), 496 (int)radius_get_code(pkt)); 497 if (resp && secret) { 498 fprintf(out, " Authenticator = %s\n", 499 (radius_check_response_authenticator(pkt, secret) == 0) 500 ? "Verified" : "NG"); 501 fprintf(out, " Message-Authenticator = %s\n", 502 (!radius_has_attr(pkt, RADIUS_TYPE_MESSAGE_AUTHENTICATOR)) 503 ? "(Not present)" 504 : (radius_check_message_authenticator(pkt, secret) == 0) 505 ? "Verified" : "NG"); 506 } 507 if (!resp) 508 fprintf(out, " Message-Authenticator = %s\n", 509 (radius_has_attr(pkt, RADIUS_TYPE_MESSAGE_AUTHENTICATOR)) 510 ? "(Present)" : "(Not present)"); 511 512 if (radius_get_string_attr(pkt, RADIUS_TYPE_USER_NAME, buf, 513 sizeof(buf)) == 0) 514 fprintf(out, " User-Name = \"%s\"\n", buf); 515 516 if (secret && 517 radius_get_user_password_attr(pkt, buf, sizeof(buf), secret) == 0) 518 fprintf(out, " User-Password = \"%s\"\n", buf); 519 520 memset(buf, 0, sizeof(buf)); 521 len = sizeof(buf); 522 if (radius_get_raw_attr(pkt, RADIUS_TYPE_CHAP_PASSWORD, buf, &len) 523 == 0) 524 fprintf(out, " CHAP-Password = %s\n", 525 (hexstr(buf, len, buf1, sizeof(buf1))) 526 ? buf1 : "(too long)"); 527 528 memset(buf, 0, sizeof(buf)); 529 len = sizeof(buf); 530 if (radius_get_raw_attr(pkt, RADIUS_TYPE_CHAP_CHALLENGE, buf, &len) 531 == 0) 532 fprintf(out, " CHAP-Challenge = %s\n", 533 (hexstr(buf, len, buf1, sizeof(buf1))) 534 ? buf1 : "(too long)"); 535 536 memset(buf, 0, sizeof(buf)); 537 len = sizeof(buf); 538 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 539 RADIUS_VTYPE_MS_CHAP_CHALLENGE, buf, &len) == 0) 540 fprintf(out, " MS-CHAP-Challenge = %s\n", 541 (hexstr(buf, len, buf1, sizeof(buf1))) 542 ? buf1 : "(too long)"); 543 544 memset(buf, 0, sizeof(buf)); 545 len = sizeof(buf); 546 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 547 RADIUS_VTYPE_MS_CHAP2_RESPONSE, buf, &len) == 0) 548 fprintf(out, " MS-CHAP2-Response = %s\n", 549 (hexstr(buf, len, buf1, sizeof(buf1))) 550 ? buf1 : "(too long)"); 551 552 memset(buf, 0, sizeof(buf)); 553 len = sizeof(buf) - 1; 554 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 555 RADIUS_VTYPE_MS_CHAP2_SUCCESS, buf, &len) == 0) { 556 fprintf(out, " MS-CHAP-Success = Id=%u \"%s\"\n", 557 (u_int)(u_char)buf[0], buf + 1); 558 } 559 560 memset(buf, 0, sizeof(buf)); 561 len = sizeof(buf) - 1; 562 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 563 RADIUS_VTYPE_MS_CHAP_ERROR, buf, &len) == 0) { 564 fprintf(out, " MS-CHAP-Error = Id=%u \"%s\"\n", 565 (u_int)(u_char)buf[0], buf + 1); 566 } 567 568 memset(buf, 0, sizeof(buf)); 569 len = sizeof(buf); 570 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 571 RADIUS_VTYPE_MPPE_SEND_KEY, buf, &len) == 0) 572 fprintf(out, " MS-MPPE-Send-Key = %s\n", 573 (hexstr(buf, len, buf1, sizeof(buf1))) 574 ? buf1 : "(too long)"); 575 576 memset(buf, 0, sizeof(buf)); 577 len = sizeof(buf); 578 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 579 RADIUS_VTYPE_MPPE_RECV_KEY, buf, &len) == 0) 580 fprintf(out, " MS-MPPE-Recv-Key = %s\n", 581 (hexstr(buf, len, buf1, sizeof(buf1))) 582 ? buf1 : "(too long)"); 583 584 memset(buf, 0, sizeof(buf)); 585 len = sizeof(buf); 586 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 587 RADIUS_VTYPE_MPPE_ENCRYPTION_POLICY, buf, &len) == 0) 588 fprintf(out, " MS-MPPE-Encryption-Policy = 0x%08x\n", 589 ntohl(*(u_long *)buf)); 590 591 memset(buf, 0, sizeof(buf)); 592 len = sizeof(buf); 593 if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 594 RADIUS_VTYPE_MPPE_ENCRYPTION_TYPES, buf, &len) == 0) 595 fprintf(out, " MS-MPPE-Encryption-Types = 0x%08x\n", 596 ntohl(*(u_long *)buf)); 597 598 if (radius_get_string_attr(pkt, RADIUS_TYPE_REPLY_MESSAGE, buf, 599 sizeof(buf)) == 0) 600 fprintf(out, " Reply-Message = \"%s\"\n", buf); 601 602 memset(buf, 0, sizeof(buf)); 603 len = sizeof(buf); 604 if (radius_get_uint32_attr(pkt, RADIUS_TYPE_NAS_PORT, &u32val) == 0) 605 fprintf(out, " NAS-Port = %lu\n", 606 (u_long)u32val); 607 608 memset(buf, 0, sizeof(buf)); 609 len = sizeof(buf); 610 if (radius_get_ipv4_attr(pkt, RADIUS_TYPE_NAS_IP_ADDRESS, &ipv4) == 0) 611 fprintf(out, " NAS-IP-Address = %s\n", 612 inet_ntoa(ipv4)); 613 614 memset(buf, 0, sizeof(buf)); 615 len = sizeof(buf); 616 if (radius_get_raw_attr(pkt, RADIUS_TYPE_NAS_IPV6_ADDRESS, buf, &len) 617 == 0) 618 fprintf(out, " NAS-IPv6-Address = %s\n", 619 inet_ntop(AF_INET6, buf, buf1, len)); 620 621 } 622 623 /*********************************************************************** 624 * ipcp 625 ***********************************************************************/ 626 int 627 ipcp_handle_imsg(struct parse_result *res, struct imsg *imsg, int cnt) 628 { 629 ssize_t datalen; 630 struct radiusd_ipcp_db_dump *dump; 631 struct radiusd_ipcp_statistics *stat; 632 int done = 0; 633 634 datalen = imsg->hdr.len - IMSG_HEADER_SIZE; 635 switch (imsg->hdr.type) { 636 case IMSG_OK: 637 if (datalen > 0 && *((char *)imsg->data + datalen - 1) == '\0') 638 fprintf(stderr, "OK: %s\n", (char *)imsg->data); 639 else 640 fprintf(stderr, "OK\n"); 641 done = 1; 642 break; 643 case IMSG_NG: 644 if (datalen > 0 && *((char *)imsg->data + datalen - 1) == '\0') 645 fprintf(stderr, "error: %s\n", (char *)imsg->data); 646 else 647 fprintf(stderr, "error\n"); 648 exit(EXIT_FAILURE); 649 case IMSG_RADIUSD_MODULE_IPCP_DUMP: 650 if ((size_t)datalen < sizeof(struct 651 radiusd_ipcp_db_dump)) 652 errx(1, "received a message which size is invalid"); 653 dump = imsg->data; 654 if (res->action == IPCP_SHOW) 655 ipcp_handle_show(dump, datalen, (cnt++ == 0)? 1 : 0); 656 else { 657 if (res->flags & FLAGS_JSON) 658 ipcp_handle_jsons(dump, datalen, 659 (cnt++ == 0)? 1 : 0); 660 else 661 ipcp_handle_dumps(dump, datalen, 662 (cnt++ == 0)? 1 : 0); 663 } 664 if (dump->islast && 665 (res->action == IPCP_SHOW || res->action == IPCP_DUMP)) 666 done = 1; 667 break; 668 case IMSG_RADIUSD_MODULE_IPCP_START: 669 if ((size_t)datalen < offsetof(struct 670 radiusd_ipcp_db_dump, records[1])) 671 errx(1, "received a message which size is invalid"); 672 dump = imsg->data; 673 if (res->flags & FLAGS_JSON) 674 ipcp_handle_json(dump, datalen, NULL, 0); 675 else { 676 printf("Start\n"); 677 ipcp_handle_dump(dump, datalen, 0); 678 } 679 break; 680 case IMSG_RADIUSD_MODULE_IPCP_STOP: 681 if ((size_t)datalen < offsetof( 682 struct radiusd_ipcp_db_dump, 683 records[1]) + 684 sizeof(struct 685 radiusd_ipcp_statistics)) 686 errx(1, "received a message which size is invalid"); 687 dump = imsg->data; 688 stat = (struct radiusd_ipcp_statistics *) 689 ((char *)imsg->data + offsetof( 690 struct radiusd_ipcp_db_dump, records[1])); 691 if (res->flags & FLAGS_JSON) 692 ipcp_handle_json(dump, datalen, stat, 0); 693 else { 694 printf("Stop\n"); 695 ipcp_handle_dump(dump, datalen, 0); 696 ipcp_handle_stat(stat); 697 } 698 break; 699 } 700 701 return (done); 702 } 703 704 static void 705 ipcp_handle_show(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, int first) 706 { 707 int i, width; 708 uint32_t maxseq = 999; 709 char buf0[128], buf1[NI_MAXHOST + NI_MAXSERV + 4], buf2[80]; 710 struct timespec upt, now, dif, start; 711 712 clock_gettime(CLOCK_BOOTTIME, &upt); 713 clock_gettime(CLOCK_REALTIME, &now); 714 timespecsub(&now, &upt, &upt); 715 716 for (i = 0; ; i++) { 717 if (offsetof(struct radiusd_ipcp_db_dump, records[i]) 718 >= dumpsiz) 719 break; 720 maxseq = MAXIMUM(maxseq, dump->records[i].rec.seq); 721 } 722 for (width = 0; maxseq != 0; maxseq /= 10, width++) 723 ; 724 725 for (i = 0; ; i++) { 726 if (offsetof(struct radiusd_ipcp_db_dump, records[i]) 727 >= dumpsiz) 728 break; 729 if (i == 0 && first) 730 printf("%-*s Assigned Username " 731 "Start Tunnel From\n" 732 "%.*s --------------- ---------------------- " 733 "-------- %.*s\n", width, "Seq", width, 734 "----------", 28 - width, 735 "-------------------------"); 736 timespecadd(&upt, &dump->records[i].rec.start, &start); 737 timespecsub(&now, &start, &dif); 738 printf("%*d %-15s %-22s %-8s %s\n", 739 width, dump->records[i].rec.seq, 740 inet_ntop(dump->records[i].af, &dump->records[i].addr, 741 buf0, sizeof(buf0)), dump->records[i].rec.username, 742 time_short_str(&start, &dif, buf2, sizeof(buf2)), 743 sockaddr_str( 744 (struct sockaddr *)&dump->records[i].rec.tun_client, buf1, 745 sizeof(buf1))); 746 } 747 } 748 static void 749 ipcp_handle_dump(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, int idx) 750 { 751 struct timespec upt, now, dif, start, timeout; 752 753 clock_gettime(CLOCK_BOOTTIME, &upt); 754 clock_gettime(CLOCK_REALTIME, &now); 755 timespecsub(&now, &upt, &upt); 756 757 timespecadd(&upt, &dump->records[idx].rec.start, &start); 758 timespecsub(&now, &start, &dif); 759 760 if (dump->records[idx].rec.start.tv_sec == 0) 761 ipcp_handle_dump0(dump, dumpsiz, &dif, &start, NULL, idx); 762 else { 763 timespecadd(&upt, &dump->records[idx].rec.timeout, &timeout); 764 ipcp_handle_dump0(dump, dumpsiz, &dif, &start, &timeout, idx); 765 } 766 } 767 768 static void 769 ipcp_handle_dump0(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, 770 struct timespec *dif, struct timespec *start, struct timespec *timeout, 771 int idx) 772 { 773 char buf0[128], buf1[NI_MAXHOST + NI_MAXSERV + 4], buf2[80]; 774 775 printf( 776 " Sequence Number : %u\n" 777 " Session Id : %s\n" 778 " Username : %s\n" 779 " Auth Method : %s\n" 780 " Assigned IP Address : %s\n" 781 " Start Time : %s\n" 782 " Elapsed Time : %lld second%s%s\n", 783 dump->records[idx].rec.seq, dump->records[idx].rec.session_id, 784 dump->records[idx].rec.username, dump->records[idx].rec.auth_method, 785 inet_ntop(dump->records[idx].af, &dump->records[idx].addr, buf0, 786 sizeof(buf0)), time_long_str(start, buf1, sizeof(buf1)), 787 (long long)dif->tv_sec, (dif->tv_sec == 0)? "" : "s", 788 humanize_seconds(dif->tv_sec, buf2, sizeof(buf2))); 789 if (timeout != NULL) 790 printf(" Timeout : %s\n", 791 time_long_str(timeout, buf0, sizeof(buf0))); 792 printf( 793 " NAS Identifier : %s\n" 794 " Tunnel Type : %s\n" 795 " Tunnel From : %s\n", 796 dump->records[idx].rec.nas_id, dump->records[idx].rec.tun_type, 797 sockaddr_str((struct sockaddr *) 798 &dump->records[idx].rec.tun_client, buf1, sizeof(buf1))); 799 } 800 801 void 802 ipcp_handle_stat(struct radiusd_ipcp_statistics *stat) 803 { 804 printf( 805 " Terminate Cause : %s\n" 806 " Input Packets : %"PRIu32"\n" 807 " Output Packets : %"PRIu32"\n" 808 " Input Bytes : %"PRIu64"\n" 809 " Output Bytes : %"PRIu64"\n", 810 stat->cause, stat->ipackets, stat->opackets, stat->ibytes, 811 stat->obytes); 812 } 813 814 static void 815 ipcp_handle_jsons(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, int first) 816 { 817 int i; 818 struct timespec upt, now, dif, start, timeout; 819 820 clock_gettime(CLOCK_BOOTTIME, &upt); 821 clock_gettime(CLOCK_REALTIME, &now); 822 timespecsub(&now, &upt, &upt); 823 824 for (i = 0; ; i++) { 825 if (offsetof(struct radiusd_ipcp_db_dump, records[i]) 826 >= dumpsiz) 827 break; 828 timespecadd(&upt, &dump->records[i].rec.start, &start); 829 timespecsub(&now, &start, &dif); 830 json_do_start(stdout); 831 json_do_string("action", "start"); 832 if (dump->records[i].rec.timeout.tv_sec == 0) 833 ipcp_handle_json0(dump, dumpsiz, &dif, &start, NULL, i); 834 else { 835 timespecadd(&upt, &dump->records[i].rec.timeout, 836 &timeout); 837 ipcp_handle_json0(dump, dumpsiz, &dif, &start, &timeout, 838 i); 839 } 840 json_do_finish(); 841 } 842 fflush(stdout); 843 } 844 845 static void 846 ipcp_handle_json(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, 847 struct radiusd_ipcp_statistics *stat, int idx) 848 { 849 struct timespec upt, now, dif, start, timeout; 850 851 json_do_start(stdout); 852 clock_gettime(CLOCK_BOOTTIME, &upt); 853 clock_gettime(CLOCK_REALTIME, &now); 854 timespecsub(&now, &upt, &upt); 855 timespecadd(&upt, &dump->records[idx].rec.start, &start); 856 timespecsub(&now, &start, &dif); 857 858 if (stat == NULL) 859 json_do_string("action", "start"); 860 else 861 json_do_string("action", "stop"); 862 if (dump->records[idx].rec.timeout.tv_sec == 0) 863 ipcp_handle_json0(dump, dumpsiz, &dif, &start, NULL, idx); 864 else { 865 timespecadd(&upt, &dump->records[idx].rec.timeout, &timeout); 866 ipcp_handle_json0(dump, dumpsiz, &dif, &start, &timeout, idx); 867 } 868 if (stat != NULL) { 869 json_do_string("terminate-cause", stat->cause); 870 json_do_uint("input-packets", stat->ipackets); 871 json_do_uint("output-packets", stat->opackets); 872 json_do_uint("input-bytes", stat->ibytes); 873 json_do_uint("output-bytes", stat->obytes); 874 } 875 json_do_finish(); 876 fflush(stdout); 877 } 878 879 static void 880 ipcp_handle_json0(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, 881 struct timespec *dif, struct timespec *start, struct timespec *timeout, 882 int idx) 883 { 884 char buf[128]; 885 886 json_do_uint("sequence-number", dump->records[idx].rec.seq); 887 json_do_string("session-id", dump->records[idx].rec.session_id); 888 json_do_string("username", dump->records[idx].rec.username); 889 json_do_string("auth-method", dump->records[idx].rec.auth_method); 890 json_do_string("assigned-ip-address", inet_ntop(dump->records[idx].af, 891 &dump->records[idx].addr, buf, sizeof(buf))); 892 json_do_uint("start", start->tv_sec); 893 json_do_uint("elapsed", dif->tv_sec); 894 if (timeout != NULL) 895 json_do_uint("timeout", timeout->tv_sec); 896 json_do_string("nas-identifier", dump->records[idx].rec.nas_id); 897 json_do_string("tunnel-type", dump->records[idx].rec.tun_type); 898 json_do_string("tunnel-from", 899 sockaddr_str((struct sockaddr *)&dump->records[idx].rec.tun_client, 900 buf, sizeof(buf))); 901 } 902 903 static void 904 ipcp_handle_dumps(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, int first) 905 { 906 static int cnt = 0; 907 int i; 908 struct timespec upt, now, dif, start, timeout; 909 910 clock_gettime(CLOCK_BOOTTIME, &upt); 911 clock_gettime(CLOCK_REALTIME, &now); 912 timespecsub(&now, &upt, &upt); 913 914 if (first) 915 cnt = 0; 916 for (i = 0; ; i++, cnt++) { 917 if (offsetof(struct radiusd_ipcp_db_dump, records[i]) 918 >= dumpsiz) 919 break; 920 timespecadd(&upt, &dump->records[i].rec.start, &start); 921 timespecsub(&now, &start, &dif); 922 printf("#%d\n", cnt + 1); 923 if (dump->records[i].rec.timeout.tv_sec == 0) 924 ipcp_handle_dump0(dump, dumpsiz, &dif, &start, NULL, i); 925 else { 926 timespecadd(&upt, &dump->records[i].rec.timeout, 927 &timeout); 928 ipcp_handle_dump0(dump, dumpsiz, &dif, &start, 929 &timeout, i); 930 } 931 } 932 } 933 934 935 /*********************************************************************** 936 * Miscellaneous functions 937 ***********************************************************************/ 938 const char * 939 radius_code_str(int code) 940 { 941 int i; 942 static struct _codestr { 943 int code; 944 const char *str; 945 } codestr[] = { 946 { RADIUS_CODE_ACCESS_REQUEST, "Access-Request" }, 947 { RADIUS_CODE_ACCESS_ACCEPT, "Access-Accept" }, 948 { RADIUS_CODE_ACCESS_REJECT, "Access-Reject" }, 949 { RADIUS_CODE_ACCOUNTING_REQUEST, "Accounting-Request" }, 950 { RADIUS_CODE_ACCOUNTING_RESPONSE, "Accounting-Response" }, 951 { RADIUS_CODE_ACCESS_CHALLENGE, "Access-Challenge" }, 952 { RADIUS_CODE_STATUS_SERVER, "Status-Server" }, 953 { RADIUS_CODE_STATUS_CLIENT, "Status-Client" }, 954 { -1, NULL } 955 }; 956 957 for (i = 0; codestr[i].code != -1; i++) { 958 if (codestr[i].code == code) 959 return (codestr[i].str); 960 } 961 962 return ("Unknown"); 963 } 964 965 static const char * 966 hexstr(const u_char *data, int len, char *str, int strsiz) 967 { 968 int i, off = 0; 969 static const char hex[] = "0123456789abcdef"; 970 971 for (i = 0; i < len; i++) { 972 if (strsiz - off < 3) 973 return (NULL); 974 str[off++] = hex[(data[i] & 0xf0) >> 4]; 975 str[off++] = hex[(data[i] & 0x0f)]; 976 str[off++] = ' '; 977 } 978 if (strsiz - off < 1) 979 return (NULL); 980 981 str[off++] = '\0'; 982 983 return (str); 984 } 985 986 const char * 987 sockaddr_str(struct sockaddr *sa, char *buf, size_t bufsiz) 988 { 989 int noport, ret; 990 char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; 991 992 if (ntohs(((struct sockaddr_in *)sa)->sin_port) == 0) { 993 noport = 1; 994 ret = getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), NULL, 0, 995 NI_NUMERICHOST); 996 } else { 997 noport = 0; 998 ret = getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), sbuf, 999 sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); 1000 } 1001 if (ret != 0) 1002 return ""; 1003 if (noport) 1004 strlcpy(buf, hbuf, bufsiz); 1005 else if (sa->sa_family == AF_INET6) 1006 snprintf(buf, bufsiz, "[%s]:%s", hbuf, sbuf); 1007 else 1008 snprintf(buf, bufsiz, "%s:%s", hbuf, sbuf); 1009 1010 return (buf); 1011 } 1012 1013 const char * 1014 time_long_str(struct timespec *tim, char *buf, size_t bufsiz) 1015 { 1016 struct tm tm; 1017 1018 localtime_r(&tim->tv_sec, &tm); 1019 strftime(buf, bufsiz, "%F %T", &tm); 1020 1021 return (buf); 1022 } 1023 1024 const char * 1025 time_short_str(struct timespec *tim, struct timespec *dif, char *buf, 1026 size_t bufsiz) 1027 { 1028 struct tm tm; 1029 1030 localtime_r(&tim->tv_sec, &tm); 1031 if (dif->tv_sec < 12 * 60 * 60) 1032 strftime(buf, bufsiz, "%l:%M%p", &tm); 1033 else if (dif->tv_sec < 7 * 24 * 60 * 60) 1034 strftime(buf, bufsiz, "%e%b%y", &tm); 1035 else 1036 strftime(buf, bufsiz, "%m/%d", &tm); 1037 1038 return (buf); 1039 } 1040 1041 const char * 1042 humanize_seconds(long seconds, char *buf, size_t bufsiz) 1043 { 1044 char fbuf[80]; 1045 int hour, min; 1046 1047 hour = seconds / 3600; 1048 min = (seconds % 3600) / 60; 1049 1050 if (bufsiz == 0) 1051 return NULL; 1052 buf[0] = '\0'; 1053 if (hour != 0 || min != 0) { 1054 strlcat(buf, " (", bufsiz); 1055 if (hour != 0) { 1056 snprintf(fbuf, sizeof(fbuf), "%d hour%s", hour, 1057 (hour == 1)? "" : "s"); 1058 strlcat(buf, fbuf, bufsiz); 1059 } 1060 if (hour != 0 && min != 0) 1061 strlcat(buf, " and ", bufsiz); 1062 if (min != 0) { 1063 snprintf(fbuf, sizeof(fbuf), "%d minute%s", min, 1064 (min == 1)? "" : "s"); 1065 strlcat(buf, fbuf, bufsiz); 1066 } 1067 strlcat(buf, ")", bufsiz); 1068 } 1069 1070 return (buf); 1071 } 1072