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
usage(void)87 usage(void)
88 {
89 extern char *__progname;
90
91 fprintf(stderr, "usage: %s command [argument ...]\n", __progname);
92 }
93
94 int
main(int argc,char * argv[])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
radius_test(struct parse_result * res)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
radius_test_send(int thing,short revents,void * arg)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
radius_test_recv(int sock,short revents,void * arg)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
radius_test_timedout(int thing,short revents,void * arg)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
radius_dump(FILE * out,RADIUS_PACKET * pkt,bool resp,const char * secret)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
ipcp_handle_imsg(struct parse_result * res,struct imsg * imsg,int cnt)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
ipcp_handle_show(struct radiusd_ipcp_db_dump * dump,size_t dumpsiz,int first)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
ipcp_handle_dump(struct radiusd_ipcp_db_dump * dump,size_t dumpsiz,int idx)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
ipcp_handle_dump0(struct radiusd_ipcp_db_dump * dump,size_t dumpsiz,struct timespec * dif,struct timespec * start,struct timespec * timeout,int idx)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
ipcp_handle_stat(struct radiusd_ipcp_statistics * stat)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
ipcp_handle_jsons(struct radiusd_ipcp_db_dump * dump,size_t dumpsiz,int first)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
ipcp_handle_json(struct radiusd_ipcp_db_dump * dump,size_t dumpsiz,struct radiusd_ipcp_statistics * stat,int idx)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
ipcp_handle_json0(struct radiusd_ipcp_db_dump * dump,size_t dumpsiz,struct timespec * dif,struct timespec * start,struct timespec * timeout,int idx)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
ipcp_handle_dumps(struct radiusd_ipcp_db_dump * dump,size_t dumpsiz,int first)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 *
radius_code_str(int code)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 *
hexstr(const u_char * data,int len,char * str,int strsiz)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 *
sockaddr_str(struct sockaddr * sa,char * buf,size_t bufsiz)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 *
time_long_str(struct timespec * tim,char * buf,size_t bufsiz)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 *
time_short_str(struct timespec * tim,struct timespec * dif,char * buf,size_t bufsiz)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 *
humanize_seconds(long seconds,char * buf,size_t bufsiz)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