xref: /openbsd-src/usr.sbin/radiusd/radiusd_module.c (revision 1ad61ae0a79a724d2d3ec69e69c8e1d1ff6b53a0)
1 /*	$OpenBSD: radiusd_module.c,v 1.14 2023/09/08 05:56:22 yasuoka Exp $	*/
2 
3 /*
4  * Copyright (c) 2015 YASUOKA Masahiko <yasuoka@yasuoka.net>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /* radiusd_module.c -- helper functions for radiusd modules */
20 
21 #include <sys/types.h>
22 #include <sys/queue.h>
23 #include <sys/uio.h>
24 
25 #include <err.h>
26 #include <errno.h>
27 #include <event.h>
28 #include <fcntl.h>
29 #include <imsg.h>
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <syslog.h>
34 #include <unistd.h>
35 #include <pwd.h>
36 
37 #include "radiusd.h"
38 #include "radiusd_module.h"
39 #include "imsg_subr.h"
40 
41 static void	(*module_config_set) (void *, const char *, int,
42 		    char * const *) = NULL;
43 static void	(*module_start_module) (void *) = NULL;
44 static void	(*module_stop_module) (void *) = NULL;
45 static void	(*module_userpass) (void *, u_int, const char *, const char *)
46 		    = NULL;
47 static void	(*module_access_request) (void *, u_int, const u_char *,
48 		    size_t) = NULL;
49 static void	(*module_request_decoration) (void *, u_int, const u_char *,
50 		    size_t) = NULL;
51 static void	(*module_response_decoration) (void *, u_int, const u_char *,
52 		    size_t) = NULL;
53 
54 struct module_base {
55 	void			*ctx;
56 	struct imsgbuf		 ibuf;
57 	bool			 priv_dropped;
58 
59 	/* Buffer for receiving the RADIUS packet */
60 	u_char			*radpkt;
61 	int			 radpktsiz;
62 	int			 radpktoff;
63 
64 #ifdef USE_LIBEVENT
65 	struct module_imsgbuf	*module_imsgbuf;
66 	bool			 writeready;
67 	bool			 stopped;
68 	bool			 ev_onhandler;
69 	struct event		 ev;
70 #endif
71 };
72 
73 static int	 module_common_radpkt(struct module_base *, uint32_t, u_int,
74 		    const u_char *, size_t);
75 static int	 module_recv_imsg(struct module_base *);
76 static int	 module_imsg_handler(struct module_base *, struct imsg *);
77 #ifdef USE_LIBEVENT
78 static void	 module_on_event(int, short, void *);
79 #endif
80 static void	 module_reset_event(struct module_base *);
81 
82 struct module_base *
83 module_create(int sock, void *ctx, struct module_handlers *handler)
84 {
85 	struct module_base	*base;
86 
87 	if ((base = calloc(1, sizeof(struct module_base))) == NULL)
88 		return (NULL);
89 
90 	imsg_init(&base->ibuf, sock);
91 	base->ctx = ctx;
92 
93 	module_userpass = handler->userpass;
94 	module_access_request = handler->access_request;
95 	module_config_set = handler->config_set;
96 	module_request_decoration = handler->request_decoration;
97 	module_response_decoration = handler->response_decoration;
98 	module_start_module = handler->start;
99 	module_stop_module = handler->stop;
100 
101 	return (base);
102 }
103 
104 void
105 module_start(struct module_base *base)
106 {
107 #ifdef USE_LIBEVENT
108 	int	 ival;
109 
110 	if ((ival = fcntl(base->ibuf.fd, F_GETFL)) == -1)
111 		err(1, "Failed to F_GETFL");
112 	if (fcntl(base->ibuf.fd, F_SETFL, ival | O_NONBLOCK) == -1)
113 		err(1, "Failed to setup NONBLOCK");
114 	event_set(&base->ev, base->ibuf.fd, EV_READ, module_on_event, base);
115 	event_add(&base->ev, NULL);
116 #endif
117 }
118 
119 int
120 module_run(struct module_base *base)
121 {
122 	int	 ret;
123 
124 	ret = module_recv_imsg(base);
125 	if (ret == 0)
126 		imsg_flush(&base->ibuf);
127 
128 	return (ret);
129 }
130 
131 void
132 module_destroy(struct module_base *base)
133 {
134 	imsg_clear(&base->ibuf);
135 	free(base);
136 }
137 
138 void
139 module_load(struct module_base *base)
140 {
141 	struct radiusd_module_load_arg	 load;
142 
143 	memset(&load, 0, sizeof(load));
144 	if (module_userpass != NULL)
145 		load.cap |= RADIUSD_MODULE_CAP_USERPASS;
146 	if (module_access_request != NULL)
147 		load.cap |= RADIUSD_MODULE_CAP_ACCSREQ;
148 	if (module_request_decoration != NULL)
149 		load.cap |= RADIUSD_MODULE_CAP_REQDECO;
150 	if (module_response_decoration != NULL)
151 		load.cap |= RADIUSD_MODULE_CAP_RESDECO;
152 	imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_LOAD, 0, 0, -1, &load,
153 	    sizeof(load));
154 	imsg_flush(&base->ibuf);
155 }
156 
157 void
158 module_drop_privilege(struct module_base *base)
159 {
160 	struct passwd	*pw;
161 
162 	tzset();
163 
164 	/* Drop the privilege */
165 	if ((pw = getpwnam(RADIUSD_USER)) == NULL)
166 		goto on_fail;
167 	if (chroot(pw->pw_dir) == -1)
168 		goto on_fail;
169 	if (chdir("/") == -1)
170 		goto on_fail;
171 	if (setgroups(1, &pw->pw_gid) ||
172 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
173 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
174 		goto on_fail;
175 	base->priv_dropped = true;
176 
177 on_fail:
178 	return;
179 }
180 
181 int
182 module_notify_secret(struct module_base *base, const char *secret)
183 {
184 	int		 ret;
185 
186 	ret = imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_NOTIFY_SECRET,
187 	    0, 0, -1, secret, strlen(secret) + 1);
188 	module_reset_event(base);
189 
190 	return (ret);
191 }
192 
193 int
194 module_send_message(struct module_base *base, uint32_t cmd, const char *fmt,
195     ...)
196 {
197 	char	*msg;
198 	va_list	 ap;
199 	int	 ret;
200 
201 	if (fmt == NULL)
202 		ret = imsg_compose(&base->ibuf, cmd, 0, 0, -1, NULL, 0);
203 	else {
204 		va_start(ap, fmt);
205 		vasprintf(&msg, fmt, ap);
206 		va_end(ap);
207 		if (msg == NULL)
208 			return (-1);
209 		ret = imsg_compose(&base->ibuf, cmd, 0, 0, -1, msg,
210 		    strlen(msg) + 1);
211 		free(msg);
212 	}
213 	module_reset_event(base);
214 
215 	return (ret);
216 }
217 
218 int
219 module_userpass_ok(struct module_base *base, u_int q_id, const char *msg)
220 {
221 	int		 ret;
222 	struct iovec	 iov[2];
223 
224 	iov[0].iov_base = &q_id;
225 	iov[0].iov_len = sizeof(q_id);
226 	iov[1].iov_base = (char *)msg;
227 	iov[1].iov_len = strlen(msg) + 1;
228 	ret = imsg_composev(&base->ibuf, IMSG_RADIUSD_MODULE_USERPASS_OK,
229 	    0, 0, -1, iov, 2);
230 	module_reset_event(base);
231 
232 	return (ret);
233 }
234 
235 int
236 module_userpass_fail(struct module_base *base, u_int q_id, const char *msg)
237 {
238 	int		 ret;
239 	struct iovec	 iov[2];
240 
241 	iov[0].iov_base = &q_id;
242 	iov[0].iov_len = sizeof(q_id);
243 	iov[1].iov_base = (char *)msg;
244 	iov[1].iov_len = strlen(msg) + 1;
245 	ret = imsg_composev(&base->ibuf, IMSG_RADIUSD_MODULE_USERPASS_FAIL,
246 	    0, 0, -1, iov, 2);
247 	module_reset_event(base);
248 
249 	return (ret);
250 }
251 
252 int
253 module_accsreq_answer(struct module_base *base, u_int q_id, const u_char *pkt,
254     size_t pktlen)
255 {
256 	return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER,
257 	    q_id, pkt, pktlen));
258 }
259 
260 int
261 module_accsreq_aborted(struct module_base *base, u_int q_id)
262 {
263 	int	 ret;
264 
265 	ret = imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_ACCSREQ_ABORTED,
266 	    0, 0, -1, &q_id, sizeof(u_int));
267 	module_reset_event(base);
268 
269 	return (ret);
270 }
271 
272 int
273 module_reqdeco_done(struct module_base *base, u_int q_id, const u_char *pkt,
274     size_t pktlen)
275 {
276 	return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_REQDECO_DONE,
277 	    q_id, pkt, pktlen));
278 }
279 
280 int
281 module_resdeco_done(struct module_base *base, u_int q_id, const u_char *pkt,
282     size_t pktlen)
283 {
284 	return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_RESDECO_DONE,
285 	    q_id, pkt, pktlen));
286 }
287 
288 static int
289 module_common_radpkt(struct module_base *base, uint32_t imsg_type, u_int q_id,
290     const u_char *pkt, size_t pktlen)
291 {
292 	int		 ret = 0, off = 0, len, siz;
293 	struct iovec	 iov[2];
294 	struct radiusd_module_radpkt_arg	 ans;
295 
296 	len = pktlen;
297 	ans.q_id = q_id;
298 	ans.pktlen = pktlen;
299 	ans.final = false;
300 
301 	while (!ans.final) {
302 		siz = MAX_IMSGSIZE - sizeof(ans);
303 		if (len - off <= siz) {
304 			ans.final = true;
305 			siz = len - off;
306 		}
307 		iov[0].iov_base = &ans;
308 		iov[0].iov_len = sizeof(ans);
309 		if (siz > 0) {
310 			iov[1].iov_base = (u_char *)pkt + off;
311 			iov[1].iov_len = siz;
312 		}
313 		ret = imsg_composev(&base->ibuf, imsg_type, 0, 0, -1, iov,
314 		    (siz > 0)? 2 : 1);
315 		if (ret == -1)
316 			break;
317 		off += siz;
318 	}
319 	module_reset_event(base);
320 
321 	return (ret);
322 }
323 
324 static int
325 module_recv_imsg(struct module_base *base)
326 {
327 	ssize_t		 n;
328 	struct imsg	 imsg;
329 
330 	if (((n = imsg_read(&base->ibuf)) == -1 && errno != EAGAIN) || n == 0) {
331 		if (n != 0)
332 			syslog(LOG_ERR, "%s: imsg_read(): %m", __func__);
333 		module_stop(base);
334 		return (-1);
335 	}
336 	for (;;) {
337 		if ((n = imsg_get(&base->ibuf, &imsg)) == -1) {
338 			syslog(LOG_ERR, "%s: imsg_get(): %m", __func__);
339 			module_stop(base);
340 			return (-1);
341 		}
342 		if (n == 0)
343 			break;
344 		module_imsg_handler(base, &imsg);
345 		imsg_free(&imsg);
346 	}
347 	module_reset_event(base);
348 
349 	return (0);
350 }
351 
352 static int
353 module_imsg_handler(struct module_base *base, struct imsg *imsg)
354 {
355 	ssize_t	 datalen;
356 
357 	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
358 	switch (imsg->hdr.type) {
359 	case IMSG_RADIUSD_MODULE_SET_CONFIG:
360 	    {
361 		struct radiusd_module_set_arg	 *arg;
362 		struct radiusd_module_object	 *val;
363 		u_int				  i;
364 		size_t				  off;
365 		char				**argv;
366 
367 		arg = (struct radiusd_module_set_arg *)imsg->data;
368 		off = sizeof(struct radiusd_module_set_arg);
369 
370 		if ((argv = calloc(sizeof(const char *), arg->nparamval))
371 		    == NULL) {
372 			module_send_message(base, IMSG_NG,
373 			    "Out of memory: %s", strerror(errno));
374 			break;
375 		}
376 		for (i = 0; i < arg->nparamval; i++) {
377 			if (datalen - off <
378 			    sizeof(struct radiusd_module_object))
379 				break;
380 			val = (struct radiusd_module_object *)
381 			    ((caddr_t)imsg->data + off);
382 			if (datalen - off < val->size)
383 				break;
384 			argv[i] = (char *)(val + 1);
385 			off += val->size;
386 		}
387 		if (i >= arg->nparamval)
388 			module_config_set(base->ctx, arg->paramname,
389 			    arg->nparamval, argv);
390 		else
391 			module_send_message(base, IMSG_NG,
392 			    "Internal protocol error");
393 		free(argv);
394 
395 		break;
396 	    }
397 	case IMSG_RADIUSD_MODULE_START:
398 		if (module_start_module != NULL) {
399 			module_start_module(base->ctx);
400 			if (!base->priv_dropped) {
401 				syslog(LOG_ERR, "Module tried to start with "
402 				    "root privileges");
403 				abort();
404 			}
405 		} else {
406 			if (!base->priv_dropped) {
407 				syslog(LOG_ERR, "Module tried to start with "
408 				    "root privileges");
409 				abort();
410 			}
411 			module_send_message(base, IMSG_OK, NULL);
412 		}
413 		break;
414 	case IMSG_RADIUSD_MODULE_STOP:
415 		module_stop(base);
416 		break;
417 	case IMSG_RADIUSD_MODULE_USERPASS:
418 	    {
419 		struct radiusd_module_userpass_arg *userpass;
420 
421 		if (module_userpass == NULL) {
422 			syslog(LOG_ERR, "Received USERPASS message, but "
423 			    "module doesn't support");
424 			break;
425 		}
426 		if (datalen <
427 		    (ssize_t)sizeof(struct radiusd_module_userpass_arg)) {
428 			syslog(LOG_ERR, "Received USERPASS message, but "
429 			    "length is wrong");
430 			break;
431 		}
432 		userpass = (struct radiusd_module_userpass_arg *)imsg->data;
433 		module_userpass(base->ctx, userpass->q_id, userpass->user,
434 		    (userpass->has_pass)? userpass->pass : NULL);
435 		explicit_bzero(userpass,
436 		    sizeof(struct radiusd_module_userpass_arg));
437 		break;
438 	    }
439 	case IMSG_RADIUSD_MODULE_ACCSREQ:
440 	case IMSG_RADIUSD_MODULE_REQDECO:
441 	case IMSG_RADIUSD_MODULE_RESDECO:
442 	    {
443 		struct radiusd_module_radpkt_arg	*accessreq;
444 		int					 chunklen;
445 		const char				*typestr;
446 
447 		if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCSREQ) {
448 			if (module_access_request == NULL) {
449 				syslog(LOG_ERR, "Received ACCSREQ message, but "
450 				    "module doesn't support");
451 				break;
452 			}
453 			typestr = "ACCSREQ";
454 		} else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_REQDECO) {
455 			if (module_request_decoration == NULL) {
456 				syslog(LOG_ERR, "Received REQDECO message, but "
457 				    "module doesn't support");
458 				break;
459 			}
460 			typestr = "REQDECO";
461 		} else {
462 			if (module_response_decoration == NULL) {
463 				syslog(LOG_ERR, "Received RESDECO message, but "
464 				    "module doesn't support");
465 				break;
466 			}
467 			typestr = "RESDECO";
468 		}
469 
470 		if (datalen <
471 		    (ssize_t)sizeof(struct radiusd_module_radpkt_arg)) {
472 			syslog(LOG_ERR, "Received %s message, but "
473 			    "length is wrong", typestr);
474 			break;
475 		}
476 		accessreq = (struct radiusd_module_radpkt_arg *)imsg->data;
477 		if (base->radpktsiz < accessreq->pktlen) {
478 			u_char *nradpkt;
479 			if ((nradpkt = realloc(base->radpkt,
480 			    accessreq->pktlen)) == NULL) {
481 				syslog(LOG_ERR, "Could not handle received "
482 				    "%s message: %m", typestr);
483 				base->radpktoff = 0;
484 				goto accsreq_out;
485 			}
486 			base->radpkt = nradpkt;
487 			base->radpktsiz = accessreq->pktlen;
488 		}
489 		chunklen = datalen - sizeof(struct radiusd_module_radpkt_arg);
490 		if (chunklen > base->radpktsiz - base->radpktoff){
491 			syslog(LOG_ERR,
492 			    "Could not handle received %s message: "
493 			    "received length is too big", typestr);
494 			base->radpktoff = 0;
495 			goto accsreq_out;
496 		}
497 		memcpy(base->radpkt + base->radpktoff,
498 		    (caddr_t)(accessreq + 1), chunklen);
499 		base->radpktoff += chunklen;
500 		if (!accessreq->final)
501 			goto accsreq_out;
502 		if (base->radpktoff != accessreq->pktlen) {
503 			syslog(LOG_ERR,
504 			    "Could not handle received %s "
505 			    "message: length is mismatch", typestr);
506 			base->radpktoff = 0;
507 			goto accsreq_out;
508 		}
509 		if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCSREQ)
510 			module_access_request(base->ctx, accessreq->q_id,
511 			    base->radpkt, base->radpktoff);
512 		else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_REQDECO)
513 			module_request_decoration(base->ctx, accessreq->q_id,
514 			    base->radpkt, base->radpktoff);
515 		else
516 			module_response_decoration(base->ctx, accessreq->q_id,
517 			    base->radpkt, base->radpktoff);
518 		base->radpktoff = 0;
519 accsreq_out:
520 		break;
521 	    }
522 	}
523 
524 	return (0);
525 }
526 
527 void
528 module_stop(struct module_base *base)
529 {
530 	if (module_stop_module != NULL)
531 		module_stop_module(base->ctx);
532 #ifdef USE_LIBEVENT
533 	event_del(&base->ev);
534 	base->stopped = true;
535 #endif
536 	close(base->ibuf.fd);
537 }
538 
539 #ifdef USE_LIBEVENT
540 static void
541 module_on_event(int fd, short evmask, void *ctx)
542 {
543 	struct module_base	*base = ctx;
544 	int			 ret;
545 
546 	base->ev_onhandler = true;
547 	if (evmask & EV_WRITE)
548 		base->writeready = true;
549 	if (evmask & EV_READ) {
550 		ret = module_recv_imsg(base);
551 		if (ret < 0)
552 			return;
553 	}
554 	while (base->writeready && base->ibuf.w.queued) {
555 		ret = msgbuf_write(&base->ibuf.w);
556 		if (ret > 0)
557 			continue;
558 		base->writeready = false;
559 		if (ret == 0 && errno == EAGAIN)
560 			break;
561 		syslog(LOG_ERR, "%s: msgbuf_write: %m", __func__);
562 		module_stop(base);
563 		return;
564 	}
565 	base->ev_onhandler = false;
566 	module_reset_event(base);
567 	return;
568 }
569 #endif
570 
571 static void
572 module_reset_event(struct module_base *base)
573 {
574 #ifdef USE_LIBEVENT
575 	short		 evmask = 0;
576 	struct timeval	*tvp = NULL, tv = { 0, 0 };
577 
578 	if (base->ev_onhandler)
579 		return;
580 	if (base->stopped)
581 		return;
582 	event_del(&base->ev);
583 
584 	evmask |= EV_READ;
585 	if (base->ibuf.w.queued) {
586 		if (!base->writeready)
587 			evmask |= EV_WRITE;
588 		else
589 			tvp = &tv;	/* fire immediately */
590 	}
591 	event_set(&base->ev, base->ibuf.fd, evmask, module_on_event, base);
592 	if (event_add(&base->ev, tvp) == -1)
593 		syslog(LOG_ERR, "event_add() failed in %s()", __func__);
594 #endif
595 }
596