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