1*882428cdSclaudio /* $OpenBSD: radiusctl.c,v 1.17 2024/11/21 13:43:10 claudio Exp $ */ 2a7ca44b8Syasuoka /* 3a7ca44b8Syasuoka * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net> 4a7ca44b8Syasuoka * 5a7ca44b8Syasuoka * Permission to use, copy, modify, and distribute this software for any 6a7ca44b8Syasuoka * purpose with or without fee is hereby granted, provided that the above 7a7ca44b8Syasuoka * copyright notice and this permission notice appear in all copies. 8a7ca44b8Syasuoka * 9a7ca44b8Syasuoka * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10a7ca44b8Syasuoka * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11a7ca44b8Syasuoka * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12a7ca44b8Syasuoka * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13a7ca44b8Syasuoka * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14a7ca44b8Syasuoka * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15a7ca44b8Syasuoka * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16a7ca44b8Syasuoka */ 17a7ca44b8Syasuoka #include <sys/types.h> 18842565f2Syasuoka #include <sys/cdefs.h> 19a7ca44b8Syasuoka #include <sys/socket.h> 20842565f2Syasuoka #include <sys/time.h> 21842565f2Syasuoka #include <sys/uio.h> 22842565f2Syasuoka #include <sys/un.h> 23a7ca44b8Syasuoka #include <netinet/in.h> 24a7ca44b8Syasuoka #include <arpa/inet.h> 25842565f2Syasuoka 26a7ca44b8Syasuoka #include <err.h> 27842565f2Syasuoka #include <errno.h> 28842565f2Syasuoka #include <event.h> 29842565f2Syasuoka #include <imsg.h> 30842565f2Syasuoka #include <inttypes.h> 31a7ca44b8Syasuoka #include <md5.h> 32a7ca44b8Syasuoka #include <netdb.h> 33842565f2Syasuoka #include <radius.h> 34a7ca44b8Syasuoka #include <stdbool.h> 35842565f2Syasuoka #include <stddef.h> 36842565f2Syasuoka #include <stdint.h> 37a7ca44b8Syasuoka #include <stdio.h> 38a7ca44b8Syasuoka #include <stdlib.h> 39a7ca44b8Syasuoka #include <string.h> 40842565f2Syasuoka #include <sysexits.h> 41842565f2Syasuoka #include <time.h> 42a7ca44b8Syasuoka #include <unistd.h> 43a7ca44b8Syasuoka 44a7ca44b8Syasuoka #include "parser.h" 45842565f2Syasuoka #include "radiusd.h" 46842565f2Syasuoka #include "radiusd_ipcp.h" 47a7ca44b8Syasuoka #include "chap_ms.h" 48842565f2Syasuoka #include "json.h" 49a7ca44b8Syasuoka 50842565f2Syasuoka #ifndef MAXIMUM 51842565f2Syasuoka #define MAXIMUM(_a, _b) (((_a) > (_b))? (_a) : (_b)) 52842565f2Syasuoka #endif 53a7ca44b8Syasuoka 545d013a5eSdlg static int radius_test(struct parse_result *); 55a7ca44b8Syasuoka static void radius_dump(FILE *, RADIUS_PACKET *, bool, 56a7ca44b8Syasuoka const char *); 57842565f2Syasuoka 58842565f2Syasuoka static int ipcp_handle_imsg(struct parse_result *, struct imsg *, 59842565f2Syasuoka int); 60842565f2Syasuoka static void ipcp_handle_show(struct radiusd_ipcp_db_dump *, 61842565f2Syasuoka size_t, int); 62842565f2Syasuoka static void ipcp_handle_dumps(struct radiusd_ipcp_db_dump *, 63842565f2Syasuoka size_t, int); 64842565f2Syasuoka static void ipcp_handle_dump(struct radiusd_ipcp_db_dump *, 65842565f2Syasuoka size_t, int); 66842565f2Syasuoka static void ipcp_handle_dump0(struct radiusd_ipcp_db_dump *, 67842565f2Syasuoka size_t, struct timespec *, struct timespec *, 68842565f2Syasuoka struct timespec *, int); 69842565f2Syasuoka static void ipcp_handle_stat(struct radiusd_ipcp_statistics *); 70842565f2Syasuoka static void ipcp_handle_jsons(struct radiusd_ipcp_db_dump *, 71842565f2Syasuoka size_t, int); 72842565f2Syasuoka static void ipcp_handle_json(struct radiusd_ipcp_db_dump *, 73842565f2Syasuoka size_t, struct radiusd_ipcp_statistics *, int); 74842565f2Syasuoka static void ipcp_handle_json0(struct radiusd_ipcp_db_dump *, 75842565f2Syasuoka size_t, struct timespec *, struct timespec *, 76842565f2Syasuoka struct timespec *, int); 77842565f2Syasuoka 78a7ca44b8Syasuoka static const char *radius_code_str(int code); 79a7ca44b8Syasuoka static const char *hexstr(const u_char *, int, char *, int); 80842565f2Syasuoka static const char *sockaddr_str(struct sockaddr *, char *, size_t); 81842565f2Syasuoka static const char *time_long_str(struct timespec *, char *, size_t); 82842565f2Syasuoka static const char *time_short_str(struct timespec *, struct timespec *, 83842565f2Syasuoka char *, size_t); 84842565f2Syasuoka static const char *humanize_seconds(long, char *, size_t); 85a7ca44b8Syasuoka 86a7ca44b8Syasuoka static void 87a7ca44b8Syasuoka usage(void) 88a7ca44b8Syasuoka { 89a7ca44b8Syasuoka extern char *__progname; 90a7ca44b8Syasuoka 91de90af90Syasuoka fprintf(stderr, "usage: %s command [argument ...]\n", __progname); 92a7ca44b8Syasuoka } 93a7ca44b8Syasuoka 94a7ca44b8Syasuoka int 95a7ca44b8Syasuoka main(int argc, char *argv[]) 96a7ca44b8Syasuoka { 97842565f2Syasuoka int ch, sock, done = 0; 98842565f2Syasuoka ssize_t n; 99842565f2Syasuoka struct parse_result *res; 100842565f2Syasuoka struct sockaddr_un sun; 101842565f2Syasuoka struct imsgbuf ibuf; 102842565f2Syasuoka struct imsg imsg; 103842565f2Syasuoka struct iovec iov[5]; 104842565f2Syasuoka int niov = 0, cnt = 0; 105842565f2Syasuoka char module_name[RADIUSD_MODULE_NAME_LEN + 1]; 106a7ca44b8Syasuoka 10783aeedf6Syasuoka while ((ch = getopt(argc, argv, "")) != -1) 108a7ca44b8Syasuoka switch (ch) { 10983aeedf6Syasuoka default: 110a7ca44b8Syasuoka usage(); 111ef9ad095Smillert return (EXIT_FAILURE); 112a7ca44b8Syasuoka } 113a7ca44b8Syasuoka argc -= optind; 114a7ca44b8Syasuoka argv += optind; 115a7ca44b8Syasuoka 116842565f2Syasuoka if (unveil(RADIUSD_SOCK, "rw") == -1) 117842565f2Syasuoka err(EX_OSERR, "unveil"); 118842565f2Syasuoka if (pledge("stdio unix rpath dns inet", NULL) == -1) 119842565f2Syasuoka err(EX_OSERR, "pledge"); 120a7ca44b8Syasuoka 121842565f2Syasuoka res = parse(argc, argv); 122842565f2Syasuoka if (res == NULL) 123842565f2Syasuoka exit(EX_USAGE); 124842565f2Syasuoka 125842565f2Syasuoka switch (res->action) { 126842565f2Syasuoka default: 127842565f2Syasuoka break; 128a7ca44b8Syasuoka case NONE: 129842565f2Syasuoka exit(EXIT_SUCCESS); 130a7ca44b8Syasuoka break; 131a7ca44b8Syasuoka case TEST: 13204581dc7Syasuoka if (pledge("stdio dns inet", NULL) == -1) 13304581dc7Syasuoka err(EXIT_FAILURE, "pledge"); 134842565f2Syasuoka exit(radius_test(res)); 135a7ca44b8Syasuoka break; 136a7ca44b8Syasuoka } 137a7ca44b8Syasuoka 138842565f2Syasuoka if (pledge("stdio unix rpath", NULL) == -1) 139842565f2Syasuoka err(EX_OSERR, "pledge"); 140842565f2Syasuoka 141842565f2Syasuoka memset(&sun, 0, sizeof(sun)); 142842565f2Syasuoka sun.sun_family = AF_UNIX; 143842565f2Syasuoka sun.sun_len = sizeof(sun); 144842565f2Syasuoka strlcpy(sun.sun_path, RADIUSD_SOCK, sizeof(sun.sun_path)); 145842565f2Syasuoka 146842565f2Syasuoka if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) 147842565f2Syasuoka err(EX_OSERR, "socket"); 148842565f2Syasuoka if (connect(sock, (struct sockaddr *)&sun, sizeof(sun)) == -1) 149842565f2Syasuoka err(EX_OSERR, "connect"); 150*882428cdSclaudio if (imsgbuf_init(&ibuf, sock) == -1) 151*882428cdSclaudio err(EX_OSERR, "imsgbuf_init"); 152842565f2Syasuoka 153842565f2Syasuoka res = parse(argc, argv); 154842565f2Syasuoka if (res == NULL) 155842565f2Syasuoka exit(EX_USAGE); 156842565f2Syasuoka 157842565f2Syasuoka switch (res->action) { 158842565f2Syasuoka case TEST: 159842565f2Syasuoka case NONE: 160842565f2Syasuoka abort(); 161842565f2Syasuoka break; 162842565f2Syasuoka case IPCP_SHOW: 163842565f2Syasuoka case IPCP_DUMP: 164842565f2Syasuoka case IPCP_MONITOR: 165842565f2Syasuoka memset(module_name, 0, sizeof(module_name)); 166842565f2Syasuoka strlcpy(module_name, "ipcp", 167842565f2Syasuoka sizeof(module_name)); 168842565f2Syasuoka iov[niov].iov_base = module_name; 169842565f2Syasuoka iov[niov++].iov_len = RADIUSD_MODULE_NAME_LEN; 170842565f2Syasuoka imsg_composev(&ibuf, (res->action == IPCP_MONITOR)? 171842565f2Syasuoka IMSG_RADIUSD_MODULE_IPCP_MONITOR : 172842565f2Syasuoka IMSG_RADIUSD_MODULE_IPCP_DUMP, 0, 0, -1, iov, niov); 173842565f2Syasuoka break; 174eff8f878Syasuoka case IPCP_DELETE: 175842565f2Syasuoka case IPCP_DISCONNECT: 176842565f2Syasuoka memset(module_name, 0, sizeof(module_name)); 177842565f2Syasuoka strlcpy(module_name, "ipcp", 178842565f2Syasuoka sizeof(module_name)); 179842565f2Syasuoka iov[niov].iov_base = module_name; 180842565f2Syasuoka iov[niov++].iov_len = RADIUSD_MODULE_NAME_LEN; 181842565f2Syasuoka iov[niov].iov_base = &res->session_seq; 182842565f2Syasuoka iov[niov++].iov_len = sizeof(res->session_seq); 183eff8f878Syasuoka imsg_composev(&ibuf, 184eff8f878Syasuoka (res->action == IPCP_DELETE) 185eff8f878Syasuoka ? IMSG_RADIUSD_MODULE_IPCP_DELETE 186eff8f878Syasuoka : IMSG_RADIUSD_MODULE_IPCP_DISCONNECT, 0, 0, -1, iov, niov); 187842565f2Syasuoka break; 188842565f2Syasuoka } 189dd7efffeSclaudio if (imsgbuf_flush(&ibuf) == -1) 190dd7efffeSclaudio err(1, "ibuf_ctl: imsgbuf_flush error"); 191842565f2Syasuoka while (!done) { 1924f3fb1ffSclaudio if (imsgbuf_read(&ibuf) != 1) 193842565f2Syasuoka break; 194842565f2Syasuoka for (;;) { 195842565f2Syasuoka if ((n = imsg_get(&ibuf, &imsg)) <= 0) { 196842565f2Syasuoka if (n != 0) 197842565f2Syasuoka done = 1; 198842565f2Syasuoka break; 199842565f2Syasuoka } 200842565f2Syasuoka switch (res->action) { 201842565f2Syasuoka case IPCP_SHOW: 202842565f2Syasuoka case IPCP_DUMP: 203842565f2Syasuoka case IPCP_MONITOR: 204eff8f878Syasuoka case IPCP_DELETE: 205c0127aecSyasuoka case IPCP_DISCONNECT: 206842565f2Syasuoka done = ipcp_handle_imsg(res, &imsg, cnt++); 207842565f2Syasuoka break; 208842565f2Syasuoka default: 209842565f2Syasuoka break; 210842565f2Syasuoka } 211842565f2Syasuoka imsg_free(&imsg); 212842565f2Syasuoka if (done) 213842565f2Syasuoka break; 214842565f2Syasuoka 215842565f2Syasuoka } 216842565f2Syasuoka } 217842565f2Syasuoka close(sock); 218842565f2Syasuoka 219842565f2Syasuoka exit(EXIT_SUCCESS); 220a7ca44b8Syasuoka } 221a7ca44b8Syasuoka 222842565f2Syasuoka /*********************************************************************** 223842565f2Syasuoka * "test" 224842565f2Syasuoka ***********************************************************************/ 2255d013a5eSdlg struct radius_test { 2265d013a5eSdlg const struct parse_result *res; 2275d013a5eSdlg int ecode; 2285d013a5eSdlg 2295d013a5eSdlg RADIUS_PACKET *reqpkt; 2305d013a5eSdlg int sock; 2315d013a5eSdlg unsigned int tries; 2325d013a5eSdlg struct event ev_send; 2335d013a5eSdlg struct event ev_recv; 2345d013a5eSdlg struct event ev_timedout; 2355d013a5eSdlg }; 2365d013a5eSdlg 2375d013a5eSdlg static void radius_test_send(int, short, void *); 2385d013a5eSdlg static void radius_test_recv(int, short, void *); 2395d013a5eSdlg static void radius_test_timedout(int, short, void *); 2405d013a5eSdlg 2415d013a5eSdlg static int 242a7ca44b8Syasuoka radius_test(struct parse_result *res) 243a7ca44b8Syasuoka { 2445d013a5eSdlg struct radius_test test = { .res = res }; 2455d013a5eSdlg RADIUS_PACKET *reqpkt; 246a7ca44b8Syasuoka struct addrinfo hints, *ai; 247a7ca44b8Syasuoka int sock, retval; 248a7ca44b8Syasuoka struct sockaddr_storage sockaddr; 249a7ca44b8Syasuoka socklen_t sockaddrlen; 250a7ca44b8Syasuoka struct sockaddr_in *sin4; 251a7ca44b8Syasuoka struct sockaddr_in6 *sin6; 252a7ca44b8Syasuoka uint32_t u32val; 253a7ca44b8Syasuoka uint8_t id; 254a7ca44b8Syasuoka 255a7ca44b8Syasuoka reqpkt = radius_new_request_packet(RADIUS_CODE_ACCESS_REQUEST); 256a7ca44b8Syasuoka if (reqpkt == NULL) 257a7ca44b8Syasuoka err(1, "radius_new_request_packet"); 258a7ca44b8Syasuoka id = arc4random(); 259a7ca44b8Syasuoka radius_set_id(reqpkt, id); 260a7ca44b8Syasuoka 261a7ca44b8Syasuoka memset(&hints, 0, sizeof(hints)); 262a7ca44b8Syasuoka hints.ai_family = PF_UNSPEC; 263a7ca44b8Syasuoka hints.ai_socktype = SOCK_DGRAM; 264a7ca44b8Syasuoka 265a7ca44b8Syasuoka retval = getaddrinfo(res->hostname, "radius", &hints, &ai); 266a7ca44b8Syasuoka if (retval) 267a7ca44b8Syasuoka errx(1, "%s %s", res->hostname, gai_strerror(retval)); 268a7ca44b8Syasuoka 269a7ca44b8Syasuoka if (res->port != 0) 270a7ca44b8Syasuoka ((struct sockaddr_in *)ai->ai_addr)->sin_port = 271a7ca44b8Syasuoka htons(res->port); 272a7ca44b8Syasuoka 2735d013a5eSdlg sock = socket(ai->ai_family, ai->ai_socktype | SOCK_NONBLOCK, 2745d013a5eSdlg ai->ai_protocol); 275a7ca44b8Syasuoka if (sock == -1) 276a7ca44b8Syasuoka err(1, "socket"); 277a7ca44b8Syasuoka 278a7ca44b8Syasuoka /* Prepare NAS-IP{,V6}-ADDRESS attribute */ 279a7ca44b8Syasuoka if (connect(sock, ai->ai_addr, ai->ai_addrlen) == -1) 280a7ca44b8Syasuoka err(1, "connect"); 281a7ca44b8Syasuoka sockaddrlen = sizeof(sockaddr); 282a7ca44b8Syasuoka if (getsockname(sock, (struct sockaddr *)&sockaddr, &sockaddrlen) == -1) 283a7ca44b8Syasuoka err(1, "getsockname"); 284a7ca44b8Syasuoka sin4 = (struct sockaddr_in *)&sockaddr; 285a7ca44b8Syasuoka sin6 = (struct sockaddr_in6 *)&sockaddr; 286a7ca44b8Syasuoka switch (sockaddr.ss_family) { 287a7ca44b8Syasuoka case AF_INET: 288a7ca44b8Syasuoka radius_put_ipv4_attr(reqpkt, RADIUS_TYPE_NAS_IP_ADDRESS, 289a7ca44b8Syasuoka sin4->sin_addr); 290a7ca44b8Syasuoka break; 291a7ca44b8Syasuoka case AF_INET6: 292a7ca44b8Syasuoka radius_put_raw_attr(reqpkt, RADIUS_TYPE_NAS_IPV6_ADDRESS, 293a7ca44b8Syasuoka sin6->sin6_addr.s6_addr, sizeof(sin6->sin6_addr.s6_addr)); 294a7ca44b8Syasuoka break; 295a7ca44b8Syasuoka } 296a7ca44b8Syasuoka 297a7ca44b8Syasuoka /* User-Name and User-Password */ 298a7ca44b8Syasuoka radius_put_string_attr(reqpkt, RADIUS_TYPE_USER_NAME, 299a7ca44b8Syasuoka res->username); 300a7ca44b8Syasuoka 301a7ca44b8Syasuoka switch (res->auth_method) { 302a7ca44b8Syasuoka case PAP: 303a7ca44b8Syasuoka if (res->password != NULL) 304a7ca44b8Syasuoka radius_put_user_password_attr(reqpkt, res->password, 305a7ca44b8Syasuoka res->secret); 306a7ca44b8Syasuoka break; 307a7ca44b8Syasuoka case CHAP: 308a7ca44b8Syasuoka { 309a7ca44b8Syasuoka u_char chal[16]; 310a7ca44b8Syasuoka u_char resp[1 + MD5_DIGEST_LENGTH]; /* "1 + " for CHAP Id */ 311a7ca44b8Syasuoka MD5_CTX md5ctx; 312a7ca44b8Syasuoka 3137aed478bSyasuoka arc4random_buf(chal, sizeof(chal)); 314a7ca44b8Syasuoka arc4random_buf(resp, 1); /* CHAP Id is random */ 315a7ca44b8Syasuoka MD5Init(&md5ctx); 316a7ca44b8Syasuoka MD5Update(&md5ctx, resp, 1); 317a7ca44b8Syasuoka if (res->password != NULL) 318a7ca44b8Syasuoka MD5Update(&md5ctx, res->password, 319a7ca44b8Syasuoka strlen(res->password)); 320a7ca44b8Syasuoka MD5Update(&md5ctx, chal, sizeof(chal)); 321a7ca44b8Syasuoka MD5Final(resp + 1, &md5ctx); 322a7ca44b8Syasuoka radius_put_raw_attr(reqpkt, RADIUS_TYPE_CHAP_CHALLENGE, 323a7ca44b8Syasuoka chal, sizeof(chal)); 324a7ca44b8Syasuoka radius_put_raw_attr(reqpkt, RADIUS_TYPE_CHAP_PASSWORD, 325a7ca44b8Syasuoka resp, sizeof(resp)); 326a7ca44b8Syasuoka } 327a7ca44b8Syasuoka break; 328a7ca44b8Syasuoka case MSCHAPV2: 329a7ca44b8Syasuoka { 330a7ca44b8Syasuoka u_char pass[256], chal[16]; 331a7ca44b8Syasuoka u_int i, lpass; 332a7ca44b8Syasuoka struct _resp { 333a7ca44b8Syasuoka u_int8_t ident; 334a7ca44b8Syasuoka u_int8_t flags; 335a7ca44b8Syasuoka char peer_challenge[16]; 336a7ca44b8Syasuoka char reserved[8]; 337a7ca44b8Syasuoka char response[24]; 338a7ca44b8Syasuoka } __packed resp; 339a7ca44b8Syasuoka 340a7ca44b8Syasuoka if (res->password == NULL) { 341a7ca44b8Syasuoka lpass = 0; 342a7ca44b8Syasuoka } else { 343a7ca44b8Syasuoka lpass = strlen(res->password); 344a7ca44b8Syasuoka if (lpass * 2 >= sizeof(pass)) 345a7ca44b8Syasuoka err(1, "password too long"); 346a7ca44b8Syasuoka for (i = 0; i < lpass; i++) { 347a7ca44b8Syasuoka pass[i * 2] = res->password[i]; 348a7ca44b8Syasuoka pass[i * 2 + 1] = 0; 349a7ca44b8Syasuoka } 350a7ca44b8Syasuoka } 351a7ca44b8Syasuoka 352a7ca44b8Syasuoka memset(&resp, 0, sizeof(resp)); 353a7ca44b8Syasuoka resp.ident = arc4random(); 354a7ca44b8Syasuoka arc4random_buf(chal, sizeof(chal)); 355a7ca44b8Syasuoka arc4random_buf(resp.peer_challenge, 356a7ca44b8Syasuoka sizeof(resp.peer_challenge)); 357a7ca44b8Syasuoka 358a7ca44b8Syasuoka mschap_nt_response(chal, resp.peer_challenge, 359a7ca44b8Syasuoka (char *)res->username, strlen(res->username), pass, 360a7ca44b8Syasuoka lpass * 2, resp.response); 361a7ca44b8Syasuoka 362a7ca44b8Syasuoka radius_put_vs_raw_attr(reqpkt, RADIUS_VENDOR_MICROSOFT, 363a7ca44b8Syasuoka RADIUS_VTYPE_MS_CHAP_CHALLENGE, chal, sizeof(chal)); 364a7ca44b8Syasuoka radius_put_vs_raw_attr(reqpkt, RADIUS_VENDOR_MICROSOFT, 365a7ca44b8Syasuoka RADIUS_VTYPE_MS_CHAP2_RESPONSE, &resp, sizeof(resp)); 366a7ca44b8Syasuoka explicit_bzero(pass, sizeof(pass)); 367a7ca44b8Syasuoka } 368a7ca44b8Syasuoka break; 369a7ca44b8Syasuoka 370a7ca44b8Syasuoka } 371a7ca44b8Syasuoka u32val = htonl(res->nas_port); 372a7ca44b8Syasuoka radius_put_raw_attr(reqpkt, RADIUS_TYPE_NAS_PORT, &u32val, 4); 373a7ca44b8Syasuoka 374a852e27aSyasuoka if (res->msgauth) 375a7ca44b8Syasuoka radius_put_message_authenticator(reqpkt, res->secret); 376a7ca44b8Syasuoka 3775d013a5eSdlg event_init(); 3785d013a5eSdlg 3795d013a5eSdlg test.ecode = EXIT_FAILURE; 3805d013a5eSdlg test.res = res; 3815d013a5eSdlg test.sock = sock; 3825d013a5eSdlg test.reqpkt = reqpkt; 3835d013a5eSdlg 3845d013a5eSdlg event_set(&test.ev_recv, sock, EV_READ|EV_PERSIST, 3855d013a5eSdlg radius_test_recv, &test); 3865d013a5eSdlg 3875d013a5eSdlg evtimer_set(&test.ev_send, radius_test_send, &test); 3885d013a5eSdlg evtimer_set(&test.ev_timedout, radius_test_timedout, &test); 3895d013a5eSdlg 3905d013a5eSdlg event_add(&test.ev_recv, NULL); 3915d013a5eSdlg evtimer_add(&test.ev_timedout, &res->maxwait); 3925d013a5eSdlg 393a7ca44b8Syasuoka /* Send! */ 394a7ca44b8Syasuoka fprintf(stderr, "Sending:\n"); 395a7ca44b8Syasuoka radius_dump(stdout, reqpkt, false, res->secret); 3965d013a5eSdlg radius_test_send(0, EV_TIMEOUT, &test); 3975d013a5eSdlg 3985d013a5eSdlg event_dispatch(); 399a7ca44b8Syasuoka 400a7ca44b8Syasuoka /* Release the resources */ 401a7ca44b8Syasuoka radius_delete_packet(reqpkt); 402a7ca44b8Syasuoka close(sock); 403a7ca44b8Syasuoka freeaddrinfo(ai); 404a7ca44b8Syasuoka 405a7ca44b8Syasuoka explicit_bzero((char *)res->secret, strlen(res->secret)); 406a7ca44b8Syasuoka if (res->password) 407a7ca44b8Syasuoka explicit_bzero((char *)res->password, strlen(res->password)); 408a7ca44b8Syasuoka 4095d013a5eSdlg return (test.ecode); 4105d013a5eSdlg } 4115d013a5eSdlg 4125d013a5eSdlg static void 4135d013a5eSdlg radius_test_send(int thing, short revents, void *arg) 4145d013a5eSdlg { 4155d013a5eSdlg struct radius_test *test = arg; 4165d013a5eSdlg RADIUS_PACKET *reqpkt = test->reqpkt; 4175d013a5eSdlg ssize_t rv; 4185d013a5eSdlg 4195d013a5eSdlg retry: 4205d013a5eSdlg rv = send(test->sock, 4215d013a5eSdlg radius_get_data(reqpkt), radius_get_length(reqpkt), 0); 4225d013a5eSdlg if (rv == -1) { 4235d013a5eSdlg switch (errno) { 4245d013a5eSdlg case EINTR: 4255d013a5eSdlg case EAGAIN: 4265d013a5eSdlg goto retry; 4275d013a5eSdlg default: 4285d013a5eSdlg break; 4295d013a5eSdlg } 4305d013a5eSdlg 4315d013a5eSdlg warn("send"); 4325d013a5eSdlg } 4335d013a5eSdlg 4345d013a5eSdlg if (++test->tries >= test->res->tries) 435a7ca44b8Syasuoka return; 4365d013a5eSdlg 4375d013a5eSdlg evtimer_add(&test->ev_send, &test->res->interval); 4385d013a5eSdlg } 4395d013a5eSdlg 4405d013a5eSdlg static void 4415d013a5eSdlg radius_test_recv(int sock, short revents, void *arg) 4425d013a5eSdlg { 4435d013a5eSdlg struct radius_test *test = arg; 4445d013a5eSdlg RADIUS_PACKET *respkt; 4455d013a5eSdlg RADIUS_PACKET *reqpkt = test->reqpkt; 4465d013a5eSdlg 4475d013a5eSdlg retry: 4485d013a5eSdlg respkt = radius_recv(sock, 0); 4495d013a5eSdlg if (respkt == NULL) { 4505d013a5eSdlg switch (errno) { 4515d013a5eSdlg case EINTR: 4525d013a5eSdlg case EAGAIN: 4535d013a5eSdlg goto retry; 4545d013a5eSdlg default: 4555d013a5eSdlg break; 4565d013a5eSdlg } 4575d013a5eSdlg 4585d013a5eSdlg warn("recv"); 4595d013a5eSdlg return; 4605d013a5eSdlg } 4615d013a5eSdlg 4625d013a5eSdlg radius_set_request_packet(respkt, reqpkt); 4635d013a5eSdlg if (radius_get_id(respkt) == radius_get_id(reqpkt)) { 4645d013a5eSdlg fprintf(stderr, "\nReceived:\n"); 4655d013a5eSdlg radius_dump(stdout, respkt, true, test->res->secret); 4665d013a5eSdlg 4675d013a5eSdlg event_del(&test->ev_recv); 4685d013a5eSdlg evtimer_del(&test->ev_send); 4695d013a5eSdlg evtimer_del(&test->ev_timedout); 4705d013a5eSdlg test->ecode = EXIT_SUCCESS; 4715d013a5eSdlg } 4725d013a5eSdlg 4735d013a5eSdlg radius_delete_packet(respkt); 4745d013a5eSdlg } 4755d013a5eSdlg 4765d013a5eSdlg static void 4775d013a5eSdlg radius_test_timedout(int thing, short revents, void *arg) 4785d013a5eSdlg { 4795d013a5eSdlg struct radius_test *test = arg; 4805d013a5eSdlg 4815d013a5eSdlg event_del(&test->ev_recv); 482a7ca44b8Syasuoka } 483a7ca44b8Syasuoka 484a7ca44b8Syasuoka static void 485a7ca44b8Syasuoka radius_dump(FILE *out, RADIUS_PACKET *pkt, bool resp, const char *secret) 486a7ca44b8Syasuoka { 487a7ca44b8Syasuoka size_t len; 488a7ca44b8Syasuoka char buf[256], buf1[256]; 489a7ca44b8Syasuoka uint32_t u32val; 490a7ca44b8Syasuoka struct in_addr ipv4; 491a7ca44b8Syasuoka 492a7ca44b8Syasuoka fprintf(out, 493a7ca44b8Syasuoka " Id = %d\n" 494a7ca44b8Syasuoka " Code = %s(%d)\n", 495a7ca44b8Syasuoka (int)radius_get_id(pkt), radius_code_str((int)radius_get_code(pkt)), 496a7ca44b8Syasuoka (int)radius_get_code(pkt)); 497b9225786Syasuoka if (resp && secret) { 498b9225786Syasuoka fprintf(out, " Authenticator = %s\n", 499a7ca44b8Syasuoka (radius_check_response_authenticator(pkt, secret) == 0) 500a7ca44b8Syasuoka ? "Verified" : "NG"); 501b9225786Syasuoka fprintf(out, " Message-Authenticator = %s\n", 502b9225786Syasuoka (!radius_has_attr(pkt, RADIUS_TYPE_MESSAGE_AUTHENTICATOR)) 503b9225786Syasuoka ? "(Not present)" 504b9225786Syasuoka : (radius_check_message_authenticator(pkt, secret) == 0) 505b9225786Syasuoka ? "Verified" : "NG"); 506b9225786Syasuoka } 507a852e27aSyasuoka if (!resp) 508a852e27aSyasuoka fprintf(out, " Message-Authenticator = %s\n", 509a852e27aSyasuoka (radius_has_attr(pkt, RADIUS_TYPE_MESSAGE_AUTHENTICATOR)) 510a852e27aSyasuoka ? "(Present)" : "(Not present)"); 511a7ca44b8Syasuoka 512a7ca44b8Syasuoka if (radius_get_string_attr(pkt, RADIUS_TYPE_USER_NAME, buf, 513a7ca44b8Syasuoka sizeof(buf)) == 0) 514a7ca44b8Syasuoka fprintf(out, " User-Name = \"%s\"\n", buf); 515a7ca44b8Syasuoka 516a7ca44b8Syasuoka if (secret && 517a7ca44b8Syasuoka radius_get_user_password_attr(pkt, buf, sizeof(buf), secret) == 0) 518a7ca44b8Syasuoka fprintf(out, " User-Password = \"%s\"\n", buf); 519a7ca44b8Syasuoka 520a7ca44b8Syasuoka memset(buf, 0, sizeof(buf)); 521a7ca44b8Syasuoka len = sizeof(buf); 522a7ca44b8Syasuoka if (radius_get_raw_attr(pkt, RADIUS_TYPE_CHAP_PASSWORD, buf, &len) 523a7ca44b8Syasuoka == 0) 524a7ca44b8Syasuoka fprintf(out, " CHAP-Password = %s\n", 52583aeedf6Syasuoka (hexstr(buf, len, buf1, sizeof(buf1))) 52683aeedf6Syasuoka ? buf1 : "(too long)"); 527a7ca44b8Syasuoka 528a7ca44b8Syasuoka memset(buf, 0, sizeof(buf)); 529a7ca44b8Syasuoka len = sizeof(buf); 530a7ca44b8Syasuoka if (radius_get_raw_attr(pkt, RADIUS_TYPE_CHAP_CHALLENGE, buf, &len) 531a7ca44b8Syasuoka == 0) 532a7ca44b8Syasuoka fprintf(out, " CHAP-Challenge = %s\n", 53383aeedf6Syasuoka (hexstr(buf, len, buf1, sizeof(buf1))) 53483aeedf6Syasuoka ? buf1 : "(too long)"); 535a7ca44b8Syasuoka 536a7ca44b8Syasuoka memset(buf, 0, sizeof(buf)); 537a7ca44b8Syasuoka len = sizeof(buf); 538a7ca44b8Syasuoka if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 539a7ca44b8Syasuoka RADIUS_VTYPE_MS_CHAP_CHALLENGE, buf, &len) == 0) 540a7ca44b8Syasuoka fprintf(out, " MS-CHAP-Challenge = %s\n", 54183aeedf6Syasuoka (hexstr(buf, len, buf1, sizeof(buf1))) 54283aeedf6Syasuoka ? buf1 : "(too long)"); 543a7ca44b8Syasuoka 544a7ca44b8Syasuoka memset(buf, 0, sizeof(buf)); 545a7ca44b8Syasuoka len = sizeof(buf); 546a7ca44b8Syasuoka if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 547a7ca44b8Syasuoka RADIUS_VTYPE_MS_CHAP2_RESPONSE, buf, &len) == 0) 548a7ca44b8Syasuoka fprintf(out, " MS-CHAP2-Response = %s\n", 549a7ca44b8Syasuoka (hexstr(buf, len, buf1, sizeof(buf1))) 550a7ca44b8Syasuoka ? buf1 : "(too long)"); 551a7ca44b8Syasuoka 552a7ca44b8Syasuoka memset(buf, 0, sizeof(buf)); 553a7ca44b8Syasuoka len = sizeof(buf) - 1; 554a7ca44b8Syasuoka if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 555a7ca44b8Syasuoka RADIUS_VTYPE_MS_CHAP2_SUCCESS, buf, &len) == 0) { 556a7ca44b8Syasuoka fprintf(out, " MS-CHAP-Success = Id=%u \"%s\"\n", 557a7ca44b8Syasuoka (u_int)(u_char)buf[0], buf + 1); 558a7ca44b8Syasuoka } 559a7ca44b8Syasuoka 560a7ca44b8Syasuoka memset(buf, 0, sizeof(buf)); 561a7ca44b8Syasuoka len = sizeof(buf) - 1; 562a7ca44b8Syasuoka if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 563a7ca44b8Syasuoka RADIUS_VTYPE_MS_CHAP_ERROR, buf, &len) == 0) { 564a7ca44b8Syasuoka fprintf(out, " MS-CHAP-Error = Id=%u \"%s\"\n", 565a7ca44b8Syasuoka (u_int)(u_char)buf[0], buf + 1); 566a7ca44b8Syasuoka } 567a7ca44b8Syasuoka 568a7ca44b8Syasuoka memset(buf, 0, sizeof(buf)); 569a7ca44b8Syasuoka len = sizeof(buf); 570a7ca44b8Syasuoka if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 571a7ca44b8Syasuoka RADIUS_VTYPE_MPPE_SEND_KEY, buf, &len) == 0) 572a7ca44b8Syasuoka fprintf(out, " MS-MPPE-Send-Key = %s\n", 573a7ca44b8Syasuoka (hexstr(buf, len, buf1, sizeof(buf1))) 574a7ca44b8Syasuoka ? buf1 : "(too long)"); 575a7ca44b8Syasuoka 576a7ca44b8Syasuoka memset(buf, 0, sizeof(buf)); 577a7ca44b8Syasuoka len = sizeof(buf); 578a7ca44b8Syasuoka if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 579a7ca44b8Syasuoka RADIUS_VTYPE_MPPE_RECV_KEY, buf, &len) == 0) 580a7ca44b8Syasuoka fprintf(out, " MS-MPPE-Recv-Key = %s\n", 581a7ca44b8Syasuoka (hexstr(buf, len, buf1, sizeof(buf1))) 582a7ca44b8Syasuoka ? buf1 : "(too long)"); 583a7ca44b8Syasuoka 584a7ca44b8Syasuoka memset(buf, 0, sizeof(buf)); 585a7ca44b8Syasuoka len = sizeof(buf); 586a7ca44b8Syasuoka if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 587a7ca44b8Syasuoka RADIUS_VTYPE_MPPE_ENCRYPTION_POLICY, buf, &len) == 0) 588a7ca44b8Syasuoka fprintf(out, " MS-MPPE-Encryption-Policy = 0x%08x\n", 589a7ca44b8Syasuoka ntohl(*(u_long *)buf)); 590a7ca44b8Syasuoka 591a7ca44b8Syasuoka memset(buf, 0, sizeof(buf)); 592a7ca44b8Syasuoka len = sizeof(buf); 593a7ca44b8Syasuoka if (radius_get_vs_raw_attr(pkt, RADIUS_VENDOR_MICROSOFT, 594a7ca44b8Syasuoka RADIUS_VTYPE_MPPE_ENCRYPTION_TYPES, buf, &len) == 0) 595a7ca44b8Syasuoka fprintf(out, " MS-MPPE-Encryption-Types = 0x%08x\n", 596a7ca44b8Syasuoka ntohl(*(u_long *)buf)); 597a7ca44b8Syasuoka 598a7ca44b8Syasuoka if (radius_get_string_attr(pkt, RADIUS_TYPE_REPLY_MESSAGE, buf, 599a7ca44b8Syasuoka sizeof(buf)) == 0) 600a7ca44b8Syasuoka fprintf(out, " Reply-Message = \"%s\"\n", buf); 601a7ca44b8Syasuoka 602a7ca44b8Syasuoka memset(buf, 0, sizeof(buf)); 603a7ca44b8Syasuoka len = sizeof(buf); 604a7ca44b8Syasuoka if (radius_get_uint32_attr(pkt, RADIUS_TYPE_NAS_PORT, &u32val) == 0) 605a7ca44b8Syasuoka fprintf(out, " NAS-Port = %lu\n", 606a7ca44b8Syasuoka (u_long)u32val); 607a7ca44b8Syasuoka 608a7ca44b8Syasuoka memset(buf, 0, sizeof(buf)); 609a7ca44b8Syasuoka len = sizeof(buf); 610a7ca44b8Syasuoka if (radius_get_ipv4_attr(pkt, RADIUS_TYPE_NAS_IP_ADDRESS, &ipv4) == 0) 611a7ca44b8Syasuoka fprintf(out, " NAS-IP-Address = %s\n", 612a7ca44b8Syasuoka inet_ntoa(ipv4)); 613a7ca44b8Syasuoka 614a7ca44b8Syasuoka memset(buf, 0, sizeof(buf)); 615a7ca44b8Syasuoka len = sizeof(buf); 616a7ca44b8Syasuoka if (radius_get_raw_attr(pkt, RADIUS_TYPE_NAS_IPV6_ADDRESS, buf, &len) 617a7ca44b8Syasuoka == 0) 618a7ca44b8Syasuoka fprintf(out, " NAS-IPv6-Address = %s\n", 619a7ca44b8Syasuoka inet_ntop(AF_INET6, buf, buf1, len)); 620a7ca44b8Syasuoka 621a7ca44b8Syasuoka } 622a7ca44b8Syasuoka 623842565f2Syasuoka /*********************************************************************** 624842565f2Syasuoka * ipcp 625842565f2Syasuoka ***********************************************************************/ 626842565f2Syasuoka int 627842565f2Syasuoka ipcp_handle_imsg(struct parse_result *res, struct imsg *imsg, int cnt) 628842565f2Syasuoka { 629842565f2Syasuoka ssize_t datalen; 630842565f2Syasuoka struct radiusd_ipcp_db_dump *dump; 631842565f2Syasuoka struct radiusd_ipcp_statistics *stat; 632842565f2Syasuoka int done = 0; 633842565f2Syasuoka 634842565f2Syasuoka datalen = imsg->hdr.len - IMSG_HEADER_SIZE; 635842565f2Syasuoka switch (imsg->hdr.type) { 636c0127aecSyasuoka case IMSG_OK: 637c0127aecSyasuoka if (datalen > 0 && *((char *)imsg->data + datalen - 1) == '\0') 638c0127aecSyasuoka fprintf(stderr, "OK: %s\n", (char *)imsg->data); 639c0127aecSyasuoka else 640c0127aecSyasuoka fprintf(stderr, "OK\n"); 641c0127aecSyasuoka done = 1; 642c0127aecSyasuoka break; 643842565f2Syasuoka case IMSG_NG: 644842565f2Syasuoka if (datalen > 0 && *((char *)imsg->data + datalen - 1) == '\0') 645842565f2Syasuoka fprintf(stderr, "error: %s\n", (char *)imsg->data); 646842565f2Syasuoka else 647842565f2Syasuoka fprintf(stderr, "error\n"); 648842565f2Syasuoka exit(EXIT_FAILURE); 649842565f2Syasuoka case IMSG_RADIUSD_MODULE_IPCP_DUMP: 650842565f2Syasuoka if ((size_t)datalen < sizeof(struct 651842565f2Syasuoka radiusd_ipcp_db_dump)) 652842565f2Syasuoka errx(1, "received a message which size is invalid"); 653842565f2Syasuoka dump = imsg->data; 654842565f2Syasuoka if (res->action == IPCP_SHOW) 655842565f2Syasuoka ipcp_handle_show(dump, datalen, (cnt++ == 0)? 1 : 0); 656842565f2Syasuoka else { 657842565f2Syasuoka if (res->flags & FLAGS_JSON) 658842565f2Syasuoka ipcp_handle_jsons(dump, datalen, 659842565f2Syasuoka (cnt++ == 0)? 1 : 0); 660842565f2Syasuoka else 661842565f2Syasuoka ipcp_handle_dumps(dump, datalen, 662842565f2Syasuoka (cnt++ == 0)? 1 : 0); 663842565f2Syasuoka } 664842565f2Syasuoka if (dump->islast && 665842565f2Syasuoka (res->action == IPCP_SHOW || res->action == IPCP_DUMP)) 666842565f2Syasuoka done = 1; 667842565f2Syasuoka break; 668842565f2Syasuoka case IMSG_RADIUSD_MODULE_IPCP_START: 669842565f2Syasuoka if ((size_t)datalen < offsetof(struct 670842565f2Syasuoka radiusd_ipcp_db_dump, records[1])) 671842565f2Syasuoka errx(1, "received a message which size is invalid"); 672842565f2Syasuoka dump = imsg->data; 673842565f2Syasuoka if (res->flags & FLAGS_JSON) 674842565f2Syasuoka ipcp_handle_json(dump, datalen, NULL, 0); 675842565f2Syasuoka else { 676842565f2Syasuoka printf("Start\n"); 677842565f2Syasuoka ipcp_handle_dump(dump, datalen, 0); 678842565f2Syasuoka } 679842565f2Syasuoka break; 680842565f2Syasuoka case IMSG_RADIUSD_MODULE_IPCP_STOP: 681842565f2Syasuoka if ((size_t)datalen < offsetof( 682842565f2Syasuoka struct radiusd_ipcp_db_dump, 683842565f2Syasuoka records[1]) + 684842565f2Syasuoka sizeof(struct 685842565f2Syasuoka radiusd_ipcp_statistics)) 686842565f2Syasuoka errx(1, "received a message which size is invalid"); 687842565f2Syasuoka dump = imsg->data; 688842565f2Syasuoka stat = (struct radiusd_ipcp_statistics *) 689842565f2Syasuoka ((char *)imsg->data + offsetof( 690842565f2Syasuoka struct radiusd_ipcp_db_dump, records[1])); 691842565f2Syasuoka if (res->flags & FLAGS_JSON) 692842565f2Syasuoka ipcp_handle_json(dump, datalen, stat, 0); 693842565f2Syasuoka else { 694842565f2Syasuoka printf("Stop\n"); 695842565f2Syasuoka ipcp_handle_dump(dump, datalen, 0); 696842565f2Syasuoka ipcp_handle_stat(stat); 697842565f2Syasuoka } 698842565f2Syasuoka break; 699842565f2Syasuoka } 700842565f2Syasuoka 701842565f2Syasuoka return (done); 702842565f2Syasuoka } 703842565f2Syasuoka 704842565f2Syasuoka static void 705842565f2Syasuoka ipcp_handle_show(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, int first) 706842565f2Syasuoka { 707842565f2Syasuoka int i, width; 708842565f2Syasuoka uint32_t maxseq = 999; 709842565f2Syasuoka char buf0[128], buf1[NI_MAXHOST + NI_MAXSERV + 4], buf2[80]; 710842565f2Syasuoka struct timespec upt, now, dif, start; 711842565f2Syasuoka 712842565f2Syasuoka clock_gettime(CLOCK_BOOTTIME, &upt); 713842565f2Syasuoka clock_gettime(CLOCK_REALTIME, &now); 714842565f2Syasuoka timespecsub(&now, &upt, &upt); 715842565f2Syasuoka 716842565f2Syasuoka for (i = 0; ; i++) { 717842565f2Syasuoka if (offsetof(struct radiusd_ipcp_db_dump, records[i]) 718842565f2Syasuoka >= dumpsiz) 719842565f2Syasuoka break; 720842565f2Syasuoka maxseq = MAXIMUM(maxseq, dump->records[i].rec.seq); 721842565f2Syasuoka } 722842565f2Syasuoka for (width = 0; maxseq != 0; maxseq /= 10, width++) 723842565f2Syasuoka ; 724842565f2Syasuoka 725842565f2Syasuoka for (i = 0; ; i++) { 726842565f2Syasuoka if (offsetof(struct radiusd_ipcp_db_dump, records[i]) 727842565f2Syasuoka >= dumpsiz) 728842565f2Syasuoka break; 729842565f2Syasuoka if (i == 0 && first) 730842565f2Syasuoka printf("%-*s Assigned Username " 731842565f2Syasuoka "Start Tunnel From\n" 732842565f2Syasuoka "%.*s --------------- ---------------------- " 733842565f2Syasuoka "-------- %.*s\n", width, "Seq", width, 734842565f2Syasuoka "----------", 28 - width, 735842565f2Syasuoka "-------------------------"); 736842565f2Syasuoka timespecadd(&upt, &dump->records[i].rec.start, &start); 737842565f2Syasuoka timespecsub(&now, &start, &dif); 738842565f2Syasuoka printf("%*d %-15s %-22s %-8s %s\n", 739842565f2Syasuoka width, dump->records[i].rec.seq, 740842565f2Syasuoka inet_ntop(dump->records[i].af, &dump->records[i].addr, 741842565f2Syasuoka buf0, sizeof(buf0)), dump->records[i].rec.username, 742842565f2Syasuoka time_short_str(&start, &dif, buf2, sizeof(buf2)), 743842565f2Syasuoka sockaddr_str( 744842565f2Syasuoka (struct sockaddr *)&dump->records[i].rec.tun_client, buf1, 745842565f2Syasuoka sizeof(buf1))); 746842565f2Syasuoka } 747842565f2Syasuoka } 748842565f2Syasuoka static void 749842565f2Syasuoka ipcp_handle_dump(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, int idx) 750842565f2Syasuoka { 751842565f2Syasuoka struct timespec upt, now, dif, start, timeout; 752842565f2Syasuoka 753842565f2Syasuoka clock_gettime(CLOCK_BOOTTIME, &upt); 754842565f2Syasuoka clock_gettime(CLOCK_REALTIME, &now); 755842565f2Syasuoka timespecsub(&now, &upt, &upt); 756842565f2Syasuoka 757842565f2Syasuoka timespecadd(&upt, &dump->records[idx].rec.start, &start); 758842565f2Syasuoka timespecsub(&now, &start, &dif); 759842565f2Syasuoka 760842565f2Syasuoka if (dump->records[idx].rec.start.tv_sec == 0) 761842565f2Syasuoka ipcp_handle_dump0(dump, dumpsiz, &dif, &start, NULL, idx); 762842565f2Syasuoka else { 763842565f2Syasuoka timespecadd(&upt, &dump->records[idx].rec.timeout, &timeout); 764842565f2Syasuoka ipcp_handle_dump0(dump, dumpsiz, &dif, &start, &timeout, idx); 765842565f2Syasuoka } 766842565f2Syasuoka } 767842565f2Syasuoka 768842565f2Syasuoka static void 769842565f2Syasuoka ipcp_handle_dump0(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, 770842565f2Syasuoka struct timespec *dif, struct timespec *start, struct timespec *timeout, 771842565f2Syasuoka int idx) 772842565f2Syasuoka { 773842565f2Syasuoka char buf0[128], buf1[NI_MAXHOST + NI_MAXSERV + 4], buf2[80]; 774842565f2Syasuoka 775842565f2Syasuoka printf( 776842565f2Syasuoka " Sequence Number : %u\n" 777842565f2Syasuoka " Session Id : %s\n" 778842565f2Syasuoka " Username : %s\n" 779842565f2Syasuoka " Auth Method : %s\n" 780842565f2Syasuoka " Assigned IP Address : %s\n" 781842565f2Syasuoka " Start Time : %s\n" 782842565f2Syasuoka " Elapsed Time : %lld second%s%s\n", 783842565f2Syasuoka dump->records[idx].rec.seq, dump->records[idx].rec.session_id, 784842565f2Syasuoka dump->records[idx].rec.username, dump->records[idx].rec.auth_method, 785842565f2Syasuoka inet_ntop(dump->records[idx].af, &dump->records[idx].addr, buf0, 786842565f2Syasuoka sizeof(buf0)), time_long_str(start, buf1, sizeof(buf1)), 787842565f2Syasuoka (long long)dif->tv_sec, (dif->tv_sec == 0)? "" : "s", 788842565f2Syasuoka humanize_seconds(dif->tv_sec, buf2, sizeof(buf2))); 789842565f2Syasuoka if (timeout != NULL) 790842565f2Syasuoka printf(" Timeout : %s\n", 791842565f2Syasuoka time_long_str(timeout, buf0, sizeof(buf0))); 792842565f2Syasuoka printf( 793842565f2Syasuoka " NAS Identifier : %s\n" 794842565f2Syasuoka " Tunnel Type : %s\n" 795842565f2Syasuoka " Tunnel From : %s\n", 796842565f2Syasuoka dump->records[idx].rec.nas_id, dump->records[idx].rec.tun_type, 797842565f2Syasuoka sockaddr_str((struct sockaddr *) 798842565f2Syasuoka &dump->records[idx].rec.tun_client, buf1, sizeof(buf1))); 799842565f2Syasuoka } 800842565f2Syasuoka 801842565f2Syasuoka void 802842565f2Syasuoka ipcp_handle_stat(struct radiusd_ipcp_statistics *stat) 803842565f2Syasuoka { 804842565f2Syasuoka printf( 805842565f2Syasuoka " Terminate Cause : %s\n" 806842565f2Syasuoka " Input Packets : %"PRIu32"\n" 807842565f2Syasuoka " Output Packets : %"PRIu32"\n" 808842565f2Syasuoka " Input Bytes : %"PRIu64"\n" 809842565f2Syasuoka " Output Bytes : %"PRIu64"\n", 810842565f2Syasuoka stat->cause, stat->ipackets, stat->opackets, stat->ibytes, 811842565f2Syasuoka stat->obytes); 812842565f2Syasuoka } 813842565f2Syasuoka 814842565f2Syasuoka static void 815842565f2Syasuoka ipcp_handle_jsons(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, int first) 816842565f2Syasuoka { 817842565f2Syasuoka int i; 818842565f2Syasuoka struct timespec upt, now, dif, start, timeout; 819842565f2Syasuoka 820842565f2Syasuoka clock_gettime(CLOCK_BOOTTIME, &upt); 821842565f2Syasuoka clock_gettime(CLOCK_REALTIME, &now); 822842565f2Syasuoka timespecsub(&now, &upt, &upt); 823842565f2Syasuoka 824842565f2Syasuoka for (i = 0; ; i++) { 825842565f2Syasuoka if (offsetof(struct radiusd_ipcp_db_dump, records[i]) 826842565f2Syasuoka >= dumpsiz) 827842565f2Syasuoka break; 828842565f2Syasuoka timespecadd(&upt, &dump->records[i].rec.start, &start); 829842565f2Syasuoka timespecsub(&now, &start, &dif); 830842565f2Syasuoka json_do_start(stdout); 831842565f2Syasuoka json_do_string("action", "start"); 832842565f2Syasuoka if (dump->records[i].rec.timeout.tv_sec == 0) 833842565f2Syasuoka ipcp_handle_json0(dump, dumpsiz, &dif, &start, NULL, i); 834842565f2Syasuoka else { 835842565f2Syasuoka timespecadd(&upt, &dump->records[i].rec.timeout, 836842565f2Syasuoka &timeout); 837842565f2Syasuoka ipcp_handle_json0(dump, dumpsiz, &dif, &start, &timeout, 838842565f2Syasuoka i); 839842565f2Syasuoka } 840842565f2Syasuoka json_do_finish(); 841842565f2Syasuoka } 842842565f2Syasuoka fflush(stdout); 843842565f2Syasuoka } 844842565f2Syasuoka 845842565f2Syasuoka static void 846842565f2Syasuoka ipcp_handle_json(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, 847842565f2Syasuoka struct radiusd_ipcp_statistics *stat, int idx) 848842565f2Syasuoka { 849842565f2Syasuoka struct timespec upt, now, dif, start, timeout; 850842565f2Syasuoka 851842565f2Syasuoka json_do_start(stdout); 852842565f2Syasuoka clock_gettime(CLOCK_BOOTTIME, &upt); 853842565f2Syasuoka clock_gettime(CLOCK_REALTIME, &now); 854842565f2Syasuoka timespecsub(&now, &upt, &upt); 855842565f2Syasuoka timespecadd(&upt, &dump->records[idx].rec.start, &start); 856842565f2Syasuoka timespecsub(&now, &start, &dif); 857842565f2Syasuoka 858842565f2Syasuoka if (stat == NULL) 859842565f2Syasuoka json_do_string("action", "start"); 860842565f2Syasuoka else 861842565f2Syasuoka json_do_string("action", "stop"); 862842565f2Syasuoka if (dump->records[idx].rec.timeout.tv_sec == 0) 863842565f2Syasuoka ipcp_handle_json0(dump, dumpsiz, &dif, &start, NULL, idx); 864842565f2Syasuoka else { 865842565f2Syasuoka timespecadd(&upt, &dump->records[idx].rec.timeout, &timeout); 866842565f2Syasuoka ipcp_handle_json0(dump, dumpsiz, &dif, &start, &timeout, idx); 867842565f2Syasuoka } 868842565f2Syasuoka if (stat != NULL) { 869842565f2Syasuoka json_do_string("terminate-cause", stat->cause); 870842565f2Syasuoka json_do_uint("input-packets", stat->ipackets); 871842565f2Syasuoka json_do_uint("output-packets", stat->opackets); 872842565f2Syasuoka json_do_uint("input-bytes", stat->ibytes); 873842565f2Syasuoka json_do_uint("output-bytes", stat->obytes); 874842565f2Syasuoka } 875842565f2Syasuoka json_do_finish(); 876842565f2Syasuoka fflush(stdout); 877842565f2Syasuoka } 878842565f2Syasuoka 879842565f2Syasuoka static void 880842565f2Syasuoka ipcp_handle_json0(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, 881842565f2Syasuoka struct timespec *dif, struct timespec *start, struct timespec *timeout, 882842565f2Syasuoka int idx) 883842565f2Syasuoka { 884842565f2Syasuoka char buf[128]; 885842565f2Syasuoka 886842565f2Syasuoka json_do_uint("sequence-number", dump->records[idx].rec.seq); 887842565f2Syasuoka json_do_string("session-id", dump->records[idx].rec.session_id); 888842565f2Syasuoka json_do_string("username", dump->records[idx].rec.username); 889842565f2Syasuoka json_do_string("auth-method", dump->records[idx].rec.auth_method); 890842565f2Syasuoka json_do_string("assigned-ip-address", inet_ntop(dump->records[idx].af, 891842565f2Syasuoka &dump->records[idx].addr, buf, sizeof(buf))); 892842565f2Syasuoka json_do_uint("start", start->tv_sec); 893842565f2Syasuoka json_do_uint("elapsed", dif->tv_sec); 894842565f2Syasuoka if (timeout != NULL) 895842565f2Syasuoka json_do_uint("timeout", timeout->tv_sec); 896842565f2Syasuoka json_do_string("nas-identifier", dump->records[idx].rec.nas_id); 897842565f2Syasuoka json_do_string("tunnel-type", dump->records[idx].rec.tun_type); 898842565f2Syasuoka json_do_string("tunnel-from", 899842565f2Syasuoka sockaddr_str((struct sockaddr *)&dump->records[idx].rec.tun_client, 900842565f2Syasuoka buf, sizeof(buf))); 901842565f2Syasuoka } 902842565f2Syasuoka 903842565f2Syasuoka static void 904842565f2Syasuoka ipcp_handle_dumps(struct radiusd_ipcp_db_dump *dump, size_t dumpsiz, int first) 905842565f2Syasuoka { 906842565f2Syasuoka static int cnt = 0; 907842565f2Syasuoka int i; 908842565f2Syasuoka struct timespec upt, now, dif, start, timeout; 909842565f2Syasuoka 910842565f2Syasuoka clock_gettime(CLOCK_BOOTTIME, &upt); 911842565f2Syasuoka clock_gettime(CLOCK_REALTIME, &now); 912842565f2Syasuoka timespecsub(&now, &upt, &upt); 913842565f2Syasuoka 914842565f2Syasuoka if (first) 915842565f2Syasuoka cnt = 0; 916842565f2Syasuoka for (i = 0; ; i++, cnt++) { 917842565f2Syasuoka if (offsetof(struct radiusd_ipcp_db_dump, records[i]) 918842565f2Syasuoka >= dumpsiz) 919842565f2Syasuoka break; 920842565f2Syasuoka timespecadd(&upt, &dump->records[i].rec.start, &start); 921842565f2Syasuoka timespecsub(&now, &start, &dif); 922842565f2Syasuoka printf("#%d\n", cnt + 1); 923842565f2Syasuoka if (dump->records[i].rec.timeout.tv_sec == 0) 924842565f2Syasuoka ipcp_handle_dump0(dump, dumpsiz, &dif, &start, NULL, i); 925842565f2Syasuoka else { 926842565f2Syasuoka timespecadd(&upt, &dump->records[i].rec.timeout, 927842565f2Syasuoka &timeout); 928842565f2Syasuoka ipcp_handle_dump0(dump, dumpsiz, &dif, &start, 929842565f2Syasuoka &timeout, i); 930842565f2Syasuoka } 931842565f2Syasuoka } 932842565f2Syasuoka } 933842565f2Syasuoka 934842565f2Syasuoka 935842565f2Syasuoka /*********************************************************************** 936842565f2Syasuoka * Miscellaneous functions 937842565f2Syasuoka ***********************************************************************/ 938842565f2Syasuoka const char * 939a7ca44b8Syasuoka radius_code_str(int code) 940a7ca44b8Syasuoka { 941a7ca44b8Syasuoka int i; 942a7ca44b8Syasuoka static struct _codestr { 943a7ca44b8Syasuoka int code; 944a7ca44b8Syasuoka const char *str; 945a7ca44b8Syasuoka } codestr[] = { 946a7ca44b8Syasuoka { RADIUS_CODE_ACCESS_REQUEST, "Access-Request" }, 947a7ca44b8Syasuoka { RADIUS_CODE_ACCESS_ACCEPT, "Access-Accept" }, 948a7ca44b8Syasuoka { RADIUS_CODE_ACCESS_REJECT, "Access-Reject" }, 949a7ca44b8Syasuoka { RADIUS_CODE_ACCOUNTING_REQUEST, "Accounting-Request" }, 950a7ca44b8Syasuoka { RADIUS_CODE_ACCOUNTING_RESPONSE, "Accounting-Response" }, 951a7ca44b8Syasuoka { RADIUS_CODE_ACCESS_CHALLENGE, "Access-Challenge" }, 952a7ca44b8Syasuoka { RADIUS_CODE_STATUS_SERVER, "Status-Server" }, 953a7ca44b8Syasuoka { RADIUS_CODE_STATUS_CLIENT, "Status-Client" }, 954a7ca44b8Syasuoka { -1, NULL } 955a7ca44b8Syasuoka }; 956a7ca44b8Syasuoka 957a7ca44b8Syasuoka for (i = 0; codestr[i].code != -1; i++) { 958a7ca44b8Syasuoka if (codestr[i].code == code) 959a7ca44b8Syasuoka return (codestr[i].str); 960a7ca44b8Syasuoka } 961a7ca44b8Syasuoka 962a7ca44b8Syasuoka return ("Unknown"); 963a7ca44b8Syasuoka } 964a7ca44b8Syasuoka 965a7ca44b8Syasuoka static const char * 966a7ca44b8Syasuoka hexstr(const u_char *data, int len, char *str, int strsiz) 967a7ca44b8Syasuoka { 968a7ca44b8Syasuoka int i, off = 0; 969a7ca44b8Syasuoka static const char hex[] = "0123456789abcdef"; 970a7ca44b8Syasuoka 971a7ca44b8Syasuoka for (i = 0; i < len; i++) { 972a7ca44b8Syasuoka if (strsiz - off < 3) 973a7ca44b8Syasuoka return (NULL); 974a7ca44b8Syasuoka str[off++] = hex[(data[i] & 0xf0) >> 4]; 975a7ca44b8Syasuoka str[off++] = hex[(data[i] & 0x0f)]; 976a7ca44b8Syasuoka str[off++] = ' '; 977a7ca44b8Syasuoka } 978a7ca44b8Syasuoka if (strsiz - off < 1) 979a7ca44b8Syasuoka return (NULL); 980a7ca44b8Syasuoka 981a7ca44b8Syasuoka str[off++] = '\0'; 982a7ca44b8Syasuoka 983a7ca44b8Syasuoka return (str); 984a7ca44b8Syasuoka } 985842565f2Syasuoka 986842565f2Syasuoka const char * 987842565f2Syasuoka sockaddr_str(struct sockaddr *sa, char *buf, size_t bufsiz) 988842565f2Syasuoka { 989842565f2Syasuoka int noport, ret; 990842565f2Syasuoka char hbuf[NI_MAXHOST], sbuf[NI_MAXSERV]; 991842565f2Syasuoka 992842565f2Syasuoka if (ntohs(((struct sockaddr_in *)sa)->sin_port) == 0) { 993842565f2Syasuoka noport = 1; 994842565f2Syasuoka ret = getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), NULL, 0, 995842565f2Syasuoka NI_NUMERICHOST); 996842565f2Syasuoka } else { 997842565f2Syasuoka noport = 0; 998842565f2Syasuoka ret = getnameinfo(sa, sa->sa_len, hbuf, sizeof(hbuf), sbuf, 999842565f2Syasuoka sizeof(sbuf), NI_NUMERICHOST | NI_NUMERICSERV); 1000842565f2Syasuoka } 1001842565f2Syasuoka if (ret != 0) 1002842565f2Syasuoka return ""; 1003842565f2Syasuoka if (noport) 1004842565f2Syasuoka strlcpy(buf, hbuf, bufsiz); 1005842565f2Syasuoka else if (sa->sa_family == AF_INET6) 1006842565f2Syasuoka snprintf(buf, bufsiz, "[%s]:%s", hbuf, sbuf); 1007842565f2Syasuoka else 1008842565f2Syasuoka snprintf(buf, bufsiz, "%s:%s", hbuf, sbuf); 1009842565f2Syasuoka 1010842565f2Syasuoka return (buf); 1011842565f2Syasuoka } 1012842565f2Syasuoka 1013842565f2Syasuoka const char * 1014842565f2Syasuoka time_long_str(struct timespec *tim, char *buf, size_t bufsiz) 1015842565f2Syasuoka { 1016842565f2Syasuoka struct tm tm; 1017842565f2Syasuoka 1018842565f2Syasuoka localtime_r(&tim->tv_sec, &tm); 1019842565f2Syasuoka strftime(buf, bufsiz, "%F %T", &tm); 1020842565f2Syasuoka 1021842565f2Syasuoka return (buf); 1022842565f2Syasuoka } 1023842565f2Syasuoka 1024842565f2Syasuoka const char * 1025842565f2Syasuoka time_short_str(struct timespec *tim, struct timespec *dif, char *buf, 1026842565f2Syasuoka size_t bufsiz) 1027842565f2Syasuoka { 1028842565f2Syasuoka struct tm tm; 1029842565f2Syasuoka 1030842565f2Syasuoka localtime_r(&tim->tv_sec, &tm); 1031842565f2Syasuoka if (dif->tv_sec < 12 * 60 * 60) 1032842565f2Syasuoka strftime(buf, bufsiz, "%l:%M%p", &tm); 1033842565f2Syasuoka else if (dif->tv_sec < 7 * 24 * 60 * 60) 1034842565f2Syasuoka strftime(buf, bufsiz, "%e%b%y", &tm); 1035842565f2Syasuoka else 1036842565f2Syasuoka strftime(buf, bufsiz, "%m/%d", &tm); 1037842565f2Syasuoka 1038842565f2Syasuoka return (buf); 1039842565f2Syasuoka } 1040842565f2Syasuoka 1041842565f2Syasuoka const char * 1042842565f2Syasuoka humanize_seconds(long seconds, char *buf, size_t bufsiz) 1043842565f2Syasuoka { 1044842565f2Syasuoka char fbuf[80]; 1045842565f2Syasuoka int hour, min; 1046842565f2Syasuoka 1047842565f2Syasuoka hour = seconds / 3600; 1048842565f2Syasuoka min = (seconds % 3600) / 60; 1049842565f2Syasuoka 1050842565f2Syasuoka if (bufsiz == 0) 1051842565f2Syasuoka return NULL; 1052842565f2Syasuoka buf[0] = '\0'; 1053842565f2Syasuoka if (hour != 0 || min != 0) { 1054842565f2Syasuoka strlcat(buf, " (", bufsiz); 1055842565f2Syasuoka if (hour != 0) { 1056842565f2Syasuoka snprintf(fbuf, sizeof(fbuf), "%d hour%s", hour, 1057842565f2Syasuoka (hour == 1)? "" : "s"); 1058842565f2Syasuoka strlcat(buf, fbuf, bufsiz); 1059842565f2Syasuoka } 1060842565f2Syasuoka if (hour != 0 && min != 0) 1061842565f2Syasuoka strlcat(buf, " and ", bufsiz); 1062842565f2Syasuoka if (min != 0) { 1063842565f2Syasuoka snprintf(fbuf, sizeof(fbuf), "%d minute%s", min, 1064842565f2Syasuoka (min == 1)? "" : "s"); 1065842565f2Syasuoka strlcat(buf, fbuf, bufsiz); 1066842565f2Syasuoka } 1067842565f2Syasuoka strlcat(buf, ")", bufsiz); 1068842565f2Syasuoka } 1069842565f2Syasuoka 1070842565f2Syasuoka return (buf); 1071842565f2Syasuoka } 1072