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