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