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