xref: /openbsd-src/usr.sbin/radiusd/radiusd.c (revision abc3a9caf406707c5ef3c80adcf491e5111b4294)
1*abc3a9caSyasuoka /*	$OpenBSD: radiusd.c,v 1.62 2025/01/29 10:12:22 yasuoka Exp $	*/
2a7ca44b8Syasuoka 
3a7ca44b8Syasuoka /*
4237e61d9Syasuoka  * Copyright (c) 2013, 2023 Internet Initiative Japan Inc.
5a7ca44b8Syasuoka  *
6a7ca44b8Syasuoka  * Permission to use, copy, modify, and distribute this software for any
7a7ca44b8Syasuoka  * purpose with or without fee is hereby granted, provided that the above
8a7ca44b8Syasuoka  * copyright notice and this permission notice appear in all copies.
9a7ca44b8Syasuoka  *
10a7ca44b8Syasuoka  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11a7ca44b8Syasuoka  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12a7ca44b8Syasuoka  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13a7ca44b8Syasuoka  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14a7ca44b8Syasuoka  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15a7ca44b8Syasuoka  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16a7ca44b8Syasuoka  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17a7ca44b8Syasuoka  */
18a7ca44b8Syasuoka 
19a7ca44b8Syasuoka #include <sys/types.h>
20237e61d9Syasuoka #include <netinet/in.h>
21237e61d9Syasuoka #include <arpa/inet.h>
22a7ca44b8Syasuoka #include <sys/queue.h>
23237e61d9Syasuoka #include <sys/socket.h>
24237e61d9Syasuoka #include <sys/time.h>
25a7ca44b8Syasuoka #include <sys/uio.h>
26a7ca44b8Syasuoka #include <sys/wait.h>
27a7ca44b8Syasuoka 
28a7ca44b8Syasuoka #include <err.h>
29a7ca44b8Syasuoka #include <errno.h>
30a7ca44b8Syasuoka #include <event.h>
31ab3cfbd9Syasuoka #include <fcntl.h>
32a7ca44b8Syasuoka #include <fnmatch.h>
33a7ca44b8Syasuoka #include <imsg.h>
34a7ca44b8Syasuoka #include <netdb.h>
358c9be245Syasuoka #include <paths.h>
36a7ca44b8Syasuoka #include <pwd.h>
37a7ca44b8Syasuoka #include <signal.h>
38a7ca44b8Syasuoka #include <stdbool.h>
39a7ca44b8Syasuoka #include <stdio.h>
40a7ca44b8Syasuoka #include <stdlib.h>
41a7ca44b8Syasuoka #include <string.h>
42a7ca44b8Syasuoka #include <syslog.h>
43a7ca44b8Syasuoka #include <unistd.h>
44a7ca44b8Syasuoka 
45a7ca44b8Syasuoka #include <radius.h>
46a7ca44b8Syasuoka 
47a7ca44b8Syasuoka #include "radiusd.h"
48a7ca44b8Syasuoka #include "radiusd_local.h"
49a6eb37b5Syasuoka #include "radius_subr.h"
50a7ca44b8Syasuoka #include "log.h"
51a7ca44b8Syasuoka #include "util.h"
52a7ca44b8Syasuoka #include "imsg_subr.h"
53842565f2Syasuoka #include "control.h"
54a7ca44b8Syasuoka 
55a7ca44b8Syasuoka static int		 radiusd_start(struct radiusd *);
56a7ca44b8Syasuoka static void		 radiusd_stop(struct radiusd *);
57a7ca44b8Syasuoka static void		 radiusd_free(struct radiusd *);
58a7ca44b8Syasuoka static void		 radiusd_listen_on_event(int, short, void *);
59936475aaSyasuoka static void		 radiusd_listen_handle_packet(struct radiusd_listen *,
60936475aaSyasuoka 			    RADIUS_PACKET *, struct sockaddr *, socklen_t);
61a7ca44b8Syasuoka static void		 radiusd_on_sigterm(int, short, void *);
62a7ca44b8Syasuoka static void		 radiusd_on_sigint(int, short, void *);
63a7ca44b8Syasuoka static void		 radiusd_on_sighup(int, short, void *);
64a7ca44b8Syasuoka static void		 radiusd_on_sigchld(int, short, void *);
65936475aaSyasuoka static void		 raidus_query_access_request(struct radius_query *);
66936475aaSyasuoka static void		 radius_query_access_response(struct radius_query *);
67747da5e9Syasuoka static void		 raidus_query_accounting_request(
68747da5e9Syasuoka 			    struct radiusd_accounting *, struct radius_query *);
69ed1dc925Syasuoka static void		 radius_query_accounting_response(
70ed1dc925Syasuoka 			    struct radius_query *);
71ed1dc925Syasuoka static const char	*radius_query_client_secret(struct radius_query *);
72a7ca44b8Syasuoka static const char	*radius_code_string(int);
73747da5e9Syasuoka static const char	*radius_acct_status_type_string(uint32_t);
74ed1dc925Syasuoka static int		 radiusd_access_response_fixup(struct radius_query *,
75ed1dc925Syasuoka 			    struct radius_query *, bool);
76a7ca44b8Syasuoka static void		 radiusd_module_reset_ev_handler(
77a7ca44b8Syasuoka 			    struct radiusd_module *);
784a4dce94Syasuoka static int		 radiusd_module_imsg_read(struct radiusd_module *);
79a7ca44b8Syasuoka static void		 radiusd_module_imsg(struct radiusd_module *,
80a7ca44b8Syasuoka 			    struct imsg *);
81a7ca44b8Syasuoka 
82a7ca44b8Syasuoka static struct radiusd_module_radpkt_arg *
83a7ca44b8Syasuoka 			 radiusd_module_recv_radpkt(struct radiusd_module *,
84a7ca44b8Syasuoka 			    struct imsg *, uint32_t, const char *);
85a7ca44b8Syasuoka static void		 radiusd_module_on_imsg_io(int, short, void *);
8627a2e2e1Syasuoka static void		 radiusd_module_start(struct radiusd_module *);
8727a2e2e1Syasuoka static void		 radiusd_module_stop(struct radiusd_module *);
88a7ca44b8Syasuoka static void		 radiusd_module_close(struct radiusd_module *);
89a7ca44b8Syasuoka static void		 radiusd_module_userpass(struct radiusd_module *,
90a7ca44b8Syasuoka 			    struct radius_query *);
91a7ca44b8Syasuoka static void		 radiusd_module_access_request(struct radiusd_module *,
92a7ca44b8Syasuoka 			    struct radius_query *);
93ed1dc925Syasuoka static void		 radiusd_module_next_response(struct radiusd_module *,
94ed1dc925Syasuoka 			    struct radius_query *, RADIUS_PACKET *);
95237e61d9Syasuoka static void		 radiusd_module_request_decoration(
96237e61d9Syasuoka 			    struct radiusd_module *, struct radius_query *);
97237e61d9Syasuoka static void		 radiusd_module_response_decoration(
98237e61d9Syasuoka 			    struct radiusd_module *, struct radius_query *);
99747da5e9Syasuoka static void		 radiusd_module_account_request(struct radiusd_module *,
100747da5e9Syasuoka 			    struct radius_query *);
101237e61d9Syasuoka static int		 imsg_compose_radius_packet(struct imsgbuf *,
102237e61d9Syasuoka 			    uint32_t, u_int, RADIUS_PACKET *);
103747da5e9Syasuoka static void		 close_stdio(void);
104a7ca44b8Syasuoka 
105a7ca44b8Syasuoka static u_int		 radius_query_id_seq = 0;
106a7ca44b8Syasuoka int			 debug = 0;
107842565f2Syasuoka struct radiusd		*radiusd_s = NULL;
108a7ca44b8Syasuoka 
109a7ca44b8Syasuoka static __dead void
110a7ca44b8Syasuoka usage(void)
111a7ca44b8Syasuoka {
112a7ca44b8Syasuoka 	extern char *__progname;
113a7ca44b8Syasuoka 
1140630f070Syasuoka 	fprintf(stderr, "usage: %s [-dn] [-f file]\n", __progname);
115ef9ad095Smillert 	exit(EXIT_FAILURE);
116a7ca44b8Syasuoka }
117a7ca44b8Syasuoka 
118a7ca44b8Syasuoka int
119a7ca44b8Syasuoka main(int argc, char *argv[])
120a7ca44b8Syasuoka {
121a7ca44b8Syasuoka 	extern char		*__progname;
122a7ca44b8Syasuoka 	const char		*conffile = CONFFILE;
12343436be7Syasuoka 	int			 ch, error;
124a7ca44b8Syasuoka 	struct radiusd		*radiusd;
125a7ca44b8Syasuoka 	bool			 noaction = false;
126a7ca44b8Syasuoka 	struct passwd		*pw;
127a7ca44b8Syasuoka 
1280630f070Syasuoka 	while ((ch = getopt(argc, argv, "df:n")) != -1)
129a7ca44b8Syasuoka 		switch (ch) {
130a7ca44b8Syasuoka 		case 'd':
131a7ca44b8Syasuoka 			debug++;
132a7ca44b8Syasuoka 			break;
133a7ca44b8Syasuoka 
134a7ca44b8Syasuoka 		case 'f':
135a7ca44b8Syasuoka 			conffile = optarg;
136a7ca44b8Syasuoka 			break;
137a7ca44b8Syasuoka 
138a7ca44b8Syasuoka 		case 'n':
139a7ca44b8Syasuoka 			noaction = true;
140a7ca44b8Syasuoka 			break;
141a7ca44b8Syasuoka 
1420630f070Syasuoka 		default:
143a7ca44b8Syasuoka 			usage();
144a7ca44b8Syasuoka 			/* NOTREACHED */
145a7ca44b8Syasuoka 		}
146a7ca44b8Syasuoka 
147a7ca44b8Syasuoka 	argc -= optind;
148a7ca44b8Syasuoka 	argv += optind;
149a7ca44b8Syasuoka 
150a7ca44b8Syasuoka 	if (argc != 0)
151a7ca44b8Syasuoka 		usage();
152a7ca44b8Syasuoka 
153a7ca44b8Syasuoka 	if ((radiusd = calloc(1, sizeof(*radiusd))) == NULL)
154a7ca44b8Syasuoka 		err(1, "calloc");
155842565f2Syasuoka 	radiusd_s = radiusd;
156a7ca44b8Syasuoka 	TAILQ_INIT(&radiusd->listen);
157a7ca44b8Syasuoka 	TAILQ_INIT(&radiusd->query);
158a7ca44b8Syasuoka 
1598c9be245Syasuoka 	if (!noaction && debug == 0)
1608c9be245Syasuoka 		daemon(0, 1);	/* pend closing stdio files */
1618c9be245Syasuoka 
162a7ca44b8Syasuoka 	if (parse_config(conffile, radiusd) != 0)
163ef9ad095Smillert 		errx(EXIT_FAILURE, "config error");
164c917c3caSyasuoka 	log_init(debug);
165a7ca44b8Syasuoka 	if (noaction) {
166a7ca44b8Syasuoka 		fprintf(stderr, "configuration OK\n");
167a7ca44b8Syasuoka 		exit(EXIT_SUCCESS);
168a7ca44b8Syasuoka 	}
169a7ca44b8Syasuoka 
170a7ca44b8Syasuoka 	if (debug == 0)
1718c9be245Syasuoka 		close_stdio(); /* close stdio files now */
1728c9be245Syasuoka 
173842565f2Syasuoka 	if (control_init(RADIUSD_SOCK) == -1)
174842565f2Syasuoka 		exit(EXIT_FAILURE);
175842565f2Syasuoka 
176c469475aSyasuoka 	event_init();
177a7ca44b8Syasuoka 
178a7ca44b8Syasuoka 	if ((pw = getpwnam(RADIUSD_USER)) == NULL)
179a7ca44b8Syasuoka 		errx(EXIT_FAILURE, "user `%s' is not found in password "
180a7ca44b8Syasuoka 		    "database", RADIUSD_USER);
181a7ca44b8Syasuoka 
182a7ca44b8Syasuoka 	if (chroot(pw->pw_dir) == -1)
183a7ca44b8Syasuoka 		err(EXIT_FAILURE, "chroot");
184a7ca44b8Syasuoka 	if (chdir("/") == -1)
185a7ca44b8Syasuoka 		err(EXIT_FAILURE, "chdir(\"/\")");
186a7ca44b8Syasuoka 
187a7ca44b8Syasuoka 	if (setgroups(1, &pw->pw_gid) ||
188a7ca44b8Syasuoka 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
189a7ca44b8Syasuoka 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
190a7ca44b8Syasuoka 		err(EXIT_FAILURE, "cannot drop privileges");
191a7ca44b8Syasuoka 
192a7ca44b8Syasuoka 	signal(SIGPIPE, SIG_IGN);
193a7ca44b8Syasuoka 	openlog(NULL, LOG_PID, LOG_DAEMON);
194a7ca44b8Syasuoka 
195a7ca44b8Syasuoka 	signal_set(&radiusd->ev_sigterm, SIGTERM, radiusd_on_sigterm, radiusd);
196a7ca44b8Syasuoka 	signal_set(&radiusd->ev_sigint,  SIGINT,  radiusd_on_sigint,  radiusd);
197a7ca44b8Syasuoka 	signal_set(&radiusd->ev_sighup,  SIGHUP,  radiusd_on_sighup,  radiusd);
198a7ca44b8Syasuoka 	signal_set(&radiusd->ev_sigchld, SIGCHLD, radiusd_on_sigchld, radiusd);
199a7ca44b8Syasuoka 
200a7ca44b8Syasuoka 	if (radiusd_start(radiusd) != 0)
201ef9ad095Smillert 		errx(EXIT_FAILURE, "start failed");
202842565f2Syasuoka 	if (control_listen() == -1)
203842565f2Syasuoka 		exit(EXIT_FAILURE);
204a7ca44b8Syasuoka 
20504581dc7Syasuoka 	if (pledge("stdio inet", NULL) == -1)
20604581dc7Syasuoka 		err(EXIT_FAILURE, "pledge");
20704581dc7Syasuoka 
208842565f2Syasuoka 	event_loop(0);
209a7ca44b8Syasuoka 
21059396270Syasuoka 	if (radiusd->error != 0)
21159396270Syasuoka 		log_warnx("exiting on error");
21259396270Syasuoka 
213842565f2Syasuoka 	radiusd_stop(radiusd);
214842565f2Syasuoka 	control_cleanup();
215842565f2Syasuoka 
216842565f2Syasuoka 	event_loop(0);
217842565f2Syasuoka 
21843436be7Syasuoka 	error = radiusd->error;
219a7ca44b8Syasuoka 	radiusd_free(radiusd);
220a7ca44b8Syasuoka 	event_base_free(NULL);
221a7ca44b8Syasuoka 
22243436be7Syasuoka 	if (error != 0)
22359396270Syasuoka 		exit(EXIT_FAILURE);
22459396270Syasuoka 	else
225a7ca44b8Syasuoka 		exit(EXIT_SUCCESS);
226a7ca44b8Syasuoka }
227a7ca44b8Syasuoka 
228a7ca44b8Syasuoka static int
229a7ca44b8Syasuoka radiusd_start(struct radiusd *radiusd)
230a7ca44b8Syasuoka {
231a7ca44b8Syasuoka 	struct radiusd_listen	*l;
232a7ca44b8Syasuoka 	struct radiusd_module	*module;
23355b9f5beSyasuoka 	int			 s, on;
234a7ca44b8Syasuoka 	char			 hbuf[NI_MAXHOST];
235a7ca44b8Syasuoka 
236a7ca44b8Syasuoka 	TAILQ_FOREACH(l, &radiusd->listen, next) {
237a7ca44b8Syasuoka 		if (getnameinfo(
238a7ca44b8Syasuoka 		    (struct sockaddr *)&l->addr, l->addr.ipv4.sin_len,
239a7ca44b8Syasuoka 		    hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) {
240a7ca44b8Syasuoka 			log_warn("%s: getnameinfo()", __func__);
241a7ca44b8Syasuoka 			goto on_error;
242a7ca44b8Syasuoka 		}
2435f13b87eSguenther 		if ((s = socket(l->addr.ipv4.sin_family,
244df69c215Sderaadt 		    l->stype | SOCK_NONBLOCK, l->sproto)) == -1) {
245a7ca44b8Syasuoka 			log_warn("Listen %s port %d is failed: socket()",
246a7ca44b8Syasuoka 			    hbuf, (int)htons(l->addr.ipv4.sin_port));
247a7ca44b8Syasuoka 			goto on_error;
248a7ca44b8Syasuoka 		}
24955b9f5beSyasuoka 
25055b9f5beSyasuoka 		on = 1;
25155b9f5beSyasuoka 		if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on))
25255b9f5beSyasuoka 		    == -1)
25355b9f5beSyasuoka 			log_warn("%s: setsockopt(,,SO_REUSEADDR) failed: %m",
25455b9f5beSyasuoka 			    __func__);
255a7ca44b8Syasuoka 		if (bind(s, (struct sockaddr *)&l->addr, l->addr.ipv4.sin_len)
256a7ca44b8Syasuoka 		    != 0) {
257a7ca44b8Syasuoka 			log_warn("Listen %s port %d is failed: bind()",
258a7ca44b8Syasuoka 			    hbuf, (int)htons(l->addr.ipv4.sin_port));
259a7ca44b8Syasuoka 			close(s);
260a7ca44b8Syasuoka 			goto on_error;
261a7ca44b8Syasuoka 		}
262a7ca44b8Syasuoka 		if (l->addr.ipv4.sin_family == AF_INET)
263a7ca44b8Syasuoka 			log_info("Start listening on %s:%d/udp", hbuf,
264a7ca44b8Syasuoka 			    (int)ntohs(l->addr.ipv4.sin_port));
265a7ca44b8Syasuoka 		else
266a7ca44b8Syasuoka 			log_info("Start listening on [%s]:%d/udp", hbuf,
267a7ca44b8Syasuoka 			    (int)ntohs(l->addr.ipv4.sin_port));
268a7ca44b8Syasuoka 		event_set(&l->ev, s, EV_READ | EV_PERSIST,
269a7ca44b8Syasuoka 		    radiusd_listen_on_event, l);
270a7ca44b8Syasuoka 		if (event_add(&l->ev, NULL) != 0) {
271a7ca44b8Syasuoka 			log_warn("event_add() failed at %s()", __func__);
272a7ca44b8Syasuoka 			close(s);
273a7ca44b8Syasuoka 			goto on_error;
274a7ca44b8Syasuoka 		}
275a7ca44b8Syasuoka 		l->sock = s;
276a7ca44b8Syasuoka 		l->radiusd = radiusd;
277a7ca44b8Syasuoka 	}
278a7ca44b8Syasuoka 
279a7ca44b8Syasuoka 	signal_add(&radiusd->ev_sigterm, NULL);
280a7ca44b8Syasuoka 	signal_add(&radiusd->ev_sigint, NULL);
281a7ca44b8Syasuoka 	signal_add(&radiusd->ev_sighup, NULL);
282a7ca44b8Syasuoka 	signal_add(&radiusd->ev_sigchld, NULL);
283a7ca44b8Syasuoka 
284a7ca44b8Syasuoka 	TAILQ_FOREACH(module, &radiusd->module, next) {
28516971584Syasuoka 		if (debug > 0)
28616971584Syasuoka 			radiusd_module_set(module, "_debug", 0, NULL);
287a7ca44b8Syasuoka 		radiusd_module_start(module);
288a7ca44b8Syasuoka 	}
289a7ca44b8Syasuoka 
290a7ca44b8Syasuoka 	return (0);
291a7ca44b8Syasuoka on_error:
29259396270Syasuoka 	radiusd->error++;
293842565f2Syasuoka 	event_loopbreak();
294a7ca44b8Syasuoka 
295a7ca44b8Syasuoka 	return (-1);
296a7ca44b8Syasuoka }
297a7ca44b8Syasuoka 
298a7ca44b8Syasuoka static void
299a7ca44b8Syasuoka radiusd_stop(struct radiusd *radiusd)
300a7ca44b8Syasuoka {
301a7ca44b8Syasuoka 	char			 hbuf[NI_MAXHOST];
302a7ca44b8Syasuoka 	struct radiusd_listen	*l;
303a7ca44b8Syasuoka 	struct radiusd_module	*module;
304a7ca44b8Syasuoka 
305a7ca44b8Syasuoka 	TAILQ_FOREACH_REVERSE(l, &radiusd->listen, radiusd_listen_head, next) {
306a7ca44b8Syasuoka 		if (l->sock >= 0) {
307a7ca44b8Syasuoka 			if (getnameinfo(
308a7ca44b8Syasuoka 			    (struct sockaddr *)&l->addr, l->addr.ipv4.sin_len,
309a7ca44b8Syasuoka 			    hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0)
310a7ca44b8Syasuoka 				strlcpy(hbuf, "error", sizeof(hbuf));
311a7ca44b8Syasuoka 			if (l->addr.ipv4.sin_family == AF_INET)
312a7ca44b8Syasuoka 				log_info("Stop listening on %s:%d/udp", hbuf,
313a7ca44b8Syasuoka 				    (int)ntohs(l->addr.ipv4.sin_port));
314a7ca44b8Syasuoka 			else
315a7ca44b8Syasuoka 				log_info("Stop listening on [%s]:%d/udp", hbuf,
316a7ca44b8Syasuoka 				    (int)ntohs(l->addr.ipv4.sin_port));
317a7ca44b8Syasuoka 			event_del(&l->ev);
318a7ca44b8Syasuoka 			close(l->sock);
319a7ca44b8Syasuoka 		}
320a7ca44b8Syasuoka 		l->sock = -1;
321a7ca44b8Syasuoka 	}
322a7ca44b8Syasuoka 	TAILQ_FOREACH(module, &radiusd->module, next) {
323a7ca44b8Syasuoka 		radiusd_module_stop(module);
324a7ca44b8Syasuoka 		radiusd_module_close(module);
325a7ca44b8Syasuoka 	}
326a7ca44b8Syasuoka 	if (signal_pending(&radiusd->ev_sigterm, NULL))
327a7ca44b8Syasuoka 		signal_del(&radiusd->ev_sigterm);
328a7ca44b8Syasuoka 	if (signal_pending(&radiusd->ev_sigint, NULL))
329a7ca44b8Syasuoka 		signal_del(&radiusd->ev_sigint);
330a7ca44b8Syasuoka 	if (signal_pending(&radiusd->ev_sighup, NULL))
331a7ca44b8Syasuoka 		signal_del(&radiusd->ev_sighup);
332a7ca44b8Syasuoka 	if (signal_pending(&radiusd->ev_sigchld, NULL))
333a7ca44b8Syasuoka 		signal_del(&radiusd->ev_sigchld);
334a7ca44b8Syasuoka }
335a7ca44b8Syasuoka 
336a7ca44b8Syasuoka static void
337a7ca44b8Syasuoka radiusd_free(struct radiusd *radiusd)
338a7ca44b8Syasuoka {
339a7ca44b8Syasuoka 	int				 i;
340a7ca44b8Syasuoka 	struct radiusd_listen		*listn, *listnt;
341a7ca44b8Syasuoka 	struct radiusd_client		*client, *clientt;
342a7ca44b8Syasuoka 	struct radiusd_module		*module, *modulet;
343a7ca44b8Syasuoka 	struct radiusd_module_ref	*modref, *modreft;
344a7ca44b8Syasuoka 	struct radiusd_authentication	*authen, *authent;
34543436be7Syasuoka 	struct radiusd_accounting	*acct, *acctt;
346a7ca44b8Syasuoka 
347a7ca44b8Syasuoka 	TAILQ_FOREACH_SAFE(authen, &radiusd->authen, next, authent) {
348a7ca44b8Syasuoka 		TAILQ_REMOVE(&radiusd->authen, authen, next);
349a7ca44b8Syasuoka 		free(authen->auth);
350a7ca44b8Syasuoka 		TAILQ_FOREACH_SAFE(modref, &authen->deco, next, modreft) {
351a7ca44b8Syasuoka 			TAILQ_REMOVE(&authen->deco, modref, next);
352a7ca44b8Syasuoka 			free(modref);
353a7ca44b8Syasuoka 		}
354a7ca44b8Syasuoka 		for (i = 0; authen->username[i] != NULL; i++)
355a7ca44b8Syasuoka 			free(authen->username[i]);
356a7ca44b8Syasuoka 		free(authen->username);
357a7ca44b8Syasuoka 		free(authen);
358a7ca44b8Syasuoka 	}
35943436be7Syasuoka 	TAILQ_FOREACH_SAFE(acct, &radiusd->account, next, acctt) {
36043436be7Syasuoka 		TAILQ_REMOVE(&radiusd->account, acct, next);
36143436be7Syasuoka 		free(acct->secret);
36243436be7Syasuoka 		free(acct->acct);
36343436be7Syasuoka 		TAILQ_FOREACH_SAFE(modref, &acct->deco, next, modreft) {
36443436be7Syasuoka 			TAILQ_REMOVE(&acct->deco, modref, next);
36543436be7Syasuoka 			free(modref);
36643436be7Syasuoka 		}
36743436be7Syasuoka 		for (i = 0; acct->username[i] != NULL; i++)
36843436be7Syasuoka 			free(acct->username[i]);
36943436be7Syasuoka 		free(acct->username);
37043436be7Syasuoka 		free(acct);
37143436be7Syasuoka 	}
372a7ca44b8Syasuoka 	TAILQ_FOREACH_SAFE(module, &radiusd->module, next, modulet) {
373a7ca44b8Syasuoka 		TAILQ_REMOVE(&radiusd->module, module, next);
374a7ca44b8Syasuoka 		radiusd_module_unload(module);
375a7ca44b8Syasuoka 	}
376a7ca44b8Syasuoka 	TAILQ_FOREACH_SAFE(client, &radiusd->client, next, clientt) {
377a7ca44b8Syasuoka 		TAILQ_REMOVE(&radiusd->client, client, next);
378a7ca44b8Syasuoka 		explicit_bzero(client->secret, sizeof(client->secret));
379a7ca44b8Syasuoka 		free(client);
380a7ca44b8Syasuoka 	}
381a7ca44b8Syasuoka 	TAILQ_FOREACH_SAFE(listn, &radiusd->listen, next, listnt) {
382a7ca44b8Syasuoka 		TAILQ_REMOVE(&radiusd->listen, listn, next);
383a7ca44b8Syasuoka 		free(listn);
384a7ca44b8Syasuoka 	}
385a7ca44b8Syasuoka 	free(radiusd);
386a7ca44b8Syasuoka }
387a7ca44b8Syasuoka 
388a7ca44b8Syasuoka /***********************************************************************
389a7ca44b8Syasuoka  * Network event handlers
390a7ca44b8Syasuoka  ***********************************************************************/
391a7ca44b8Syasuoka #define IPv4_cmp(_in, _addr, _mask) (				\
392a7ca44b8Syasuoka 	((_in)->s_addr & (_mask)->addr.ipv4.s_addr) ==		\
393a7ca44b8Syasuoka 	    (_addr)->addr.ipv4.s_addr)
394a7ca44b8Syasuoka #define	s6_addr32(_in6)	((uint32_t *)(_in6)->s6_addr)
395a7ca44b8Syasuoka #define IPv6_cmp(_in6, _addr, _mask) (				\
396a7ca44b8Syasuoka 	((s6_addr32(_in6)[3] & (_mask)->addr.addr32[3])		\
397a7ca44b8Syasuoka 	    == (_addr)->addr.addr32[3]) &&			\
398a7ca44b8Syasuoka 	((s6_addr32(_in6)[2] & (_mask)->addr.addr32[2])		\
399a7ca44b8Syasuoka 	    == (_addr)->addr.addr32[2]) &&			\
400a7ca44b8Syasuoka 	((s6_addr32(_in6)[1] & (_mask)->addr.addr32[1])		\
401a7ca44b8Syasuoka 	    == (_addr)->addr.addr32[1]) &&			\
402a7ca44b8Syasuoka 	((s6_addr32(_in6)[0] & (_mask)->addr.addr32[0])		\
403a7ca44b8Syasuoka 	    == (_addr)->addr.addr32[0]))
404a7ca44b8Syasuoka 
405a7ca44b8Syasuoka static void
406a7ca44b8Syasuoka radiusd_listen_on_event(int fd, short evmask, void *ctx)
407a7ca44b8Syasuoka {
408936475aaSyasuoka 	int				 sz;
409936475aaSyasuoka 	RADIUS_PACKET			*packet = NULL;
410a7ca44b8Syasuoka 	struct sockaddr_storage		 peer;
411a7ca44b8Syasuoka 	socklen_t			 peersz;
412936475aaSyasuoka 	struct radiusd_listen		*listn = ctx;
413936475aaSyasuoka 	static u_char			 buf[65535];
414a7ca44b8Syasuoka 
415a7ca44b8Syasuoka 	if (evmask & EV_READ) {
416a7ca44b8Syasuoka 		peersz = sizeof(peer);
417a7ca44b8Syasuoka 		if ((sz = recvfrom(listn->sock, buf, sizeof(buf), 0,
418df69c215Sderaadt 		    (struct sockaddr *)&peer, &peersz)) == -1) {
41951da3916Syasuoka 			if (errno == EAGAIN)
42051da3916Syasuoka 				return;
421a7ca44b8Syasuoka 			log_warn("%s: recvfrom() failed", __func__);
422936475aaSyasuoka 			return;
423a7ca44b8Syasuoka 		}
424a7ca44b8Syasuoka 		RADIUSD_ASSERT(peer.ss_family == AF_INET ||
425a7ca44b8Syasuoka 		    peer.ss_family == AF_INET6);
426936475aaSyasuoka 		if ((packet = radius_convert_packet(buf, sz)) == NULL)
427936475aaSyasuoka 			log_warn("%s: radius_convert_packet() failed",
428936475aaSyasuoka 			    __func__);
429936475aaSyasuoka 		else
430936475aaSyasuoka 			radiusd_listen_handle_packet(listn, packet,
431936475aaSyasuoka 			    (struct sockaddr *)&peer, peersz);
432936475aaSyasuoka 	}
433936475aaSyasuoka }
434a7ca44b8Syasuoka 
435936475aaSyasuoka static void
436936475aaSyasuoka radiusd_listen_handle_packet(struct radiusd_listen *listn,
437936475aaSyasuoka     RADIUS_PACKET *packet, struct sockaddr *peer, socklen_t peerlen)
438936475aaSyasuoka {
439936475aaSyasuoka 	int				 i, req_id, req_code;
440936475aaSyasuoka 	static char			 username[256];
441936475aaSyasuoka 	char				 peerstr[NI_MAXHOST + NI_MAXSERV + 30];
442936475aaSyasuoka 	struct radiusd_authentication	*authen;
443747da5e9Syasuoka 	struct radiusd_accounting	*accounting;
444936475aaSyasuoka 	struct radiusd_client		*client;
445936475aaSyasuoka 	struct radius_query		*q = NULL;
446747da5e9Syasuoka 	uint32_t			 acct_status;
447936475aaSyasuoka #define in(_x)	(((struct sockaddr_in  *)_x)->sin_addr)
448936475aaSyasuoka #define in6(_x)	(((struct sockaddr_in6 *)_x)->sin6_addr)
449936475aaSyasuoka 
450936475aaSyasuoka 	req_id = radius_get_id(packet);
451936475aaSyasuoka 	req_code = radius_get_code(packet);
452a7ca44b8Syasuoka 	/* prepare some information about this messages */
453936475aaSyasuoka 	if (addrport_tostring(peer, peerlen, peerstr, sizeof(peerstr)) ==
454936475aaSyasuoka 	    NULL) {
455a7ca44b8Syasuoka 		log_warn("%s: getnameinfo() failed", __func__);
456a7ca44b8Syasuoka 		goto on_error;
457a7ca44b8Syasuoka 	}
458a7ca44b8Syasuoka 
459a7ca44b8Syasuoka 	/*
460a7ca44b8Syasuoka 	 * Find a matching `client' entry
461a7ca44b8Syasuoka 	 */
462a7ca44b8Syasuoka 	TAILQ_FOREACH(client, &listn->radiusd->client, next) {
463936475aaSyasuoka 		if (client->af != peer->sa_family)
464a7ca44b8Syasuoka 			continue;
465936475aaSyasuoka 		if (peer->sa_family == AF_INET && IPv4_cmp(
466936475aaSyasuoka 		    &in(peer), &client->addr, &client->mask))
467a7ca44b8Syasuoka 			break;
468936475aaSyasuoka 		else if (peer->sa_family == AF_INET6 && IPv6_cmp(
469936475aaSyasuoka 		    &in6(peer), &client->addr, &client->mask))
470a7ca44b8Syasuoka 			break;
471a7ca44b8Syasuoka 	}
472a7ca44b8Syasuoka 	if (client == NULL) {
473936475aaSyasuoka 		log_warnx("Received %s(code=%d) from %s id=%d: no `client' "
474936475aaSyasuoka 		    "matches", radius_code_string(req_code), req_code, peerstr,
475936475aaSyasuoka 		    req_id);
476a7ca44b8Syasuoka 		goto on_error;
477a7ca44b8Syasuoka 	}
478a7ca44b8Syasuoka 
479747da5e9Syasuoka 	/* Check the request authenticator if accounting */
480e0671980Syasuoka 	if (req_code == RADIUS_CODE_ACCOUNTING_REQUEST &&
481e0671980Syasuoka 	    radius_check_accounting_request_authenticator(packet,
482e0671980Syasuoka 	    client->secret) != 0) {
483747da5e9Syasuoka 		log_warnx("Received %s(code=%d) from %s id=%d: bad request "
484747da5e9Syasuoka 		    "authenticator", radius_code_string(req_code), req_code,
485747da5e9Syasuoka 		    peerstr, req_id);
486747da5e9Syasuoka 		goto on_error;
487747da5e9Syasuoka 	}
488747da5e9Syasuoka 
489a7ca44b8Syasuoka 	/* Check the client's Message-Authenticator */
490747da5e9Syasuoka 	if (client->msgauth_required && !listn->accounting &&
491747da5e9Syasuoka 	    !radius_has_attr(packet, RADIUS_TYPE_MESSAGE_AUTHENTICATOR)) {
492936475aaSyasuoka 		log_warnx("Received %s(code=%d) from %s id=%d: no message "
493936475aaSyasuoka 		    "authenticator", radius_code_string(req_code), req_code,
494936475aaSyasuoka 		    peerstr, req_id);
495a7ca44b8Syasuoka 		goto on_error;
496a7ca44b8Syasuoka 	}
497a7ca44b8Syasuoka 
498936475aaSyasuoka 	if (radius_has_attr(packet, RADIUS_TYPE_MESSAGE_AUTHENTICATOR) &&
499936475aaSyasuoka 	    radius_check_message_authenticator(packet, client->secret) != 0) {
500936475aaSyasuoka 		log_warnx("Received %s(code=%d) from %s id=%d: bad message "
501936475aaSyasuoka 		    "authenticator", radius_code_string(req_code), req_code,
502936475aaSyasuoka 		    peerstr, req_id);
503a7ca44b8Syasuoka 		goto on_error;
504a7ca44b8Syasuoka 	}
505a7ca44b8Syasuoka 
506a7ca44b8Syasuoka 	/*
507936475aaSyasuoka 	 * Find a duplicate request.  In RFC 2865, it has the same source IP
508936475aaSyasuoka 	 * address and source UDP port and Identifier.
509a7ca44b8Syasuoka 	 */
510a7ca44b8Syasuoka 	TAILQ_FOREACH(q, &listn->radiusd->query, next) {
511936475aaSyasuoka 		if (peer->sa_family == q->clientaddr.ss_family &&
512936475aaSyasuoka 		    ((peer->sa_family == AF_INET && in(&q->clientaddr).s_addr ==
513936475aaSyasuoka 		    in(peer).s_addr) || (peer->sa_family == AF_INET6 &&
514936475aaSyasuoka 		    IN6_ARE_ADDR_EQUAL(&in6(&q->clientaddr), &in6(peer)))) &&
515a7ca44b8Syasuoka 		    ((struct sockaddr_in *)&q->clientaddr)->sin_port ==
516936475aaSyasuoka 		    ((struct sockaddr_in *)peer)->sin_port &&
517a7ca44b8Syasuoka 		    req_id == q->req_id)
518a7ca44b8Syasuoka 			break;	/* found it */
519a7ca44b8Syasuoka 	}
520a7ca44b8Syasuoka 	if (q != NULL) {
521ce1078f6Syasuoka 		log_info("Received %s(code=%d) from %s id=%d: duplicated "
522ce1078f6Syasuoka 		    "with q=%u", radius_code_string(req_code), req_code,
523936475aaSyasuoka 		    peerstr, req_id, q->id);
524ce1078f6Syasuoka 		q = NULL;
525c50650beSyasuoka 		goto on_error;
526a7ca44b8Syasuoka 	}
527a7ca44b8Syasuoka 
528936475aaSyasuoka 	if ((q = calloc(1, sizeof(struct radius_query))) == NULL) {
529936475aaSyasuoka 		log_warn("%s: Out of memory", __func__);
530a7ca44b8Syasuoka 		goto on_error;
531a7ca44b8Syasuoka 	}
532936475aaSyasuoka 	if (radius_get_string_attr(packet, RADIUS_TYPE_USER_NAME, username,
533936475aaSyasuoka 	    sizeof(username)) != 0) {
534936475aaSyasuoka 		log_info("Received %s(code=%d) from %s id=%d: no User-Name "
535936475aaSyasuoka 		    "attribute", radius_code_string(req_code), req_code,
536936475aaSyasuoka 		    peerstr, req_id);
537936475aaSyasuoka 	} else
538936475aaSyasuoka 		strlcpy(q->username, username, sizeof(q->username));
539a7ca44b8Syasuoka 
540936475aaSyasuoka 	q->id = ++radius_query_id_seq;
541836eeaedSyasuoka 	q->radiusd = listn->radiusd;
542936475aaSyasuoka 	q->clientaddrlen = peerlen;
543936475aaSyasuoka 	memcpy(&q->clientaddr, peer, peerlen);
544936475aaSyasuoka 	q->listen = listn;
545936475aaSyasuoka 	q->req = packet;
546936475aaSyasuoka 	q->client = client;
547936475aaSyasuoka 	q->req_id = req_id;
548936475aaSyasuoka 	radius_get_authenticator(packet, q->req_auth);
549936475aaSyasuoka 	packet = NULL;
550936475aaSyasuoka 	TAILQ_INSERT_TAIL(&listn->radiusd->query, q, next);
551936475aaSyasuoka 
552936475aaSyasuoka 	switch (req_code) {
553936475aaSyasuoka 	case RADIUS_CODE_ACCESS_REQUEST:
554747da5e9Syasuoka 		if (listn->accounting) {
555747da5e9Syasuoka 			log_info("Received %s(code=%d) from %s id=%d: "
556747da5e9Syasuoka 			    "ignored because the port is for authentication",
557747da5e9Syasuoka 			    radius_code_string(req_code), req_code, peerstr,
558747da5e9Syasuoka 			    req_id);
559747da5e9Syasuoka 			break;
560747da5e9Syasuoka 		}
561a7ca44b8Syasuoka 		/*
562a7ca44b8Syasuoka 		 * Find a matching `authenticate' entry
563a7ca44b8Syasuoka 		 */
564a7ca44b8Syasuoka 		TAILQ_FOREACH(authen, &listn->radiusd->authen, next) {
565a7ca44b8Syasuoka 			for (i = 0; authen->username[i] != NULL; i++) {
566a7ca44b8Syasuoka 				if (fnmatch(authen->username[i], username, 0)
567a7ca44b8Syasuoka 				    == 0)
568a7ca44b8Syasuoka 					goto found;
569a7ca44b8Syasuoka 			}
570a7ca44b8Syasuoka 		}
571237e61d9Syasuoka  found:
572a7ca44b8Syasuoka 		if (authen == NULL) {
573a7ca44b8Syasuoka 			log_warnx("Received %s(code=%d) from %s id=%d "
574a7ca44b8Syasuoka 			    "username=%s: no `authenticate' matches.",
575a7ca44b8Syasuoka 			    radius_code_string(req_code), req_code, peerstr,
576a7ca44b8Syasuoka 			    req_id, username);
577a7ca44b8Syasuoka 			goto on_error;
578a7ca44b8Syasuoka 		}
579936475aaSyasuoka 		q->authen = authen;
58036a3d429Syasuoka 
581a7ca44b8Syasuoka 		if (!MODULE_DO_USERPASS(authen->auth->module) &&
582a7ca44b8Syasuoka 		    !MODULE_DO_ACCSREQ(authen->auth->module)) {
583a7ca44b8Syasuoka 			log_warnx("Received %s(code=%d) from %s id=%d "
584a7ca44b8Syasuoka 			    "username=%s: module `%s' is not running.",
585a7ca44b8Syasuoka 			    radius_code_string(req_code), req_code, peerstr,
586a7ca44b8Syasuoka 			    req_id, username, authen->auth->module->name);
587a7ca44b8Syasuoka 			goto on_error;
588a7ca44b8Syasuoka 		}
589a7ca44b8Syasuoka 
590a7ca44b8Syasuoka 		log_info("Received %s(code=%d) from %s id=%d username=%s "
591a7ca44b8Syasuoka 		    "q=%u: `%s' authentication is starting",
592a7ca44b8Syasuoka 		    radius_code_string(req_code), req_code, peerstr, q->req_id,
593a7ca44b8Syasuoka 		    q->username, q->id, q->authen->auth->module->name);
594a7ca44b8Syasuoka 
595936475aaSyasuoka 		raidus_query_access_request(q);
596a7ca44b8Syasuoka 		return;
597747da5e9Syasuoka 	case RADIUS_CODE_ACCOUNTING_REQUEST:
598747da5e9Syasuoka 		if (!listn->accounting) {
599747da5e9Syasuoka 			log_info("Received %s(code=%d) from %s id=%d: "
600747da5e9Syasuoka 			    "ignored because the port is for accounting",
601747da5e9Syasuoka 			    radius_code_string(req_code), req_code, peerstr,
602747da5e9Syasuoka 			    req_id);
603747da5e9Syasuoka 			break;
604747da5e9Syasuoka 		}
605747da5e9Syasuoka 		if (radius_get_uint32_attr(q->req, RADIUS_TYPE_ACCT_STATUS_TYPE,
606747da5e9Syasuoka 		    &acct_status) != 0)
607747da5e9Syasuoka 			acct_status = 0;
608747da5e9Syasuoka 		/*
609747da5e9Syasuoka 		 * Find a matching `accounting' entry
610747da5e9Syasuoka 		 */
611747da5e9Syasuoka 		TAILQ_FOREACH(accounting, &listn->radiusd->account, next) {
612747da5e9Syasuoka 			if (acct_status == RADIUS_ACCT_STATUS_TYPE_ACCT_ON ||
613747da5e9Syasuoka 			    acct_status == RADIUS_ACCT_STATUS_TYPE_ACCT_OFF) {
614747da5e9Syasuoka 				raidus_query_accounting_request(accounting, q);
615747da5e9Syasuoka 				continue;
616747da5e9Syasuoka 			}
617747da5e9Syasuoka 			for (i = 0; accounting->username[i] != NULL; i++) {
618747da5e9Syasuoka 				if (fnmatch(accounting->username[i], username,
619747da5e9Syasuoka 				    0) == 0)
620747da5e9Syasuoka 					break;
621747da5e9Syasuoka 			}
622747da5e9Syasuoka 			if (accounting->username[i] == NULL)
623747da5e9Syasuoka 				continue;
624747da5e9Syasuoka 			raidus_query_accounting_request(accounting, q);
625747da5e9Syasuoka 			if (accounting->quick)
626747da5e9Syasuoka 				break;
627747da5e9Syasuoka 		}
628747da5e9Syasuoka 		/* pass NULL to hadnle this self without module */
629747da5e9Syasuoka 		raidus_query_accounting_request(NULL, q);
630747da5e9Syasuoka 
631747da5e9Syasuoka 		if ((q->res = radius_new_response_packet(
632747da5e9Syasuoka 		    RADIUS_CODE_ACCOUNTING_RESPONSE, q->req)) == NULL)
633747da5e9Syasuoka 			log_warn("%s: radius_new_response_packet() failed",
634747da5e9Syasuoka 			    __func__);
635747da5e9Syasuoka 		else
636747da5e9Syasuoka 			radius_query_accounting_response(q);
637747da5e9Syasuoka 		break;
638936475aaSyasuoka 	default:
639936475aaSyasuoka 		log_info("Received %s(code=%d) from %s id=%d: %s is not "
640936475aaSyasuoka 		    "supported in this implementation", radius_code_string(
641936475aaSyasuoka 		    req_code), req_code, peerstr, req_id, radius_code_string(
642936475aaSyasuoka 		    req_code));
643936475aaSyasuoka 		break;
644a7ca44b8Syasuoka 	}
645a7ca44b8Syasuoka on_error:
646a7ca44b8Syasuoka 	if (packet != NULL)
647a7ca44b8Syasuoka 		radius_delete_packet(packet);
648936475aaSyasuoka 	if (q != NULL)
649936475aaSyasuoka 		radiusd_access_request_aborted(q);
650a7ca44b8Syasuoka #undef in
651a7ca44b8Syasuoka #undef in6
652a7ca44b8Syasuoka }
653a7ca44b8Syasuoka 
654237e61d9Syasuoka static void
655936475aaSyasuoka raidus_query_access_request(struct radius_query *q)
656a7ca44b8Syasuoka {
657237e61d9Syasuoka 	struct radiusd_authentication	*authen = q->authen;
658a7ca44b8Syasuoka 
659237e61d9Syasuoka 	/* first or next request decoration */
660237e61d9Syasuoka 	for (;;) {
661237e61d9Syasuoka 		if (q->deco == NULL)
662237e61d9Syasuoka 			q->deco = TAILQ_FIRST(&q->authen->deco);
663237e61d9Syasuoka 		else
664237e61d9Syasuoka 			q->deco = TAILQ_NEXT(q->deco, next);
665237e61d9Syasuoka 		if (q->deco == NULL || MODULE_DO_REQDECO(q->deco->module))
666237e61d9Syasuoka 			break;
667237e61d9Syasuoka 	}
668237e61d9Syasuoka 
669237e61d9Syasuoka 	if (q->deco != NULL)
670237e61d9Syasuoka 		radiusd_module_request_decoration(q->deco->module, q);
671237e61d9Syasuoka 	else {
672237e61d9Syasuoka 		RADIUSD_ASSERT(authen->auth != NULL);
673237e61d9Syasuoka 		if (MODULE_DO_ACCSREQ(authen->auth->module))
674237e61d9Syasuoka 			radiusd_module_access_request(authen->auth->module, q);
675237e61d9Syasuoka 		else if (MODULE_DO_USERPASS(authen->auth->module))
676237e61d9Syasuoka 			radiusd_module_userpass(authen->auth->module, q);
677a7ca44b8Syasuoka 	}
678a7ca44b8Syasuoka }
679a7ca44b8Syasuoka 
680237e61d9Syasuoka static void
681936475aaSyasuoka radius_query_access_response(struct radius_query *q)
682a7ca44b8Syasuoka {
683a7ca44b8Syasuoka 	int			 sz, res_id, res_code;
684a7ca44b8Syasuoka 	char			 buf[NI_MAXHOST + NI_MAXSERV + 30];
685ed1dc925Syasuoka 	struct radius_query	*q_last, *q0;
686a7ca44b8Syasuoka 
687ed1dc925Syasuoka 	q_last = q;
688ed1dc925Syasuoka  next:
689237e61d9Syasuoka 	/* first or next response decoration */
690237e61d9Syasuoka 	for (;;) {
691237e61d9Syasuoka 		if (q->deco == NULL)
692237e61d9Syasuoka 			q->deco = TAILQ_FIRST(&q->authen->deco);
693237e61d9Syasuoka 		else
694237e61d9Syasuoka 			q->deco = TAILQ_NEXT(q->deco, next);
695237e61d9Syasuoka 		if (q->deco == NULL || MODULE_DO_RESDECO(q->deco->module))
696237e61d9Syasuoka 			break;
697a7ca44b8Syasuoka 	}
698a7ca44b8Syasuoka 
699237e61d9Syasuoka 	if (q->deco != NULL) {
700237e61d9Syasuoka 		radiusd_module_response_decoration(q->deco->module, q);
701237e61d9Syasuoka 		return;
702237e61d9Syasuoka 	}
703a7ca44b8Syasuoka 
704ed1dc925Syasuoka 	if (q->prev != NULL) {
705ed1dc925Syasuoka 		if (MODULE_DO_NEXTRES(q->prev->authen->auth->module)) {
706ed1dc925Syasuoka 			if (radiusd_access_response_fixup(q->prev, q_last, 0)
707ed1dc925Syasuoka 			    != 0)
708ed1dc925Syasuoka 				goto on_error;
709ed1dc925Syasuoka 			q0 = q;
710ed1dc925Syasuoka 			q = q->prev;
71130c0952cSyasuoka 			/* dissolve the relation */
71230c0952cSyasuoka 			q0->prev = NULL;
71330c0952cSyasuoka 			q->hasnext = false;
714ed1dc925Syasuoka 			radiusd_module_next_response(q->authen->auth->module,
715ed1dc925Syasuoka 			    q, q_last->res);
716ed1dc925Syasuoka 			radiusd_access_request_aborted(q0);
717ed1dc925Syasuoka 			return;
718ed1dc925Syasuoka 		}
719ed1dc925Syasuoka 		q = q->prev;
720ed1dc925Syasuoka 		goto next;
721ed1dc925Syasuoka 	}
722ed1dc925Syasuoka 
723ed1dc925Syasuoka 	if (radiusd_access_response_fixup(q, q_last, 1) != 0)
724a7ca44b8Syasuoka 		goto on_error;
725a7ca44b8Syasuoka 
726a7ca44b8Syasuoka 	res_id = radius_get_id(q->res);
727a7ca44b8Syasuoka 	res_code = radius_get_code(q->res);
728a7ca44b8Syasuoka 
729d7548b59Syasuoka 	/* Reset response/message authenticator */
730d7548b59Syasuoka 	if (radius_has_attr(q->res, RADIUS_TYPE_MESSAGE_AUTHENTICATOR))
731d7548b59Syasuoka 		radius_del_attr_all(q->res, RADIUS_TYPE_MESSAGE_AUTHENTICATOR);
732d7548b59Syasuoka 	radius_put_message_authenticator(q->res, q->client->secret);
733a7ca44b8Syasuoka 	radius_set_response_authenticator(q->res, q->client->secret);
734a7ca44b8Syasuoka 
735a7ca44b8Syasuoka 	log_info("Sending %s(code=%d) to %s id=%u q=%u",
736a7ca44b8Syasuoka 	    radius_code_string(res_code), res_code,
737a7ca44b8Syasuoka 	    addrport_tostring((struct sockaddr *)&q->clientaddr,
738a7ca44b8Syasuoka 		    q->clientaddrlen, buf, sizeof(buf)), res_id, q->id);
739a7ca44b8Syasuoka 
740a7ca44b8Syasuoka 	if ((sz = sendto(q->listen->sock, radius_get_data(q->res),
741a7ca44b8Syasuoka 	    radius_get_length(q->res), 0,
742a7ca44b8Syasuoka 	    (struct sockaddr *)&q->clientaddr, q->clientaddrlen)) <= 0)
743a7ca44b8Syasuoka 		log_warn("Sending a RADIUS response failed");
744a7ca44b8Syasuoka on_error:
745a7ca44b8Syasuoka 	radiusd_access_request_aborted(q);
746237e61d9Syasuoka }
747237e61d9Syasuoka 
748747da5e9Syasuoka static void
749747da5e9Syasuoka raidus_query_accounting_request(struct radiusd_accounting *accounting,
750747da5e9Syasuoka     struct radius_query *q)
751747da5e9Syasuoka {
752747da5e9Syasuoka 	int		 req_code;
753747da5e9Syasuoka 	uint32_t	 acct_status;
754747da5e9Syasuoka 	char		 buf0[NI_MAXHOST + NI_MAXSERV + 30];
755747da5e9Syasuoka 
756747da5e9Syasuoka 	if (accounting != NULL) {
757747da5e9Syasuoka 		/* handle by the module */
758747da5e9Syasuoka 		if (MODULE_DO_ACCTREQ(accounting->acct->module))
759747da5e9Syasuoka 			radiusd_module_account_request(accounting->acct->module,
760747da5e9Syasuoka 			    q);
761747da5e9Syasuoka 		return;
762747da5e9Syasuoka 	}
763747da5e9Syasuoka 	req_code = radius_get_code(q->req);
764747da5e9Syasuoka 	if (radius_get_uint32_attr(q->req, RADIUS_TYPE_ACCT_STATUS_TYPE,
765747da5e9Syasuoka 	    &acct_status) != 0)
766747da5e9Syasuoka 		acct_status = 0;
767747da5e9Syasuoka 	log_info("Received %s(code=%d) type=%s(%lu) from %s id=%d username=%s "
768747da5e9Syasuoka 	    "q=%u", radius_code_string(req_code), req_code,
769747da5e9Syasuoka 	    radius_acct_status_type_string(acct_status), (unsigned long)
770747da5e9Syasuoka 	    acct_status, addrport_tostring((struct sockaddr *)&q->clientaddr,
771747da5e9Syasuoka 	    q->clientaddrlen, buf0, sizeof(buf0)), q->req_id, q->username,
772747da5e9Syasuoka 	    q->id);
773747da5e9Syasuoka }
774747da5e9Syasuoka 
775747da5e9Syasuoka static void
776747da5e9Syasuoka radius_query_accounting_response(struct radius_query *q)
777747da5e9Syasuoka {
778747da5e9Syasuoka 	int		 sz, res_id, res_code;
779747da5e9Syasuoka 	char		 buf[NI_MAXHOST + NI_MAXSERV + 30];
780747da5e9Syasuoka 
781747da5e9Syasuoka 	radius_set_response_authenticator(q->res, q->client->secret);
782747da5e9Syasuoka 	res_id = radius_get_id(q->res);
783747da5e9Syasuoka 	res_code = radius_get_code(q->res);
784747da5e9Syasuoka 
785747da5e9Syasuoka 	log_info("Sending %s(code=%d) to %s id=%u q=%u",
786747da5e9Syasuoka 	    radius_code_string(res_code), res_code,
787747da5e9Syasuoka 	    addrport_tostring((struct sockaddr *)&q->clientaddr,
788747da5e9Syasuoka 		    q->clientaddrlen, buf, sizeof(buf)), res_id, q->id);
789747da5e9Syasuoka 
790747da5e9Syasuoka 	if ((sz = sendto(q->listen->sock, radius_get_data(q->res),
791747da5e9Syasuoka 	    radius_get_length(q->res), 0,
792747da5e9Syasuoka 	    (struct sockaddr *)&q->clientaddr, q->clientaddrlen)) <= 0)
793747da5e9Syasuoka 		log_warn("Sending a RADIUS response failed");
794747da5e9Syasuoka }
795ed1dc925Syasuoka 
796ed1dc925Syasuoka static const char *
797ed1dc925Syasuoka radius_query_client_secret(struct radius_query *q)
798ed1dc925Syasuoka {
799ed1dc925Syasuoka 	struct radius_query	*q0;
800ed1dc925Syasuoka 	const char		*client_secret = NULL;
801ed1dc925Syasuoka 
802ed1dc925Syasuoka 	for (q0 = q; q0 != NULL && client_secret == NULL; q0 = q0->prev) {
803ed1dc925Syasuoka 		if (q0->client != NULL)
804ed1dc925Syasuoka 			client_secret = q0->client->secret;
805ed1dc925Syasuoka 	}
806ed1dc925Syasuoka 	RADIUSD_ASSERT(client_secret != NULL);
807ed1dc925Syasuoka 
808ed1dc925Syasuoka 	return (client_secret);
809ed1dc925Syasuoka }
810237e61d9Syasuoka /***********************************************************************
811237e61d9Syasuoka  * Callback functions from the modules
812237e61d9Syasuoka  ***********************************************************************/
813237e61d9Syasuoka void
814237e61d9Syasuoka radiusd_access_request_answer(struct radius_query *q)
815237e61d9Syasuoka {
816237e61d9Syasuoka 	radius_set_request_packet(q->res, q->req);
817237e61d9Syasuoka 	RADIUSD_ASSERT(q->deco == NULL);
818237e61d9Syasuoka 
819a449bbceSyasuoka 	radius_query_access_response(q);
820a7ca44b8Syasuoka }
821a7ca44b8Syasuoka 
822a7ca44b8Syasuoka void
823ed1dc925Syasuoka radiusd_access_request_next(struct radius_query *q, RADIUS_PACKET *pkt)
824ed1dc925Syasuoka {
825ed1dc925Syasuoka 	struct radius_query		*q_next = NULL;
826ed1dc925Syasuoka 	static char			 username[256];
827ed1dc925Syasuoka 	struct radiusd_authentication	*authen;
828ed1dc925Syasuoka 	int				 i;
829ed1dc925Syasuoka 
830ed1dc925Syasuoka 	RADIUSD_ASSERT(q->deco == NULL);
831ed1dc925Syasuoka 
832ed1dc925Syasuoka 	if (!q->authen->isfilter) {
833ed1dc925Syasuoka 		log_warnx("q=%u `%s' requested next authentication, but it's "
834ed1dc925Syasuoka 		    "not authentication-filter", q->id,
835ed1dc925Syasuoka 		    q->authen->auth->module->name);
836ed1dc925Syasuoka 		goto on_error;
837ed1dc925Syasuoka 	}
838ed1dc925Syasuoka 	if (radius_get_string_attr(pkt, RADIUS_TYPE_USER_NAME, username,
839ed1dc925Syasuoka 	    sizeof(username)) != 0)
840ed1dc925Syasuoka 		username[0] = '\0';
841ed1dc925Syasuoka 
842ed1dc925Syasuoka 	for (authen = TAILQ_NEXT(q->authen, next); authen != NULL;
843ed1dc925Syasuoka 	    authen = TAILQ_NEXT(authen, next)) {
844ed1dc925Syasuoka 		for (i = 0; authen->username[i] != NULL; i++) {
845ed1dc925Syasuoka 			if (fnmatch(authen->username[i], username, 0)
846ed1dc925Syasuoka 			    == 0)
847ed1dc925Syasuoka 				goto found;
848ed1dc925Syasuoka 		}
849ed1dc925Syasuoka 	}
850ed1dc925Syasuoka  found:
851ed1dc925Syasuoka 	if (authen == NULL) {	/* no more authentication */
852ed1dc925Syasuoka 		log_warnx("q=%u module `%s' requested next authentication "
853ed1dc925Syasuoka 		    "no more `authenticate' matches", q->id,
854ed1dc925Syasuoka 		    q->authen->auth->module->name);
855ed1dc925Syasuoka 		goto on_error;
856ed1dc925Syasuoka 	}
857ed1dc925Syasuoka 
858ed1dc925Syasuoka 	if ((q_next = calloc(1, sizeof(struct radius_query))) == NULL) {
859ed1dc925Syasuoka 		log_warn("%s: q=%u calloc: %m", __func__, q->id);
860ed1dc925Syasuoka 		goto on_error;
861ed1dc925Syasuoka 	}
862ed1dc925Syasuoka 	q_next->id = ++radius_query_id_seq;
863ed1dc925Syasuoka 	q_next->radiusd = q->radiusd;
864ed1dc925Syasuoka 	q_next->req_id = q->req_id;
865ed1dc925Syasuoka 	q_next->req = pkt;
866ed1dc925Syasuoka 	radius_get_authenticator(pkt, q_next->req_auth);
867ed1dc925Syasuoka 	q_next->authen = authen;
868ed1dc925Syasuoka 	q_next->prev = q;
86930c0952cSyasuoka 	q->hasnext = true;
870ed1dc925Syasuoka 	strlcpy(q_next->username, username, sizeof(q_next->username));
871ed1dc925Syasuoka 	TAILQ_INSERT_TAIL(&q->radiusd->query, q_next, next);
872ed1dc925Syasuoka 
873ed1dc925Syasuoka 	raidus_query_access_request(q_next);
874ed1dc925Syasuoka 	return;
875ed1dc925Syasuoka  on_error:
876ed1dc925Syasuoka 	RADIUSD_ASSERT(q_next == NULL);
877ed1dc925Syasuoka 	radius_delete_packet(pkt);
878ed1dc925Syasuoka 	radiusd_access_request_aborted(q);
879ed1dc925Syasuoka }
880ed1dc925Syasuoka 
881ed1dc925Syasuoka void
882a7ca44b8Syasuoka radiusd_access_request_aborted(struct radius_query *q)
883a7ca44b8Syasuoka {
88430c0952cSyasuoka 	if (q->hasnext)	/* don't abort if filtering */
88530c0952cSyasuoka 		return;
88630c0952cSyasuoka 	if (q->prev != NULL) {
88730c0952cSyasuoka 		q->prev->hasnext = false;
888ed1dc925Syasuoka 		radiusd_access_request_aborted(q->prev);
88930c0952cSyasuoka 	}
890a7ca44b8Syasuoka 	if (q->req != NULL)
891a7ca44b8Syasuoka 		radius_delete_packet(q->req);
892a7ca44b8Syasuoka 	if (q->res != NULL)
893a7ca44b8Syasuoka 		radius_delete_packet(q->res);
894836eeaedSyasuoka 	TAILQ_REMOVE(&q->radiusd->query, q, next);
895a7ca44b8Syasuoka 	free(q);
896a7ca44b8Syasuoka }
897a7ca44b8Syasuoka 
898a7ca44b8Syasuoka /***********************************************************************
899a7ca44b8Syasuoka  * Signal handlers
900a7ca44b8Syasuoka  ***********************************************************************/
901a7ca44b8Syasuoka static void
902a7ca44b8Syasuoka radiusd_on_sigterm(int fd, short evmask, void *ctx)
903a7ca44b8Syasuoka {
904a7ca44b8Syasuoka 	log_info("Received SIGTERM");
905842565f2Syasuoka 	event_loopbreak();
906a7ca44b8Syasuoka }
907a7ca44b8Syasuoka 
908a7ca44b8Syasuoka static void
909a7ca44b8Syasuoka radiusd_on_sigint(int fd, short evmask, void *ctx)
910a7ca44b8Syasuoka {
911a7ca44b8Syasuoka 	log_info("Received SIGINT");
912842565f2Syasuoka 	event_loopbreak();
913a7ca44b8Syasuoka }
914a7ca44b8Syasuoka 
915a7ca44b8Syasuoka static void
916a7ca44b8Syasuoka radiusd_on_sighup(int fd, short evmask, void *ctx)
917a7ca44b8Syasuoka {
918a7ca44b8Syasuoka 	log_info("Received SIGHUP");
919a7ca44b8Syasuoka }
920a7ca44b8Syasuoka 
921a7ca44b8Syasuoka static void
922a7ca44b8Syasuoka radiusd_on_sigchld(int fd, short evmask, void *ctx)
923a7ca44b8Syasuoka {
924a7ca44b8Syasuoka 	struct radiusd		*radiusd = ctx;
925a7ca44b8Syasuoka 	struct radiusd_module	*module;
926a7ca44b8Syasuoka 	pid_t			 pid;
92759396270Syasuoka 	int			 status, ndeath = 0;
928a7ca44b8Syasuoka 
929a7ca44b8Syasuoka 	log_debug("Received SIGCHLD");
930a7ca44b8Syasuoka 	while ((pid = wait3(&status, WNOHANG, NULL)) != 0) {
931a7ca44b8Syasuoka 		if (pid == -1)
932a7ca44b8Syasuoka 			break;
933a7ca44b8Syasuoka 		TAILQ_FOREACH(module, &radiusd->module, next) {
934a7ca44b8Syasuoka 			if (module->pid == pid) {
935a7ca44b8Syasuoka 				if (WIFEXITED(status))
936a7ca44b8Syasuoka 					log_warnx("module `%s'(pid=%d) exited "
937a7ca44b8Syasuoka 					    "with status %d", module->name,
938a7ca44b8Syasuoka 					    (int)pid, WEXITSTATUS(status));
939a7ca44b8Syasuoka 				else
940a7ca44b8Syasuoka 					log_warnx("module `%s'(pid=%d) exited "
941a7ca44b8Syasuoka 					    "by signal %d", module->name,
942a7ca44b8Syasuoka 					    (int)pid, WTERMSIG(status));
94359396270Syasuoka 				ndeath++;
944a7ca44b8Syasuoka 				break;
945a7ca44b8Syasuoka 			}
946a7ca44b8Syasuoka 		}
947a7ca44b8Syasuoka 		if (!module) {
948a7ca44b8Syasuoka 			if (WIFEXITED(status))
949*abc3a9caSyasuoka 				log_warnx("unknown child process pid=%d exited "
950a7ca44b8Syasuoka 				    "with status %d", (int)pid,
951a7ca44b8Syasuoka 				     WEXITSTATUS(status));
952a7ca44b8Syasuoka 			else
953*abc3a9caSyasuoka 				log_warnx("unknown child process pid=%d exited "
954a7ca44b8Syasuoka 				    "by signal %d", (int)pid,
955a7ca44b8Syasuoka 				    WTERMSIG(status));
956a7ca44b8Syasuoka 		}
957a7ca44b8Syasuoka 	}
95859396270Syasuoka 	if (ndeath > 0) {
95959396270Syasuoka 		radiusd->error++;
96059396270Syasuoka 		event_loopbreak();
96159396270Syasuoka 	}
962a7ca44b8Syasuoka }
963a7ca44b8Syasuoka 
964a7ca44b8Syasuoka static const char *
965a7ca44b8Syasuoka radius_code_string(int code)
966a7ca44b8Syasuoka {
967a7ca44b8Syasuoka 	int			i;
968a7ca44b8Syasuoka 	struct _codestrings {
969a7ca44b8Syasuoka 		int		 code;
970a7ca44b8Syasuoka 		const char	*string;
971a7ca44b8Syasuoka 	} codestrings[] = {
972a7ca44b8Syasuoka 	    { RADIUS_CODE_ACCESS_REQUEST,	"Access-Request" },
973a7ca44b8Syasuoka 	    { RADIUS_CODE_ACCESS_ACCEPT,	"Access-Accept" },
974a7ca44b8Syasuoka 	    { RADIUS_CODE_ACCESS_REJECT,	"Access-Reject" },
975a7ca44b8Syasuoka 	    { RADIUS_CODE_ACCOUNTING_REQUEST,	"Accounting-Request" },
976a7ca44b8Syasuoka 	    { RADIUS_CODE_ACCOUNTING_RESPONSE,	"Accounting-Response" },
977a7ca44b8Syasuoka 	    { RADIUS_CODE_ACCESS_CHALLENGE,	"Access-Challenge" },
978a7ca44b8Syasuoka 	    { RADIUS_CODE_STATUS_SERVER,	"Status-Server" },
97905a7ce52Syasuoka 	    { RADIUS_CODE_STATUS_CLIENT,	"Status-Client" },
980a7ca44b8Syasuoka 	    { -1,				NULL }
981a7ca44b8Syasuoka 	};
982a7ca44b8Syasuoka 
983a7ca44b8Syasuoka 	for (i = 0; codestrings[i].code != -1; i++)
984a7ca44b8Syasuoka 		if (codestrings[i].code == code)
985a7ca44b8Syasuoka 			return (codestrings[i].string);
986a7ca44b8Syasuoka 
987240892bdSyasuoka 	return ("Unknown");
988a7ca44b8Syasuoka }
989a7ca44b8Syasuoka 
990747da5e9Syasuoka static const char *
991747da5e9Syasuoka radius_acct_status_type_string(uint32_t type)
992747da5e9Syasuoka {
993747da5e9Syasuoka 	int			i;
994747da5e9Syasuoka 	struct _typestrings {
995747da5e9Syasuoka 		uint32_t	 type;
996747da5e9Syasuoka 		const char	*string;
997747da5e9Syasuoka 	} typestrings[] = {
998747da5e9Syasuoka 	    { RADIUS_ACCT_STATUS_TYPE_START,		"Start" },
999747da5e9Syasuoka 	    { RADIUS_ACCT_STATUS_TYPE_STOP,		"Stop" },
1000747da5e9Syasuoka 	    { RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE,	"Interim-Update" },
1001747da5e9Syasuoka 	    { RADIUS_ACCT_STATUS_TYPE_ACCT_ON,		"Accounting-On" },
1002747da5e9Syasuoka 	    { RADIUS_ACCT_STATUS_TYPE_ACCT_OFF,		"Accounting-Off" },
1003747da5e9Syasuoka 	    { -1,					NULL }
1004747da5e9Syasuoka 	};
1005747da5e9Syasuoka 
1006747da5e9Syasuoka 	for (i = 0; typestrings[i].string != NULL; i++)
1007747da5e9Syasuoka 		if (typestrings[i].type == type)
1008747da5e9Syasuoka 			return (typestrings[i].string);
1009747da5e9Syasuoka 
1010747da5e9Syasuoka 	return ("Unknown");
1011747da5e9Syasuoka }
1012747da5e9Syasuoka 
1013a7ca44b8Syasuoka void
1014a7ca44b8Syasuoka radiusd_conf_init(struct radiusd *conf)
1015a7ca44b8Syasuoka {
1016a7ca44b8Syasuoka 
1017a7ca44b8Syasuoka 	TAILQ_INIT(&conf->listen);
1018a7ca44b8Syasuoka 	TAILQ_INIT(&conf->module);
1019a7ca44b8Syasuoka 	TAILQ_INIT(&conf->authen);
1020747da5e9Syasuoka 	TAILQ_INIT(&conf->account);
1021a7ca44b8Syasuoka 	TAILQ_INIT(&conf->client);
1022a7ca44b8Syasuoka 
1023a7ca44b8Syasuoka 	return;
1024a7ca44b8Syasuoka }
1025a7ca44b8Syasuoka 
1026a7ca44b8Syasuoka /*
1027a7ca44b8Syasuoka  * Fix some attributes which depend the secret value.
1028a7ca44b8Syasuoka  */
1029a7ca44b8Syasuoka static int
1030ed1dc925Syasuoka radiusd_access_response_fixup(struct radius_query *q, struct radius_query *q0,
1031ed1dc925Syasuoka     bool islast)
1032a7ca44b8Syasuoka {
1033a7ca44b8Syasuoka 	int			 res_id;
1034a7ca44b8Syasuoka 	size_t			 attrlen;
1035ed1dc925Syasuoka 	u_char			 authen_req_auth[16], attrbuf[256];
1036ed1dc925Syasuoka 	const char		*client_req_auth;
1037ed1dc925Syasuoka 	const char		*authen_secret, *client_secret;
1038a7ca44b8Syasuoka 
1039ed1dc925Syasuoka 	authen_secret = q0->authen->auth->module->secret;
1040ed1dc925Syasuoka 	client_secret = (islast)? q->client->secret :
1041ed1dc925Syasuoka 	    q->authen->auth->module->secret;
1042a7ca44b8Syasuoka 
1043ed1dc925Syasuoka 	radius_get_authenticator(q0->req, authen_req_auth);
1044ed1dc925Syasuoka 	client_req_auth = q->req_auth;
1045a7ca44b8Syasuoka 
1046ed1dc925Syasuoka 	if (client_secret == NULL && authen_secret == NULL)
1047ed1dc925Syasuoka 		return (0);
1048ed1dc925Syasuoka 	if (!(authen_secret != NULL && client_secret != NULL &&
1049ed1dc925Syasuoka 	    strcmp(authen_secret, client_secret) == 0 &&
1050ed1dc925Syasuoka 	    timingsafe_bcmp(authen_req_auth, client_req_auth, 16) == 0)) {
1051a7ca44b8Syasuoka 		/* RFC 2865 Tunnel-Password */
10520fcb2422Syasuoka 		attrlen = sizeof(attrbuf);
1053ed1dc925Syasuoka 		if (radius_get_raw_attr(q0->res, RADIUS_TYPE_TUNNEL_PASSWORD,
1054a7ca44b8Syasuoka 		    attrbuf, &attrlen) == 0) {
1055ed1dc925Syasuoka 			if (authen_secret != NULL)
1056ed1dc925Syasuoka 				radius_attr_unhide(authen_secret,
1057ed1dc925Syasuoka 				    authen_req_auth, attrbuf, attrbuf + 3,
1058ed1dc925Syasuoka 				    attrlen - 3);
1059ed1dc925Syasuoka 			if (client_secret != NULL)
1060ed1dc925Syasuoka 				radius_attr_hide(client_secret, client_req_auth,
1061a7ca44b8Syasuoka 				    attrbuf, attrbuf + 3, attrlen - 3);
1062ed1dc925Syasuoka 			radius_del_attr_all(q0->res,
1063a7ca44b8Syasuoka 			    RADIUS_TYPE_TUNNEL_PASSWORD);
1064ed1dc925Syasuoka 			radius_put_raw_attr(q0->res,
1065a7ca44b8Syasuoka 			    RADIUS_TYPE_TUNNEL_PASSWORD, attrbuf, attrlen);
1066a7ca44b8Syasuoka 		}
1067a7ca44b8Syasuoka 
1068a7ca44b8Syasuoka 		/* RFC 2548 Microsoft MPPE-{Send,Recv}-Key */
10690fcb2422Syasuoka 		attrlen = sizeof(attrbuf);
1070ed1dc925Syasuoka 		if (radius_get_vs_raw_attr(q0->res, RADIUS_VENDOR_MICROSOFT,
1071a7ca44b8Syasuoka 		    RADIUS_VTYPE_MPPE_SEND_KEY, attrbuf, &attrlen) == 0) {
1072ed1dc925Syasuoka 			if (authen_secret != NULL)
1073ed1dc925Syasuoka 				radius_attr_unhide(authen_secret,
1074ed1dc925Syasuoka 				    authen_req_auth, attrbuf, attrbuf + 2,
1075ed1dc925Syasuoka 				    attrlen - 2);
1076ed1dc925Syasuoka 			if (client_secret != NULL)
1077ed1dc925Syasuoka 				radius_attr_hide(client_secret, client_req_auth,
1078a7ca44b8Syasuoka 				    attrbuf, attrbuf + 2, attrlen - 2);
1079ed1dc925Syasuoka 			radius_del_vs_attr_all(q0->res, RADIUS_VENDOR_MICROSOFT,
1080a7ca44b8Syasuoka 			    RADIUS_VTYPE_MPPE_SEND_KEY);
1081ed1dc925Syasuoka 			radius_put_vs_raw_attr(q0->res, RADIUS_VENDOR_MICROSOFT,
1082a7ca44b8Syasuoka 			    RADIUS_VTYPE_MPPE_SEND_KEY, attrbuf, attrlen);
1083a7ca44b8Syasuoka 		}
10840fcb2422Syasuoka 		attrlen = sizeof(attrbuf);
1085ed1dc925Syasuoka 		if (radius_get_vs_raw_attr(q0->res, RADIUS_VENDOR_MICROSOFT,
1086a7ca44b8Syasuoka 		    RADIUS_VTYPE_MPPE_RECV_KEY, attrbuf, &attrlen) == 0) {
1087ed1dc925Syasuoka 			if (authen_secret != NULL)
1088ed1dc925Syasuoka 				radius_attr_unhide(authen_secret,
1089ed1dc925Syasuoka 				    authen_req_auth, attrbuf, attrbuf + 2,
1090ed1dc925Syasuoka 				    attrlen - 2);
1091ed1dc925Syasuoka 			if (client_secret != NULL)
1092ed1dc925Syasuoka 				radius_attr_hide(client_secret, client_req_auth,
1093a7ca44b8Syasuoka 				    attrbuf, attrbuf + 2, attrlen - 2);
1094a7ca44b8Syasuoka 
1095ed1dc925Syasuoka 			radius_del_vs_attr_all(q0->res, RADIUS_VENDOR_MICROSOFT,
1096a7ca44b8Syasuoka 			    RADIUS_VTYPE_MPPE_RECV_KEY);
1097ed1dc925Syasuoka 			radius_put_vs_raw_attr(q0->res, RADIUS_VENDOR_MICROSOFT,
1098a7ca44b8Syasuoka 			    RADIUS_VTYPE_MPPE_RECV_KEY, attrbuf, attrlen);
1099a7ca44b8Syasuoka 		}
1100a7ca44b8Syasuoka 	}
1101ed1dc925Syasuoka 	res_id = radius_get_id(q0->res);
1102a7ca44b8Syasuoka 	if (res_id != q->req_id) {
1103a7ca44b8Syasuoka 		/* authentication server change the id */
1104ed1dc925Syasuoka 		radius_set_id(q0->res, q->req_id);
1105a7ca44b8Syasuoka 	}
1106a7ca44b8Syasuoka 
1107a7ca44b8Syasuoka 	return (0);
1108a7ca44b8Syasuoka }
1109a7ca44b8Syasuoka 
1110a7ca44b8Syasuoka static struct radius_query *
1111a7ca44b8Syasuoka radiusd_find_query(struct radiusd *radiusd, u_int q_id)
1112a7ca44b8Syasuoka {
1113a7ca44b8Syasuoka 	struct radius_query	*q;
1114a7ca44b8Syasuoka 
1115a7ca44b8Syasuoka 	TAILQ_FOREACH(q, &radiusd->query, next) {
1116a7ca44b8Syasuoka 		if (q->id == q_id)
1117a7ca44b8Syasuoka 			return (q);
1118a7ca44b8Syasuoka 	}
1119a7ca44b8Syasuoka 	return (NULL);
1120a7ca44b8Syasuoka }
1121a7ca44b8Syasuoka 
1122842565f2Syasuoka int
1123842565f2Syasuoka radiusd_imsg_compose_module(struct radiusd *radiusd, const char *module_name,
1124842565f2Syasuoka     uint32_t type, uint32_t id, pid_t pid, int fd, void *data, size_t datalen)
1125842565f2Syasuoka {
1126842565f2Syasuoka 	struct radiusd_module	*module;
1127842565f2Syasuoka 
1128842565f2Syasuoka 	TAILQ_FOREACH(module, &radiusd_s->module, next) {
1129842565f2Syasuoka 		if (strcmp(module->name, module_name) == 0)
1130842565f2Syasuoka 			break;
1131842565f2Syasuoka 	}
1132842565f2Syasuoka 	if (module == NULL ||
1133842565f2Syasuoka 	    (module->capabilities & RADIUSD_MODULE_CAP_CONTROL) == 0 ||
1134842565f2Syasuoka 	    module->fd < 0)
1135842565f2Syasuoka 		return (-1);
1136842565f2Syasuoka 
1137842565f2Syasuoka 	if (imsg_compose(&module->ibuf, type, id, pid, fd, data,
1138842565f2Syasuoka 	    datalen) == -1)
1139842565f2Syasuoka 		return (-1);
1140842565f2Syasuoka 	radiusd_module_reset_ev_handler(module);
1141842565f2Syasuoka 
1142842565f2Syasuoka 	return (0);
1143842565f2Syasuoka }
1144842565f2Syasuoka 
1145a7ca44b8Syasuoka /***********************************************************************
1146a7ca44b8Syasuoka  * radiusd module handling
1147a7ca44b8Syasuoka  ***********************************************************************/
1148a7ca44b8Syasuoka struct radiusd_module *
1149a7ca44b8Syasuoka radiusd_module_load(struct radiusd *radiusd, const char *path, const char *name)
1150a7ca44b8Syasuoka {
1151a7ca44b8Syasuoka 	struct radiusd_module		*module = NULL;
1152a7ca44b8Syasuoka 	pid_t				 pid;
1153ab3cfbd9Syasuoka 	int				 ival, pairsock[] = { -1, -1 };
1154a7ca44b8Syasuoka 	const char			*av[3];
1155a7ca44b8Syasuoka 	ssize_t				 n;
1156a7ca44b8Syasuoka 	struct imsg			 imsg;
1157a7ca44b8Syasuoka 
1158a7ca44b8Syasuoka 	module = calloc(1, sizeof(struct radiusd_module));
1159a7ca44b8Syasuoka 	if (module == NULL)
1160a7ca44b8Syasuoka 		fatal("Out of memory");
1161a7ca44b8Syasuoka 	module->radiusd = radiusd;
1162a7ca44b8Syasuoka 
1163ab3cfbd9Syasuoka 	if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pairsock) == -1) {
1164a7ca44b8Syasuoka 		log_warn("Could not load module `%s'(%s): pipe()", name, path);
1165a7ca44b8Syasuoka 		goto on_error;
1166a7ca44b8Syasuoka 	}
1167a7ca44b8Syasuoka 
1168a7ca44b8Syasuoka 	pid = fork();
1169a7ca44b8Syasuoka 	if (pid == -1) {
1170a7ca44b8Syasuoka 		log_warn("Could not load module `%s'(%s): fork()", name, path);
1171a7ca44b8Syasuoka 		goto on_error;
1172a7ca44b8Syasuoka 	}
1173a7ca44b8Syasuoka 	if (pid == 0) {
1174a7ca44b8Syasuoka 		setsid();
1175a7ca44b8Syasuoka 		close(pairsock[0]);
1176a7ca44b8Syasuoka 		av[0] = path;
1177a7ca44b8Syasuoka 		av[1] = name;
1178a7ca44b8Syasuoka 		av[2] = NULL;
1179a7ca44b8Syasuoka 		dup2(pairsock[1], STDIN_FILENO);
1180a7ca44b8Syasuoka 		dup2(pairsock[1], STDOUT_FILENO);
1181a7ca44b8Syasuoka 		close(pairsock[1]);
1182a7ca44b8Syasuoka 		closefrom(STDERR_FILENO + 1);
1183a7ca44b8Syasuoka 		execv(path, (char * const *)av);
1184240892bdSyasuoka 		log_warn("Failed to execute %s", path);
1185a7ca44b8Syasuoka 		_exit(EXIT_FAILURE);
1186a7ca44b8Syasuoka 	}
1187a7ca44b8Syasuoka 	close(pairsock[1]);
1188a7ca44b8Syasuoka 
1189a7ca44b8Syasuoka 	module->fd = pairsock[0];
1190ab3cfbd9Syasuoka 	if ((ival = fcntl(module->fd, F_GETFL)) == -1) {
1191ab3cfbd9Syasuoka 		log_warn("Could not load module `%s': fcntl(F_GETFL)",
1192ab3cfbd9Syasuoka 		    name);
1193ab3cfbd9Syasuoka 		goto on_error;
1194ab3cfbd9Syasuoka 	}
1195ab3cfbd9Syasuoka 	if (fcntl(module->fd, F_SETFL, ival | O_NONBLOCK) == -1) {
1196936475aaSyasuoka 		log_warn(
1197936475aaSyasuoka 		    "Could not load module `%s': fcntl(F_SETFL,O_NONBLOCK)",
1198ab3cfbd9Syasuoka 		    name);
1199ab3cfbd9Syasuoka 		goto on_error;
1200ab3cfbd9Syasuoka 	}
1201a7ca44b8Syasuoka 	strlcpy(module->name, name, sizeof(module->name));
1202a7ca44b8Syasuoka 	module->pid = pid;
1203882428cdSclaudio 	if (imsgbuf_init(&module->ibuf, module->fd) == -1) {
1204882428cdSclaudio 		log_warn("Could not load module `%s': imsgbuf_init", name);
1205882428cdSclaudio 		goto on_error;
1206882428cdSclaudio 	}
1207a7ca44b8Syasuoka 
1208a7ca44b8Syasuoka 	if (imsg_sync_read(&module->ibuf, MODULE_IO_TIMEOUT) <= 0 ||
1209a7ca44b8Syasuoka 	    (n = imsg_get(&module->ibuf, &imsg)) <= 0) {
1210a7ca44b8Syasuoka 		log_warnx("Could not load module `%s': module didn't "
1211a7ca44b8Syasuoka 		    "respond", name);
1212a7ca44b8Syasuoka 		goto on_error;
1213a7ca44b8Syasuoka 	}
1214a7ca44b8Syasuoka 	if (imsg.hdr.type != IMSG_RADIUSD_MODULE_LOAD) {
1215a7ca44b8Syasuoka 		imsg_free(&imsg);
1216a7ca44b8Syasuoka 		log_warnx("Could not load module `%s': unknown imsg type=%d",
1217a7ca44b8Syasuoka 		    name, imsg.hdr.type);
1218a7ca44b8Syasuoka 		goto on_error;
1219a7ca44b8Syasuoka 	}
1220a7ca44b8Syasuoka 
1221a7ca44b8Syasuoka 	module->capabilities =
1222a7ca44b8Syasuoka 	    ((struct radiusd_module_load_arg *)imsg.data)->cap;
1223a7ca44b8Syasuoka 
1224a7ca44b8Syasuoka 	log_debug("Loaded module `%s' successfully.  pid=%d", module->name,
1225a7ca44b8Syasuoka 	    module->pid);
1226a7ca44b8Syasuoka 	imsg_free(&imsg);
1227a7ca44b8Syasuoka 
1228a7ca44b8Syasuoka 	return (module);
1229a7ca44b8Syasuoka 
1230a7ca44b8Syasuoka on_error:
1231a7ca44b8Syasuoka 	free(module);
1232a7ca44b8Syasuoka 	if (pairsock[0] >= 0)
1233a7ca44b8Syasuoka 		close(pairsock[0]);
1234a7ca44b8Syasuoka 	if (pairsock[1] >= 0)
1235a7ca44b8Syasuoka 		close(pairsock[1]);
1236a7ca44b8Syasuoka 
1237a7ca44b8Syasuoka 	return (NULL);
1238a7ca44b8Syasuoka }
1239a7ca44b8Syasuoka 
1240a7ca44b8Syasuoka void
1241a7ca44b8Syasuoka radiusd_module_start(struct radiusd_module *module)
1242a7ca44b8Syasuoka {
1243a7ca44b8Syasuoka 	int		 datalen;
1244a7ca44b8Syasuoka 	struct imsg	 imsg;
124544c4cd84Syasuoka 	struct timeval	 tv = { 0, 0 };
1246a7ca44b8Syasuoka 
1247a7ca44b8Syasuoka 	RADIUSD_ASSERT(module->fd >= 0);
1248a7ca44b8Syasuoka 	imsg_compose(&module->ibuf, IMSG_RADIUSD_MODULE_START, 0, 0, -1,
1249a7ca44b8Syasuoka 	    NULL, 0);
1250a7ca44b8Syasuoka 	imsg_sync_flush(&module->ibuf, MODULE_IO_TIMEOUT);
1251a7ca44b8Syasuoka 	if (imsg_sync_read(&module->ibuf, MODULE_IO_TIMEOUT) <= 0 ||
1252a7ca44b8Syasuoka 	    imsg_get(&module->ibuf, &imsg) <= 0) {
1253a7ca44b8Syasuoka 		log_warnx("Module `%s' could not start: no response",
1254a7ca44b8Syasuoka 		    module->name);
1255a7ca44b8Syasuoka 		goto on_fail;
1256a7ca44b8Syasuoka 	}
1257a7ca44b8Syasuoka 
1258a7ca44b8Syasuoka 	datalen = imsg.hdr.len - IMSG_HEADER_SIZE;
1259a7ca44b8Syasuoka 	if (imsg.hdr.type != IMSG_OK) {
1260a7ca44b8Syasuoka 		if (imsg.hdr.type == IMSG_NG) {
1261a7ca44b8Syasuoka 			if (datalen > 0)
1262a7ca44b8Syasuoka 				log_warnx("Module `%s' could not start: %s",
1263a7ca44b8Syasuoka 				    module->name, (char *)imsg.data);
1264a7ca44b8Syasuoka 			else
1265a7ca44b8Syasuoka 				log_warnx("Module `%s' could not start",
1266a7ca44b8Syasuoka 				    module->name);
1267a7ca44b8Syasuoka 		} else
1268a7ca44b8Syasuoka 			log_warnx("Module `%s' could not started: module "
12693a50f0a9Sjmc 			    "returned unknown message type %d", module->name,
1270a7ca44b8Syasuoka 			    imsg.hdr.type);
1271a7ca44b8Syasuoka 		goto on_fail;
1272a7ca44b8Syasuoka 	}
1273a7ca44b8Syasuoka 
1274c469475aSyasuoka 	event_set(&module->ev, module->fd, EV_READ, radiusd_module_on_imsg_io,
1275c469475aSyasuoka 	    module);
127644c4cd84Syasuoka 	event_add(&module->ev, &tv);
1277a7ca44b8Syasuoka 	log_debug("Module `%s' started successfully", module->name);
1278c469475aSyasuoka 
1279a7ca44b8Syasuoka 	return;
1280a7ca44b8Syasuoka on_fail:
1281a7ca44b8Syasuoka 	radiusd_module_close(module);
1282a7ca44b8Syasuoka 	return;
1283a7ca44b8Syasuoka }
1284a7ca44b8Syasuoka 
1285a7ca44b8Syasuoka void
1286a7ca44b8Syasuoka radiusd_module_stop(struct radiusd_module *module)
1287a7ca44b8Syasuoka {
1288a7ca44b8Syasuoka 	module->stopped = true;
1289a7ca44b8Syasuoka 
1290fda17215Smestre 	if (module->secret != NULL) {
129129b4e2eaSderaadt 		freezero(module->secret, strlen(module->secret));
1292a7ca44b8Syasuoka 		module->secret = NULL;
1293fda17215Smestre 	}
1294a7ca44b8Syasuoka 
1295d0175656Syasuoka 	if (module->fd >= 0) {
1296a7ca44b8Syasuoka 		imsg_compose(&module->ibuf, IMSG_RADIUSD_MODULE_STOP, 0, 0, -1,
1297a7ca44b8Syasuoka 		    NULL, 0);
1298a7ca44b8Syasuoka 		radiusd_module_reset_ev_handler(module);
1299a7ca44b8Syasuoka 	}
1300d0175656Syasuoka }
1301a7ca44b8Syasuoka 
1302a7ca44b8Syasuoka static void
1303a7ca44b8Syasuoka radiusd_module_close(struct radiusd_module *module)
1304a7ca44b8Syasuoka {
1305a7ca44b8Syasuoka 	if (module->fd >= 0) {
1306a7ca44b8Syasuoka 		event_del(&module->ev);
1307dd7efffeSclaudio 		imsgbuf_clear(&module->ibuf);
1308a7ca44b8Syasuoka 		close(module->fd);
1309a7ca44b8Syasuoka 		module->fd = -1;
1310a7ca44b8Syasuoka 	}
1311a7ca44b8Syasuoka }
1312a7ca44b8Syasuoka 
1313a7ca44b8Syasuoka void
1314a7ca44b8Syasuoka radiusd_module_unload(struct radiusd_module *module)
1315a7ca44b8Syasuoka {
1316a7ca44b8Syasuoka 	free(module->radpkt);
1317a7ca44b8Syasuoka 	radiusd_module_close(module);
1318a7ca44b8Syasuoka 	free(module);
1319a7ca44b8Syasuoka }
1320a7ca44b8Syasuoka 
1321a7ca44b8Syasuoka static void
1322a7ca44b8Syasuoka radiusd_module_on_imsg_io(int fd, short evmask, void *ctx)
1323a7ca44b8Syasuoka {
1324a7ca44b8Syasuoka 	struct radiusd_module	*module = ctx;
1325a7ca44b8Syasuoka 
1326c1aa9554Sclaudio 	if (evmask & EV_WRITE) {
1327a7ca44b8Syasuoka 		module->writeready = true;
1328dd7efffeSclaudio 		if (imsgbuf_write(&module->ibuf) == -1) {
1329dd7efffeSclaudio 			log_warn("Failed to write to module `%s': "
1330dd7efffeSclaudio 			    "imsgbuf_write()", module->name);
1331c1aa9554Sclaudio 			goto on_error;
1332c1aa9554Sclaudio 		}
1333c1aa9554Sclaudio 		module->writeready = false;
1334c1aa9554Sclaudio 	}
1335a7ca44b8Syasuoka 
13364a4dce94Syasuoka 	if (evmask & EV_READ) {
13374a4dce94Syasuoka 		if (radiusd_module_imsg_read(module) == -1)
1338a7ca44b8Syasuoka 			goto on_error;
1339a7ca44b8Syasuoka 	}
1340a7ca44b8Syasuoka 
1341a7ca44b8Syasuoka 	radiusd_module_reset_ev_handler(module);
1342a7ca44b8Syasuoka 
1343a7ca44b8Syasuoka 	return;
1344a7ca44b8Syasuoka on_error:
1345a7ca44b8Syasuoka 	radiusd_module_close(module);
1346a7ca44b8Syasuoka }
1347a7ca44b8Syasuoka 
1348a7ca44b8Syasuoka static void
1349a7ca44b8Syasuoka radiusd_module_reset_ev_handler(struct radiusd_module *module)
1350a7ca44b8Syasuoka {
1351a7ca44b8Syasuoka 	short		 evmask;
1352a7ca44b8Syasuoka 	struct timeval	*tvp = NULL, tv = { 0, 0 };
1353a7ca44b8Syasuoka 
1354a7ca44b8Syasuoka 	RADIUSD_ASSERT(module->fd >= 0);
1355a7ca44b8Syasuoka 	event_del(&module->ev);
1356a7ca44b8Syasuoka 
1357a7ca44b8Syasuoka 	evmask = EV_READ;
135831be28caSclaudio 	if (imsgbuf_queuelen(&module->ibuf) > 0) {
1359a7ca44b8Syasuoka 		if (!module->writeready)
1360a7ca44b8Syasuoka 			evmask |= EV_WRITE;
1361a7ca44b8Syasuoka 		else
1362a7ca44b8Syasuoka 			tvp = &tv;	/* fire immediately */
13634a4dce94Syasuoka 	}
1364a7ca44b8Syasuoka 
1365a7ca44b8Syasuoka 	/* module stopped and no event handler is set */
1366a7ca44b8Syasuoka 	if (evmask & EV_WRITE && tvp == NULL && module->stopped) {
1367a7ca44b8Syasuoka 		/* stop requested and no more to write */
1368a7ca44b8Syasuoka 		radiusd_module_close(module);
1369a7ca44b8Syasuoka 		return;
1370a7ca44b8Syasuoka 	}
1371a7ca44b8Syasuoka 
1372a7ca44b8Syasuoka 	event_set(&module->ev, module->fd, evmask, radiusd_module_on_imsg_io,
1373a7ca44b8Syasuoka 	    module);
1374a7ca44b8Syasuoka 	if (event_add(&module->ev, tvp) == -1) {
1375a7ca44b8Syasuoka 		log_warn("Could not set event handlers for module `%s': "
1376a7ca44b8Syasuoka 		    "event_add()", module->name);
1377a7ca44b8Syasuoka 		radiusd_module_close(module);
1378a7ca44b8Syasuoka 	}
1379a7ca44b8Syasuoka }
1380a7ca44b8Syasuoka 
1381a7ca44b8Syasuoka static int
13824a4dce94Syasuoka radiusd_module_imsg_read(struct radiusd_module *module)
1383a7ca44b8Syasuoka {
1384a7ca44b8Syasuoka 	int		 n;
1385a7ca44b8Syasuoka 	struct imsg	 imsg;
1386a7ca44b8Syasuoka 
13874f3fb1ffSclaudio 	if ((n = imsgbuf_read(&module->ibuf)) != 1) {
1388a7ca44b8Syasuoka 		if (n == -1)
1389a7ca44b8Syasuoka 			log_warn("Receiving a message from module `%s' "
1390dd7efffeSclaudio 			    "failed: imsgbuf_read", module->name);
1391a7ca44b8Syasuoka 		/* else closed */
1392a7ca44b8Syasuoka 		radiusd_module_close(module);
1393a7ca44b8Syasuoka 		return (-1);
1394a7ca44b8Syasuoka 	}
1395a7ca44b8Syasuoka 	for (;;) {
1396a7ca44b8Syasuoka 		if ((n = imsg_get(&module->ibuf, &imsg)) == -1) {
1397a7ca44b8Syasuoka 			log_warn("Receiving a message from module `%s' failed: "
1398a7ca44b8Syasuoka 			    "imsg_get", module->name);
1399a7ca44b8Syasuoka 			return (-1);
1400a7ca44b8Syasuoka 		}
1401a7ca44b8Syasuoka 		if (n == 0)
1402a7ca44b8Syasuoka 			return (0);
1403a7ca44b8Syasuoka 		radiusd_module_imsg(module, &imsg);
140458911fd1Syasuoka 		imsg_free(&imsg);
1405a7ca44b8Syasuoka 	}
1406a7ca44b8Syasuoka 
1407a7ca44b8Syasuoka 	return (0);
1408a7ca44b8Syasuoka }
1409a7ca44b8Syasuoka 
1410a7ca44b8Syasuoka static void
1411a7ca44b8Syasuoka radiusd_module_imsg(struct radiusd_module *module, struct imsg *imsg)
1412a7ca44b8Syasuoka {
1413a7ca44b8Syasuoka 	int			 datalen;
1414a7ca44b8Syasuoka 	struct radius_query	*q;
1415a7ca44b8Syasuoka 	u_int			 q_id;
1416a7ca44b8Syasuoka 
1417a7ca44b8Syasuoka 	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
1418a7ca44b8Syasuoka 	switch (imsg->hdr.type) {
1419a7ca44b8Syasuoka 	case IMSG_RADIUSD_MODULE_NOTIFY_SECRET:
1420a7ca44b8Syasuoka 		if (datalen > 0) {
1421a7ca44b8Syasuoka 			module->secret = strdup(imsg->data);
1422a7ca44b8Syasuoka 			if (module->secret == NULL)
1423a7ca44b8Syasuoka 				log_warn("Could not handle NOTIFY_SECRET "
1424a7ca44b8Syasuoka 				    "from `%s'", module->name);
1425a7ca44b8Syasuoka 		}
1426a7ca44b8Syasuoka 		break;
1427a7ca44b8Syasuoka 	case IMSG_RADIUSD_MODULE_USERPASS_OK:
1428a7ca44b8Syasuoka 	case IMSG_RADIUSD_MODULE_USERPASS_FAIL:
1429a7ca44b8Syasuoka 	    {
1430a7ca44b8Syasuoka 		char			*msg = NULL;
1431a7ca44b8Syasuoka 		const char		*msgtypestr;
1432a7ca44b8Syasuoka 
1433a7ca44b8Syasuoka 		msgtypestr = (imsg->hdr.type == IMSG_RADIUSD_MODULE_USERPASS_OK)
1434a7ca44b8Syasuoka 		    ? "USERPASS_OK" : "USERPASS_NG";
1435a7ca44b8Syasuoka 
1436a7ca44b8Syasuoka 		q_id = *(u_int *)imsg->data;
1437a7ca44b8Syasuoka 		if (datalen > (ssize_t)sizeof(u_int))
1438a7ca44b8Syasuoka 			msg = (char *)(((u_int *)imsg->data) + 1);
1439a7ca44b8Syasuoka 
1440a7ca44b8Syasuoka 		q = radiusd_find_query(module->radiusd, q_id);
1441a7ca44b8Syasuoka 		if (q == NULL) {
1442a7ca44b8Syasuoka 			log_warnx("Received %s from `%s', but query id=%u "
1443a7ca44b8Syasuoka 			    "unknown", msgtypestr, module->name, q_id);
1444a7ca44b8Syasuoka 			break;
1445a7ca44b8Syasuoka 		}
1446a7ca44b8Syasuoka 
1447a7ca44b8Syasuoka 		if ((q->res = radius_new_response_packet(
1448a7ca44b8Syasuoka 		    (imsg->hdr.type == IMSG_RADIUSD_MODULE_USERPASS_OK)
1449a7ca44b8Syasuoka 		    ? RADIUS_CODE_ACCESS_ACCEPT : RADIUS_CODE_ACCESS_REJECT,
1450a7ca44b8Syasuoka 		    q->req)) == NULL) {
1451a7ca44b8Syasuoka 			log_warn("radius_new_response_packet() failed");
1452a7ca44b8Syasuoka 			radiusd_access_request_aborted(q);
1453a7ca44b8Syasuoka 		} else {
1454a7ca44b8Syasuoka 			if (msg)
1455a7ca44b8Syasuoka 				radius_put_string_attr(q->res,
1456a7ca44b8Syasuoka 				    RADIUS_TYPE_REPLY_MESSAGE, msg);
1457a7ca44b8Syasuoka 			radius_set_response_authenticator(q->res,
1458ed1dc925Syasuoka 			    radius_query_client_secret(q));
1459a7ca44b8Syasuoka 			radiusd_access_request_answer(q);
146099ace5a5Sjsg 		}
1461a7ca44b8Syasuoka 		break;
1462a7ca44b8Syasuoka 	    }
1463a7ca44b8Syasuoka 	case IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER:
1464ed1dc925Syasuoka 	case IMSG_RADIUSD_MODULE_ACCSREQ_NEXT:
1465237e61d9Syasuoka 	case IMSG_RADIUSD_MODULE_REQDECO_DONE:
1466237e61d9Syasuoka 	case IMSG_RADIUSD_MODULE_RESDECO_DONE:
1467a7ca44b8Syasuoka 	    {
1468a7ca44b8Syasuoka 		static struct radiusd_module_radpkt_arg *ans;
1469237e61d9Syasuoka 		const char *typestr = "unknown";
1470237e61d9Syasuoka 
1471237e61d9Syasuoka 		switch (imsg->hdr.type) {
1472237e61d9Syasuoka 		case IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER:
1473237e61d9Syasuoka 			typestr = "ACCSREQ_ANSWER";
1474237e61d9Syasuoka 			break;
1475ed1dc925Syasuoka 		case IMSG_RADIUSD_MODULE_ACCSREQ_NEXT:
1476ed1dc925Syasuoka 			typestr = "ACCSREQ_NEXT";
1477ed1dc925Syasuoka 			break;
1478237e61d9Syasuoka 		case IMSG_RADIUSD_MODULE_REQDECO_DONE:
1479237e61d9Syasuoka 			typestr = "REQDECO_DONE";
1480237e61d9Syasuoka 			break;
1481237e61d9Syasuoka 		case IMSG_RADIUSD_MODULE_RESDECO_DONE:
1482237e61d9Syasuoka 			typestr = "RESDECO_DONE";
1483237e61d9Syasuoka 			break;
1484237e61d9Syasuoka 		}
1485237e61d9Syasuoka 
1486a7ca44b8Syasuoka 		if (datalen <
1487a7ca44b8Syasuoka 		    (ssize_t)sizeof(struct radiusd_module_radpkt_arg)) {
1488237e61d9Syasuoka 			log_warnx("Received %s message, but length is wrong",
1489237e61d9Syasuoka 			    typestr);
1490a7ca44b8Syasuoka 			break;
1491a7ca44b8Syasuoka 		}
1492a7ca44b8Syasuoka 		q_id = ((struct radiusd_module_radpkt_arg *)imsg->data)->q_id;
1493a7ca44b8Syasuoka 		q = radiusd_find_query(module->radiusd, q_id);
1494a7ca44b8Syasuoka 		if (q == NULL) {
1495237e61d9Syasuoka 			log_warnx("Received %s from %s, but query id=%u "
1496237e61d9Syasuoka 			    "unknown", typestr, module->name, q_id);
1497a7ca44b8Syasuoka 			break;
1498a7ca44b8Syasuoka 		}
1499a7ca44b8Syasuoka 		if ((ans = radiusd_module_recv_radpkt(module, imsg,
1500237e61d9Syasuoka 		    imsg->hdr.type, typestr)) != NULL) {
1501237e61d9Syasuoka 			RADIUS_PACKET *radpkt = NULL;
1502237e61d9Syasuoka 
1503237e61d9Syasuoka 			if (module->radpktoff > 0 &&
1504237e61d9Syasuoka 			    (radpkt = radius_convert_packet(
1505237e61d9Syasuoka 			    module->radpkt, module->radpktoff)) == NULL) {
1506237e61d9Syasuoka 				log_warn("q=%u radius_convert_packet() failed",
1507237e61d9Syasuoka 				    q->id);
1508237e61d9Syasuoka 				radiusd_access_request_aborted(q);
1509237e61d9Syasuoka 				break;
1510237e61d9Syasuoka 			}
1511a7ca44b8Syasuoka 			module->radpktoff = 0;
1512237e61d9Syasuoka 			switch (imsg->hdr.type) {
1513237e61d9Syasuoka 			case IMSG_RADIUSD_MODULE_REQDECO_DONE:
1514c9e9c1c9Syasuoka 				if (q->deco == NULL || q->deco->type !=
1515c9e9c1c9Syasuoka 				    IMSG_RADIUSD_MODULE_REQDECO) {
1516a449bbceSyasuoka 					log_warnx("q=%u received %s but not "
1517a449bbceSyasuoka 					    "requested", q->id, typestr);
1518c9e9c1c9Syasuoka 					if (radpkt != NULL)
1519c9e9c1c9Syasuoka 						radius_delete_packet(radpkt);
1520c9e9c1c9Syasuoka 					break;
1521c9e9c1c9Syasuoka 				}
1522237e61d9Syasuoka 				if (radpkt != NULL) {
1523237e61d9Syasuoka 					radius_delete_packet(q->req);
1524237e61d9Syasuoka 					q->req = radpkt;
1525237e61d9Syasuoka 				}
1526936475aaSyasuoka 				raidus_query_access_request(q);
1527237e61d9Syasuoka 				break;
1528237e61d9Syasuoka 			case IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER:
1529237e61d9Syasuoka 				if (radpkt == NULL) {
1530c9e9c1c9Syasuoka 					log_warnx("q=%u wrong pkt from module",
1531237e61d9Syasuoka 					    q->id);
1532237e61d9Syasuoka 					radiusd_access_request_aborted(q);
15335e763c0bSjsg 					break;
1534237e61d9Syasuoka 				}
1535237e61d9Syasuoka 				q->res = radpkt;
1536237e61d9Syasuoka 				radiusd_access_request_answer(q);
1537237e61d9Syasuoka 				break;
1538ed1dc925Syasuoka 			case IMSG_RADIUSD_MODULE_ACCSREQ_NEXT:
1539ed1dc925Syasuoka 				if (radpkt == NULL) {
1540ed1dc925Syasuoka 					log_warnx("q=%u wrong pkt from module",
1541ed1dc925Syasuoka 					    q->id);
1542ed1dc925Syasuoka 					radiusd_access_request_aborted(q);
1543ed1dc925Syasuoka 					break;
1544ed1dc925Syasuoka 				}
1545ed1dc925Syasuoka 				radiusd_access_request_next(q, radpkt);
1546ed1dc925Syasuoka 				break;
1547237e61d9Syasuoka 			case IMSG_RADIUSD_MODULE_RESDECO_DONE:
1548c9e9c1c9Syasuoka 				if (q->deco == NULL || q->deco->type !=
1549c9e9c1c9Syasuoka 				    IMSG_RADIUSD_MODULE_RESDECO) {
1550c9e9c1c9Syasuoka 					log_warnx("q=%u received %s but not "
1551c9e9c1c9Syasuoka 					    "requested", q->id, typestr);
1552c9e9c1c9Syasuoka 					if (radpkt != NULL)
1553c9e9c1c9Syasuoka 						radius_delete_packet(radpkt);
1554c9e9c1c9Syasuoka 					break;
1555c9e9c1c9Syasuoka 				}
1556237e61d9Syasuoka 				if (radpkt != NULL) {
1557237e61d9Syasuoka 					radius_delete_packet(q->res);
1558237e61d9Syasuoka 					radius_set_request_packet(radpkt,
1559237e61d9Syasuoka 					    q->req);
1560237e61d9Syasuoka 					q->res = radpkt;
1561237e61d9Syasuoka 				}
1562936475aaSyasuoka 				radius_query_access_response(q);
1563237e61d9Syasuoka 				break;
1564237e61d9Syasuoka 			}
1565a7ca44b8Syasuoka 		}
1566a7ca44b8Syasuoka 		break;
1567a7ca44b8Syasuoka 	    }
1568a7ca44b8Syasuoka 	case IMSG_RADIUSD_MODULE_ACCSREQ_ABORTED:
1569a7ca44b8Syasuoka 	    {
15702e151769Syasuoka 		if (datalen < (ssize_t)sizeof(u_int)) {
15712e151769Syasuoka 			log_warnx("Received ACCSREQ_ABORTED message, but "
15722e151769Syasuoka 			    "length is wrong");
15732e151769Syasuoka 			break;
15742e151769Syasuoka 		}
1575a7ca44b8Syasuoka 		q_id = *((u_int *)imsg->data);
1576a7ca44b8Syasuoka 		q = radiusd_find_query(module->radiusd, q_id);
1577a7ca44b8Syasuoka 		if (q == NULL) {
1578a7ca44b8Syasuoka 			log_warnx("Received ACCSREQ_ABORT from %s, but query "
1579a7ca44b8Syasuoka 			    "id=%u unknown", module->name, q_id);
1580a7ca44b8Syasuoka 			break;
1581a7ca44b8Syasuoka 		}
1582a7ca44b8Syasuoka 		radiusd_access_request_aborted(q);
1583a7ca44b8Syasuoka 		break;
1584a7ca44b8Syasuoka 	    }
1585842565f2Syasuoka 	case IMSG_RADIUSD_MODULE_CTRL_BIND:
1586842565f2Syasuoka 		control_conn_bind(imsg->hdr.peerid, module->name);
1587842565f2Syasuoka 		break;
1588a7ca44b8Syasuoka 	default:
1589842565f2Syasuoka 		if (imsg->hdr.peerid != 0)
1590842565f2Syasuoka 			control_imsg_relay(imsg);
1591842565f2Syasuoka 		else
1592842565f2Syasuoka 			RADIUSD_DBG(("Unhandled imsg type=%d from %s",
1593842565f2Syasuoka 			    imsg->hdr.type, module->name));
1594a7ca44b8Syasuoka 	}
1595a7ca44b8Syasuoka }
1596a7ca44b8Syasuoka 
1597a7ca44b8Syasuoka static struct radiusd_module_radpkt_arg *
1598a7ca44b8Syasuoka radiusd_module_recv_radpkt(struct radiusd_module *module, struct imsg *imsg,
1599a7ca44b8Syasuoka     uint32_t imsg_type, const char *type_str)
1600a7ca44b8Syasuoka {
1601a7ca44b8Syasuoka 	struct radiusd_module_radpkt_arg	*ans;
1602a7ca44b8Syasuoka 	int					 datalen, chunklen;
1603a7ca44b8Syasuoka 
1604a7ca44b8Syasuoka 	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
1605a7ca44b8Syasuoka 	ans = (struct radiusd_module_radpkt_arg *)imsg->data;
16063f8258c8Syasuoka 	if (module->radpktsiz < ans->pktlen) {
1607a7ca44b8Syasuoka 		u_char *nradpkt;
16083f8258c8Syasuoka 		if ((nradpkt = realloc(module->radpkt, ans->pktlen)) == NULL) {
1609a7ca44b8Syasuoka 			log_warn("Could not handle received %s message from "
1610a7ca44b8Syasuoka 			    "`%s'", type_str, module->name);
1611a7ca44b8Syasuoka 			goto on_fail;
1612a7ca44b8Syasuoka 		}
1613a7ca44b8Syasuoka 		module->radpkt = nradpkt;
16143f8258c8Syasuoka 		module->radpktsiz = ans->pktlen;
1615a7ca44b8Syasuoka 	}
1616a7ca44b8Syasuoka 	chunklen = datalen - sizeof(struct radiusd_module_radpkt_arg);
1617a7ca44b8Syasuoka 	if (chunklen > module->radpktsiz - module->radpktoff) {
1618a7ca44b8Syasuoka 		log_warnx("Could not handle received %s message from `%s': "
1619a7ca44b8Syasuoka 		    "received length is too big", type_str, module->name);
1620a7ca44b8Syasuoka 		goto on_fail;
1621a7ca44b8Syasuoka 	}
1622237e61d9Syasuoka 	if (chunklen > 0) {
1623a7ca44b8Syasuoka 		memcpy(module->radpkt + module->radpktoff,
1624a7ca44b8Syasuoka 		    (caddr_t)(ans + 1), chunklen);
1625a7ca44b8Syasuoka 		module->radpktoff += chunklen;
1626237e61d9Syasuoka 	}
1627a7ca44b8Syasuoka 	if (!ans->final)
1628a7ca44b8Syasuoka 		return (NULL);	/* again */
16293f8258c8Syasuoka 	if (module->radpktoff != ans->pktlen) {
1630a7ca44b8Syasuoka 		log_warnx("Could not handle received %s message from `%s': "
1631a7ca44b8Syasuoka 		    "length is mismatch", type_str, module->name);
1632a7ca44b8Syasuoka 		goto on_fail;
1633a7ca44b8Syasuoka 	}
1634a7ca44b8Syasuoka 
1635a7ca44b8Syasuoka 	return (ans);
1636a7ca44b8Syasuoka on_fail:
1637a7ca44b8Syasuoka 	module->radpktoff = 0;
1638a7ca44b8Syasuoka 	return (NULL);
1639a7ca44b8Syasuoka }
1640a7ca44b8Syasuoka 
1641a7ca44b8Syasuoka int
1642a7ca44b8Syasuoka radiusd_module_set(struct radiusd_module *module, const char *name,
1643a7ca44b8Syasuoka     int argc, char * const * argv)
1644a7ca44b8Syasuoka {
1645a7ca44b8Syasuoka 	struct radiusd_module_set_arg	 arg;
1646a7ca44b8Syasuoka 	struct radiusd_module_object	*val;
1647a7ca44b8Syasuoka 	int				 i, niov = 0;
1648a7ca44b8Syasuoka 	u_char				*buf = NULL, *buf0;
1649a7ca44b8Syasuoka 	ssize_t				 n;
1650a7ca44b8Syasuoka 	size_t				 bufsiz = 0, bufoff = 0, bufsiz0;
1651a7ca44b8Syasuoka 	size_t				 vallen, valsiz;
1652a7ca44b8Syasuoka 	struct iovec			 iov[2];
1653a7ca44b8Syasuoka 	struct imsg			 imsg;
1654a7ca44b8Syasuoka 
1655a7ca44b8Syasuoka 	memset(&arg, 0, sizeof(arg));
1656a7ca44b8Syasuoka 	arg.nparamval = argc;
1657a7ca44b8Syasuoka 	strlcpy(arg.paramname, name, sizeof(arg.paramname));
1658a7ca44b8Syasuoka 
1659a7ca44b8Syasuoka 	iov[niov].iov_base = &arg;
1660a7ca44b8Syasuoka 	iov[niov].iov_len = sizeof(struct radiusd_module_set_arg);
1661a7ca44b8Syasuoka 	niov++;
1662a7ca44b8Syasuoka 
1663a7ca44b8Syasuoka 	for (i = 0; i < argc; i++) {
1664a7ca44b8Syasuoka 		vallen = strlen(argv[i]) + 1;
1665a7ca44b8Syasuoka 		valsiz = sizeof(struct radiusd_module_object) + vallen;
1666a7ca44b8Syasuoka 		if (bufsiz < bufoff + valsiz) {
1667a7ca44b8Syasuoka 			bufsiz0 = bufoff + valsiz + 128;
1668a7ca44b8Syasuoka 			if ((buf0 = realloc(buf, bufsiz0)) == NULL) {
1669a7ca44b8Syasuoka 				log_warn("Failed to set config parameter to "
1670a7ca44b8Syasuoka 				    "module `%s': realloc", module->name);
1671a7ca44b8Syasuoka 				goto on_error;
1672a7ca44b8Syasuoka 			}
1673a7ca44b8Syasuoka 			buf = buf0;
1674a7ca44b8Syasuoka 			bufsiz = bufsiz0;
1675a7ca44b8Syasuoka 			memset(buf + bufoff, 0, bufsiz - bufoff);
1676a7ca44b8Syasuoka 		}
1677a7ca44b8Syasuoka 		val = (struct radiusd_module_object *)(buf + bufoff);
1678a7ca44b8Syasuoka 		val->size = valsiz;
1679a7ca44b8Syasuoka 		memcpy(val + 1, argv[i], vallen);
1680a7ca44b8Syasuoka 
1681a7ca44b8Syasuoka 		bufoff += valsiz;
1682a7ca44b8Syasuoka 	}
1683a7ca44b8Syasuoka 	iov[niov].iov_base = buf;
1684a7ca44b8Syasuoka 	iov[niov].iov_len = bufoff;
1685a7ca44b8Syasuoka 	niov++;
1686a7ca44b8Syasuoka 
1687a7ca44b8Syasuoka 	if (imsg_composev(&module->ibuf, IMSG_RADIUSD_MODULE_SET_CONFIG, 0, 0,
1688a7ca44b8Syasuoka 	    -1, iov, niov) == -1) {
1689a7ca44b8Syasuoka 		log_warn("Failed to set config parameter to module `%s': "
1690a7ca44b8Syasuoka 		    "imsg_composev", module->name);
1691a7ca44b8Syasuoka 		goto on_error;
1692a7ca44b8Syasuoka 	}
1693a7ca44b8Syasuoka 	if (imsg_sync_flush(&module->ibuf, MODULE_IO_TIMEOUT) == -1) {
1694a7ca44b8Syasuoka 		log_warn("Failed to set config parameter to module `%s': "
1695a7ca44b8Syasuoka 		    "imsg_flush_timeout", module->name);
1696a7ca44b8Syasuoka 		goto on_error;
1697a7ca44b8Syasuoka 	}
1698a7ca44b8Syasuoka 	for (;;) {
1699a7ca44b8Syasuoka 		if (imsg_sync_read(&module->ibuf, MODULE_IO_TIMEOUT) <= 0) {
1700a7ca44b8Syasuoka 			log_warn("Failed to get reply from module `%s': "
1701a7ca44b8Syasuoka 			    "imsg_sync_read", module->name);
1702a7ca44b8Syasuoka 			goto on_error;
1703a7ca44b8Syasuoka 		}
1704a7ca44b8Syasuoka 		if ((n = imsg_get(&module->ibuf, &imsg)) > 0)
1705a7ca44b8Syasuoka 			break;
1706a7ca44b8Syasuoka 		if (n < 0) {
1707a7ca44b8Syasuoka 			log_warn("Failed to get reply from module `%s': "
1708a7ca44b8Syasuoka 			    "imsg_get", module->name);
1709a7ca44b8Syasuoka 			goto on_error;
1710a7ca44b8Syasuoka 		}
1711a7ca44b8Syasuoka 	}
1712a7ca44b8Syasuoka 	if (imsg.hdr.type == IMSG_NG) {
1713a7ca44b8Syasuoka 		log_warnx("Could not set `%s' for module `%s': %s", name,
1714a7ca44b8Syasuoka 		    module->name, (char *)imsg.data);
1715a7ca44b8Syasuoka 		goto on_error;
1716a7ca44b8Syasuoka 	} else if (imsg.hdr.type != IMSG_OK) {
1717a7ca44b8Syasuoka 		imsg_free(&imsg);
1718a7ca44b8Syasuoka 		log_warnx("Failed to get reply from module `%s': "
1719a7ca44b8Syasuoka 		    "unknown imsg type=%d", module->name, imsg.hdr.type);
1720a7ca44b8Syasuoka 		goto on_error;
1721a7ca44b8Syasuoka 	}
1722a7ca44b8Syasuoka 	imsg_free(&imsg);
1723a7ca44b8Syasuoka 
1724a7ca44b8Syasuoka 	free(buf);
1725a7ca44b8Syasuoka 	return (0);
1726a7ca44b8Syasuoka 
1727a7ca44b8Syasuoka on_error:
1728a7ca44b8Syasuoka 	free(buf);
1729a7ca44b8Syasuoka 	return (-1);
1730a7ca44b8Syasuoka }
1731a7ca44b8Syasuoka 
1732a7ca44b8Syasuoka static void
1733a7ca44b8Syasuoka radiusd_module_userpass(struct radiusd_module *module, struct radius_query *q)
1734a7ca44b8Syasuoka {
1735a7ca44b8Syasuoka 	struct radiusd_module_userpass_arg userpass;
1736a7ca44b8Syasuoka 
1737a7ca44b8Syasuoka 	memset(&userpass, 0, sizeof(userpass));
1738a7ca44b8Syasuoka 	userpass.q_id = q->id;
1739a7ca44b8Syasuoka 
1740a7ca44b8Syasuoka 	if (radius_get_user_password_attr(q->req, userpass.pass,
1741ed1dc925Syasuoka 	    sizeof(userpass.pass), radius_query_client_secret(q)) == 0)
1742a7ca44b8Syasuoka 		userpass.has_pass = true;
1743a7ca44b8Syasuoka 	else
1744a7ca44b8Syasuoka 		userpass.has_pass = false;
1745237e61d9Syasuoka 	if (radius_get_string_attr(q->req, RADIUS_TYPE_USER_NAME,
1746237e61d9Syasuoka 	    userpass.user, sizeof(userpass.user)) != 0) {
1747237e61d9Syasuoka 		log_warnx("q=%u no User-Name attribute", q->id);
1748a7ca44b8Syasuoka 		goto on_error;
1749a7ca44b8Syasuoka 	}
1750a7ca44b8Syasuoka 	imsg_compose(&module->ibuf, IMSG_RADIUSD_MODULE_USERPASS, 0, 0, -1,
1751a7ca44b8Syasuoka 	    &userpass, sizeof(userpass));
1752a7ca44b8Syasuoka 	radiusd_module_reset_ev_handler(module);
1753a7ca44b8Syasuoka 	return;
1754a7ca44b8Syasuoka on_error:
1755a7ca44b8Syasuoka 	radiusd_access_request_aborted(q);
1756a7ca44b8Syasuoka }
1757a7ca44b8Syasuoka 
1758a7ca44b8Syasuoka static void
1759a7ca44b8Syasuoka radiusd_module_access_request(struct radiusd_module *module,
1760a7ca44b8Syasuoka     struct radius_query *q)
1761a7ca44b8Syasuoka {
1762a7ca44b8Syasuoka 	RADIUS_PACKET	*radpkt;
1763a7ca44b8Syasuoka 	char		 pass[256];
1764a7ca44b8Syasuoka 
1765a7ca44b8Syasuoka 	if ((radpkt = radius_convert_packet(radius_get_data(q->req),
1766a7ca44b8Syasuoka 	    radius_get_length(q->req))) == NULL) {
1767237e61d9Syasuoka 		log_warn("q=%u Could not send ACCSREQ to `%s'", q->id,
1768237e61d9Syasuoka 		    module->name);
1769237e61d9Syasuoka 		radiusd_access_request_aborted(q);
1770a7ca44b8Syasuoka 		return;
1771a7ca44b8Syasuoka 	}
1772a449bbceSyasuoka 	if (radius_get_user_password_attr(radpkt, pass, sizeof(pass),
1773a7ca44b8Syasuoka 	    q->client->secret) == 0) {
1774a7ca44b8Syasuoka 		radius_del_attr_all(radpkt, RADIUS_TYPE_USER_PASSWORD);
1775a7ca44b8Syasuoka 		(void)radius_put_raw_attr(radpkt, RADIUS_TYPE_USER_PASSWORD,
1776a7ca44b8Syasuoka 		    pass, strlen(pass));
1777a7ca44b8Syasuoka 	}
1778237e61d9Syasuoka 	if (imsg_compose_radius_packet(&module->ibuf,
1779237e61d9Syasuoka 	    IMSG_RADIUSD_MODULE_ACCSREQ, q->id, radpkt) == -1) {
1780237e61d9Syasuoka 		log_warn("q=%u Could not send ACCSREQ to `%s'", q->id,
1781237e61d9Syasuoka 		    module->name);
1782237e61d9Syasuoka 		radiusd_access_request_aborted(q);
1783a7ca44b8Syasuoka 	}
1784a7ca44b8Syasuoka 	radiusd_module_reset_ev_handler(module);
1785a7ca44b8Syasuoka 	radius_delete_packet(radpkt);
1786237e61d9Syasuoka }
1787a7ca44b8Syasuoka 
1788237e61d9Syasuoka static void
1789ed1dc925Syasuoka radiusd_module_next_response(struct radiusd_module *module,
1790ed1dc925Syasuoka     struct radius_query *q, RADIUS_PACKET *pkt)
1791ed1dc925Syasuoka {
1792ed1dc925Syasuoka 	if (imsg_compose_radius_packet(&module->ibuf,
1793ed1dc925Syasuoka 	    IMSG_RADIUSD_MODULE_NEXTRES, q->id, pkt) == -1) {
1794ed1dc925Syasuoka 		log_warn("q=%u Could not send NEXTRES to `%s'", q->id,
1795ed1dc925Syasuoka 		    module->name);
1796ed1dc925Syasuoka 		radiusd_access_request_aborted(q);
1797ed1dc925Syasuoka 	}
1798ed1dc925Syasuoka 	radiusd_module_reset_ev_handler(module);
1799ed1dc925Syasuoka }
1800ed1dc925Syasuoka 
1801ed1dc925Syasuoka static void
1802237e61d9Syasuoka radiusd_module_request_decoration(struct radiusd_module *module,
1803237e61d9Syasuoka     struct radius_query *q)
1804237e61d9Syasuoka {
1805237e61d9Syasuoka 	if (module->fd < 0) {
1806237e61d9Syasuoka 		log_warnx("q=%u Could not send REQDECO to `%s': module is "
1807237e61d9Syasuoka 		    "not running?", q->id, module->name);
1808237e61d9Syasuoka 		radiusd_access_request_aborted(q);
1809a7ca44b8Syasuoka 		return;
1810a7ca44b8Syasuoka 	}
1811237e61d9Syasuoka 	if (imsg_compose_radius_packet(&module->ibuf,
1812237e61d9Syasuoka 	    IMSG_RADIUSD_MODULE_REQDECO, q->id, q->req) == -1) {
1813237e61d9Syasuoka 		log_warn("q=%u Could not send REQDECO to `%s'", q->id,
1814237e61d9Syasuoka 		    module->name);
1815237e61d9Syasuoka 		radiusd_access_request_aborted(q);
1816237e61d9Syasuoka 		return;
1817237e61d9Syasuoka 	}
1818c9e9c1c9Syasuoka 	RADIUSD_ASSERT(q->deco != NULL);
1819c9e9c1c9Syasuoka 	q->deco->type = IMSG_RADIUSD_MODULE_REQDECO;
1820237e61d9Syasuoka 	radiusd_module_reset_ev_handler(module);
1821237e61d9Syasuoka }
1822237e61d9Syasuoka 
1823237e61d9Syasuoka static void
1824237e61d9Syasuoka radiusd_module_response_decoration(struct radiusd_module *module,
1825237e61d9Syasuoka     struct radius_query *q)
1826237e61d9Syasuoka {
1827237e61d9Syasuoka 	if (module->fd < 0) {
1828237e61d9Syasuoka 		log_warnx("q=%u Could not send RESDECO to `%s': module is "
1829237e61d9Syasuoka 		    "not running?", q->id, module->name);
1830237e61d9Syasuoka 		radiusd_access_request_aborted(q);
1831237e61d9Syasuoka 		return;
1832237e61d9Syasuoka 	}
1833237e61d9Syasuoka 	if (imsg_compose_radius_packet(&module->ibuf,
183476e157acSyasuoka 	    IMSG_RADIUSD_MODULE_RESDECO0_REQ, q->id, q->req) == -1) {
183576e157acSyasuoka 		log_warn("q=%u Could not send RESDECO0_REQ to `%s'", q->id,
183676e157acSyasuoka 		    module->name);
183776e157acSyasuoka 		radiusd_access_request_aborted(q);
183876e157acSyasuoka 		return;
183976e157acSyasuoka 	}
184076e157acSyasuoka 	if (imsg_compose_radius_packet(&module->ibuf,
1841237e61d9Syasuoka 	    IMSG_RADIUSD_MODULE_RESDECO, q->id, q->res) == -1) {
1842237e61d9Syasuoka 		log_warn("q=%u Could not send RESDECO to `%s'", q->id,
1843237e61d9Syasuoka 		    module->name);
1844237e61d9Syasuoka 		radiusd_access_request_aborted(q);
1845237e61d9Syasuoka 		return;
1846237e61d9Syasuoka 	}
1847c9e9c1c9Syasuoka 	RADIUSD_ASSERT(q->deco != NULL);
1848c9e9c1c9Syasuoka 	q->deco->type = IMSG_RADIUSD_MODULE_RESDECO;
1849237e61d9Syasuoka 	radiusd_module_reset_ev_handler(module);
1850237e61d9Syasuoka }
1851237e61d9Syasuoka 
1852747da5e9Syasuoka static void
1853747da5e9Syasuoka radiusd_module_account_request(struct radiusd_module *module,
1854747da5e9Syasuoka     struct radius_query *q)
1855747da5e9Syasuoka {
1856747da5e9Syasuoka 	RADIUS_PACKET				*radpkt;
1857747da5e9Syasuoka 
1858747da5e9Syasuoka 	if ((radpkt = radius_convert_packet(radius_get_data(q->req),
1859747da5e9Syasuoka 	    radius_get_length(q->req))) == NULL) {
1860747da5e9Syasuoka 		log_warn("q=%u Could not send ACCSREQ to `%s'", q->id,
1861747da5e9Syasuoka 		    module->name);
1862747da5e9Syasuoka 		radiusd_access_request_aborted(q);
1863747da5e9Syasuoka 		return;
1864747da5e9Syasuoka 	}
1865747da5e9Syasuoka 	if (imsg_compose_radius_packet(&module->ibuf,
1866747da5e9Syasuoka 	    IMSG_RADIUSD_MODULE_ACCTREQ, q->id, radpkt) == -1) {
1867747da5e9Syasuoka 		log_warn("q=%u Could not send ACCTREQ to `%s'", q->id,
1868747da5e9Syasuoka 		    module->name);
1869747da5e9Syasuoka 		radiusd_access_request_aborted(q);
1870747da5e9Syasuoka 	}
1871747da5e9Syasuoka 	radiusd_module_reset_ev_handler(module);
1872747da5e9Syasuoka 	radius_delete_packet(radpkt);
1873747da5e9Syasuoka }
1874747da5e9Syasuoka 
1875237e61d9Syasuoka static int
1876237e61d9Syasuoka imsg_compose_radius_packet(struct imsgbuf *ibuf, uint32_t type, u_int q_id,
1877237e61d9Syasuoka     RADIUS_PACKET *radpkt)
1878237e61d9Syasuoka {
1879237e61d9Syasuoka 	struct radiusd_module_radpkt_arg	 arg;
1880237e61d9Syasuoka 	int					 off = 0, len, siz;
1881237e61d9Syasuoka 	struct iovec				 iov[2];
1882237e61d9Syasuoka 	const u_char				*pkt;
1883237e61d9Syasuoka 
1884237e61d9Syasuoka 	pkt = radius_get_data(radpkt);
1885237e61d9Syasuoka 	len = radius_get_length(radpkt);
1886237e61d9Syasuoka 	memset(&arg, 0, sizeof(arg));
1887237e61d9Syasuoka 	arg.q_id = q_id;
1888237e61d9Syasuoka 	arg.pktlen = len;
1889237e61d9Syasuoka 	while (off < len) {
1890237e61d9Syasuoka 		siz = MAX_IMSGSIZE - sizeof(arg);
1891237e61d9Syasuoka 		if (len - off > siz)
1892237e61d9Syasuoka 			arg.final = false;
1893237e61d9Syasuoka 		else {
1894237e61d9Syasuoka 			arg.final = true;
1895237e61d9Syasuoka 			siz = len - off;
1896237e61d9Syasuoka 		}
1897237e61d9Syasuoka 		iov[0].iov_base = &arg;
1898237e61d9Syasuoka 		iov[0].iov_len = sizeof(arg);
1899237e61d9Syasuoka 		iov[1].iov_base = (caddr_t)pkt + off;
1900237e61d9Syasuoka 		iov[1].iov_len = siz;
1901237e61d9Syasuoka 		if (imsg_composev(ibuf, type, 0, 0, -1, iov, 2) == -1)
1902237e61d9Syasuoka 			return (-1);
1903237e61d9Syasuoka 		off += siz;
1904237e61d9Syasuoka 	}
1905237e61d9Syasuoka 	return (0);
1906237e61d9Syasuoka }
19078c9be245Syasuoka 
19088c9be245Syasuoka static void
19098c9be245Syasuoka close_stdio(void)
19108c9be245Syasuoka {
19118c9be245Syasuoka 	int	fd;
19128c9be245Syasuoka 
19138c9be245Syasuoka 	if ((fd = open(_PATH_DEVNULL, O_RDWR)) != -1) {
19148c9be245Syasuoka 		dup2(fd, STDIN_FILENO);
19158c9be245Syasuoka 		dup2(fd, STDOUT_FILENO);
19168c9be245Syasuoka 		dup2(fd, STDERR_FILENO);
19178c9be245Syasuoka 		if (fd > STDERR_FILENO)
19188c9be245Syasuoka 			close(fd);
19198c9be245Syasuoka 	}
19208c9be245Syasuoka }
1921842565f2Syasuoka 
1922842565f2Syasuoka /***********************************************************************
1923842565f2Syasuoka  * imsg_event
1924842565f2Syasuoka  ***********************************************************************/
1925842565f2Syasuoka struct iovec;
1926842565f2Syasuoka 
1927842565f2Syasuoka void
1928842565f2Syasuoka imsg_event_add(struct imsgev *iev)
1929842565f2Syasuoka {
1930842565f2Syasuoka 	iev->events = EV_READ;
193131be28caSclaudio 	if (imsgbuf_queuelen(&iev->ibuf) > 0)
1932842565f2Syasuoka 		iev->events |= EV_WRITE;
1933842565f2Syasuoka 
1934842565f2Syasuoka 	event_del(&iev->ev);
1935842565f2Syasuoka 	event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev);
1936842565f2Syasuoka 	event_add(&iev->ev, NULL);
1937842565f2Syasuoka }
1938842565f2Syasuoka 
1939842565f2Syasuoka int
1940842565f2Syasuoka imsg_compose_event(struct imsgev *iev, uint32_t type, uint32_t peerid,
1941842565f2Syasuoka     pid_t pid, int fd, void *data, size_t datalen)
1942842565f2Syasuoka {
1943842565f2Syasuoka 	int	ret;
1944842565f2Syasuoka 
1945842565f2Syasuoka 	if ((ret = imsg_compose(&iev->ibuf, type, peerid,
1946842565f2Syasuoka 	    pid, fd, data, datalen)) != -1)
1947842565f2Syasuoka 		imsg_event_add(iev);
1948842565f2Syasuoka 	return (ret);
1949842565f2Syasuoka }
1950842565f2Syasuoka 
1951842565f2Syasuoka int
1952842565f2Syasuoka imsg_composev_event(struct imsgev *iev, uint32_t type, uint32_t peerid,
1953842565f2Syasuoka     pid_t pid, int fd, struct iovec *iov, int niov)
1954842565f2Syasuoka {
1955842565f2Syasuoka 	int	ret;
1956842565f2Syasuoka 
1957842565f2Syasuoka 	if ((ret = imsg_composev(&iev->ibuf, type, peerid,
1958842565f2Syasuoka 	    pid, fd, iov, niov)) != -1)
1959842565f2Syasuoka 		imsg_event_add(iev);
1960842565f2Syasuoka 	return (ret);
1961842565f2Syasuoka }
1962