xref: /openbsd/usr.sbin/radiusd/radiusd_radius.c (revision 3cab2bb3)
1 /*	$OpenBSD: radiusd_radius.c,v 1.17 2019/06/28 13:32:49 deraadt Exp $	*/
2 
3 /*
4  * Copyright (c) 2013 Internet Initiative Japan Inc.
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 #include <sys/types.h>
20 #include <sys/queue.h>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 
24 #include <err.h>
25 #include <errno.h>
26 #include <event.h>
27 #include <fcntl.h>
28 #include <stdbool.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <syslog.h>
33 #include <unistd.h>
34 
35 #include <radius.h>
36 
37 #include "radiusd.h"
38 #include "radiusd_module.h"
39 #include "util.h"
40 #include "log.h"
41 
42 struct radius_server {
43 	struct module_radius		*module;
44 	int				 sock;
45 	union {
46 		struct sockaddr_in6	 sin6;
47 		struct sockaddr_in	 sin4;
48 	}				 addr;
49 	union {
50 		struct sockaddr_in6	 sin6;
51 		struct sockaddr_in	 sin4;
52 	}				 local;
53 	struct event			 ev;
54 	u_char				 req_id_seq;
55 };
56 
57 struct module_radius {
58 	struct module_base		*base;
59 	struct radius_server		 server[4];
60 	char				 secret[RADIUSD_SECRET_MAX];
61 	u_int				 nserver;
62 	u_int				 curr_server;
63 	u_int				 req_timeout;
64 	u_int				 max_tries;
65 	u_int				 max_failovers;
66 	u_int				 nfailover;
67 	TAILQ_HEAD(,module_radius_req)	 req;
68 };
69 
70 struct module_radius_req {
71 	struct module_radius		*module;
72 	struct radius_server		*server;
73 	u_int				 q_id;
74 	RADIUS_PACKET			*q_pkt;
75 	u_int				 ntry;
76 	u_int				 nfailover;
77 	u_char				 req_id;
78 	struct event			 ev;
79 	TAILQ_ENTRY(module_radius_req)	 next;
80 };
81 
82 static void	 module_radius_init(struct module_radius *);
83 static void	 module_radius_config_set(void *, const char *, int,
84 		    char * const *);
85 static void	 module_radius_start(void *);
86 static void	 module_radius_stop(void *);
87 static void	 module_radius_access_request(void *, u_int, const u_char *,
88 		    size_t);
89 static int	 radius_server_start(struct radius_server *);
90 static void	 radius_server_stop(struct radius_server *);
91 static void	 radius_server_on_event(int, short, void *);
92 static void	 radius_server_on_fail(struct radius_server *, const char *);
93 static void	 module_radius_req_send(struct module_radius_req *);
94 static int	 module_radius_req_reset_event(struct module_radius_req *);
95 static void	 module_radius_req_on_timeout(int, short, void *);
96 static void	 module_radius_req_on_success(struct module_radius_req *,
97 		    const u_char *, size_t);
98 static void	 module_radius_req_on_failure(struct module_radius_req *);
99 
100 static void	 module_radius_req_free(struct module_radius_req *);
101 static void	 module_radius_req_select_server(struct module_radius_req *);
102 
103 static void	 module_radius_req_reset_msgauth(struct module_radius_req *);
104 static void	 module_radius_log(struct module_radius *, int, const char *, ...);
105 
106 static struct module_handlers module_radius_handlers = {
107 	.config_set = module_radius_config_set,
108 	.start = module_radius_start,
109 	.stop = module_radius_stop,
110 	.access_request = module_radius_access_request
111 };
112 
113 #ifndef nitems
114 #define nitems(_x)    (sizeof((_x)) / sizeof((_x)[0]))
115 #endif
116 
117 int
118 main(int argc, char *argv[])
119 {
120 	static struct module_radius module_radius;
121 
122 	module_radius_init(&module_radius);
123 	openlog(NULL, LOG_PID, LOG_DAEMON);
124 
125 	if ((module_radius.base = module_create(
126 	    STDIN_FILENO, &module_radius, &module_radius_handlers)) == NULL)
127 		err(1, "Could not create a module instance");
128 	module_drop_privilege(module_radius.base);
129 	setproctitle("[main]");
130 
131 	module_load(module_radius.base);
132 	log_init(0);
133 	event_init();
134 
135 	if (pledge("stdio inet", NULL) == -1)
136 		err(EXIT_FAILURE, "pledge");
137 
138 	module_start(module_radius.base);
139 	event_loop(0);
140 
141 	exit(EXIT_SUCCESS);
142 }
143 
144 static void
145 module_radius_init(struct module_radius *module)
146 {
147 	memset(module, 0, sizeof(struct module_radius));
148 	TAILQ_INIT(&module->req);
149 }
150 
151 static void
152 module_radius_config_set(void *ctx, const char *paramname, int paramvalc,
153     char * const * paramvalv)
154 {
155 	const char		*errmsg = NULL;
156 	struct addrinfo		*res;
157 	struct module_radius	*module = ctx;
158 
159 	if (strcmp(paramname, "server") == 0) {
160 		SYNTAX_ASSERT(paramvalc == 1,
161 		    "`server' must have just one argument");
162 		SYNTAX_ASSERT(module->nserver < (int)nitems(module->server),
163 		    "number of server reached limit");
164 
165 		if (addrport_parse(paramvalv[0], IPPROTO_UDP, &res) != 0)
166 			SYNTAX_ASSERT(0, "could not parse address and port");
167 		memcpy(&module->server[module->nserver].addr, res->ai_addr,
168 		    res->ai_addrlen);
169 
170 		if (ntohs(module->server[module->nserver].addr.sin4.sin_port)
171 		    == 0)
172 			module->server[module->nserver].addr.sin4.sin_port
173 			    = htons(RADIUS_DEFAULT_PORT);
174 
175 		module->server[module->nserver].sock = -1;
176 		module->nserver++;
177 		freeaddrinfo(res);
178 	} else if (strcmp(paramname, "request-timeout") == 0) {
179 		SYNTAX_ASSERT(paramvalc == 1,
180 		    "`request-timeout' must have just one argument");
181 		module->req_timeout = (int)strtonum(paramvalv[0], 0,
182 		    UINT16_MAX, &errmsg);
183 		if (module->req_timeout == 0 && errmsg != NULL) {
184 			module_send_message(module->base, IMSG_NG,
185 			    "`request-timeout must be 0-%d", UINT16_MAX);
186 			return;
187 		}
188 	} else if (strcmp(paramname, "max-tries") == 0) {
189 		SYNTAX_ASSERT(paramvalc == 1,
190 		    "`max-tries' must have just one argument");
191 		module->max_tries = (int)strtonum(paramvalv[0], 0,
192 		    UINT16_MAX, &errmsg);
193 		if (module->max_tries == 0 && errmsg != NULL) {
194 			module_send_message(module->base, IMSG_NG,
195 			    "`max-tries must be 0-%d", UINT16_MAX);
196 			return;
197 		}
198 
199 	} else if (strcmp(paramname, "max-failovers") == 0) {
200 		SYNTAX_ASSERT(paramvalc == 1,
201 		    "`max-failovers' must have just one argument");
202 		module->max_failovers = (int)strtonum(paramvalv[0], 0,
203 		    UINT16_MAX, &errmsg);
204 		if (module->max_failovers == 0 && errmsg != NULL) {
205 			module_send_message(module->base, IMSG_NG,
206 			    "`max-failovers' must be 0-%d", UINT16_MAX);
207 			return;
208 		}
209 	} else if (strcmp(paramname, "secret") == 0) {
210 		SYNTAX_ASSERT(paramvalc == 1,
211 		    "`secret' must have just one argument");
212 		if (strlcpy(module->secret, paramvalv[0],
213 		    sizeof(module->secret)) >= sizeof(module->secret)) {
214 			module_send_message(module->base, IMSG_NG,
215 			    "`secret' length must be 0-%lu",
216 			    (u_long) sizeof(module->secret) - 1);
217 			return;
218 		}
219 	} else if (strcmp(paramname, "_debug") == 0)
220 		log_init(1);
221 	else if (strncmp(paramname, "_", 1) == 0)
222 		/* ignore all internal messages */
223 		module_send_message(module->base, IMSG_OK, NULL);
224 	else {
225 		module_send_message(module->base, IMSG_NG,
226 		    "Unknown config parameter name `%s'", paramname);
227 		return;
228 	}
229 	module_send_message(module->base, IMSG_OK, NULL);
230 
231 	return;
232 syntax_error:
233 	module_send_message(module->base, IMSG_NG, "%s", errmsg);
234 }
235 
236 static void
237 module_radius_start(void *ctx)
238 {
239 	u_int			 i;
240 	struct module_radius	*module = ctx;
241 
242 	if (module->nserver <= 0) {
243 		module_send_message(module->base, IMSG_NG,
244 			"needs one `server' at least");
245 		return;
246 	}
247 
248 	if (module->secret[0] == '\0') {
249 		module_send_message(module->base, IMSG_NG,
250 		    "`secret' configuration is required");
251 		return;
252 	}
253 
254 	for (i = 0; i < module->nserver; i++) {
255 		module->server[i].module = module;
256 		if (radius_server_start(&module->server[i]) != 0) {
257 			module_send_message(module->base, IMSG_NG,
258 				"module `radius' failed to start one of "
259 				"the servers");
260 			return;
261 		}
262 	}
263 	module_send_message(module->base, IMSG_OK, NULL);
264 
265 	module_notify_secret(module->base, module->secret);
266 }
267 
268 static void
269 module_radius_stop(void *ctx)
270 {
271 	u_int				 i;
272 	struct module_radius_req	*req, *treq;
273 	struct module_radius		*module = ctx;
274 
275 	TAILQ_FOREACH_SAFE(req, &module->req, next, treq)
276 		module_radius_req_on_failure(req);
277 
278 	for (i = 0; i < module->nserver; i++)
279 		radius_server_stop(&module->server[i]);
280 }
281 
282 static void
283 module_radius_access_request(void *ctx, u_int q_id, const u_char *pkt,
284     size_t pktlen)
285 {
286 	struct module_radius		*module = ctx;
287 	struct module_radius_req	*req;
288 	u_char				 attrbuf[256];
289 	ssize_t				 attrlen;
290 
291 	req = calloc(1, sizeof(struct module_radius_req));
292 	if (req == NULL) {
293 		module_radius_log(module, LOG_WARNING,
294 		    "%s: Out of memory: %m", __func__);
295 		goto on_fail;
296 	}
297 
298 	req->ntry = 0;
299 	req->module = module;
300 	req->q_id = q_id;
301 	if ((req->q_pkt = radius_convert_packet(pkt, pktlen)) == NULL) {
302 		module_radius_log(module, LOG_WARNING,
303 		    "%s: radius_convert_packet() failed: %m", __func__);
304 		goto on_fail;
305 	}
306 	evtimer_set(&req->ev, module_radius_req_on_timeout, req);
307 	TAILQ_INSERT_TAIL(&req->module->req, req, next);
308 
309 	/*
310 	 * radiusd decrypt User-Password attribute.  crypt it again with our
311 	 * secret.
312 	 */
313 	attrlen = sizeof(attrbuf);
314 	if (radius_get_raw_attr(req->q_pkt, RADIUS_TYPE_USER_PASSWORD,
315 		    attrbuf, &attrlen) == 0) {
316 		attrbuf[attrlen] = '\0';
317 		radius_del_attr_all(req->q_pkt, RADIUS_TYPE_USER_PASSWORD);
318 		radius_put_user_password_attr(req->q_pkt, attrbuf,
319 		    module->secret);
320 	}
321 
322 	/* select current server */
323 	module_radius_req_select_server(req);
324 
325 	module_radius_req_send(req);
326 
327 	return;
328 
329 on_fail:
330 	free(req);
331 	module_accsreq_aborted(module->base, q_id);
332 }
333 
334 /*
335  * radius_server
336  */
337 static int
338 radius_server_start(struct radius_server *server)
339 {
340 	socklen_t	 locallen;
341 	char		 buf0[NI_MAXHOST + NI_MAXSERV + 32];
342 	char		 buf1[NI_MAXHOST + NI_MAXSERV + 32];
343 
344 	if ((server->sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0))
345 	    == -1) {
346 		module_radius_log(server->module, LOG_WARNING,
347 		    "%s: socket() failed", __func__);
348 		goto on_error;
349 	}
350 	if (connect(server->sock, (struct sockaddr *)&server->addr,
351 		server->addr.sin4.sin_len) != 0) {
352 		module_radius_log(server->module, LOG_WARNING,
353 		    "%s: connect to %s failed", __func__,
354 		    addrport_tostring((struct sockaddr *)&server->addr,
355 			server->addr.sin4.sin_len, buf1, sizeof(buf1)));
356 		goto on_error;
357 	}
358 	locallen = sizeof(server->local);
359 	if (getsockname(server->sock, (struct sockaddr *)&server->local,
360 	    &locallen) != 0) {
361 		module_radius_log(server->module, LOG_WARNING,
362 		    "%s: getsockanme() failed", __func__);
363 		goto on_error;
364 	}
365 	module_radius_log(server->module, LOG_INFO,
366 	    "Use %s to send requests for %s",
367 	    addrport_tostring((struct sockaddr *)&server->local,
368 		    locallen, buf0, sizeof(buf0)),
369 	    addrport_tostring((struct sockaddr *)&server->addr,
370 		    server->addr.sin4.sin_len, buf1, sizeof(buf1)));
371 
372 	event_set(&server->ev, server->sock, EV_READ | EV_PERSIST,
373 	    radius_server_on_event, server);
374 	if (event_add(&server->ev, NULL)) {
375 		module_radius_log(server->module, LOG_WARNING,
376 		    "%s: event_add() failed", __func__);
377 		goto on_error;
378 	}
379 
380 	return (0);
381 on_error:
382 	if (server->sock >= 0)
383 		close(server->sock);
384 	server->sock = -1;
385 	return (-1);
386 }
387 
388 static void
389 radius_server_stop(struct radius_server *server)
390 {
391 	event_del(&server->ev);
392 	if (server->sock >= 0)
393 		close(server->sock);
394 	server->sock = -1;
395 }
396 
397 static void
398 radius_server_on_event(int fd, short evmask, void *ctx)
399 {
400 	int				 sz, res_id;
401 	u_char				 pkt[65535];
402 	char				 buf[NI_MAXHOST + NI_MAXSERV + 32];
403 	struct radius_server		*server = ctx;
404 	RADIUS_PACKET			*radpkt = NULL;
405 	struct module_radius_req	*req;
406 	struct sockaddr			*peer;
407 
408 	peer = (struct sockaddr *)&server->addr;
409 	if ((sz = recv(server->sock, pkt, sizeof(pkt), 0)) == -1) {
410 		if (errno == EAGAIN)
411 			return;
412 		module_radius_log(server->module, LOG_WARNING,
413 		    "server=%s recv() failed: %m",
414 		    addrport_tostring(peer, peer->sa_len, buf, sizeof(buf)));
415 		return;
416 	}
417 	if ((radpkt = radius_convert_packet(pkt, sz)) == NULL) {
418 		module_radius_log(server->module, LOG_WARNING,
419 		    "server=%s could not convert the received message to a "
420 		    "RADIUS packet object: %m",
421 		    addrport_tostring(peer, peer->sa_len, buf, sizeof(buf)));
422 		return;
423 	}
424 	res_id = radius_get_id(radpkt);
425 	TAILQ_FOREACH(req, &server->module->req, next) {
426 		if (req->server == server && req->req_id == res_id)
427 			break;
428 	}
429 	if (req == NULL) {
430 		module_radius_log(server->module, LOG_WARNING,
431 		    "server=%s Received radius message has unknown id=%d",
432 		    addrport_tostring(peer, peer->sa_len, buf, sizeof(buf)),
433 		    res_id);
434 		goto out;
435 	}
436 	radius_set_request_packet(radpkt, req->q_pkt);
437 
438 	if (radius_check_response_authenticator(radpkt,
439 	    server->module->secret) != 0) {
440 		module_radius_log(server->module, LOG_WARNING,
441 		    "server=%s Received radius message(id=%d) has bad "
442 		    "authenticator",
443 		    addrport_tostring(peer, peer->sa_len, buf,
444 		    sizeof(buf)), res_id);
445 		goto out;
446 	}
447 	if (radius_has_attr(radpkt,
448 	    RADIUS_TYPE_MESSAGE_AUTHENTICATOR) &&
449 	    radius_check_message_authenticator(radpkt,
450 		    server->module->secret) != 0) {
451 		module_radius_log(server->module, LOG_WARNING,
452 		    "server=%s Received radius message(id=%d) has bad "
453 		    "message authenticator",
454 		    addrport_tostring(peer, peer->sa_len, buf,
455 		    sizeof(buf)), res_id);
456 		goto out;
457 	}
458 
459 	module_radius_log(server->module, LOG_INFO,
460 	    "q=%u received a response from server %s", req->q_id,
461 	    addrport_tostring(peer, peer->sa_len, buf, sizeof(buf)));
462 
463 	module_radius_req_on_success(req, radius_get_data(radpkt),
464 	    radius_get_length(radpkt));
465 out:
466 	if (radpkt != NULL)
467 		radius_delete_packet(radpkt);
468 }
469 
470 static void
471 radius_server_on_fail(struct radius_server *server, const char *failmsg)
472 {
473 	char		 buf0[NI_MAXHOST + NI_MAXSERV + 32];
474 	char		 buf1[NI_MAXHOST + NI_MAXSERV + 32];
475 	struct sockaddr	*caddr, *naddr;
476 
477 	caddr = (struct sockaddr *)&server->addr;
478 	if (server->module->nserver <= 1) {
479 		module_radius_log(server->module, LOG_WARNING,
480 		    "Server %s failed: %s",
481 		    addrport_tostring(caddr, caddr->sa_len, buf0, sizeof(buf0)),
482 		    failmsg);
483 		return;
484 	}
485 	server->module->curr_server++;
486 	server->module->curr_server %= server->module->nserver;
487 	naddr = (struct sockaddr *)
488 	    &server->module->server[server->module->curr_server].addr;
489 
490 	module_radius_log(server->module, LOG_WARNING,
491 	    "Server %s failed: %s  Fail over to %s",
492 	    addrport_tostring(caddr, caddr->sa_len, buf0, sizeof(buf0)),
493 	    failmsg,
494 	    addrport_tostring(naddr, naddr->sa_len, buf1, sizeof(buf1)));
495 }
496 
497 /* module_radius_req */
498 
499 static void
500 module_radius_req_send(struct module_radius_req *req)
501 {
502 	int		 sz;
503 	struct sockaddr	*peer;
504 	char		 msg[BUFSIZ];
505 
506 	peer = (struct sockaddr *)&req->server->addr;
507 	if ((sz = send(req->server->sock, radius_get_data(req->q_pkt),
508 	    radius_get_length(req->q_pkt), 0)) < 0) {
509 		module_radius_log(req->module, LOG_WARNING,
510 		    "Sending RADIUS query q=%u to %s failed: %m",
511 		    req->q_id,
512 		    addrport_tostring(peer, peer->sa_len, msg, sizeof(msg)));
513 		/* retry anyway */
514 	}
515 	module_radius_log(req->module, LOG_INFO,
516 	    "Send RADIUS query q=%u id=%d to %s successfully",
517 	    req->q_id, req->req_id,
518 	    addrport_tostring(peer, peer->sa_len, msg, sizeof(msg)));
519 	if (module_radius_req_reset_event(req) != -1)
520 		req->ntry++;
521 }
522 
523 static int
524 module_radius_req_reset_event(struct module_radius_req *req)
525 {
526 	struct timeval	 tv;
527 	static int	 timeouts[] = { 2, 4, 8 };
528 
529 	tv.tv_usec = 0;
530 	if (req->module->req_timeout != 0)
531 		tv.tv_sec = req->module->req_timeout;
532 	else {
533 		if (req->ntry < nitems(timeouts))
534 			tv.tv_sec = timeouts[req->ntry];
535 		else
536 			tv.tv_sec = timeouts[nitems(timeouts) - 1];
537 	}
538 	if (evtimer_add(&req->ev, &tv) != 0) {
539 		module_radius_log(req->module, LOG_WARNING,
540 		    "Cannot proccess the request for q=%u: "
541 		    "evtimer_add() failed: %m", req->q_id);
542 		module_radius_req_on_failure(req);
543 		return (-1);
544 	}
545 	return (0);
546 }
547 
548 static void
549 module_radius_req_on_timeout(int fd, short evmask, void *ctx)
550 {
551 	struct module_radius_req	*req = ctx;
552 	char				 msg[BUFSIZ];
553 
554 
555 	if (req->module->max_tries <= req->ntry) {
556 		snprintf(msg, sizeof(msg), "q=%u didn't response RADIUS query "
557 		    "%d time%s", req->q_id, req->ntry,
558 		    (req->ntry > 0)? "s" : "");
559 		radius_server_on_fail(req->server, msg);
560 		if (++req->nfailover >= req->module->max_failovers) {
561 			module_radius_log(req->module,
562 			    LOG_WARNING, "RADIUS query q=%u time out",
563 			    req->q_id);
564 			module_radius_req_on_failure(req);
565 			return;
566 		}
567 		/* select the next server */
568 		module_radius_req_select_server(req);
569 	}
570 	module_radius_req_send(req);
571 }
572 
573 static void
574 module_radius_req_on_success(struct module_radius_req *req,
575     const u_char *pkt, size_t pktlen)
576 {
577 	module_accsreq_answer(req->module->base, req->q_id, pkt, pktlen);
578 	module_radius_req_free(req);
579 }
580 
581 static void
582 module_radius_req_on_failure(struct module_radius_req *req)
583 {
584 	module_accsreq_aborted(req->module->base, req->q_id);
585 	module_radius_req_free(req);
586 }
587 
588 
589 static void
590 module_radius_req_free(struct module_radius_req *req)
591 {
592 	evtimer_del(&req->ev);
593 	TAILQ_REMOVE(&req->module->req, req, next);
594 	if (req->q_pkt != NULL)
595 		radius_delete_packet(req->q_pkt);
596 	free(req);
597 }
598 
599 static void
600 module_radius_req_select_server(struct module_radius_req *req)
601 {
602 	req->server = &req->module->server[req->module->curr_server];
603 	req->ntry = 0;
604 	req->req_id = req->server->req_id_seq++;
605 	radius_set_id(req->q_pkt, req->req_id);
606 	module_radius_req_reset_msgauth(req);
607 }
608 
609 static void
610 module_radius_req_reset_msgauth(struct module_radius_req *req)
611 {
612 	if (radius_has_attr(req->q_pkt, RADIUS_TYPE_MESSAGE_AUTHENTICATOR))
613 		radius_del_attr_all(req->q_pkt,
614 		    RADIUS_TYPE_MESSAGE_AUTHENTICATOR);
615 	radius_put_message_authenticator(req->q_pkt,
616 	    req->module->secret);
617 }
618 
619 static void
620 module_radius_log(struct module_radius *module, int pri, const char *fmt, ...)
621 {
622 	char		fmt0[BUFSIZ];
623 	va_list		va;
624 
625 	snprintf(fmt0, sizeof(fmt0), "radius: %s", fmt);
626 	va_start(va, fmt);
627 	vlog(pri, fmt0, va);
628 	va_end(va);
629 }
630