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