xref: /openbsd-src/usr.sbin/radiusctl/radiusctl.c (revision 882428cdbdd2944d8f59bc8621c131fd814fb6ee)
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
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 	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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 *
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 *
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 *
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 *
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 *
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 *
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