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