xref: /openbsd-src/usr.sbin/radiusd/radiusd_module.c (revision 68dd5bb1859285b71cb62a10bf107b8ad54064d9)
1 /*	$OpenBSD: radiusd_module.c,v 1.15 2024/01/08 04:16:48 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, const u_char *, 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 	u_char			*radpkt2;
64 	int			 radpkt2siz;	/* allocated size */
65 	int			 radpkt2len;	/* actual size */
66 
67 #ifdef USE_LIBEVENT
68 	struct module_imsgbuf	*module_imsgbuf;
69 	bool			 writeready;
70 	bool			 stopped;
71 	bool			 ev_onhandler;
72 	struct event		 ev;
73 #endif
74 };
75 
76 static int	 module_common_radpkt(struct module_base *, uint32_t, u_int,
77 		    const u_char *, size_t);
78 static int	 module_recv_imsg(struct module_base *);
79 static int	 module_imsg_handler(struct module_base *, struct imsg *);
80 #ifdef USE_LIBEVENT
81 static void	 module_on_event(int, short, void *);
82 #endif
83 static void	 module_reset_event(struct module_base *);
84 
85 struct module_base *
86 module_create(int sock, void *ctx, struct module_handlers *handler)
87 {
88 	struct module_base	*base;
89 
90 	if ((base = calloc(1, sizeof(struct module_base))) == NULL)
91 		return (NULL);
92 
93 	imsg_init(&base->ibuf, sock);
94 	base->ctx = ctx;
95 
96 	module_userpass = handler->userpass;
97 	module_access_request = handler->access_request;
98 	module_config_set = handler->config_set;
99 	module_request_decoration = handler->request_decoration;
100 	module_response_decoration = handler->response_decoration;
101 	module_start_module = handler->start;
102 	module_stop_module = handler->stop;
103 
104 	return (base);
105 }
106 
107 void
108 module_start(struct module_base *base)
109 {
110 #ifdef USE_LIBEVENT
111 	int	 ival;
112 
113 	if ((ival = fcntl(base->ibuf.fd, F_GETFL)) == -1)
114 		err(1, "Failed to F_GETFL");
115 	if (fcntl(base->ibuf.fd, F_SETFL, ival | O_NONBLOCK) == -1)
116 		err(1, "Failed to setup NONBLOCK");
117 	event_set(&base->ev, base->ibuf.fd, EV_READ, module_on_event, base);
118 	event_add(&base->ev, NULL);
119 #endif
120 }
121 
122 int
123 module_run(struct module_base *base)
124 {
125 	int	 ret;
126 
127 	ret = module_recv_imsg(base);
128 	if (ret == 0)
129 		imsg_flush(&base->ibuf);
130 
131 	return (ret);
132 }
133 
134 void
135 module_destroy(struct module_base *base)
136 {
137 	if (base != NULL) {
138 		free(base->radpkt);
139 		free(base->radpkt2);
140 		imsg_clear(&base->ibuf);
141 	}
142 	free(base);
143 }
144 
145 void
146 module_load(struct module_base *base)
147 {
148 	struct radiusd_module_load_arg	 load;
149 
150 	memset(&load, 0, sizeof(load));
151 	if (module_userpass != NULL)
152 		load.cap |= RADIUSD_MODULE_CAP_USERPASS;
153 	if (module_access_request != NULL)
154 		load.cap |= RADIUSD_MODULE_CAP_ACCSREQ;
155 	if (module_request_decoration != NULL)
156 		load.cap |= RADIUSD_MODULE_CAP_REQDECO;
157 	if (module_response_decoration != NULL)
158 		load.cap |= RADIUSD_MODULE_CAP_RESDECO;
159 	imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_LOAD, 0, 0, -1, &load,
160 	    sizeof(load));
161 	imsg_flush(&base->ibuf);
162 }
163 
164 void
165 module_drop_privilege(struct module_base *base)
166 {
167 	struct passwd	*pw;
168 
169 	tzset();
170 
171 	/* Drop the privilege */
172 	if ((pw = getpwnam(RADIUSD_USER)) == NULL)
173 		goto on_fail;
174 	if (chroot(pw->pw_dir) == -1)
175 		goto on_fail;
176 	if (chdir("/") == -1)
177 		goto on_fail;
178 	if (setgroups(1, &pw->pw_gid) ||
179 	    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
180 	    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
181 		goto on_fail;
182 	base->priv_dropped = true;
183 
184 on_fail:
185 	return;
186 }
187 
188 int
189 module_notify_secret(struct module_base *base, const char *secret)
190 {
191 	int		 ret;
192 
193 	ret = imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_NOTIFY_SECRET,
194 	    0, 0, -1, secret, strlen(secret) + 1);
195 	module_reset_event(base);
196 
197 	return (ret);
198 }
199 
200 int
201 module_send_message(struct module_base *base, uint32_t cmd, const char *fmt,
202     ...)
203 {
204 	char	*msg;
205 	va_list	 ap;
206 	int	 ret;
207 
208 	if (fmt == NULL)
209 		ret = imsg_compose(&base->ibuf, cmd, 0, 0, -1, NULL, 0);
210 	else {
211 		va_start(ap, fmt);
212 		vasprintf(&msg, fmt, ap);
213 		va_end(ap);
214 		if (msg == NULL)
215 			return (-1);
216 		ret = imsg_compose(&base->ibuf, cmd, 0, 0, -1, msg,
217 		    strlen(msg) + 1);
218 		free(msg);
219 	}
220 	module_reset_event(base);
221 
222 	return (ret);
223 }
224 
225 int
226 module_userpass_ok(struct module_base *base, u_int q_id, const char *msg)
227 {
228 	int		 ret;
229 	struct iovec	 iov[2];
230 
231 	iov[0].iov_base = &q_id;
232 	iov[0].iov_len = sizeof(q_id);
233 	iov[1].iov_base = (char *)msg;
234 	iov[1].iov_len = strlen(msg) + 1;
235 	ret = imsg_composev(&base->ibuf, IMSG_RADIUSD_MODULE_USERPASS_OK,
236 	    0, 0, -1, iov, 2);
237 	module_reset_event(base);
238 
239 	return (ret);
240 }
241 
242 int
243 module_userpass_fail(struct module_base *base, u_int q_id, const char *msg)
244 {
245 	int		 ret;
246 	struct iovec	 iov[2];
247 
248 	iov[0].iov_base = &q_id;
249 	iov[0].iov_len = sizeof(q_id);
250 	iov[1].iov_base = (char *)msg;
251 	iov[1].iov_len = strlen(msg) + 1;
252 	ret = imsg_composev(&base->ibuf, IMSG_RADIUSD_MODULE_USERPASS_FAIL,
253 	    0, 0, -1, iov, 2);
254 	module_reset_event(base);
255 
256 	return (ret);
257 }
258 
259 int
260 module_accsreq_answer(struct module_base *base, u_int q_id, const u_char *pkt,
261     size_t pktlen)
262 {
263 	return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER,
264 	    q_id, pkt, pktlen));
265 }
266 
267 int
268 module_accsreq_aborted(struct module_base *base, u_int q_id)
269 {
270 	int	 ret;
271 
272 	ret = imsg_compose(&base->ibuf, IMSG_RADIUSD_MODULE_ACCSREQ_ABORTED,
273 	    0, 0, -1, &q_id, sizeof(u_int));
274 	module_reset_event(base);
275 
276 	return (ret);
277 }
278 
279 int
280 module_reqdeco_done(struct module_base *base, u_int q_id, const u_char *pkt,
281     size_t pktlen)
282 {
283 	return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_REQDECO_DONE,
284 	    q_id, pkt, pktlen));
285 }
286 
287 int
288 module_resdeco_done(struct module_base *base, u_int q_id, const u_char *pkt,
289     size_t pktlen)
290 {
291 	return (module_common_radpkt(base, IMSG_RADIUSD_MODULE_RESDECO_DONE,
292 	    q_id, pkt, pktlen));
293 }
294 
295 static int
296 module_common_radpkt(struct module_base *base, uint32_t imsg_type, u_int q_id,
297     const u_char *pkt, size_t pktlen)
298 {
299 	int		 ret = 0, off = 0, len, siz;
300 	struct iovec	 iov[2];
301 	struct radiusd_module_radpkt_arg	 ans;
302 
303 	len = pktlen;
304 	ans.q_id = q_id;
305 	ans.pktlen = pktlen;
306 	ans.final = false;
307 
308 	while (!ans.final) {
309 		siz = MAX_IMSGSIZE - sizeof(ans);
310 		if (len - off <= siz) {
311 			ans.final = true;
312 			siz = len - off;
313 		}
314 		iov[0].iov_base = &ans;
315 		iov[0].iov_len = sizeof(ans);
316 		if (siz > 0) {
317 			iov[1].iov_base = (u_char *)pkt + off;
318 			iov[1].iov_len = siz;
319 		}
320 		ret = imsg_composev(&base->ibuf, imsg_type, 0, 0, -1, iov,
321 		    (siz > 0)? 2 : 1);
322 		if (ret == -1)
323 			break;
324 		off += siz;
325 	}
326 	module_reset_event(base);
327 
328 	return (ret);
329 }
330 
331 static int
332 module_recv_imsg(struct module_base *base)
333 {
334 	ssize_t		 n;
335 	struct imsg	 imsg;
336 
337 	if (((n = imsg_read(&base->ibuf)) == -1 && errno != EAGAIN) || n == 0) {
338 		if (n != 0)
339 			syslog(LOG_ERR, "%s: imsg_read(): %m", __func__);
340 		module_stop(base);
341 		return (-1);
342 	}
343 	for (;;) {
344 		if ((n = imsg_get(&base->ibuf, &imsg)) == -1) {
345 			syslog(LOG_ERR, "%s: imsg_get(): %m", __func__);
346 			module_stop(base);
347 			return (-1);
348 		}
349 		if (n == 0)
350 			break;
351 		module_imsg_handler(base, &imsg);
352 		imsg_free(&imsg);
353 	}
354 	module_reset_event(base);
355 
356 	return (0);
357 }
358 
359 static int
360 module_imsg_handler(struct module_base *base, struct imsg *imsg)
361 {
362 	ssize_t	 datalen;
363 
364 	datalen = imsg->hdr.len - IMSG_HEADER_SIZE;
365 	switch (imsg->hdr.type) {
366 	case IMSG_RADIUSD_MODULE_SET_CONFIG:
367 	    {
368 		struct radiusd_module_set_arg	 *arg;
369 		struct radiusd_module_object	 *val;
370 		u_int				  i;
371 		size_t				  off;
372 		char				**argv;
373 
374 		arg = (struct radiusd_module_set_arg *)imsg->data;
375 		off = sizeof(struct radiusd_module_set_arg);
376 
377 		if ((argv = calloc(sizeof(const char *), arg->nparamval))
378 		    == NULL) {
379 			module_send_message(base, IMSG_NG,
380 			    "Out of memory: %s", strerror(errno));
381 			break;
382 		}
383 		for (i = 0; i < arg->nparamval; i++) {
384 			if (datalen - off <
385 			    sizeof(struct radiusd_module_object))
386 				break;
387 			val = (struct radiusd_module_object *)
388 			    ((caddr_t)imsg->data + off);
389 			if (datalen - off < val->size)
390 				break;
391 			argv[i] = (char *)(val + 1);
392 			off += val->size;
393 		}
394 		if (i >= arg->nparamval)
395 			module_config_set(base->ctx, arg->paramname,
396 			    arg->nparamval, argv);
397 		else
398 			module_send_message(base, IMSG_NG,
399 			    "Internal protocol error");
400 		free(argv);
401 
402 		break;
403 	    }
404 	case IMSG_RADIUSD_MODULE_START:
405 		if (module_start_module != NULL) {
406 			module_start_module(base->ctx);
407 			if (!base->priv_dropped) {
408 				syslog(LOG_ERR, "Module tried to start with "
409 				    "root privileges");
410 				abort();
411 			}
412 		} else {
413 			if (!base->priv_dropped) {
414 				syslog(LOG_ERR, "Module tried to start with "
415 				    "root privileges");
416 				abort();
417 			}
418 			module_send_message(base, IMSG_OK, NULL);
419 		}
420 		break;
421 	case IMSG_RADIUSD_MODULE_STOP:
422 		module_stop(base);
423 		break;
424 	case IMSG_RADIUSD_MODULE_USERPASS:
425 	    {
426 		struct radiusd_module_userpass_arg *userpass;
427 
428 		if (module_userpass == NULL) {
429 			syslog(LOG_ERR, "Received USERPASS message, but "
430 			    "module doesn't support");
431 			break;
432 		}
433 		if (datalen <
434 		    (ssize_t)sizeof(struct radiusd_module_userpass_arg)) {
435 			syslog(LOG_ERR, "Received USERPASS message, but "
436 			    "length is wrong");
437 			break;
438 		}
439 		userpass = (struct radiusd_module_userpass_arg *)imsg->data;
440 		module_userpass(base->ctx, userpass->q_id, userpass->user,
441 		    (userpass->has_pass)? userpass->pass : NULL);
442 		explicit_bzero(userpass,
443 		    sizeof(struct radiusd_module_userpass_arg));
444 		break;
445 	    }
446 	case IMSG_RADIUSD_MODULE_ACCSREQ:
447 	case IMSG_RADIUSD_MODULE_REQDECO:
448 	case IMSG_RADIUSD_MODULE_RESDECO0_REQ:
449 	case IMSG_RADIUSD_MODULE_RESDECO:
450 	    {
451 		struct radiusd_module_radpkt_arg	*accessreq;
452 		int					 chunklen;
453 		const char				*typestr;
454 
455 		if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCSREQ) {
456 			if (module_access_request == NULL) {
457 				syslog(LOG_ERR, "Received ACCSREQ message, but "
458 				    "module doesn't support");
459 				break;
460 			}
461 			typestr = "ACCSREQ";
462 		} else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_REQDECO) {
463 			if (module_request_decoration == NULL) {
464 				syslog(LOG_ERR, "Received REQDECO message, but "
465 				    "module doesn't support");
466 				break;
467 			}
468 			typestr = "REQDECO";
469 		} else {
470 			if (module_response_decoration == NULL) {
471 				syslog(LOG_ERR, "Received RESDECO message, but "
472 				    "module doesn't support");
473 				break;
474 			}
475 			if (imsg->hdr.type == IMSG_RADIUSD_MODULE_RESDECO0_REQ)
476 				typestr = "RESDECO0_REQ";
477 			else
478 				typestr = "RESDECO";
479 		}
480 
481 		if (datalen <
482 		    (ssize_t)sizeof(struct radiusd_module_radpkt_arg)) {
483 			syslog(LOG_ERR, "Received %s message, but "
484 			    "length is wrong", typestr);
485 			break;
486 		}
487 		accessreq = (struct radiusd_module_radpkt_arg *)imsg->data;
488 		if (base->radpktsiz < accessreq->pktlen) {
489 			u_char *nradpkt;
490 			if ((nradpkt = realloc(base->radpkt,
491 			    accessreq->pktlen)) == NULL) {
492 				syslog(LOG_ERR, "Could not handle received "
493 				    "%s message: %m", typestr);
494 				base->radpktoff = 0;
495 				goto accsreq_out;
496 			}
497 			base->radpkt = nradpkt;
498 			base->radpktsiz = accessreq->pktlen;
499 		}
500 		chunklen = datalen - sizeof(struct radiusd_module_radpkt_arg);
501 		if (chunklen > base->radpktsiz - base->radpktoff){
502 			syslog(LOG_ERR,
503 			    "Could not handle received %s message: "
504 			    "received length is too big", typestr);
505 			base->radpktoff = 0;
506 			goto accsreq_out;
507 		}
508 		memcpy(base->radpkt + base->radpktoff,
509 		    (caddr_t)(accessreq + 1), chunklen);
510 		base->radpktoff += chunklen;
511 		if (!accessreq->final)
512 			goto accsreq_out;
513 		if (base->radpktoff != accessreq->pktlen) {
514 			syslog(LOG_ERR,
515 			    "Could not handle received %s "
516 			    "message: length is mismatch", typestr);
517 			base->radpktoff = 0;
518 			goto accsreq_out;
519 		}
520 		if (imsg->hdr.type == IMSG_RADIUSD_MODULE_ACCSREQ)
521 			module_access_request(base->ctx, accessreq->q_id,
522 			    base->radpkt, base->radpktoff);
523 		else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_REQDECO)
524 			module_request_decoration(base->ctx, accessreq->q_id,
525 			    base->radpkt, base->radpktoff);
526 		else if (imsg->hdr.type == IMSG_RADIUSD_MODULE_RESDECO0_REQ) {
527 			/* preserve request */
528 			if (base->radpktoff > base->radpkt2siz) {
529 				u_char *nradpkt;
530 				if ((nradpkt = realloc(base->radpkt2,
531 				    base->radpktoff)) == NULL) {
532 					syslog(LOG_ERR, "Could not handle "
533 					    "received %s message: %m", typestr);
534 					base->radpktoff = 0;
535 					goto accsreq_out;
536 				}
537 				base->radpkt2 = nradpkt;
538 				base->radpkt2siz = base->radpktoff;
539 			}
540 			memcpy(base->radpkt2, base->radpkt, base->radpktoff);
541 			base->radpkt2len = base->radpktoff;
542 		} else {
543 			module_response_decoration(base->ctx, accessreq->q_id,
544 			    base->radpkt2, base->radpkt2len, base->radpkt,
545 			    base->radpktoff);
546 			base->radpkt2len = 0;
547 		}
548 		base->radpktoff = 0;
549 accsreq_out:
550 		break;
551 	    }
552 	}
553 
554 	return (0);
555 }
556 
557 void
558 module_stop(struct module_base *base)
559 {
560 	if (module_stop_module != NULL)
561 		module_stop_module(base->ctx);
562 #ifdef USE_LIBEVENT
563 	event_del(&base->ev);
564 	base->stopped = true;
565 #endif
566 	close(base->ibuf.fd);
567 }
568 
569 #ifdef USE_LIBEVENT
570 static void
571 module_on_event(int fd, short evmask, void *ctx)
572 {
573 	struct module_base	*base = ctx;
574 	int			 ret;
575 
576 	base->ev_onhandler = true;
577 	if (evmask & EV_WRITE)
578 		base->writeready = true;
579 	if (evmask & EV_READ) {
580 		ret = module_recv_imsg(base);
581 		if (ret < 0)
582 			return;
583 	}
584 	while (base->writeready && base->ibuf.w.queued) {
585 		ret = msgbuf_write(&base->ibuf.w);
586 		if (ret > 0)
587 			continue;
588 		base->writeready = false;
589 		if (ret == 0 && errno == EAGAIN)
590 			break;
591 		syslog(LOG_ERR, "%s: msgbuf_write: %m", __func__);
592 		module_stop(base);
593 		return;
594 	}
595 	base->ev_onhandler = false;
596 	module_reset_event(base);
597 	return;
598 }
599 #endif
600 
601 static void
602 module_reset_event(struct module_base *base)
603 {
604 #ifdef USE_LIBEVENT
605 	short		 evmask = 0;
606 	struct timeval	*tvp = NULL, tv = { 0, 0 };
607 
608 	if (base->ev_onhandler)
609 		return;
610 	if (base->stopped)
611 		return;
612 	event_del(&base->ev);
613 
614 	evmask |= EV_READ;
615 	if (base->ibuf.w.queued) {
616 		if (!base->writeready)
617 			evmask |= EV_WRITE;
618 		else
619 			tvp = &tv;	/* fire immediately */
620 	}
621 	event_set(&base->ev, base->ibuf.fd, evmask, module_on_event, base);
622 	if (event_add(&base->ev, tvp) == -1)
623 		syslog(LOG_ERR, "event_add() failed in %s()", __func__);
624 #endif
625 }
626