xref: /openbsd-src/usr.sbin/radiusd/radiusd_module.c (revision 882428cdbdd2944d8f59bc8621c131fd814fb6ee)
1*882428cdSclaudio /*	$OpenBSD: radiusd_module.c,v 1.26 2024/11/21 13:43:10 claudio Exp $	*/
2a7ca44b8Syasuoka 
3a7ca44b8Syasuoka /*
4a7ca44b8Syasuoka  * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
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 /* radiusd_module.c -- helper functions for radiusd modules */
20a7ca44b8Syasuoka 
21a7ca44b8Syasuoka #include <sys/types.h>
22a7ca44b8Syasuoka #include <sys/queue.h>
23a7ca44b8Syasuoka #include <sys/uio.h>
24a7ca44b8Syasuoka 
25a7ca44b8Syasuoka #include <err.h>
26a7ca44b8Syasuoka #include <errno.h>
27a7ca44b8Syasuoka #include <event.h>
28a7ca44b8Syasuoka #include <fcntl.h>
29a7ca44b8Syasuoka #include <imsg.h>
30a7ca44b8Syasuoka #include <stdio.h>
31a7ca44b8Syasuoka #include <stdlib.h>
32a7ca44b8Syasuoka #include <string.h>
33a7ca44b8Syasuoka #include <syslog.h>
34a7ca44b8Syasuoka #include <unistd.h>
353d3cf35cSyasuoka #include <pwd.h>
36a7ca44b8Syasuoka 
37a7ca44b8Syasuoka #include "radiusd.h"
38a7ca44b8Syasuoka #include "radiusd_module.h"
39a7ca44b8Syasuoka #include "imsg_subr.h"
40a7ca44b8Syasuoka 
41a7ca44b8Syasuoka static void	(*module_config_set) (void *, const char *, int,
42a7ca44b8Syasuoka 		    char * const *) = NULL;
43a7ca44b8Syasuoka static void	(*module_start_module) (void *) = NULL;
44a7ca44b8Syasuoka static void	(*module_stop_module) (void *) = NULL;
45a7ca44b8Syasuoka static void	(*module_userpass) (void *, u_int, const char *, const char *)
46a7ca44b8Syasuoka 		    = NULL;
47a7ca44b8Syasuoka static void	(*module_access_request) (void *, u_int, const u_char *,
48a7ca44b8Syasuoka 		    size_t) = NULL;
49ed1dc925Syasuoka static void	(*module_next_response) (void *, u_int, const u_char *,
50ed1dc925Syasuoka 		    size_t) = NULL;
51237e61d9Syasuoka static void	(*module_request_decoration) (void *, u_int, const u_char *,
52237e61d9Syasuoka 		    size_t) = NULL;
53237e61d9Syasuoka static void	(*module_response_decoration) (void *, u_int, const u_char *,
5476e157acSyasuoka 		    size_t, const u_char *, size_t) = NULL;
55747da5e9Syasuoka static void	(*module_accounting_request) (void *, u_int, const u_char *,
56747da5e9Syasuoka 		    size_t) = NULL;
57842565f2Syasuoka static void	(*module_dispatch_control) (void *, struct imsg *) = NULL;
58a7ca44b8Syasuoka 
59a7ca44b8Syasuoka struct module_base {
60a7ca44b8Syasuoka 	void			*ctx;
61a7ca44b8Syasuoka 	struct imsgbuf		 ibuf;
623d3cf35cSyasuoka 	bool			 priv_dropped;
63a7ca44b8Syasuoka 
64a7ca44b8Syasuoka 	/* Buffer for receiving the RADIUS packet */
65a7ca44b8Syasuoka 	u_char			*radpkt;
66a7ca44b8Syasuoka 	int			 radpktsiz;
67a7ca44b8Syasuoka 	int			 radpktoff;
6876e157acSyasuoka 	u_char			*radpkt2;
6976e157acSyasuoka 	int			 radpkt2siz;	/* allocated size */
7076e157acSyasuoka 	int			 radpkt2len;	/* actual size */
71a7ca44b8Syasuoka 
72a7ca44b8Syasuoka #ifdef USE_LIBEVENT
73a7ca44b8Syasuoka 	struct module_imsgbuf	*module_imsgbuf;
74a7ca44b8Syasuoka 	bool			 writeready;
754655f5f2Syasuoka 	bool			 stopped;
76a7ca44b8Syasuoka 	bool			 ev_onhandler;
77a7ca44b8Syasuoka 	struct event		 ev;
78a7ca44b8Syasuoka #endif
79a7ca44b8Syasuoka };
80a7ca44b8Syasuoka 
81a7ca44b8Syasuoka static int	 module_common_radpkt(struct module_base *, uint32_t, u_int,
82d7548b59Syasuoka 		    const u_char *, size_t);
83a7ca44b8Syasuoka static int	 module_recv_imsg(struct module_base *);
84a7ca44b8Syasuoka static int	 module_imsg_handler(struct module_base *, struct imsg *);
85a7ca44b8Syasuoka #ifdef USE_LIBEVENT
86a7ca44b8Syasuoka static void	 module_on_event(int, short, void *);
87a7ca44b8Syasuoka #endif
88a7ca44b8Syasuoka static void	 module_reset_event(struct module_base *);
89a7ca44b8Syasuoka 
90a7ca44b8Syasuoka struct module_base *
91a7ca44b8Syasuoka module_create(int sock, void *ctx, struct module_handlers *handler)
92a7ca44b8Syasuoka {
93a7ca44b8Syasuoka 	struct module_base	*base;
94a7ca44b8Syasuoka 
95a7ca44b8Syasuoka 	if ((base = calloc(1, sizeof(struct module_base))) == NULL)
96a7ca44b8Syasuoka 		return (NULL);
97a7ca44b8Syasuoka 
98*882428cdSclaudio 	if (imsgbuf_init(&base->ibuf, sock) == -1) {
99*882428cdSclaudio 		free(base);
100*882428cdSclaudio 		return (NULL);
101*882428cdSclaudio 	}
102a7ca44b8Syasuoka 	base->ctx = ctx;
103a7ca44b8Syasuoka 
104a7ca44b8Syasuoka 	module_userpass = handler->userpass;
105a7ca44b8Syasuoka 	module_access_request = handler->access_request;
106ed1dc925Syasuoka 	module_next_response = handler->next_response;
107a7ca44b8Syasuoka 	module_config_set = handler->config_set;
108237e61d9Syasuoka 	module_request_decoration = handler->request_decoration;
109237e61d9Syasuoka 	module_response_decoration = handler->response_decoration;
110747da5e9Syasuoka 	module_accounting_request = handler->accounting_request;
111a7ca44b8Syasuoka 	module_start_module = handler->start;
112a7ca44b8Syasuoka 	module_stop_module = handler->stop;
113842565f2Syasuoka 	module_dispatch_control = handler->dispatch_control;
114a7ca44b8Syasuoka 
115a7ca44b8Syasuoka 	return (base);
116a7ca44b8Syasuoka }
117a7ca44b8Syasuoka 
118a7ca44b8Syasuoka void
119a7ca44b8Syasuoka module_start(struct module_base *base)
120a7ca44b8Syasuoka {
121a7ca44b8Syasuoka #ifdef USE_LIBEVENT
1221df00c46Syasuoka 	int	 ival;
123a7ca44b8Syasuoka 
124df69c215Sderaadt 	if ((ival = fcntl(base->ibuf.fd, F_GETFL)) == -1)
1251df00c46Syasuoka 		err(1, "Failed to F_GETFL");
126df69c215Sderaadt 	if (fcntl(base->ibuf.fd, F_SETFL, ival | O_NONBLOCK) == -1)
127a7ca44b8Syasuoka 		err(1, "Failed to setup NONBLOCK");
1284655f5f2Syasuoka 	event_set(&base->ev, base->ibuf.fd, EV_READ, module_on_event, base);
1294655f5f2Syasuoka 	event_add(&base->ev, NULL);
130a7ca44b8Syasuoka #endif
131a7ca44b8Syasuoka }
132a7ca44b8Syasuoka 
133a7ca44b8Syasuoka int
134a7ca44b8Syasuoka module_run(struct module_base *base)
135a7ca44b8Syasuoka {
136a7ca44b8Syasuoka 	int	 ret;
137a7ca44b8Syasuoka 
138a7ca44b8Syasuoka 	ret = module_recv_imsg(base);
139a7ca44b8Syasuoka 	if (ret == 0)
140dd7efffeSclaudio 		imsgbuf_flush(&base->ibuf);
141a7ca44b8Syasuoka 
142a7ca44b8Syasuoka 	return (ret);
143a7ca44b8Syasuoka }
144a7ca44b8Syasuoka 
145a7ca44b8Syasuoka void
146a7ca44b8Syasuoka module_destroy(struct module_base *base)
147a7ca44b8Syasuoka {
14876e157acSyasuoka 	if (base != NULL) {
14976e157acSyasuoka 		free(base->radpkt);
15076e157acSyasuoka 		free(base->radpkt2);
151dd7efffeSclaudio 		imsgbuf_clear(&base->ibuf);
15276e157acSyasuoka 	}
153a7ca44b8Syasuoka 	free(base);
154a7ca44b8Syasuoka }
155a7ca44b8Syasuoka 
156a7ca44b8Syasuoka void
157a7ca44b8Syasuoka module_load(struct module_base *base)
158a7ca44b8Syasuoka {
159a7ca44b8Syasuoka 	struct radiusd_module_load_arg	 load;
160a7ca44b8Syasuoka 
161a7ca44b8Syasuoka 	memset(&load, 0, sizeof(load));
162a7ca44b8Syasuoka 	if (module_userpass != NULL)
163a7ca44b8Syasuoka 		load.cap |= RADIUSD_MODULE_CAP_USERPASS;
164a7ca44b8Syasuoka 	if (module_access_request != NULL)
165a7ca44b8Syasuoka 		load.cap |= RADIUSD_MODULE_CAP_ACCSREQ;
166ed1dc925Syasuoka 	if (module_next_response != NULL)
167ed1dc925Syasuoka 		load.cap |= RADIUSD_MODULE_CAP_NEXTRES;
168237e61d9Syasuoka 	if (module_request_decoration != NULL)
169237e61d9Syasuoka 		load.cap |= RADIUSD_MODULE_CAP_REQDECO;
170237e61d9Syasuoka 	if (module_response_decoration != NULL)
171237e61d9Syasuoka 		load.cap |= RADIUSD_MODULE_CAP_RESDECO;
172747da5e9Syasuoka 	if (module_accounting_request != NULL)
173747da5e9Syasuoka 		load.cap |= RADIUSD_MODULE_CAP_ACCTREQ;
174842565f2Syasuoka 	if (module_dispatch_control != NULL)
175842565f2Syasuoka 		load.cap |= RADIUSD_MODULE_CAP_CONTROL;
176a7ca44b8Syasuoka 	imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_LOAD, 0, 0, -1, &load,
177a7ca44b8Syasuoka 	    sizeof(load));
178dd7efffeSclaudio 	imsgbuf_flush(&base->ibuf);
179a7ca44b8Syasuoka }
180a7ca44b8Syasuoka 
1813d3cf35cSyasuoka void
182edd79a0eSyasuoka module_drop_privilege(struct module_base *base, int nochroot)
1833d3cf35cSyasuoka {
1843d3cf35cSyasuoka 	struct passwd	*pw;
1853d3cf35cSyasuoka 
18661125a90Syasuoka 	tzset();
18761125a90Syasuoka 
1883d3cf35cSyasuoka 	/* Drop the privilege */
1893d3cf35cSyasuoka 	if ((pw = getpwnam(RADIUSD_USER)) == NULL)
1903d3cf35cSyasuoka 		goto on_fail;
191edd79a0eSyasuoka 	if (nochroot == 0 && chroot(pw->pw_dir) == -1)
1923d3cf35cSyasuoka 		goto on_fail;
1933d3cf35cSyasuoka 	if (chdir("/") == -1)
1943d3cf35cSyasuoka 		goto on_fail;
1953d3cf35cSyasuoka 	if (setgroups(1, &pw->pw_gid) ||
1963d3cf35cSyasuoka 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
1973d3cf35cSyasuoka 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
1983d3cf35cSyasuoka 		goto on_fail;
1993d3cf35cSyasuoka 	base->priv_dropped = true;
2003d3cf35cSyasuoka 
2013d3cf35cSyasuoka on_fail:
2023d3cf35cSyasuoka 	return;
2033d3cf35cSyasuoka }
2043d3cf35cSyasuoka 
205a7ca44b8Syasuoka int
206a7ca44b8Syasuoka module_notify_secret(struct module_base *base, const char *secret)
207a7ca44b8Syasuoka {
208a7ca44b8Syasuoka 	int		 ret;
209a7ca44b8Syasuoka 
210a7ca44b8Syasuoka 	ret = imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_NOTIFY_SECRET,
211a7ca44b8Syasuoka 	    0, 0, -1, secret, strlen(secret) + 1);
212a7ca44b8Syasuoka 	module_reset_event(base);
213a7ca44b8Syasuoka 
214a7ca44b8Syasuoka 	return (ret);
215a7ca44b8Syasuoka }
216a7ca44b8Syasuoka 
217a7ca44b8Syasuoka int
218a7ca44b8Syasuoka module_send_message(struct module_base *base, uint32_t cmd, const char *fmt,
219a7ca44b8Syasuoka     ...)
220a7ca44b8Syasuoka {
221a7ca44b8Syasuoka 	char	*msg;
222a7ca44b8Syasuoka 	va_list	 ap;
223a7ca44b8Syasuoka 	int	 ret;
224a7ca44b8Syasuoka 
225a7ca44b8Syasuoka 	if (fmt == NULL)
226a7ca44b8Syasuoka 		ret = imsg_compose(&base->ibuf, cmd, 0, 0, -1, NULL, 0);
227a7ca44b8Syasuoka 	else {
228a7ca44b8Syasuoka 		va_start(ap, fmt);
229a7ca44b8Syasuoka 		vasprintf(&msg, fmt, ap);
230a7ca44b8Syasuoka 		va_end(ap);
231a7ca44b8Syasuoka 		if (msg == NULL)
232a7ca44b8Syasuoka 			return (-1);
233a7ca44b8Syasuoka 		ret = imsg_compose(&base->ibuf, cmd, 0, 0, -1, msg,
234a7ca44b8Syasuoka 		    strlen(msg) + 1);
235a7ca44b8Syasuoka 		free(msg);
236a7ca44b8Syasuoka 	}
237a7ca44b8Syasuoka 	module_reset_event(base);
238a7ca44b8Syasuoka 
239a7ca44b8Syasuoka 	return (ret);
240a7ca44b8Syasuoka }
241a7ca44b8Syasuoka 
242a7ca44b8Syasuoka int
243a7ca44b8Syasuoka module_userpass_ok(struct module_base *base, u_int q_id, const char *msg)
244a7ca44b8Syasuoka {
245a7ca44b8Syasuoka 	int		 ret;
246a7ca44b8Syasuoka 	struct iovec	 iov[2];
247a7ca44b8Syasuoka 
248a7ca44b8Syasuoka 	iov[0].iov_base = &q_id;
249a7ca44b8Syasuoka 	iov[0].iov_len = sizeof(q_id);
250a7ca44b8Syasuoka 	iov[1].iov_base = (char *)msg;
251a7ca44b8Syasuoka 	iov[1].iov_len = strlen(msg) + 1;
252a7ca44b8Syasuoka 	ret = imsg_composev(&base->ibuf, IMSG_RADIUSD_MODULE_USERPASS_OK,
253a7ca44b8Syasuoka 	    0, 0, -1, iov, 2);
254a7ca44b8Syasuoka 	module_reset_event(base);
255a7ca44b8Syasuoka 
256a7ca44b8Syasuoka 	return (ret);
257a7ca44b8Syasuoka }
258a7ca44b8Syasuoka 
259a7ca44b8Syasuoka int
260a7ca44b8Syasuoka module_userpass_fail(struct module_base *base, u_int q_id, const char *msg)
261a7ca44b8Syasuoka {
262a7ca44b8Syasuoka 	int		 ret;
263a7ca44b8Syasuoka 	struct iovec	 iov[2];
264a7ca44b8Syasuoka 
265a7ca44b8Syasuoka 	iov[0].iov_base = &q_id;
266a7ca44b8Syasuoka 	iov[0].iov_len = sizeof(q_id);
267a7ca44b8Syasuoka 	iov[1].iov_base = (char *)msg;
268a7ca44b8Syasuoka 	iov[1].iov_len = strlen(msg) + 1;
269a7ca44b8Syasuoka 	ret = imsg_composev(&base->ibuf, IMSG_RADIUSD_MODULE_USERPASS_FAIL,
270a7ca44b8Syasuoka 	    0, 0, -1, iov, 2);
271a7ca44b8Syasuoka 	module_reset_event(base);
272a7ca44b8Syasuoka 
273a7ca44b8Syasuoka 	return (ret);
274a7ca44b8Syasuoka }
275a7ca44b8Syasuoka 
276a7ca44b8Syasuoka int
277d7548b59Syasuoka module_accsreq_answer(struct module_base *base, u_int q_id, const u_char *pkt,
278d7548b59Syasuoka     size_t pktlen)
279a7ca44b8Syasuoka {
280a7ca44b8Syasuoka 	return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER,
281d7548b59Syasuoka 	    q_id, pkt, pktlen));
282a7ca44b8Syasuoka }
283a7ca44b8Syasuoka 
284a7ca44b8Syasuoka int
285ed1dc925Syasuoka module_accsreq_next(struct module_base *base, u_int q_id, const u_char *pkt,
286ed1dc925Syasuoka     size_t pktlen)
287ed1dc925Syasuoka {
288ed1dc925Syasuoka 	return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_ACCSREQ_NEXT,
289ed1dc925Syasuoka 	    q_id, pkt, pktlen));
290ed1dc925Syasuoka }
291ed1dc925Syasuoka 
292ed1dc925Syasuoka int
293a7ca44b8Syasuoka module_accsreq_aborted(struct module_base *base, u_int q_id)
294a7ca44b8Syasuoka {
295a7ca44b8Syasuoka 	int	 ret;
296a7ca44b8Syasuoka 
297a7ca44b8Syasuoka 	ret = imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_ACCSREQ_ABORTED,
298a7ca44b8Syasuoka 	    0, 0, -1, &q_id, sizeof(u_int));
299a7ca44b8Syasuoka 	module_reset_event(base);
300a7ca44b8Syasuoka 
301a7ca44b8Syasuoka 	return (ret);
302a7ca44b8Syasuoka }
303a7ca44b8Syasuoka 
304237e61d9Syasuoka int
305237e61d9Syasuoka module_reqdeco_done(struct module_base *base, u_int q_id, const u_char *pkt,
306237e61d9Syasuoka     size_t pktlen)
307237e61d9Syasuoka {
308237e61d9Syasuoka 	return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_REQDECO_DONE,
309237e61d9Syasuoka 	    q_id, pkt, pktlen));
310237e61d9Syasuoka }
311237e61d9Syasuoka 
312237e61d9Syasuoka int
313237e61d9Syasuoka module_resdeco_done(struct module_base *base, u_int q_id, const u_char *pkt,
314237e61d9Syasuoka     size_t pktlen)
315237e61d9Syasuoka {
316237e61d9Syasuoka 	return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_RESDECO_DONE,
317237e61d9Syasuoka 	    q_id, pkt, pktlen));
318237e61d9Syasuoka }
319237e61d9Syasuoka 
320a7ca44b8Syasuoka static int
321a7ca44b8Syasuoka module_common_radpkt(struct module_base *base, uint32_t imsg_type, u_int q_id,
322d7548b59Syasuoka     const u_char *pkt, size_t pktlen)
323a7ca44b8Syasuoka {
324a7ca44b8Syasuoka 	int		 ret = 0, off = 0, len, siz;
325a7ca44b8Syasuoka 	struct iovec	 iov[2];
326a7ca44b8Syasuoka 	struct radiusd_module_radpkt_arg	 ans;
327a7ca44b8Syasuoka 
328a7ca44b8Syasuoka 	len = pktlen;
329a7ca44b8Syasuoka 	ans.q_id = q_id;
3303f8258c8Syasuoka 	ans.pktlen = pktlen;
3313f8258c8Syasuoka 	ans.final = false;
332237e61d9Syasuoka 
333237e61d9Syasuoka 	while (!ans.final) {
334237e61d9Syasuoka 		siz = MAX_IMSGSIZE - sizeof(ans);
335237e61d9Syasuoka 		if (len - off <= siz) {
336a7ca44b8Syasuoka 			ans.final = true;
3373f8258c8Syasuoka 			siz = len - off;
338a7ca44b8Syasuoka 		}
339a7ca44b8Syasuoka 		iov[0].iov_base = &ans;
340a7ca44b8Syasuoka 		iov[0].iov_len = sizeof(ans);
341237e61d9Syasuoka 		if (siz > 0) {
342a7ca44b8Syasuoka 			iov[1].iov_base = (u_char *)pkt + off;
3433f8258c8Syasuoka 			iov[1].iov_len = siz;
344237e61d9Syasuoka 		}
345237e61d9Syasuoka 		ret = imsg_composev(&base->ibuf, imsg_type, 0, 0, -1, iov,
346237e61d9Syasuoka 		    (siz > 0)? 2 : 1);
347a7ca44b8Syasuoka 		if (ret == -1)
348a7ca44b8Syasuoka 			break;
3493f8258c8Syasuoka 		off += siz;
350a7ca44b8Syasuoka 	}
351a7ca44b8Syasuoka 	module_reset_event(base);
352a7ca44b8Syasuoka 
353a7ca44b8Syasuoka 	return (ret);
354a7ca44b8Syasuoka }
355a7ca44b8Syasuoka 
356a7ca44b8Syasuoka static int
357a7ca44b8Syasuoka module_recv_imsg(struct module_base *base)
358a7ca44b8Syasuoka {
359a7ca44b8Syasuoka 	ssize_t		 n;
360a7ca44b8Syasuoka 	struct imsg	 imsg;
361a7ca44b8Syasuoka 
3624f3fb1ffSclaudio 	if ((n = imsgbuf_read(&base->ibuf)) != 1) {
3634f3fb1ffSclaudio 		if (n == -1)
364dd7efffeSclaudio 			syslog(LOG_ERR, "%s: imsgbuf_read(): %m", __func__);
3654655f5f2Syasuoka 		module_stop(base);
366a7ca44b8Syasuoka 		return (-1);
367a7ca44b8Syasuoka 	}
368a7ca44b8Syasuoka 	for (;;) {
3694655f5f2Syasuoka 		if ((n = imsg_get(&base->ibuf, &imsg)) == -1) {
3704655f5f2Syasuoka 			syslog(LOG_ERR, "%s: imsg_get(): %m", __func__);
3714655f5f2Syasuoka 			module_stop(base);
372a7ca44b8Syasuoka 			return (-1);
3734655f5f2Syasuoka 		}
374a7ca44b8Syasuoka 		if (n == 0)
375a7ca44b8Syasuoka 			break;
376a7ca44b8Syasuoka 		module_imsg_handler(base, &imsg);
377a7ca44b8Syasuoka 		imsg_free(&imsg);
378a7ca44b8Syasuoka 	}
379a7ca44b8Syasuoka 	module_reset_event(base);
380a7ca44b8Syasuoka 
381a7ca44b8Syasuoka 	return (0);
382a7ca44b8Syasuoka }
383a7ca44b8Syasuoka 
384a7ca44b8Syasuoka static int
385a7ca44b8Syasuoka module_imsg_handler(struct module_base *base, struct imsg *imsg)
386a7ca44b8Syasuoka {
387a7ca44b8Syasuoka 	ssize_t	 datalen;
388a7ca44b8Syasuoka 
389a7ca44b8Syasuoka 	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
390a7ca44b8Syasuoka 	switch (imsg->hdr.type) {
391a7ca44b8Syasuoka 	case IMSG_RADIUSD_MODULE_SET_CONFIG:
392a7ca44b8Syasuoka 	    {
393a7ca44b8Syasuoka 		struct radiusd_module_set_arg	 *arg;
394a7ca44b8Syasuoka 		struct radiusd_module_object	 *val;
395a7ca44b8Syasuoka 		u_int				  i;
396a7ca44b8Syasuoka 		size_t				  off;
397a7ca44b8Syasuoka 		char				**argv;
398a7ca44b8Syasuoka 
399a7ca44b8Syasuoka 		arg = (struct radiusd_module_set_arg *)imsg->data;
400a7ca44b8Syasuoka 		off = sizeof(struct radiusd_module_set_arg);
401a7ca44b8Syasuoka 
402a7ca44b8Syasuoka 		if ((argv = calloc(sizeof(const char *), arg->nparamval))
403a7ca44b8Syasuoka 		    == NULL) {
404a7ca44b8Syasuoka 			module_send_message(base, IMSG_NG,
405a7ca44b8Syasuoka 			    "Out of memory: %s", strerror(errno));
406a7ca44b8Syasuoka 			break;
407a7ca44b8Syasuoka 		}
408a7ca44b8Syasuoka 		for (i = 0; i < arg->nparamval; i++) {
409a7ca44b8Syasuoka 			if (datalen - off <
410a7ca44b8Syasuoka 			    sizeof(struct radiusd_module_object))
411a7ca44b8Syasuoka 				break;
412a7ca44b8Syasuoka 			val = (struct radiusd_module_object *)
413a7ca44b8Syasuoka 			    ((caddr_t)imsg->data + off);
414a7ca44b8Syasuoka 			if (datalen - off < val->size)
415a7ca44b8Syasuoka 				break;
416a7ca44b8Syasuoka 			argv[i] = (char *)(val + 1);
417a7ca44b8Syasuoka 			off += val->size;
418a7ca44b8Syasuoka 		}
419a7ca44b8Syasuoka 		if (i >= arg->nparamval)
420a7ca44b8Syasuoka 			module_config_set(base->ctx, arg->paramname,
421a7ca44b8Syasuoka 			    arg->nparamval, argv);
422a7ca44b8Syasuoka 		else
423a7ca44b8Syasuoka 			module_send_message(base, IMSG_NG,
424a7ca44b8Syasuoka 			    "Internal protocol error");
425a7ca44b8Syasuoka 		free(argv);
426a7ca44b8Syasuoka 
427a7ca44b8Syasuoka 		break;
428a7ca44b8Syasuoka 	    }
429a7ca44b8Syasuoka 	case IMSG_RADIUSD_MODULE_START:
4303d3cf35cSyasuoka 		if (module_start_module != NULL) {
431a7ca44b8Syasuoka 			module_start_module(base->ctx);
4323d3cf35cSyasuoka 			if (!base->priv_dropped) {
4333d3cf35cSyasuoka 				syslog(LOG_ERR, "Module tried to start with "
434fc72e2f5Smmcc 				    "root privileges");
4353d3cf35cSyasuoka 				abort();
4363d3cf35cSyasuoka 			}
4373d3cf35cSyasuoka 		} else {
4383d3cf35cSyasuoka 			if (!base->priv_dropped) {
4393d3cf35cSyasuoka 				syslog(LOG_ERR, "Module tried to start with "
440fc72e2f5Smmcc 				    "root privileges");
4413d3cf35cSyasuoka 				abort();
4423d3cf35cSyasuoka 			}
443a7ca44b8Syasuoka 			module_send_message(base, IMSG_OK, NULL);
4443d3cf35cSyasuoka 		}
445a7ca44b8Syasuoka 		break;
446a7ca44b8Syasuoka 	case IMSG_RADIUSD_MODULE_STOP:
4474655f5f2Syasuoka 		module_stop(base);
448a7ca44b8Syasuoka 		break;
449a7ca44b8Syasuoka 	case IMSG_RADIUSD_MODULE_USERPASS:
450a7ca44b8Syasuoka 	    {
451a7ca44b8Syasuoka 		struct radiusd_module_userpass_arg *userpass;
452a7ca44b8Syasuoka 
453a7ca44b8Syasuoka 		if (module_userpass == NULL) {
454a7ca44b8Syasuoka 			syslog(LOG_ERR, "Received USERPASS message, but "
455a7ca44b8Syasuoka 			    "module doesn't support");
456a7ca44b8Syasuoka 			break;
457a7ca44b8Syasuoka 		}
458a7ca44b8Syasuoka 		if (datalen <
459a7ca44b8Syasuoka 		    (ssize_t)sizeof(struct radiusd_module_userpass_arg)) {
460a7ca44b8Syasuoka 			syslog(LOG_ERR, "Received USERPASS message, but "
461a7ca44b8Syasuoka 			    "length is wrong");
462a7ca44b8Syasuoka 			break;
463a7ca44b8Syasuoka 		}
464a7ca44b8Syasuoka 		userpass = (struct radiusd_module_userpass_arg *)imsg->data;
465a7ca44b8Syasuoka 		module_userpass(base->ctx, userpass->q_id, userpass->user,
466a7ca44b8Syasuoka 		    (userpass->has_pass)? userpass->pass : NULL);
467a7ca44b8Syasuoka 		explicit_bzero(userpass,
468a7ca44b8Syasuoka 		    sizeof(struct radiusd_module_userpass_arg));
469a7ca44b8Syasuoka 		break;
470a7ca44b8Syasuoka 	    }
471a7ca44b8Syasuoka 	case IMSG_RADIUSD_MODULE_ACCSREQ:
472ed1dc925Syasuoka 	case IMSG_RADIUSD_MODULE_NEXTRES:
473237e61d9Syasuoka 	case IMSG_RADIUSD_MODULE_REQDECO:
47476e157acSyasuoka 	case IMSG_RADIUSD_MODULE_RESDECO0_REQ:
475237e61d9Syasuoka 	case IMSG_RADIUSD_MODULE_RESDECO:
476747da5e9Syasuoka 	case IMSG_RADIUSD_MODULE_ACCTREQ:
477a7ca44b8Syasuoka 	    {
478a7ca44b8Syasuoka 		struct radiusd_module_radpkt_arg	*accessreq;
479a7ca44b8Syasuoka 		int					 chunklen;
480237e61d9Syasuoka 		const char				*typestr;
481a7ca44b8Syasuoka 
482237e61d9Syasuoka 		if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCSREQ) {
483a7ca44b8Syasuoka 			if (module_access_request == NULL) {
484a7ca44b8Syasuoka 				syslog(LOG_ERR, "Received ACCSREQ message, but "
485a7ca44b8Syasuoka 				    "module doesn't support");
486a7ca44b8Syasuoka 				break;
487a7ca44b8Syasuoka 			}
488237e61d9Syasuoka 			typestr = "ACCSREQ";
489ed1dc925Syasuoka 		} else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_NEXTRES) {
490ed1dc925Syasuoka 			if (module_next_response == NULL) {
491ed1dc925Syasuoka 				syslog(LOG_ERR, "Received NEXTRES message, but "
492ed1dc925Syasuoka 				    "module doesn't support");
493ed1dc925Syasuoka 				break;
494ed1dc925Syasuoka 			}
495ed1dc925Syasuoka 			typestr = "NEXTRES";
496747da5e9Syasuoka 		} else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCTREQ) {
497747da5e9Syasuoka 			if (module_accounting_request == NULL) {
498747da5e9Syasuoka 				syslog(LOG_ERR, "Received ACCTREQ message, but "
499747da5e9Syasuoka 				    "module doesn't support");
500747da5e9Syasuoka 				break;
501747da5e9Syasuoka 			}
502747da5e9Syasuoka 			typestr = "ACCTREQ";
503237e61d9Syasuoka 		} else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_REQDECO) {
504237e61d9Syasuoka 			if (module_request_decoration == NULL) {
505237e61d9Syasuoka 				syslog(LOG_ERR, "Received REQDECO message, but "
506237e61d9Syasuoka 				    "module doesn't support");
507237e61d9Syasuoka 				break;
508237e61d9Syasuoka 			}
509237e61d9Syasuoka 			typestr = "REQDECO";
510237e61d9Syasuoka 		} else {
511237e61d9Syasuoka 			if (module_response_decoration == NULL) {
512237e61d9Syasuoka 				syslog(LOG_ERR, "Received RESDECO message, but "
513237e61d9Syasuoka 				    "module doesn't support");
514237e61d9Syasuoka 				break;
515237e61d9Syasuoka 			}
51676e157acSyasuoka 			if (imsg->hdr.type == IMSG_RADIUSD_MODULE_RESDECO0_REQ)
51776e157acSyasuoka 				typestr = "RESDECO0_REQ";
51876e157acSyasuoka 			else
519237e61d9Syasuoka 				typestr = "RESDECO";
520237e61d9Syasuoka 		}
521237e61d9Syasuoka 
522a7ca44b8Syasuoka 		if (datalen <
523a7ca44b8Syasuoka 		    (ssize_t)sizeof(struct radiusd_module_radpkt_arg)) {
524237e61d9Syasuoka 			syslog(LOG_ERR, "Received %s message, but "
525237e61d9Syasuoka 			    "length is wrong", typestr);
526a7ca44b8Syasuoka 			break;
527a7ca44b8Syasuoka 		}
528a7ca44b8Syasuoka 		accessreq = (struct radiusd_module_radpkt_arg *)imsg->data;
5293f8258c8Syasuoka 		if (base->radpktsiz < accessreq->pktlen) {
530a7ca44b8Syasuoka 			u_char *nradpkt;
531a7ca44b8Syasuoka 			if ((nradpkt = realloc(base->radpkt,
5323f8258c8Syasuoka 			    accessreq->pktlen)) == NULL) {
533a7ca44b8Syasuoka 				syslog(LOG_ERR, "Could not handle received "
534237e61d9Syasuoka 				    "%s message: %m", typestr);
535a7ca44b8Syasuoka 				base->radpktoff = 0;
536a7ca44b8Syasuoka 				goto accsreq_out;
537a7ca44b8Syasuoka 			}
538a7ca44b8Syasuoka 			base->radpkt = nradpkt;
5393f8258c8Syasuoka 			base->radpktsiz = accessreq->pktlen;
540a7ca44b8Syasuoka 		}
541a7ca44b8Syasuoka 		chunklen = datalen - sizeof(struct radiusd_module_radpkt_arg);
542a7ca44b8Syasuoka 		if (chunklen > base->radpktsiz - base->radpktoff){
543a7ca44b8Syasuoka 			syslog(LOG_ERR,
544237e61d9Syasuoka 			    "Could not handle received %s message: "
545237e61d9Syasuoka 			    "received length is too big", typestr);
546a7ca44b8Syasuoka 			base->radpktoff = 0;
547a7ca44b8Syasuoka 			goto accsreq_out;
548a7ca44b8Syasuoka 		}
549a7ca44b8Syasuoka 		memcpy(base->radpkt + base->radpktoff,
550a7ca44b8Syasuoka 		    (caddr_t)(accessreq + 1), chunklen);
551a7ca44b8Syasuoka 		base->radpktoff += chunklen;
552a7ca44b8Syasuoka 		if (!accessreq->final)
553a7ca44b8Syasuoka 			goto accsreq_out;
5543f8258c8Syasuoka 		if (base->radpktoff != accessreq->pktlen) {
555a7ca44b8Syasuoka 			syslog(LOG_ERR,
556237e61d9Syasuoka 			    "Could not handle received %s "
557237e61d9Syasuoka 			    "message: length is mismatch", typestr);
558a7ca44b8Syasuoka 			base->radpktoff = 0;
559a7ca44b8Syasuoka 			goto accsreq_out;
560a7ca44b8Syasuoka 		}
561237e61d9Syasuoka 		if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCSREQ)
562a7ca44b8Syasuoka 			module_access_request(base->ctx, accessreq->q_id,
563a7ca44b8Syasuoka 			    base->radpkt, base->radpktoff);
564ed1dc925Syasuoka 		else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_NEXTRES)
565ed1dc925Syasuoka 			module_next_response(base->ctx, accessreq->q_id,
566ed1dc925Syasuoka 			    base->radpkt, base->radpktoff);
567237e61d9Syasuoka 		else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_REQDECO)
568237e61d9Syasuoka 			module_request_decoration(base->ctx, accessreq->q_id,
569237e61d9Syasuoka 			    base->radpkt, base->radpktoff);
57076e157acSyasuoka 		else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_RESDECO0_REQ) {
57176e157acSyasuoka 			/* preserve request */
57276e157acSyasuoka 			if (base->radpktoff > base->radpkt2siz) {
57376e157acSyasuoka 				u_char *nradpkt;
57476e157acSyasuoka 				if ((nradpkt = realloc(base->radpkt2,
57576e157acSyasuoka 				    base->radpktoff)) == NULL) {
57676e157acSyasuoka 					syslog(LOG_ERR, "Could not handle "
57776e157acSyasuoka 					    "received %s message: %m", typestr);
57876e157acSyasuoka 					base->radpktoff = 0;
57976e157acSyasuoka 					goto accsreq_out;
58076e157acSyasuoka 				}
58176e157acSyasuoka 				base->radpkt2 = nradpkt;
58276e157acSyasuoka 				base->radpkt2siz = base->radpktoff;
58376e157acSyasuoka 			}
58476e157acSyasuoka 			memcpy(base->radpkt2, base->radpkt, base->radpktoff);
58576e157acSyasuoka 			base->radpkt2len = base->radpktoff;
586747da5e9Syasuoka 		} else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_RESDECO) {
587237e61d9Syasuoka 			module_response_decoration(base->ctx, accessreq->q_id,
58876e157acSyasuoka 			    base->radpkt2, base->radpkt2len, base->radpkt,
58976e157acSyasuoka 			    base->radpktoff);
59076e157acSyasuoka 			base->radpkt2len = 0;
591747da5e9Syasuoka 		} else
592747da5e9Syasuoka 			module_accounting_request(base->ctx, accessreq->q_id,
593747da5e9Syasuoka 			    base->radpkt, base->radpktoff);
594a7ca44b8Syasuoka 		base->radpktoff = 0;
595a7ca44b8Syasuoka  accsreq_out:
596a7ca44b8Syasuoka 		break;
597a7ca44b8Syasuoka 	    }
598842565f2Syasuoka 	case IMSG_RADIUSD_MODULE_CTRL_UNBIND:
599842565f2Syasuoka 		goto forward_msg;
600842565f2Syasuoka 		break;
601842565f2Syasuoka 	default:
602842565f2Syasuoka 		if (imsg->hdr.type >= IMSG_RADIUSD_MODULE_MIN) {
603842565f2Syasuoka  forward_msg:
604842565f2Syasuoka 			if (module_dispatch_control == NULL) {
605842565f2Syasuoka 				const char msg[] =
606842565f2Syasuoka 				    "the module doesn't handle any controls";
607842565f2Syasuoka 				imsg_compose(&base->ibuf, IMSG_NG,
608842565f2Syasuoka 				    imsg->hdr.peerid, 0, -1, msg, sizeof(msg));
609842565f2Syasuoka 			} else
610842565f2Syasuoka 				module_dispatch_control(base->ctx, imsg);
611842565f2Syasuoka 		}
612a7ca44b8Syasuoka 	}
613a7ca44b8Syasuoka 
614a7ca44b8Syasuoka 	return (0);
615a7ca44b8Syasuoka }
616a7ca44b8Syasuoka 
617237e61d9Syasuoka void
6184655f5f2Syasuoka module_stop(struct module_base *base)
6194655f5f2Syasuoka {
6204655f5f2Syasuoka 	if (module_stop_module != NULL)
6214655f5f2Syasuoka 		module_stop_module(base->ctx);
6224655f5f2Syasuoka #ifdef USE_LIBEVENT
6234655f5f2Syasuoka 	event_del(&base->ev);
6244655f5f2Syasuoka 	base->stopped = true;
6254655f5f2Syasuoka #endif
6264655f5f2Syasuoka 	close(base->ibuf.fd);
6274655f5f2Syasuoka }
6284655f5f2Syasuoka 
629a7ca44b8Syasuoka #ifdef USE_LIBEVENT
630a7ca44b8Syasuoka static void
631a7ca44b8Syasuoka module_on_event(int fd, short evmask, void *ctx)
632a7ca44b8Syasuoka {
633a7ca44b8Syasuoka 	struct module_base	*base = ctx;
634a7ca44b8Syasuoka 	int			 ret;
635a7ca44b8Syasuoka 
636a7ca44b8Syasuoka 	base->ev_onhandler = true;
637c1aa9554Sclaudio 	if (evmask & EV_WRITE) {
638a7ca44b8Syasuoka 		base->writeready = true;
639dd7efffeSclaudio 		if (imsgbuf_write(&base->ibuf) == -1) {
640dd7efffeSclaudio 			syslog(LOG_ERR, "%s: imsgbuf_write: %m", __func__);
641c1aa9554Sclaudio 			module_stop(base);
642c1aa9554Sclaudio 			return;
643c1aa9554Sclaudio 		}
644c1aa9554Sclaudio 		base->writeready = false;
645c1aa9554Sclaudio 	}
646a7ca44b8Syasuoka 	if (evmask & EV_READ) {
647a7ca44b8Syasuoka 		ret = module_recv_imsg(base);
648a7ca44b8Syasuoka 		if (ret < 0)
6494655f5f2Syasuoka 			return;
650a7ca44b8Syasuoka 	}
651a7ca44b8Syasuoka 	base->ev_onhandler = false;
652a7ca44b8Syasuoka 	module_reset_event(base);
653a7ca44b8Syasuoka 	return;
654a7ca44b8Syasuoka }
655a7ca44b8Syasuoka #endif
656a7ca44b8Syasuoka 
657a7ca44b8Syasuoka static void
658a7ca44b8Syasuoka module_reset_event(struct module_base *base)
659a7ca44b8Syasuoka {
660a7ca44b8Syasuoka #ifdef USE_LIBEVENT
661a7ca44b8Syasuoka 	short		 evmask = 0;
662a7ca44b8Syasuoka 	struct timeval	*tvp = NULL, tv = { 0, 0 };
663a7ca44b8Syasuoka 
664a7ca44b8Syasuoka 	if (base->ev_onhandler)
665a7ca44b8Syasuoka 		return;
6664655f5f2Syasuoka 	if (base->stopped)
6674655f5f2Syasuoka 		return;
668a7ca44b8Syasuoka 	event_del(&base->ev);
669a7ca44b8Syasuoka 
670a7ca44b8Syasuoka 	evmask |= EV_READ;
67131be28caSclaudio 	if (imsgbuf_queuelen(&base->ibuf) > 0) {
672a7ca44b8Syasuoka 		if (!base->writeready)
673a7ca44b8Syasuoka 			evmask |= EV_WRITE;
674a7ca44b8Syasuoka 		else
675a7ca44b8Syasuoka 			tvp = &tv;	/* fire immediately */
676a7ca44b8Syasuoka 	}
677a7ca44b8Syasuoka 	event_set(&base->ev, base->ibuf.fd, evmask, module_on_event, base);
678a7ca44b8Syasuoka 	if (event_add(&base->ev, tvp) == -1)
679a7ca44b8Syasuoka 		syslog(LOG_ERR, "event_add() failed in %s()", __func__);
680a7ca44b8Syasuoka #endif
681a7ca44b8Syasuoka }
682842565f2Syasuoka 
683842565f2Syasuoka int
684842565f2Syasuoka module_imsg_compose(struct module_base *base, uint32_t type, uint32_t id,
685842565f2Syasuoka     pid_t pid, int fd, const void *data, size_t datalen)
686842565f2Syasuoka {
687842565f2Syasuoka 	int	 ret;
688842565f2Syasuoka 
689842565f2Syasuoka 	if ((ret = imsg_compose(&base->ibuf, type, id, pid, fd, data, datalen))
690842565f2Syasuoka 	    != -1)
691842565f2Syasuoka 		module_reset_event(base);
692842565f2Syasuoka 
693842565f2Syasuoka 	return (ret);
694842565f2Syasuoka }
695842565f2Syasuoka 
696842565f2Syasuoka int
697842565f2Syasuoka module_imsg_composev(struct module_base *base, uint32_t type, uint32_t id,
698842565f2Syasuoka     pid_t pid, int fd, const struct iovec *iov, int iovcnt)
699842565f2Syasuoka {
700842565f2Syasuoka 	int	 ret;
701842565f2Syasuoka 
702842565f2Syasuoka 	if ((ret = imsg_composev(&base->ibuf, type, id, pid, fd, iov, iovcnt))
703842565f2Syasuoka 	    != -1)
704842565f2Syasuoka 		module_reset_event(base);
705842565f2Syasuoka 
706842565f2Syasuoka 	return (ret);
707842565f2Syasuoka }
708