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