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