xref: /openbsd-src/usr.sbin/radiusd/radiusd_radius.c (revision c0c32a87c2e29d229c3855029b107496e15a4baa)
1*c0c32a87Syasuoka /*	$OpenBSD: radiusd_radius.c,v 1.22 2024/08/16 09:52:16 yasuoka Exp $	*/
2a7ca44b8Syasuoka 
3a7ca44b8Syasuoka /*
4a7ca44b8Syasuoka  * Copyright (c) 2013 Internet Initiative Japan Inc.
5a7ca44b8Syasuoka  *
6a7ca44b8Syasuoka  * Permission to use, copy, modify, and distribute this software for any
7a7ca44b8Syasuoka  * purpose with or without fee is hereby granted, provided that the above
8a7ca44b8Syasuoka  * copyright notice and this permission notice appear in all copies.
9a7ca44b8Syasuoka  *
10a7ca44b8Syasuoka  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11a7ca44b8Syasuoka  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12a7ca44b8Syasuoka  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13a7ca44b8Syasuoka  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14a7ca44b8Syasuoka  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15a7ca44b8Syasuoka  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16a7ca44b8Syasuoka  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17a7ca44b8Syasuoka  */
18a7ca44b8Syasuoka 
19a7ca44b8Syasuoka #include <sys/types.h>
20a7ca44b8Syasuoka #include <sys/queue.h>
21a7ca44b8Syasuoka #include <sys/socket.h>
22a7ca44b8Syasuoka #include <netinet/in.h>
23a7ca44b8Syasuoka 
24a7ca44b8Syasuoka #include <err.h>
2551da3916Syasuoka #include <errno.h>
26a7ca44b8Syasuoka #include <event.h>
2751da3916Syasuoka #include <fcntl.h>
28a7ca44b8Syasuoka #include <stdbool.h>
29a7ca44b8Syasuoka #include <stdio.h>
30a7ca44b8Syasuoka #include <stdlib.h>
31a7ca44b8Syasuoka #include <string.h>
32a7ca44b8Syasuoka #include <syslog.h>
33a7ca44b8Syasuoka #include <unistd.h>
34a7ca44b8Syasuoka 
35a7ca44b8Syasuoka #include <radius.h>
36a7ca44b8Syasuoka 
37a7ca44b8Syasuoka #include "radiusd.h"
38a7ca44b8Syasuoka #include "radiusd_module.h"
39a7ca44b8Syasuoka #include "util.h"
40a7ca44b8Syasuoka #include "log.h"
41a7ca44b8Syasuoka 
42a7ca44b8Syasuoka struct radius_server {
43a7ca44b8Syasuoka 	struct module_radius		*module;
44a7ca44b8Syasuoka 	int				 sock;
45a7ca44b8Syasuoka 	union {
46a7ca44b8Syasuoka 		struct sockaddr_in6	 sin6;
47a7ca44b8Syasuoka 		struct sockaddr_in	 sin4;
48a7ca44b8Syasuoka 	}				 addr;
49a7ca44b8Syasuoka 	union {
50a7ca44b8Syasuoka 		struct sockaddr_in6	 sin6;
51a7ca44b8Syasuoka 		struct sockaddr_in	 sin4;
52a7ca44b8Syasuoka 	}				 local;
53a7ca44b8Syasuoka 	struct event			 ev;
54a7ca44b8Syasuoka 	u_char				 req_id_seq;
55a7ca44b8Syasuoka };
56a7ca44b8Syasuoka 
57a7ca44b8Syasuoka struct module_radius {
58a7ca44b8Syasuoka 	struct module_base		*base;
59a7ca44b8Syasuoka 	struct radius_server		 server[4];
60a7ca44b8Syasuoka 	char				 secret[RADIUSD_SECRET_MAX];
61a7ca44b8Syasuoka 	u_int				 nserver;
62a7ca44b8Syasuoka 	u_int				 curr_server;
63a7ca44b8Syasuoka 	u_int				 req_timeout;
64a7ca44b8Syasuoka 	u_int				 max_tries;
65a7ca44b8Syasuoka 	u_int				 max_failovers;
66a7ca44b8Syasuoka 	u_int				 nfailover;
67a7ca44b8Syasuoka 	TAILQ_HEAD(,module_radius_req)	 req;
68a7ca44b8Syasuoka };
69a7ca44b8Syasuoka 
70a7ca44b8Syasuoka struct module_radius_req {
71a7ca44b8Syasuoka 	struct module_radius		*module;
72a7ca44b8Syasuoka 	struct radius_server		*server;
73c67038c8Syasuoka 	u_int				 q_id;
74a7ca44b8Syasuoka 	RADIUS_PACKET			*q_pkt;
75a7ca44b8Syasuoka 	u_int				 ntry;
76a7ca44b8Syasuoka 	u_int				 nfailover;
77a7ca44b8Syasuoka 	u_char				 req_id;
78a7ca44b8Syasuoka 	struct event			 ev;
79a7ca44b8Syasuoka 	TAILQ_ENTRY(module_radius_req)	 next;
80a7ca44b8Syasuoka };
81a7ca44b8Syasuoka 
82a7ca44b8Syasuoka static void	 module_radius_init(struct module_radius *);
83a7ca44b8Syasuoka static void	 module_radius_config_set(void *, const char *, int,
84a7ca44b8Syasuoka 		    char * const *);
85a7ca44b8Syasuoka static void	 module_radius_start(void *);
86a7ca44b8Syasuoka static void	 module_radius_stop(void *);
87a7ca44b8Syasuoka static void	 module_radius_access_request(void *, u_int, const u_char *,
88a7ca44b8Syasuoka 		    size_t);
89a7ca44b8Syasuoka static int	 radius_server_start(struct radius_server *);
90a7ca44b8Syasuoka static void	 radius_server_stop(struct radius_server *);
91a7ca44b8Syasuoka static void	 radius_server_on_event(int, short, void *);
92a7ca44b8Syasuoka static void	 radius_server_on_fail(struct radius_server *, const char *);
93a7ca44b8Syasuoka static void	 module_radius_req_send(struct module_radius_req *);
94a7ca44b8Syasuoka static int	 module_radius_req_reset_event(struct module_radius_req *);
95a7ca44b8Syasuoka static void	 module_radius_req_on_timeout(int, short, void *);
96a7ca44b8Syasuoka static void	 module_radius_req_on_success(struct module_radius_req *,
97a7ca44b8Syasuoka 		    const u_char *, size_t);
98a7ca44b8Syasuoka static void	 module_radius_req_on_failure(struct module_radius_req *);
99a7ca44b8Syasuoka 
100a7ca44b8Syasuoka static void	 module_radius_req_free(struct module_radius_req *);
101a7ca44b8Syasuoka static void	 module_radius_req_select_server(struct module_radius_req *);
102a7ca44b8Syasuoka 
103a7ca44b8Syasuoka static void	 module_radius_req_reset_msgauth(struct module_radius_req *);
104a7ca44b8Syasuoka static void	 module_radius_log(struct module_radius *, int, const char *, ...);
105a7ca44b8Syasuoka 
106a7ca44b8Syasuoka static struct module_handlers module_radius_handlers = {
107a7ca44b8Syasuoka 	.config_set = module_radius_config_set,
108a7ca44b8Syasuoka 	.start = module_radius_start,
109a7ca44b8Syasuoka 	.stop = module_radius_stop,
110a7ca44b8Syasuoka 	.access_request = module_radius_access_request
111a7ca44b8Syasuoka };
112a7ca44b8Syasuoka 
113a7ca44b8Syasuoka #ifndef nitems
114a7ca44b8Syasuoka #define nitems(_x)    (sizeof((_x)) / sizeof((_x)[0]))
115a7ca44b8Syasuoka #endif
116a7ca44b8Syasuoka 
117a7ca44b8Syasuoka int
118a7ca44b8Syasuoka main(int argc, char *argv[])
119a7ca44b8Syasuoka {
120a7ca44b8Syasuoka 	static struct module_radius module_radius;
121a7ca44b8Syasuoka 
122a7ca44b8Syasuoka 	module_radius_init(&module_radius);
123a7ca44b8Syasuoka 	openlog(NULL, LOG_PID, LOG_DAEMON);
124a7ca44b8Syasuoka 
125a7ca44b8Syasuoka 	if ((module_radius.base = module_create(
126a7ca44b8Syasuoka 	    STDIN_FILENO, &module_radius, &module_radius_handlers)) == NULL)
127a7ca44b8Syasuoka 		err(1, "Could not create a module instance");
128edd79a0eSyasuoka 	module_drop_privilege(module_radius.base, 0);
1293d3cf35cSyasuoka 	setproctitle("[main]");
130a7ca44b8Syasuoka 
131a7ca44b8Syasuoka 	module_load(module_radius.base);
132d18e0374Syasuoka 	log_init(0);
133a7ca44b8Syasuoka 	event_init();
1340f904561Smmcc 
13504581dc7Syasuoka 	if (pledge("stdio inet", NULL) == -1)
13604581dc7Syasuoka 		err(EXIT_FAILURE, "pledge");
1370f904561Smmcc 
138a7ca44b8Syasuoka 	module_start(module_radius.base);
139a7ca44b8Syasuoka 	event_loop(0);
140a7ca44b8Syasuoka 
141c98ffa18Syasuoka 	module_destroy(module_radius.base);
142*c0c32a87Syasuoka 	event_base_free(NULL);
143c98ffa18Syasuoka 
144a7ca44b8Syasuoka 	exit(EXIT_SUCCESS);
145a7ca44b8Syasuoka }
146a7ca44b8Syasuoka 
147a7ca44b8Syasuoka static void
14858e9bc95Syasuoka module_radius_init(struct module_radius *module)
149a7ca44b8Syasuoka {
15058e9bc95Syasuoka 	memset(module, 0, sizeof(struct module_radius));
15158e9bc95Syasuoka 	TAILQ_INIT(&module->req);
152a7ca44b8Syasuoka }
153a7ca44b8Syasuoka 
154a7ca44b8Syasuoka static void
155a7ca44b8Syasuoka module_radius_config_set(void *ctx, const char *paramname, int paramvalc,
156a7ca44b8Syasuoka     char * const * paramvalv)
157a7ca44b8Syasuoka {
158a7ca44b8Syasuoka 	const char		*errmsg = NULL;
159a7ca44b8Syasuoka 	struct addrinfo		*res;
16058e9bc95Syasuoka 	struct module_radius	*module = ctx;
161a7ca44b8Syasuoka 
162a7ca44b8Syasuoka 	if (strcmp(paramname, "server") == 0) {
163a7ca44b8Syasuoka 		SYNTAX_ASSERT(paramvalc == 1,
164a7ca44b8Syasuoka 		    "`server' must have just one argument");
16558e9bc95Syasuoka 		SYNTAX_ASSERT(module->nserver < (int)nitems(module->server),
166a7ca44b8Syasuoka 		    "number of server reached limit");
167a7ca44b8Syasuoka 
168a7ca44b8Syasuoka 		if (addrport_parse(paramvalv[0], IPPROTO_UDP, &res) != 0)
169a7ca44b8Syasuoka 			SYNTAX_ASSERT(0, "could not parse address and port");
17058e9bc95Syasuoka 		memcpy(&module->server[module->nserver].addr, res->ai_addr,
171a7ca44b8Syasuoka 		    res->ai_addrlen);
172a7ca44b8Syasuoka 
17358e9bc95Syasuoka 		if (ntohs(module->server[module->nserver].addr.sin4.sin_port)
174a7ca44b8Syasuoka 		    == 0)
17558e9bc95Syasuoka 			module->server[module->nserver].addr.sin4.sin_port
176a7ca44b8Syasuoka 			    = htons(RADIUS_DEFAULT_PORT);
177a7ca44b8Syasuoka 
17858e9bc95Syasuoka 		module->server[module->nserver].sock = -1;
17958e9bc95Syasuoka 		module->nserver++;
180a7ca44b8Syasuoka 		freeaddrinfo(res);
181a7ca44b8Syasuoka 	} else if (strcmp(paramname, "request-timeout") == 0) {
182a7ca44b8Syasuoka 		SYNTAX_ASSERT(paramvalc == 1,
183a7ca44b8Syasuoka 		    "`request-timeout' must have just one argument");
18458e9bc95Syasuoka 		module->req_timeout = (int)strtonum(paramvalv[0], 0,
185a7ca44b8Syasuoka 		    UINT16_MAX, &errmsg);
18658e9bc95Syasuoka 		if (module->req_timeout == 0 && errmsg != NULL) {
18758e9bc95Syasuoka 			module_send_message(module->base, IMSG_NG,
188a7ca44b8Syasuoka 			    "`request-timeout must be 0-%d", UINT16_MAX);
189a7ca44b8Syasuoka 			return;
190a7ca44b8Syasuoka 		}
191a7ca44b8Syasuoka 	} else if (strcmp(paramname, "max-tries") == 0) {
192a7ca44b8Syasuoka 		SYNTAX_ASSERT(paramvalc == 1,
193a7ca44b8Syasuoka 		    "`max-tries' must have just one argument");
19458e9bc95Syasuoka 		module->max_tries = (int)strtonum(paramvalv[0], 0,
195a7ca44b8Syasuoka 		    UINT16_MAX, &errmsg);
19658e9bc95Syasuoka 		if (module->max_tries == 0 && errmsg != NULL) {
19758e9bc95Syasuoka 			module_send_message(module->base, IMSG_NG,
198a7ca44b8Syasuoka 			    "`max-tries must be 0-%d", UINT16_MAX);
199a7ca44b8Syasuoka 			return;
200a7ca44b8Syasuoka 		}
201a7ca44b8Syasuoka 
202a7ca44b8Syasuoka 	} else if (strcmp(paramname, "max-failovers") == 0) {
203a7ca44b8Syasuoka 		SYNTAX_ASSERT(paramvalc == 1,
204a7ca44b8Syasuoka 		    "`max-failovers' must have just one argument");
20558e9bc95Syasuoka 		module->max_failovers = (int)strtonum(paramvalv[0], 0,
206a7ca44b8Syasuoka 		    UINT16_MAX, &errmsg);
20758e9bc95Syasuoka 		if (module->max_failovers == 0 && errmsg != NULL) {
20858e9bc95Syasuoka 			module_send_message(module->base, IMSG_NG,
209a7ca44b8Syasuoka 			    "`max-failovers' must be 0-%d", UINT16_MAX);
210a7ca44b8Syasuoka 			return;
211a7ca44b8Syasuoka 		}
212a7ca44b8Syasuoka 	} else if (strcmp(paramname, "secret") == 0) {
213a7ca44b8Syasuoka 		SYNTAX_ASSERT(paramvalc == 1,
214a7ca44b8Syasuoka 		    "`secret' must have just one argument");
21558e9bc95Syasuoka 		if (strlcpy(module->secret, paramvalv[0],
21658e9bc95Syasuoka 		    sizeof(module->secret)) >= sizeof(module->secret)) {
21758e9bc95Syasuoka 			module_send_message(module->base, IMSG_NG,
218a7ca44b8Syasuoka 			    "`secret' length must be 0-%lu",
21958e9bc95Syasuoka 			    (u_long) sizeof(module->secret) - 1);
220a7ca44b8Syasuoka 			return;
221a7ca44b8Syasuoka 		}
22216971584Syasuoka 	} else if (strcmp(paramname, "_debug") == 0)
22316971584Syasuoka 		log_init(1);
22416971584Syasuoka 	else if (strncmp(paramname, "_", 1) == 0)
225c98ffa18Syasuoka 		/* nothing */; /* ignore all internal messages */
22616971584Syasuoka 	else {
22758e9bc95Syasuoka 		module_send_message(module->base, IMSG_NG,
228a7ca44b8Syasuoka 		    "Unknown config parameter name `%s'", paramname);
229a7ca44b8Syasuoka 		return;
230a7ca44b8Syasuoka 	}
23158e9bc95Syasuoka 	module_send_message(module->base, IMSG_OK, NULL);
232a7ca44b8Syasuoka 
233a7ca44b8Syasuoka 	return;
234a7ca44b8Syasuoka syntax_error:
23558e9bc95Syasuoka 	module_send_message(module->base, IMSG_NG, "%s", errmsg);
236a7ca44b8Syasuoka }
237a7ca44b8Syasuoka 
238a7ca44b8Syasuoka static void
239a7ca44b8Syasuoka module_radius_start(void *ctx)
240a7ca44b8Syasuoka {
241a7ca44b8Syasuoka 	u_int			 i;
24258e9bc95Syasuoka 	struct module_radius	*module = ctx;
243a7ca44b8Syasuoka 
24458e9bc95Syasuoka 	if (module->nserver <= 0) {
24558e9bc95Syasuoka 		module_send_message(module->base, IMSG_NG,
24616971584Syasuoka 			"needs one `server' at least");
247a7ca44b8Syasuoka 		return;
248a7ca44b8Syasuoka 	}
249a7ca44b8Syasuoka 
250583ffdd0Syasuoka 	if (module->secret[0] == '\0') {
251583ffdd0Syasuoka 		module_send_message(module->base, IMSG_NG,
252583ffdd0Syasuoka 		    "`secret' configuration is required");
253583ffdd0Syasuoka 		return;
254583ffdd0Syasuoka 	}
255583ffdd0Syasuoka 
25658e9bc95Syasuoka 	for (i = 0; i < module->nserver; i++) {
25758e9bc95Syasuoka 		module->server[i].module = module;
25858e9bc95Syasuoka 		if (radius_server_start(&module->server[i]) != 0) {
25958e9bc95Syasuoka 			module_send_message(module->base, IMSG_NG,
260a7ca44b8Syasuoka 				"module `radius' failed to start one of "
261a7ca44b8Syasuoka 				"the servers");
262a7ca44b8Syasuoka 			return;
263a7ca44b8Syasuoka 		}
264a7ca44b8Syasuoka 	}
26558e9bc95Syasuoka 	module_send_message(module->base, IMSG_OK, NULL);
266a7ca44b8Syasuoka 
26758e9bc95Syasuoka 	module_notify_secret(module->base, module->secret);
268a7ca44b8Syasuoka }
269a7ca44b8Syasuoka 
270a7ca44b8Syasuoka static void
271a7ca44b8Syasuoka module_radius_stop(void *ctx)
272a7ca44b8Syasuoka {
273a7ca44b8Syasuoka 	u_int				 i;
274a7ca44b8Syasuoka 	struct module_radius_req	*req, *treq;
27558e9bc95Syasuoka 	struct module_radius		*module = ctx;
276a7ca44b8Syasuoka 
2777eacecc0Syasuoka 	TAILQ_FOREACH_SAFE(req, &module->req, next, treq)
278a7ca44b8Syasuoka 		module_radius_req_on_failure(req);
279a7ca44b8Syasuoka 
28058e9bc95Syasuoka 	for (i = 0; i < module->nserver; i++)
28158e9bc95Syasuoka 		radius_server_stop(&module->server[i]);
282a7ca44b8Syasuoka }
283a7ca44b8Syasuoka 
284a7ca44b8Syasuoka static void
285a7ca44b8Syasuoka module_radius_access_request(void *ctx, u_int q_id, const u_char *pkt,
286a7ca44b8Syasuoka     size_t pktlen)
287a7ca44b8Syasuoka {
28858e9bc95Syasuoka 	struct module_radius		*module = ctx;
289a7ca44b8Syasuoka 	struct module_radius_req	*req;
290a7ca44b8Syasuoka 	u_char				 attrbuf[256];
291a7ca44b8Syasuoka 	ssize_t				 attrlen;
292a7ca44b8Syasuoka 
2931a710b53Syasuoka 	req = calloc(1, sizeof(struct module_radius_req));
294a7ca44b8Syasuoka 	if (req == NULL) {
29558e9bc95Syasuoka 		module_radius_log(module, LOG_WARNING,
296a7ca44b8Syasuoka 		    "%s: Out of memory: %m", __func__);
297a7ca44b8Syasuoka 		goto on_fail;
298a7ca44b8Syasuoka 	}
299a7ca44b8Syasuoka 
300a7ca44b8Syasuoka 	req->ntry = 0;
30158e9bc95Syasuoka 	req->module = module;
302a7ca44b8Syasuoka 	req->q_id = q_id;
303a7ca44b8Syasuoka 	if ((req->q_pkt = radius_convert_packet(pkt, pktlen)) == NULL) {
30458e9bc95Syasuoka 		module_radius_log(module, LOG_WARNING,
305a7ca44b8Syasuoka 		    "%s: radius_convert_packet() failed: %m", __func__);
306a7ca44b8Syasuoka 		goto on_fail;
307a7ca44b8Syasuoka 	}
308a7ca44b8Syasuoka 	evtimer_set(&req->ev, module_radius_req_on_timeout, req);
309a7ca44b8Syasuoka 	TAILQ_INSERT_TAIL(&req->module->req, req, next);
310a7ca44b8Syasuoka 
311a7ca44b8Syasuoka 	/*
312a7ca44b8Syasuoka 	 * radiusd decrypt User-Password attribute.  crypt it again with our
313a7ca44b8Syasuoka 	 * secret.
314a7ca44b8Syasuoka 	 */
315a7ca44b8Syasuoka 	attrlen = sizeof(attrbuf);
316583ffdd0Syasuoka 	if (radius_get_raw_attr(req->q_pkt, RADIUS_TYPE_USER_PASSWORD,
317a7ca44b8Syasuoka 		    attrbuf, &attrlen) == 0) {
318a7ca44b8Syasuoka 		attrbuf[attrlen] = '\0';
319a7ca44b8Syasuoka 		radius_del_attr_all(req->q_pkt, RADIUS_TYPE_USER_PASSWORD);
320a7ca44b8Syasuoka 		radius_put_user_password_attr(req->q_pkt, attrbuf,
32158e9bc95Syasuoka 		    module->secret);
322a7ca44b8Syasuoka 	}
323a7ca44b8Syasuoka 
324a7ca44b8Syasuoka 	/* select current server */
325a7ca44b8Syasuoka 	module_radius_req_select_server(req);
326a7ca44b8Syasuoka 
327a7ca44b8Syasuoka 	module_radius_req_send(req);
328a7ca44b8Syasuoka 
329a7ca44b8Syasuoka 	return;
330a7ca44b8Syasuoka 
331a7ca44b8Syasuoka on_fail:
332a7ca44b8Syasuoka 	free(req);
33358e9bc95Syasuoka 	module_accsreq_aborted(module->base, q_id);
334a7ca44b8Syasuoka }
335a7ca44b8Syasuoka 
336a7ca44b8Syasuoka /*
337a7ca44b8Syasuoka  * radius_server
338a7ca44b8Syasuoka  */
339a7ca44b8Syasuoka static int
3409c2d9512Syasuoka radius_server_start(struct radius_server *server)
341a7ca44b8Syasuoka {
342a7ca44b8Syasuoka 	socklen_t	 locallen;
343a7ca44b8Syasuoka 	char		 buf0[NI_MAXHOST + NI_MAXSERV + 32];
344a7ca44b8Syasuoka 	char		 buf1[NI_MAXHOST + NI_MAXSERV + 32];
345a7ca44b8Syasuoka 
3465f13b87eSguenther 	if ((server->sock = socket(AF_INET, SOCK_DGRAM | SOCK_NONBLOCK, 0))
347df69c215Sderaadt 	    == -1) {
3489c2d9512Syasuoka 		module_radius_log(server->module, LOG_WARNING,
3499c2d9512Syasuoka 		    "%s: socket() failed", __func__);
350a7ca44b8Syasuoka 		goto on_error;
351a7ca44b8Syasuoka 	}
3529c2d9512Syasuoka 	if (connect(server->sock, (struct sockaddr *)&server->addr,
3539c2d9512Syasuoka 		server->addr.sin4.sin_len) != 0) {
3549c2d9512Syasuoka 		module_radius_log(server->module, LOG_WARNING,
3559c2d9512Syasuoka 		    "%s: connect to %s failed", __func__,
3569c2d9512Syasuoka 		    addrport_tostring((struct sockaddr *)&server->addr,
3579c2d9512Syasuoka 			server->addr.sin4.sin_len, buf1, sizeof(buf1)));
358a7ca44b8Syasuoka 		goto on_error;
359a7ca44b8Syasuoka 	}
3609c2d9512Syasuoka 	locallen = sizeof(server->local);
3619c2d9512Syasuoka 	if (getsockname(server->sock, (struct sockaddr *)&server->local,
362a7ca44b8Syasuoka 	    &locallen) != 0) {
3639c2d9512Syasuoka 		module_radius_log(server->module, LOG_WARNING,
3649c2d9512Syasuoka 		    "%s: getsockanme() failed", __func__);
365a7ca44b8Syasuoka 		goto on_error;
366a7ca44b8Syasuoka 	}
3679c2d9512Syasuoka 	module_radius_log(server->module, LOG_INFO,
368a7ca44b8Syasuoka 	    "Use %s to send requests for %s",
3699c2d9512Syasuoka 	    addrport_tostring((struct sockaddr *)&server->local,
370a7ca44b8Syasuoka 		    locallen, buf0, sizeof(buf0)),
3719c2d9512Syasuoka 	    addrport_tostring((struct sockaddr *)&server->addr,
3729c2d9512Syasuoka 		    server->addr.sin4.sin_len, buf1, sizeof(buf1)));
373a7ca44b8Syasuoka 
3749c2d9512Syasuoka 	event_set(&server->ev, server->sock, EV_READ | EV_PERSIST,
3759c2d9512Syasuoka 	    radius_server_on_event, server);
3769c2d9512Syasuoka 	if (event_add(&server->ev, NULL)) {
3779c2d9512Syasuoka 		module_radius_log(server->module, LOG_WARNING,
3789c2d9512Syasuoka 		    "%s: event_add() failed", __func__);
379a7ca44b8Syasuoka 		goto on_error;
380a7ca44b8Syasuoka 	}
381a7ca44b8Syasuoka 
382a7ca44b8Syasuoka 	return (0);
383a7ca44b8Syasuoka on_error:
3849c2d9512Syasuoka 	if (server->sock >= 0)
3859c2d9512Syasuoka 		close(server->sock);
3869c2d9512Syasuoka 	server->sock = -1;
387a7ca44b8Syasuoka 	return (-1);
388a7ca44b8Syasuoka }
389a7ca44b8Syasuoka 
390a7ca44b8Syasuoka static void
39174683484Syasuoka radius_server_stop(struct radius_server *server)
392a7ca44b8Syasuoka {
39374683484Syasuoka 	event_del(&server->ev);
39474683484Syasuoka 	if (server->sock >= 0)
39574683484Syasuoka 		close(server->sock);
39674683484Syasuoka 	server->sock = -1;
397a7ca44b8Syasuoka }
398a7ca44b8Syasuoka 
399a7ca44b8Syasuoka static void
400a7ca44b8Syasuoka radius_server_on_event(int fd, short evmask, void *ctx)
401a7ca44b8Syasuoka {
402a7ca44b8Syasuoka 	int				 sz, res_id;
403a7ca44b8Syasuoka 	u_char				 pkt[65535];
404a7ca44b8Syasuoka 	char				 buf[NI_MAXHOST + NI_MAXSERV + 32];
40574683484Syasuoka 	struct radius_server		*server = ctx;
406a7ca44b8Syasuoka 	RADIUS_PACKET			*radpkt = NULL;
407a7ca44b8Syasuoka 	struct module_radius_req	*req;
408a7ca44b8Syasuoka 	struct sockaddr			*peer;
409a7ca44b8Syasuoka 
41074683484Syasuoka 	peer = (struct sockaddr *)&server->addr;
41151da3916Syasuoka 	if ((sz = recv(server->sock, pkt, sizeof(pkt), 0)) == -1) {
41251da3916Syasuoka 		if (errno == EAGAIN)
41351da3916Syasuoka 			return;
41474683484Syasuoka 		module_radius_log(server->module, LOG_WARNING,
415a7ca44b8Syasuoka 		    "server=%s recv() failed: %m",
416a7ca44b8Syasuoka 		    addrport_tostring(peer, peer->sa_len, buf, sizeof(buf)));
417a7ca44b8Syasuoka 		return;
418a7ca44b8Syasuoka 	}
419a7ca44b8Syasuoka 	if ((radpkt = radius_convert_packet(pkt, sz)) == NULL) {
42074683484Syasuoka 		module_radius_log(server->module, LOG_WARNING,
421a7ca44b8Syasuoka 		    "server=%s could not convert the received message to a "
422a7ca44b8Syasuoka 		    "RADIUS packet object: %m",
423a7ca44b8Syasuoka 		    addrport_tostring(peer, peer->sa_len, buf, sizeof(buf)));
424a7ca44b8Syasuoka 		return;
425a7ca44b8Syasuoka 	}
426a7ca44b8Syasuoka 	res_id = radius_get_id(radpkt);
42774683484Syasuoka 	TAILQ_FOREACH(req, &server->module->req, next) {
42874683484Syasuoka 		if (req->server == server && req->req_id == res_id)
429a7ca44b8Syasuoka 			break;
430a7ca44b8Syasuoka 	}
431a7ca44b8Syasuoka 	if (req == NULL) {
43274683484Syasuoka 		module_radius_log(server->module, LOG_WARNING,
433a7ca44b8Syasuoka 		    "server=%s Received radius message has unknown id=%d",
434a7ca44b8Syasuoka 		    addrport_tostring(peer, peer->sa_len, buf, sizeof(buf)),
435a7ca44b8Syasuoka 		    res_id);
436a7ca44b8Syasuoka 		goto out;
437a7ca44b8Syasuoka 	}
438a7ca44b8Syasuoka 	radius_set_request_packet(radpkt, req->q_pkt);
439a7ca44b8Syasuoka 
440a7ca44b8Syasuoka 	if (radius_check_response_authenticator(radpkt,
44174683484Syasuoka 	    server->module->secret) != 0) {
44274683484Syasuoka 		module_radius_log(server->module, LOG_WARNING,
443a7ca44b8Syasuoka 		    "server=%s Received radius message(id=%d) has bad "
444a7ca44b8Syasuoka 		    "authenticator",
445a7ca44b8Syasuoka 		    addrport_tostring(peer, peer->sa_len, buf,
446a7ca44b8Syasuoka 		    sizeof(buf)), res_id);
447a7ca44b8Syasuoka 		goto out;
448a7ca44b8Syasuoka 	}
449a7ca44b8Syasuoka 	if (radius_has_attr(radpkt,
450a7ca44b8Syasuoka 	    RADIUS_TYPE_MESSAGE_AUTHENTICATOR) &&
451a7ca44b8Syasuoka 	    radius_check_message_authenticator(radpkt,
45274683484Syasuoka 		    server->module->secret) != 0) {
45374683484Syasuoka 		module_radius_log(server->module, LOG_WARNING,
454a7ca44b8Syasuoka 		    "server=%s Received radius message(id=%d) has bad "
455a7ca44b8Syasuoka 		    "message authenticator",
456a7ca44b8Syasuoka 		    addrport_tostring(peer, peer->sa_len, buf,
457a7ca44b8Syasuoka 		    sizeof(buf)), res_id);
458a7ca44b8Syasuoka 		goto out;
459a7ca44b8Syasuoka 	}
460a7ca44b8Syasuoka 
46174683484Syasuoka 	module_radius_log(server->module, LOG_INFO,
462a7ca44b8Syasuoka 	    "q=%u received a response from server %s", req->q_id,
463a7ca44b8Syasuoka 	    addrport_tostring(peer, peer->sa_len, buf, sizeof(buf)));
464a7ca44b8Syasuoka 
465a7ca44b8Syasuoka 	module_radius_req_on_success(req, radius_get_data(radpkt),
466a7ca44b8Syasuoka 	    radius_get_length(radpkt));
467a7ca44b8Syasuoka out:
468a7ca44b8Syasuoka 	if (radpkt != NULL)
469a7ca44b8Syasuoka 		radius_delete_packet(radpkt);
470a7ca44b8Syasuoka }
471a7ca44b8Syasuoka 
472a7ca44b8Syasuoka static void
47374683484Syasuoka radius_server_on_fail(struct radius_server *server, const char *failmsg)
474a7ca44b8Syasuoka {
475a7ca44b8Syasuoka 	char		 buf0[NI_MAXHOST + NI_MAXSERV + 32];
476a7ca44b8Syasuoka 	char		 buf1[NI_MAXHOST + NI_MAXSERV + 32];
477a7ca44b8Syasuoka 	struct sockaddr	*caddr, *naddr;
478a7ca44b8Syasuoka 
47974683484Syasuoka 	caddr = (struct sockaddr *)&server->addr;
48074683484Syasuoka 	if (server->module->nserver <= 1) {
48174683484Syasuoka 		module_radius_log(server->module, LOG_WARNING,
482a7ca44b8Syasuoka 		    "Server %s failed: %s",
483a7ca44b8Syasuoka 		    addrport_tostring(caddr, caddr->sa_len, buf0, sizeof(buf0)),
484a7ca44b8Syasuoka 		    failmsg);
485a7ca44b8Syasuoka 		return;
486a7ca44b8Syasuoka 	}
48774683484Syasuoka 	server->module->curr_server++;
48874683484Syasuoka 	server->module->curr_server %= server->module->nserver;
489a7ca44b8Syasuoka 	naddr = (struct sockaddr *)
49074683484Syasuoka 	    &server->module->server[server->module->curr_server].addr;
491a7ca44b8Syasuoka 
49274683484Syasuoka 	module_radius_log(server->module, LOG_WARNING,
493a7ca44b8Syasuoka 	    "Server %s failed: %s  Fail over to %s",
494a7ca44b8Syasuoka 	    addrport_tostring(caddr, caddr->sa_len, buf0, sizeof(buf0)),
495a7ca44b8Syasuoka 	    failmsg,
496a7ca44b8Syasuoka 	    addrport_tostring(naddr, naddr->sa_len, buf1, sizeof(buf1)));
497a7ca44b8Syasuoka }
498a7ca44b8Syasuoka 
499a7ca44b8Syasuoka /* module_radius_req */
500a7ca44b8Syasuoka 
501a7ca44b8Syasuoka static void
502a7ca44b8Syasuoka module_radius_req_send(struct module_radius_req *req)
503a7ca44b8Syasuoka {
504a7ca44b8Syasuoka 	int		 sz;
505a7ca44b8Syasuoka 	struct sockaddr	*peer;
506a7ca44b8Syasuoka 	char		 msg[BUFSIZ];
507a7ca44b8Syasuoka 
508a7ca44b8Syasuoka 	peer = (struct sockaddr *)&req->server->addr;
509a7ca44b8Syasuoka 	if ((sz = send(req->server->sock, radius_get_data(req->q_pkt),
510a7ca44b8Syasuoka 	    radius_get_length(req->q_pkt), 0)) < 0) {
511a7ca44b8Syasuoka 		module_radius_log(req->module, LOG_WARNING,
512a7ca44b8Syasuoka 		    "Sending RADIUS query q=%u to %s failed: %m",
513a7ca44b8Syasuoka 		    req->q_id,
514a7ca44b8Syasuoka 		    addrport_tostring(peer, peer->sa_len, msg, sizeof(msg)));
515a7ca44b8Syasuoka 		/* retry anyway */
516a7ca44b8Syasuoka 	}
517a7ca44b8Syasuoka 	module_radius_log(req->module, LOG_INFO,
518a7ca44b8Syasuoka 	    "Send RADIUS query q=%u id=%d to %s successfully",
519a7ca44b8Syasuoka 	    req->q_id, req->req_id,
520a7ca44b8Syasuoka 	    addrport_tostring(peer, peer->sa_len, msg, sizeof(msg)));
521a7ca44b8Syasuoka 	if (module_radius_req_reset_event(req) != -1)
522a7ca44b8Syasuoka 		req->ntry++;
523a7ca44b8Syasuoka }
524a7ca44b8Syasuoka 
525a7ca44b8Syasuoka static int
526a7ca44b8Syasuoka module_radius_req_reset_event(struct module_radius_req *req)
527a7ca44b8Syasuoka {
528a7ca44b8Syasuoka 	struct timeval	 tv;
529a7ca44b8Syasuoka 	static int	 timeouts[] = { 2, 4, 8 };
530a7ca44b8Syasuoka 
531a7ca44b8Syasuoka 	tv.tv_usec = 0;
532a7ca44b8Syasuoka 	if (req->module->req_timeout != 0)
533a7ca44b8Syasuoka 		tv.tv_sec = req->module->req_timeout;
534a7ca44b8Syasuoka 	else {
535a7ca44b8Syasuoka 		if (req->ntry < nitems(timeouts))
536a7ca44b8Syasuoka 			tv.tv_sec = timeouts[req->ntry];
537a7ca44b8Syasuoka 		else
538a7ca44b8Syasuoka 			tv.tv_sec = timeouts[nitems(timeouts) - 1];
539a7ca44b8Syasuoka 	}
540a7ca44b8Syasuoka 	if (evtimer_add(&req->ev, &tv) != 0) {
541a7ca44b8Syasuoka 		module_radius_log(req->module, LOG_WARNING,
5423a50f0a9Sjmc 		    "Cannot process the request for q=%u: "
543a7ca44b8Syasuoka 		    "evtimer_add() failed: %m", req->q_id);
544a7ca44b8Syasuoka 		module_radius_req_on_failure(req);
545a7ca44b8Syasuoka 		return (-1);
546a7ca44b8Syasuoka 	}
547a7ca44b8Syasuoka 	return (0);
548a7ca44b8Syasuoka }
549a7ca44b8Syasuoka 
550a7ca44b8Syasuoka static void
551a7ca44b8Syasuoka module_radius_req_on_timeout(int fd, short evmask, void *ctx)
552a7ca44b8Syasuoka {
553a7ca44b8Syasuoka 	struct module_radius_req	*req = ctx;
554a7ca44b8Syasuoka 	char				 msg[BUFSIZ];
555a7ca44b8Syasuoka 
556a7ca44b8Syasuoka 
557a7ca44b8Syasuoka 	if (req->module->max_tries <= req->ntry) {
558a7ca44b8Syasuoka 		snprintf(msg, sizeof(msg), "q=%u didn't response RADIUS query "
559a7ca44b8Syasuoka 		    "%d time%s", req->q_id, req->ntry,
560a7ca44b8Syasuoka 		    (req->ntry > 0)? "s" : "");
561a7ca44b8Syasuoka 		radius_server_on_fail(req->server, msg);
562a7ca44b8Syasuoka 		if (++req->nfailover >= req->module->max_failovers) {
563a7ca44b8Syasuoka 			module_radius_log(req->module,
564a7ca44b8Syasuoka 			    LOG_WARNING, "RADIUS query q=%u time out",
565a7ca44b8Syasuoka 			    req->q_id);
566a7ca44b8Syasuoka 			module_radius_req_on_failure(req);
567a7ca44b8Syasuoka 			return;
568a7ca44b8Syasuoka 		}
569a7ca44b8Syasuoka 		/* select the next server */
570a7ca44b8Syasuoka 		module_radius_req_select_server(req);
571a7ca44b8Syasuoka 	}
572a7ca44b8Syasuoka 	module_radius_req_send(req);
573a7ca44b8Syasuoka }
574a7ca44b8Syasuoka 
575a7ca44b8Syasuoka static void
576a7ca44b8Syasuoka module_radius_req_on_success(struct module_radius_req *req,
577a7ca44b8Syasuoka     const u_char *pkt, size_t pktlen)
578a7ca44b8Syasuoka {
579d7548b59Syasuoka 	module_accsreq_answer(req->module->base, req->q_id, pkt, pktlen);
580a7ca44b8Syasuoka 	module_radius_req_free(req);
581a7ca44b8Syasuoka }
582a7ca44b8Syasuoka 
583a7ca44b8Syasuoka static void
584a7ca44b8Syasuoka module_radius_req_on_failure(struct module_radius_req *req)
585a7ca44b8Syasuoka {
586a7ca44b8Syasuoka 	module_accsreq_aborted(req->module->base, req->q_id);
587a7ca44b8Syasuoka 	module_radius_req_free(req);
588a7ca44b8Syasuoka }
589a7ca44b8Syasuoka 
590a7ca44b8Syasuoka 
591a7ca44b8Syasuoka static void
592a7ca44b8Syasuoka module_radius_req_free(struct module_radius_req *req)
593a7ca44b8Syasuoka {
594a7ca44b8Syasuoka 	evtimer_del(&req->ev);
595a7ca44b8Syasuoka 	TAILQ_REMOVE(&req->module->req, req, next);
596a7ca44b8Syasuoka 	if (req->q_pkt != NULL)
597a7ca44b8Syasuoka 		radius_delete_packet(req->q_pkt);
598a7ca44b8Syasuoka 	free(req);
599a7ca44b8Syasuoka }
600a7ca44b8Syasuoka 
601a7ca44b8Syasuoka static void
602a7ca44b8Syasuoka module_radius_req_select_server(struct module_radius_req *req)
603a7ca44b8Syasuoka {
604a7ca44b8Syasuoka 	req->server = &req->module->server[req->module->curr_server];
605a7ca44b8Syasuoka 	req->ntry = 0;
606a7ca44b8Syasuoka 	req->req_id = req->server->req_id_seq++;
607a7ca44b8Syasuoka 	radius_set_id(req->q_pkt, req->req_id);
608a7ca44b8Syasuoka 	module_radius_req_reset_msgauth(req);
609a7ca44b8Syasuoka }
610a7ca44b8Syasuoka 
611a7ca44b8Syasuoka static void
612a7ca44b8Syasuoka module_radius_req_reset_msgauth(struct module_radius_req *req)
613a7ca44b8Syasuoka {
614a7ca44b8Syasuoka 	if (radius_has_attr(req->q_pkt, RADIUS_TYPE_MESSAGE_AUTHENTICATOR))
615a7ca44b8Syasuoka 		radius_del_attr_all(req->q_pkt,
616a7ca44b8Syasuoka 		    RADIUS_TYPE_MESSAGE_AUTHENTICATOR);
61755e25771Syasuoka 	radius_put_message_authenticator(req->q_pkt, req->module->secret);
618a7ca44b8Syasuoka }
619a7ca44b8Syasuoka 
620a7ca44b8Syasuoka static void
62158e9bc95Syasuoka module_radius_log(struct module_radius *module, int pri, const char *fmt, ...)
622a7ca44b8Syasuoka {
623a7ca44b8Syasuoka 	char		fmt0[BUFSIZ];
624a7ca44b8Syasuoka 	va_list		va;
625a7ca44b8Syasuoka 
626a7ca44b8Syasuoka 	snprintf(fmt0, sizeof(fmt0), "radius: %s", fmt);
627a7ca44b8Syasuoka 	va_start(va, fmt);
628a7ca44b8Syasuoka 	vlog(pri, fmt0, va);
629a7ca44b8Syasuoka 	va_end(va);
630a7ca44b8Syasuoka }
631