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