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