1*abc3a9caSyasuoka /* $OpenBSD: radiusd.c,v 1.62 2025/01/29 10:12:22 yasuoka Exp $ */ 2a7ca44b8Syasuoka 3a7ca44b8Syasuoka /* 4237e61d9Syasuoka * Copyright (c) 2013, 2023 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> 20237e61d9Syasuoka #include <netinet/in.h> 21237e61d9Syasuoka #include <arpa/inet.h> 22a7ca44b8Syasuoka #include <sys/queue.h> 23237e61d9Syasuoka #include <sys/socket.h> 24237e61d9Syasuoka #include <sys/time.h> 25a7ca44b8Syasuoka #include <sys/uio.h> 26a7ca44b8Syasuoka #include <sys/wait.h> 27a7ca44b8Syasuoka 28a7ca44b8Syasuoka #include <err.h> 29a7ca44b8Syasuoka #include <errno.h> 30a7ca44b8Syasuoka #include <event.h> 31ab3cfbd9Syasuoka #include <fcntl.h> 32a7ca44b8Syasuoka #include <fnmatch.h> 33a7ca44b8Syasuoka #include <imsg.h> 34a7ca44b8Syasuoka #include <netdb.h> 358c9be245Syasuoka #include <paths.h> 36a7ca44b8Syasuoka #include <pwd.h> 37a7ca44b8Syasuoka #include <signal.h> 38a7ca44b8Syasuoka #include <stdbool.h> 39a7ca44b8Syasuoka #include <stdio.h> 40a7ca44b8Syasuoka #include <stdlib.h> 41a7ca44b8Syasuoka #include <string.h> 42a7ca44b8Syasuoka #include <syslog.h> 43a7ca44b8Syasuoka #include <unistd.h> 44a7ca44b8Syasuoka 45a7ca44b8Syasuoka #include <radius.h> 46a7ca44b8Syasuoka 47a7ca44b8Syasuoka #include "radiusd.h" 48a7ca44b8Syasuoka #include "radiusd_local.h" 49a6eb37b5Syasuoka #include "radius_subr.h" 50a7ca44b8Syasuoka #include "log.h" 51a7ca44b8Syasuoka #include "util.h" 52a7ca44b8Syasuoka #include "imsg_subr.h" 53842565f2Syasuoka #include "control.h" 54a7ca44b8Syasuoka 55a7ca44b8Syasuoka static int radiusd_start(struct radiusd *); 56a7ca44b8Syasuoka static void radiusd_stop(struct radiusd *); 57a7ca44b8Syasuoka static void radiusd_free(struct radiusd *); 58a7ca44b8Syasuoka static void radiusd_listen_on_event(int, short, void *); 59936475aaSyasuoka static void radiusd_listen_handle_packet(struct radiusd_listen *, 60936475aaSyasuoka RADIUS_PACKET *, struct sockaddr *, socklen_t); 61a7ca44b8Syasuoka static void radiusd_on_sigterm(int, short, void *); 62a7ca44b8Syasuoka static void radiusd_on_sigint(int, short, void *); 63a7ca44b8Syasuoka static void radiusd_on_sighup(int, short, void *); 64a7ca44b8Syasuoka static void radiusd_on_sigchld(int, short, void *); 65936475aaSyasuoka static void raidus_query_access_request(struct radius_query *); 66936475aaSyasuoka static void radius_query_access_response(struct radius_query *); 67747da5e9Syasuoka static void raidus_query_accounting_request( 68747da5e9Syasuoka struct radiusd_accounting *, struct radius_query *); 69ed1dc925Syasuoka static void radius_query_accounting_response( 70ed1dc925Syasuoka struct radius_query *); 71ed1dc925Syasuoka static const char *radius_query_client_secret(struct radius_query *); 72a7ca44b8Syasuoka static const char *radius_code_string(int); 73747da5e9Syasuoka static const char *radius_acct_status_type_string(uint32_t); 74ed1dc925Syasuoka static int radiusd_access_response_fixup(struct radius_query *, 75ed1dc925Syasuoka struct radius_query *, bool); 76a7ca44b8Syasuoka static void radiusd_module_reset_ev_handler( 77a7ca44b8Syasuoka struct radiusd_module *); 784a4dce94Syasuoka static int radiusd_module_imsg_read(struct radiusd_module *); 79a7ca44b8Syasuoka static void radiusd_module_imsg(struct radiusd_module *, 80a7ca44b8Syasuoka struct imsg *); 81a7ca44b8Syasuoka 82a7ca44b8Syasuoka static struct radiusd_module_radpkt_arg * 83a7ca44b8Syasuoka radiusd_module_recv_radpkt(struct radiusd_module *, 84a7ca44b8Syasuoka struct imsg *, uint32_t, const char *); 85a7ca44b8Syasuoka static void radiusd_module_on_imsg_io(int, short, void *); 8627a2e2e1Syasuoka static void radiusd_module_start(struct radiusd_module *); 8727a2e2e1Syasuoka static void radiusd_module_stop(struct radiusd_module *); 88a7ca44b8Syasuoka static void radiusd_module_close(struct radiusd_module *); 89a7ca44b8Syasuoka static void radiusd_module_userpass(struct radiusd_module *, 90a7ca44b8Syasuoka struct radius_query *); 91a7ca44b8Syasuoka static void radiusd_module_access_request(struct radiusd_module *, 92a7ca44b8Syasuoka struct radius_query *); 93ed1dc925Syasuoka static void radiusd_module_next_response(struct radiusd_module *, 94ed1dc925Syasuoka struct radius_query *, RADIUS_PACKET *); 95237e61d9Syasuoka static void radiusd_module_request_decoration( 96237e61d9Syasuoka struct radiusd_module *, struct radius_query *); 97237e61d9Syasuoka static void radiusd_module_response_decoration( 98237e61d9Syasuoka struct radiusd_module *, struct radius_query *); 99747da5e9Syasuoka static void radiusd_module_account_request(struct radiusd_module *, 100747da5e9Syasuoka struct radius_query *); 101237e61d9Syasuoka static int imsg_compose_radius_packet(struct imsgbuf *, 102237e61d9Syasuoka uint32_t, u_int, RADIUS_PACKET *); 103747da5e9Syasuoka static void close_stdio(void); 104a7ca44b8Syasuoka 105a7ca44b8Syasuoka static u_int radius_query_id_seq = 0; 106a7ca44b8Syasuoka int debug = 0; 107842565f2Syasuoka struct radiusd *radiusd_s = NULL; 108a7ca44b8Syasuoka 109a7ca44b8Syasuoka static __dead void 110a7ca44b8Syasuoka usage(void) 111a7ca44b8Syasuoka { 112a7ca44b8Syasuoka extern char *__progname; 113a7ca44b8Syasuoka 1140630f070Syasuoka fprintf(stderr, "usage: %s [-dn] [-f file]\n", __progname); 115ef9ad095Smillert exit(EXIT_FAILURE); 116a7ca44b8Syasuoka } 117a7ca44b8Syasuoka 118a7ca44b8Syasuoka int 119a7ca44b8Syasuoka main(int argc, char *argv[]) 120a7ca44b8Syasuoka { 121a7ca44b8Syasuoka extern char *__progname; 122a7ca44b8Syasuoka const char *conffile = CONFFILE; 12343436be7Syasuoka int ch, error; 124a7ca44b8Syasuoka struct radiusd *radiusd; 125a7ca44b8Syasuoka bool noaction = false; 126a7ca44b8Syasuoka struct passwd *pw; 127a7ca44b8Syasuoka 1280630f070Syasuoka while ((ch = getopt(argc, argv, "df:n")) != -1) 129a7ca44b8Syasuoka switch (ch) { 130a7ca44b8Syasuoka case 'd': 131a7ca44b8Syasuoka debug++; 132a7ca44b8Syasuoka break; 133a7ca44b8Syasuoka 134a7ca44b8Syasuoka case 'f': 135a7ca44b8Syasuoka conffile = optarg; 136a7ca44b8Syasuoka break; 137a7ca44b8Syasuoka 138a7ca44b8Syasuoka case 'n': 139a7ca44b8Syasuoka noaction = true; 140a7ca44b8Syasuoka break; 141a7ca44b8Syasuoka 1420630f070Syasuoka default: 143a7ca44b8Syasuoka usage(); 144a7ca44b8Syasuoka /* NOTREACHED */ 145a7ca44b8Syasuoka } 146a7ca44b8Syasuoka 147a7ca44b8Syasuoka argc -= optind; 148a7ca44b8Syasuoka argv += optind; 149a7ca44b8Syasuoka 150a7ca44b8Syasuoka if (argc != 0) 151a7ca44b8Syasuoka usage(); 152a7ca44b8Syasuoka 153a7ca44b8Syasuoka if ((radiusd = calloc(1, sizeof(*radiusd))) == NULL) 154a7ca44b8Syasuoka err(1, "calloc"); 155842565f2Syasuoka radiusd_s = radiusd; 156a7ca44b8Syasuoka TAILQ_INIT(&radiusd->listen); 157a7ca44b8Syasuoka TAILQ_INIT(&radiusd->query); 158a7ca44b8Syasuoka 1598c9be245Syasuoka if (!noaction && debug == 0) 1608c9be245Syasuoka daemon(0, 1); /* pend closing stdio files */ 1618c9be245Syasuoka 162a7ca44b8Syasuoka if (parse_config(conffile, radiusd) != 0) 163ef9ad095Smillert errx(EXIT_FAILURE, "config error"); 164c917c3caSyasuoka log_init(debug); 165a7ca44b8Syasuoka if (noaction) { 166a7ca44b8Syasuoka fprintf(stderr, "configuration OK\n"); 167a7ca44b8Syasuoka exit(EXIT_SUCCESS); 168a7ca44b8Syasuoka } 169a7ca44b8Syasuoka 170a7ca44b8Syasuoka if (debug == 0) 1718c9be245Syasuoka close_stdio(); /* close stdio files now */ 1728c9be245Syasuoka 173842565f2Syasuoka if (control_init(RADIUSD_SOCK) == -1) 174842565f2Syasuoka exit(EXIT_FAILURE); 175842565f2Syasuoka 176c469475aSyasuoka event_init(); 177a7ca44b8Syasuoka 178a7ca44b8Syasuoka if ((pw = getpwnam(RADIUSD_USER)) == NULL) 179a7ca44b8Syasuoka errx(EXIT_FAILURE, "user `%s' is not found in password " 180a7ca44b8Syasuoka "database", RADIUSD_USER); 181a7ca44b8Syasuoka 182a7ca44b8Syasuoka if (chroot(pw->pw_dir) == -1) 183a7ca44b8Syasuoka err(EXIT_FAILURE, "chroot"); 184a7ca44b8Syasuoka if (chdir("/") == -1) 185a7ca44b8Syasuoka err(EXIT_FAILURE, "chdir(\"/\")"); 186a7ca44b8Syasuoka 187a7ca44b8Syasuoka if (setgroups(1, &pw->pw_gid) || 188a7ca44b8Syasuoka setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 189a7ca44b8Syasuoka setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 190a7ca44b8Syasuoka err(EXIT_FAILURE, "cannot drop privileges"); 191a7ca44b8Syasuoka 192a7ca44b8Syasuoka signal(SIGPIPE, SIG_IGN); 193a7ca44b8Syasuoka openlog(NULL, LOG_PID, LOG_DAEMON); 194a7ca44b8Syasuoka 195a7ca44b8Syasuoka signal_set(&radiusd->ev_sigterm, SIGTERM, radiusd_on_sigterm, radiusd); 196a7ca44b8Syasuoka signal_set(&radiusd->ev_sigint, SIGINT, radiusd_on_sigint, radiusd); 197a7ca44b8Syasuoka signal_set(&radiusd->ev_sighup, SIGHUP, radiusd_on_sighup, radiusd); 198a7ca44b8Syasuoka signal_set(&radiusd->ev_sigchld, SIGCHLD, radiusd_on_sigchld, radiusd); 199a7ca44b8Syasuoka 200a7ca44b8Syasuoka if (radiusd_start(radiusd) != 0) 201ef9ad095Smillert errx(EXIT_FAILURE, "start failed"); 202842565f2Syasuoka if (control_listen() == -1) 203842565f2Syasuoka exit(EXIT_FAILURE); 204a7ca44b8Syasuoka 20504581dc7Syasuoka if (pledge("stdio inet", NULL) == -1) 20604581dc7Syasuoka err(EXIT_FAILURE, "pledge"); 20704581dc7Syasuoka 208842565f2Syasuoka event_loop(0); 209a7ca44b8Syasuoka 21059396270Syasuoka if (radiusd->error != 0) 21159396270Syasuoka log_warnx("exiting on error"); 21259396270Syasuoka 213842565f2Syasuoka radiusd_stop(radiusd); 214842565f2Syasuoka control_cleanup(); 215842565f2Syasuoka 216842565f2Syasuoka event_loop(0); 217842565f2Syasuoka 21843436be7Syasuoka error = radiusd->error; 219a7ca44b8Syasuoka radiusd_free(radiusd); 220a7ca44b8Syasuoka event_base_free(NULL); 221a7ca44b8Syasuoka 22243436be7Syasuoka if (error != 0) 22359396270Syasuoka exit(EXIT_FAILURE); 22459396270Syasuoka else 225a7ca44b8Syasuoka exit(EXIT_SUCCESS); 226a7ca44b8Syasuoka } 227a7ca44b8Syasuoka 228a7ca44b8Syasuoka static int 229a7ca44b8Syasuoka radiusd_start(struct radiusd *radiusd) 230a7ca44b8Syasuoka { 231a7ca44b8Syasuoka struct radiusd_listen *l; 232a7ca44b8Syasuoka struct radiusd_module *module; 23355b9f5beSyasuoka int s, on; 234a7ca44b8Syasuoka char hbuf[NI_MAXHOST]; 235a7ca44b8Syasuoka 236a7ca44b8Syasuoka TAILQ_FOREACH(l, &radiusd->listen, next) { 237a7ca44b8Syasuoka if (getnameinfo( 238a7ca44b8Syasuoka (struct sockaddr *)&l->addr, l->addr.ipv4.sin_len, 239a7ca44b8Syasuoka hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) { 240a7ca44b8Syasuoka log_warn("%s: getnameinfo()", __func__); 241a7ca44b8Syasuoka goto on_error; 242a7ca44b8Syasuoka } 2435f13b87eSguenther if ((s = socket(l->addr.ipv4.sin_family, 244df69c215Sderaadt l->stype | SOCK_NONBLOCK, l->sproto)) == -1) { 245a7ca44b8Syasuoka log_warn("Listen %s port %d is failed: socket()", 246a7ca44b8Syasuoka hbuf, (int)htons(l->addr.ipv4.sin_port)); 247a7ca44b8Syasuoka goto on_error; 248a7ca44b8Syasuoka } 24955b9f5beSyasuoka 25055b9f5beSyasuoka on = 1; 25155b9f5beSyasuoka if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) 25255b9f5beSyasuoka == -1) 25355b9f5beSyasuoka log_warn("%s: setsockopt(,,SO_REUSEADDR) failed: %m", 25455b9f5beSyasuoka __func__); 255a7ca44b8Syasuoka if (bind(s, (struct sockaddr *)&l->addr, l->addr.ipv4.sin_len) 256a7ca44b8Syasuoka != 0) { 257a7ca44b8Syasuoka log_warn("Listen %s port %d is failed: bind()", 258a7ca44b8Syasuoka hbuf, (int)htons(l->addr.ipv4.sin_port)); 259a7ca44b8Syasuoka close(s); 260a7ca44b8Syasuoka goto on_error; 261a7ca44b8Syasuoka } 262a7ca44b8Syasuoka if (l->addr.ipv4.sin_family == AF_INET) 263a7ca44b8Syasuoka log_info("Start listening on %s:%d/udp", hbuf, 264a7ca44b8Syasuoka (int)ntohs(l->addr.ipv4.sin_port)); 265a7ca44b8Syasuoka else 266a7ca44b8Syasuoka log_info("Start listening on [%s]:%d/udp", hbuf, 267a7ca44b8Syasuoka (int)ntohs(l->addr.ipv4.sin_port)); 268a7ca44b8Syasuoka event_set(&l->ev, s, EV_READ | EV_PERSIST, 269a7ca44b8Syasuoka radiusd_listen_on_event, l); 270a7ca44b8Syasuoka if (event_add(&l->ev, NULL) != 0) { 271a7ca44b8Syasuoka log_warn("event_add() failed at %s()", __func__); 272a7ca44b8Syasuoka close(s); 273a7ca44b8Syasuoka goto on_error; 274a7ca44b8Syasuoka } 275a7ca44b8Syasuoka l->sock = s; 276a7ca44b8Syasuoka l->radiusd = radiusd; 277a7ca44b8Syasuoka } 278a7ca44b8Syasuoka 279a7ca44b8Syasuoka signal_add(&radiusd->ev_sigterm, NULL); 280a7ca44b8Syasuoka signal_add(&radiusd->ev_sigint, NULL); 281a7ca44b8Syasuoka signal_add(&radiusd->ev_sighup, NULL); 282a7ca44b8Syasuoka signal_add(&radiusd->ev_sigchld, NULL); 283a7ca44b8Syasuoka 284a7ca44b8Syasuoka TAILQ_FOREACH(module, &radiusd->module, next) { 28516971584Syasuoka if (debug > 0) 28616971584Syasuoka radiusd_module_set(module, "_debug", 0, NULL); 287a7ca44b8Syasuoka radiusd_module_start(module); 288a7ca44b8Syasuoka } 289a7ca44b8Syasuoka 290a7ca44b8Syasuoka return (0); 291a7ca44b8Syasuoka on_error: 29259396270Syasuoka radiusd->error++; 293842565f2Syasuoka event_loopbreak(); 294a7ca44b8Syasuoka 295a7ca44b8Syasuoka return (-1); 296a7ca44b8Syasuoka } 297a7ca44b8Syasuoka 298a7ca44b8Syasuoka static void 299a7ca44b8Syasuoka radiusd_stop(struct radiusd *radiusd) 300a7ca44b8Syasuoka { 301a7ca44b8Syasuoka char hbuf[NI_MAXHOST]; 302a7ca44b8Syasuoka struct radiusd_listen *l; 303a7ca44b8Syasuoka struct radiusd_module *module; 304a7ca44b8Syasuoka 305a7ca44b8Syasuoka TAILQ_FOREACH_REVERSE(l, &radiusd->listen, radiusd_listen_head, next) { 306a7ca44b8Syasuoka if (l->sock >= 0) { 307a7ca44b8Syasuoka if (getnameinfo( 308a7ca44b8Syasuoka (struct sockaddr *)&l->addr, l->addr.ipv4.sin_len, 309a7ca44b8Syasuoka hbuf, sizeof(hbuf), NULL, 0, NI_NUMERICHOST) != 0) 310a7ca44b8Syasuoka strlcpy(hbuf, "error", sizeof(hbuf)); 311a7ca44b8Syasuoka if (l->addr.ipv4.sin_family == AF_INET) 312a7ca44b8Syasuoka log_info("Stop listening on %s:%d/udp", hbuf, 313a7ca44b8Syasuoka (int)ntohs(l->addr.ipv4.sin_port)); 314a7ca44b8Syasuoka else 315a7ca44b8Syasuoka log_info("Stop listening on [%s]:%d/udp", hbuf, 316a7ca44b8Syasuoka (int)ntohs(l->addr.ipv4.sin_port)); 317a7ca44b8Syasuoka event_del(&l->ev); 318a7ca44b8Syasuoka close(l->sock); 319a7ca44b8Syasuoka } 320a7ca44b8Syasuoka l->sock = -1; 321a7ca44b8Syasuoka } 322a7ca44b8Syasuoka TAILQ_FOREACH(module, &radiusd->module, next) { 323a7ca44b8Syasuoka radiusd_module_stop(module); 324a7ca44b8Syasuoka radiusd_module_close(module); 325a7ca44b8Syasuoka } 326a7ca44b8Syasuoka if (signal_pending(&radiusd->ev_sigterm, NULL)) 327a7ca44b8Syasuoka signal_del(&radiusd->ev_sigterm); 328a7ca44b8Syasuoka if (signal_pending(&radiusd->ev_sigint, NULL)) 329a7ca44b8Syasuoka signal_del(&radiusd->ev_sigint); 330a7ca44b8Syasuoka if (signal_pending(&radiusd->ev_sighup, NULL)) 331a7ca44b8Syasuoka signal_del(&radiusd->ev_sighup); 332a7ca44b8Syasuoka if (signal_pending(&radiusd->ev_sigchld, NULL)) 333a7ca44b8Syasuoka signal_del(&radiusd->ev_sigchld); 334a7ca44b8Syasuoka } 335a7ca44b8Syasuoka 336a7ca44b8Syasuoka static void 337a7ca44b8Syasuoka radiusd_free(struct radiusd *radiusd) 338a7ca44b8Syasuoka { 339a7ca44b8Syasuoka int i; 340a7ca44b8Syasuoka struct radiusd_listen *listn, *listnt; 341a7ca44b8Syasuoka struct radiusd_client *client, *clientt; 342a7ca44b8Syasuoka struct radiusd_module *module, *modulet; 343a7ca44b8Syasuoka struct radiusd_module_ref *modref, *modreft; 344a7ca44b8Syasuoka struct radiusd_authentication *authen, *authent; 34543436be7Syasuoka struct radiusd_accounting *acct, *acctt; 346a7ca44b8Syasuoka 347a7ca44b8Syasuoka TAILQ_FOREACH_SAFE(authen, &radiusd->authen, next, authent) { 348a7ca44b8Syasuoka TAILQ_REMOVE(&radiusd->authen, authen, next); 349a7ca44b8Syasuoka free(authen->auth); 350a7ca44b8Syasuoka TAILQ_FOREACH_SAFE(modref, &authen->deco, next, modreft) { 351a7ca44b8Syasuoka TAILQ_REMOVE(&authen->deco, modref, next); 352a7ca44b8Syasuoka free(modref); 353a7ca44b8Syasuoka } 354a7ca44b8Syasuoka for (i = 0; authen->username[i] != NULL; i++) 355a7ca44b8Syasuoka free(authen->username[i]); 356a7ca44b8Syasuoka free(authen->username); 357a7ca44b8Syasuoka free(authen); 358a7ca44b8Syasuoka } 35943436be7Syasuoka TAILQ_FOREACH_SAFE(acct, &radiusd->account, next, acctt) { 36043436be7Syasuoka TAILQ_REMOVE(&radiusd->account, acct, next); 36143436be7Syasuoka free(acct->secret); 36243436be7Syasuoka free(acct->acct); 36343436be7Syasuoka TAILQ_FOREACH_SAFE(modref, &acct->deco, next, modreft) { 36443436be7Syasuoka TAILQ_REMOVE(&acct->deco, modref, next); 36543436be7Syasuoka free(modref); 36643436be7Syasuoka } 36743436be7Syasuoka for (i = 0; acct->username[i] != NULL; i++) 36843436be7Syasuoka free(acct->username[i]); 36943436be7Syasuoka free(acct->username); 37043436be7Syasuoka free(acct); 37143436be7Syasuoka } 372a7ca44b8Syasuoka TAILQ_FOREACH_SAFE(module, &radiusd->module, next, modulet) { 373a7ca44b8Syasuoka TAILQ_REMOVE(&radiusd->module, module, next); 374a7ca44b8Syasuoka radiusd_module_unload(module); 375a7ca44b8Syasuoka } 376a7ca44b8Syasuoka TAILQ_FOREACH_SAFE(client, &radiusd->client, next, clientt) { 377a7ca44b8Syasuoka TAILQ_REMOVE(&radiusd->client, client, next); 378a7ca44b8Syasuoka explicit_bzero(client->secret, sizeof(client->secret)); 379a7ca44b8Syasuoka free(client); 380a7ca44b8Syasuoka } 381a7ca44b8Syasuoka TAILQ_FOREACH_SAFE(listn, &radiusd->listen, next, listnt) { 382a7ca44b8Syasuoka TAILQ_REMOVE(&radiusd->listen, listn, next); 383a7ca44b8Syasuoka free(listn); 384a7ca44b8Syasuoka } 385a7ca44b8Syasuoka free(radiusd); 386a7ca44b8Syasuoka } 387a7ca44b8Syasuoka 388a7ca44b8Syasuoka /*********************************************************************** 389a7ca44b8Syasuoka * Network event handlers 390a7ca44b8Syasuoka ***********************************************************************/ 391a7ca44b8Syasuoka #define IPv4_cmp(_in, _addr, _mask) ( \ 392a7ca44b8Syasuoka ((_in)->s_addr & (_mask)->addr.ipv4.s_addr) == \ 393a7ca44b8Syasuoka (_addr)->addr.ipv4.s_addr) 394a7ca44b8Syasuoka #define s6_addr32(_in6) ((uint32_t *)(_in6)->s6_addr) 395a7ca44b8Syasuoka #define IPv6_cmp(_in6, _addr, _mask) ( \ 396a7ca44b8Syasuoka ((s6_addr32(_in6)[3] & (_mask)->addr.addr32[3]) \ 397a7ca44b8Syasuoka == (_addr)->addr.addr32[3]) && \ 398a7ca44b8Syasuoka ((s6_addr32(_in6)[2] & (_mask)->addr.addr32[2]) \ 399a7ca44b8Syasuoka == (_addr)->addr.addr32[2]) && \ 400a7ca44b8Syasuoka ((s6_addr32(_in6)[1] & (_mask)->addr.addr32[1]) \ 401a7ca44b8Syasuoka == (_addr)->addr.addr32[1]) && \ 402a7ca44b8Syasuoka ((s6_addr32(_in6)[0] & (_mask)->addr.addr32[0]) \ 403a7ca44b8Syasuoka == (_addr)->addr.addr32[0])) 404a7ca44b8Syasuoka 405a7ca44b8Syasuoka static void 406a7ca44b8Syasuoka radiusd_listen_on_event(int fd, short evmask, void *ctx) 407a7ca44b8Syasuoka { 408936475aaSyasuoka int sz; 409936475aaSyasuoka RADIUS_PACKET *packet = NULL; 410a7ca44b8Syasuoka struct sockaddr_storage peer; 411a7ca44b8Syasuoka socklen_t peersz; 412936475aaSyasuoka struct radiusd_listen *listn = ctx; 413936475aaSyasuoka static u_char buf[65535]; 414a7ca44b8Syasuoka 415a7ca44b8Syasuoka if (evmask & EV_READ) { 416a7ca44b8Syasuoka peersz = sizeof(peer); 417a7ca44b8Syasuoka if ((sz = recvfrom(listn->sock, buf, sizeof(buf), 0, 418df69c215Sderaadt (struct sockaddr *)&peer, &peersz)) == -1) { 41951da3916Syasuoka if (errno == EAGAIN) 42051da3916Syasuoka return; 421a7ca44b8Syasuoka log_warn("%s: recvfrom() failed", __func__); 422936475aaSyasuoka return; 423a7ca44b8Syasuoka } 424a7ca44b8Syasuoka RADIUSD_ASSERT(peer.ss_family == AF_INET || 425a7ca44b8Syasuoka peer.ss_family == AF_INET6); 426936475aaSyasuoka if ((packet = radius_convert_packet(buf, sz)) == NULL) 427936475aaSyasuoka log_warn("%s: radius_convert_packet() failed", 428936475aaSyasuoka __func__); 429936475aaSyasuoka else 430936475aaSyasuoka radiusd_listen_handle_packet(listn, packet, 431936475aaSyasuoka (struct sockaddr *)&peer, peersz); 432936475aaSyasuoka } 433936475aaSyasuoka } 434a7ca44b8Syasuoka 435936475aaSyasuoka static void 436936475aaSyasuoka radiusd_listen_handle_packet(struct radiusd_listen *listn, 437936475aaSyasuoka RADIUS_PACKET *packet, struct sockaddr *peer, socklen_t peerlen) 438936475aaSyasuoka { 439936475aaSyasuoka int i, req_id, req_code; 440936475aaSyasuoka static char username[256]; 441936475aaSyasuoka char peerstr[NI_MAXHOST + NI_MAXSERV + 30]; 442936475aaSyasuoka struct radiusd_authentication *authen; 443747da5e9Syasuoka struct radiusd_accounting *accounting; 444936475aaSyasuoka struct radiusd_client *client; 445936475aaSyasuoka struct radius_query *q = NULL; 446747da5e9Syasuoka uint32_t acct_status; 447936475aaSyasuoka #define in(_x) (((struct sockaddr_in *)_x)->sin_addr) 448936475aaSyasuoka #define in6(_x) (((struct sockaddr_in6 *)_x)->sin6_addr) 449936475aaSyasuoka 450936475aaSyasuoka req_id = radius_get_id(packet); 451936475aaSyasuoka req_code = radius_get_code(packet); 452a7ca44b8Syasuoka /* prepare some information about this messages */ 453936475aaSyasuoka if (addrport_tostring(peer, peerlen, peerstr, sizeof(peerstr)) == 454936475aaSyasuoka NULL) { 455a7ca44b8Syasuoka log_warn("%s: getnameinfo() failed", __func__); 456a7ca44b8Syasuoka goto on_error; 457a7ca44b8Syasuoka } 458a7ca44b8Syasuoka 459a7ca44b8Syasuoka /* 460a7ca44b8Syasuoka * Find a matching `client' entry 461a7ca44b8Syasuoka */ 462a7ca44b8Syasuoka TAILQ_FOREACH(client, &listn->radiusd->client, next) { 463936475aaSyasuoka if (client->af != peer->sa_family) 464a7ca44b8Syasuoka continue; 465936475aaSyasuoka if (peer->sa_family == AF_INET && IPv4_cmp( 466936475aaSyasuoka &in(peer), &client->addr, &client->mask)) 467a7ca44b8Syasuoka break; 468936475aaSyasuoka else if (peer->sa_family == AF_INET6 && IPv6_cmp( 469936475aaSyasuoka &in6(peer), &client->addr, &client->mask)) 470a7ca44b8Syasuoka break; 471a7ca44b8Syasuoka } 472a7ca44b8Syasuoka if (client == NULL) { 473936475aaSyasuoka log_warnx("Received %s(code=%d) from %s id=%d: no `client' " 474936475aaSyasuoka "matches", radius_code_string(req_code), req_code, peerstr, 475936475aaSyasuoka req_id); 476a7ca44b8Syasuoka goto on_error; 477a7ca44b8Syasuoka } 478a7ca44b8Syasuoka 479747da5e9Syasuoka /* Check the request authenticator if accounting */ 480e0671980Syasuoka if (req_code == RADIUS_CODE_ACCOUNTING_REQUEST && 481e0671980Syasuoka radius_check_accounting_request_authenticator(packet, 482e0671980Syasuoka client->secret) != 0) { 483747da5e9Syasuoka log_warnx("Received %s(code=%d) from %s id=%d: bad request " 484747da5e9Syasuoka "authenticator", radius_code_string(req_code), req_code, 485747da5e9Syasuoka peerstr, req_id); 486747da5e9Syasuoka goto on_error; 487747da5e9Syasuoka } 488747da5e9Syasuoka 489a7ca44b8Syasuoka /* Check the client's Message-Authenticator */ 490747da5e9Syasuoka if (client->msgauth_required && !listn->accounting && 491747da5e9Syasuoka !radius_has_attr(packet, RADIUS_TYPE_MESSAGE_AUTHENTICATOR)) { 492936475aaSyasuoka log_warnx("Received %s(code=%d) from %s id=%d: no message " 493936475aaSyasuoka "authenticator", radius_code_string(req_code), req_code, 494936475aaSyasuoka peerstr, req_id); 495a7ca44b8Syasuoka goto on_error; 496a7ca44b8Syasuoka } 497a7ca44b8Syasuoka 498936475aaSyasuoka if (radius_has_attr(packet, RADIUS_TYPE_MESSAGE_AUTHENTICATOR) && 499936475aaSyasuoka radius_check_message_authenticator(packet, client->secret) != 0) { 500936475aaSyasuoka log_warnx("Received %s(code=%d) from %s id=%d: bad message " 501936475aaSyasuoka "authenticator", radius_code_string(req_code), req_code, 502936475aaSyasuoka peerstr, req_id); 503a7ca44b8Syasuoka goto on_error; 504a7ca44b8Syasuoka } 505a7ca44b8Syasuoka 506a7ca44b8Syasuoka /* 507936475aaSyasuoka * Find a duplicate request. In RFC 2865, it has the same source IP 508936475aaSyasuoka * address and source UDP port and Identifier. 509a7ca44b8Syasuoka */ 510a7ca44b8Syasuoka TAILQ_FOREACH(q, &listn->radiusd->query, next) { 511936475aaSyasuoka if (peer->sa_family == q->clientaddr.ss_family && 512936475aaSyasuoka ((peer->sa_family == AF_INET && in(&q->clientaddr).s_addr == 513936475aaSyasuoka in(peer).s_addr) || (peer->sa_family == AF_INET6 && 514936475aaSyasuoka IN6_ARE_ADDR_EQUAL(&in6(&q->clientaddr), &in6(peer)))) && 515a7ca44b8Syasuoka ((struct sockaddr_in *)&q->clientaddr)->sin_port == 516936475aaSyasuoka ((struct sockaddr_in *)peer)->sin_port && 517a7ca44b8Syasuoka req_id == q->req_id) 518a7ca44b8Syasuoka break; /* found it */ 519a7ca44b8Syasuoka } 520a7ca44b8Syasuoka if (q != NULL) { 521ce1078f6Syasuoka log_info("Received %s(code=%d) from %s id=%d: duplicated " 522ce1078f6Syasuoka "with q=%u", radius_code_string(req_code), req_code, 523936475aaSyasuoka peerstr, req_id, q->id); 524ce1078f6Syasuoka q = NULL; 525c50650beSyasuoka goto on_error; 526a7ca44b8Syasuoka } 527a7ca44b8Syasuoka 528936475aaSyasuoka if ((q = calloc(1, sizeof(struct radius_query))) == NULL) { 529936475aaSyasuoka log_warn("%s: Out of memory", __func__); 530a7ca44b8Syasuoka goto on_error; 531a7ca44b8Syasuoka } 532936475aaSyasuoka if (radius_get_string_attr(packet, RADIUS_TYPE_USER_NAME, username, 533936475aaSyasuoka sizeof(username)) != 0) { 534936475aaSyasuoka log_info("Received %s(code=%d) from %s id=%d: no User-Name " 535936475aaSyasuoka "attribute", radius_code_string(req_code), req_code, 536936475aaSyasuoka peerstr, req_id); 537936475aaSyasuoka } else 538936475aaSyasuoka strlcpy(q->username, username, sizeof(q->username)); 539a7ca44b8Syasuoka 540936475aaSyasuoka q->id = ++radius_query_id_seq; 541836eeaedSyasuoka q->radiusd = listn->radiusd; 542936475aaSyasuoka q->clientaddrlen = peerlen; 543936475aaSyasuoka memcpy(&q->clientaddr, peer, peerlen); 544936475aaSyasuoka q->listen = listn; 545936475aaSyasuoka q->req = packet; 546936475aaSyasuoka q->client = client; 547936475aaSyasuoka q->req_id = req_id; 548936475aaSyasuoka radius_get_authenticator(packet, q->req_auth); 549936475aaSyasuoka packet = NULL; 550936475aaSyasuoka TAILQ_INSERT_TAIL(&listn->radiusd->query, q, next); 551936475aaSyasuoka 552936475aaSyasuoka switch (req_code) { 553936475aaSyasuoka case RADIUS_CODE_ACCESS_REQUEST: 554747da5e9Syasuoka if (listn->accounting) { 555747da5e9Syasuoka log_info("Received %s(code=%d) from %s id=%d: " 556747da5e9Syasuoka "ignored because the port is for authentication", 557747da5e9Syasuoka radius_code_string(req_code), req_code, peerstr, 558747da5e9Syasuoka req_id); 559747da5e9Syasuoka break; 560747da5e9Syasuoka } 561a7ca44b8Syasuoka /* 562a7ca44b8Syasuoka * Find a matching `authenticate' entry 563a7ca44b8Syasuoka */ 564a7ca44b8Syasuoka TAILQ_FOREACH(authen, &listn->radiusd->authen, next) { 565a7ca44b8Syasuoka for (i = 0; authen->username[i] != NULL; i++) { 566a7ca44b8Syasuoka if (fnmatch(authen->username[i], username, 0) 567a7ca44b8Syasuoka == 0) 568a7ca44b8Syasuoka goto found; 569a7ca44b8Syasuoka } 570a7ca44b8Syasuoka } 571237e61d9Syasuoka found: 572a7ca44b8Syasuoka if (authen == NULL) { 573a7ca44b8Syasuoka log_warnx("Received %s(code=%d) from %s id=%d " 574a7ca44b8Syasuoka "username=%s: no `authenticate' matches.", 575a7ca44b8Syasuoka radius_code_string(req_code), req_code, peerstr, 576a7ca44b8Syasuoka req_id, username); 577a7ca44b8Syasuoka goto on_error; 578a7ca44b8Syasuoka } 579936475aaSyasuoka q->authen = authen; 58036a3d429Syasuoka 581a7ca44b8Syasuoka if (!MODULE_DO_USERPASS(authen->auth->module) && 582a7ca44b8Syasuoka !MODULE_DO_ACCSREQ(authen->auth->module)) { 583a7ca44b8Syasuoka log_warnx("Received %s(code=%d) from %s id=%d " 584a7ca44b8Syasuoka "username=%s: module `%s' is not running.", 585a7ca44b8Syasuoka radius_code_string(req_code), req_code, peerstr, 586a7ca44b8Syasuoka req_id, username, authen->auth->module->name); 587a7ca44b8Syasuoka goto on_error; 588a7ca44b8Syasuoka } 589a7ca44b8Syasuoka 590a7ca44b8Syasuoka log_info("Received %s(code=%d) from %s id=%d username=%s " 591a7ca44b8Syasuoka "q=%u: `%s' authentication is starting", 592a7ca44b8Syasuoka radius_code_string(req_code), req_code, peerstr, q->req_id, 593a7ca44b8Syasuoka q->username, q->id, q->authen->auth->module->name); 594a7ca44b8Syasuoka 595936475aaSyasuoka raidus_query_access_request(q); 596a7ca44b8Syasuoka return; 597747da5e9Syasuoka case RADIUS_CODE_ACCOUNTING_REQUEST: 598747da5e9Syasuoka if (!listn->accounting) { 599747da5e9Syasuoka log_info("Received %s(code=%d) from %s id=%d: " 600747da5e9Syasuoka "ignored because the port is for accounting", 601747da5e9Syasuoka radius_code_string(req_code), req_code, peerstr, 602747da5e9Syasuoka req_id); 603747da5e9Syasuoka break; 604747da5e9Syasuoka } 605747da5e9Syasuoka if (radius_get_uint32_attr(q->req, RADIUS_TYPE_ACCT_STATUS_TYPE, 606747da5e9Syasuoka &acct_status) != 0) 607747da5e9Syasuoka acct_status = 0; 608747da5e9Syasuoka /* 609747da5e9Syasuoka * Find a matching `accounting' entry 610747da5e9Syasuoka */ 611747da5e9Syasuoka TAILQ_FOREACH(accounting, &listn->radiusd->account, next) { 612747da5e9Syasuoka if (acct_status == RADIUS_ACCT_STATUS_TYPE_ACCT_ON || 613747da5e9Syasuoka acct_status == RADIUS_ACCT_STATUS_TYPE_ACCT_OFF) { 614747da5e9Syasuoka raidus_query_accounting_request(accounting, q); 615747da5e9Syasuoka continue; 616747da5e9Syasuoka } 617747da5e9Syasuoka for (i = 0; accounting->username[i] != NULL; i++) { 618747da5e9Syasuoka if (fnmatch(accounting->username[i], username, 619747da5e9Syasuoka 0) == 0) 620747da5e9Syasuoka break; 621747da5e9Syasuoka } 622747da5e9Syasuoka if (accounting->username[i] == NULL) 623747da5e9Syasuoka continue; 624747da5e9Syasuoka raidus_query_accounting_request(accounting, q); 625747da5e9Syasuoka if (accounting->quick) 626747da5e9Syasuoka break; 627747da5e9Syasuoka } 628747da5e9Syasuoka /* pass NULL to hadnle this self without module */ 629747da5e9Syasuoka raidus_query_accounting_request(NULL, q); 630747da5e9Syasuoka 631747da5e9Syasuoka if ((q->res = radius_new_response_packet( 632747da5e9Syasuoka RADIUS_CODE_ACCOUNTING_RESPONSE, q->req)) == NULL) 633747da5e9Syasuoka log_warn("%s: radius_new_response_packet() failed", 634747da5e9Syasuoka __func__); 635747da5e9Syasuoka else 636747da5e9Syasuoka radius_query_accounting_response(q); 637747da5e9Syasuoka break; 638936475aaSyasuoka default: 639936475aaSyasuoka log_info("Received %s(code=%d) from %s id=%d: %s is not " 640936475aaSyasuoka "supported in this implementation", radius_code_string( 641936475aaSyasuoka req_code), req_code, peerstr, req_id, radius_code_string( 642936475aaSyasuoka req_code)); 643936475aaSyasuoka break; 644a7ca44b8Syasuoka } 645a7ca44b8Syasuoka on_error: 646a7ca44b8Syasuoka if (packet != NULL) 647a7ca44b8Syasuoka radius_delete_packet(packet); 648936475aaSyasuoka if (q != NULL) 649936475aaSyasuoka radiusd_access_request_aborted(q); 650a7ca44b8Syasuoka #undef in 651a7ca44b8Syasuoka #undef in6 652a7ca44b8Syasuoka } 653a7ca44b8Syasuoka 654237e61d9Syasuoka static void 655936475aaSyasuoka raidus_query_access_request(struct radius_query *q) 656a7ca44b8Syasuoka { 657237e61d9Syasuoka struct radiusd_authentication *authen = q->authen; 658a7ca44b8Syasuoka 659237e61d9Syasuoka /* first or next request decoration */ 660237e61d9Syasuoka for (;;) { 661237e61d9Syasuoka if (q->deco == NULL) 662237e61d9Syasuoka q->deco = TAILQ_FIRST(&q->authen->deco); 663237e61d9Syasuoka else 664237e61d9Syasuoka q->deco = TAILQ_NEXT(q->deco, next); 665237e61d9Syasuoka if (q->deco == NULL || MODULE_DO_REQDECO(q->deco->module)) 666237e61d9Syasuoka break; 667237e61d9Syasuoka } 668237e61d9Syasuoka 669237e61d9Syasuoka if (q->deco != NULL) 670237e61d9Syasuoka radiusd_module_request_decoration(q->deco->module, q); 671237e61d9Syasuoka else { 672237e61d9Syasuoka RADIUSD_ASSERT(authen->auth != NULL); 673237e61d9Syasuoka if (MODULE_DO_ACCSREQ(authen->auth->module)) 674237e61d9Syasuoka radiusd_module_access_request(authen->auth->module, q); 675237e61d9Syasuoka else if (MODULE_DO_USERPASS(authen->auth->module)) 676237e61d9Syasuoka radiusd_module_userpass(authen->auth->module, q); 677a7ca44b8Syasuoka } 678a7ca44b8Syasuoka } 679a7ca44b8Syasuoka 680237e61d9Syasuoka static void 681936475aaSyasuoka radius_query_access_response(struct radius_query *q) 682a7ca44b8Syasuoka { 683a7ca44b8Syasuoka int sz, res_id, res_code; 684a7ca44b8Syasuoka char buf[NI_MAXHOST + NI_MAXSERV + 30]; 685ed1dc925Syasuoka struct radius_query *q_last, *q0; 686a7ca44b8Syasuoka 687ed1dc925Syasuoka q_last = q; 688ed1dc925Syasuoka next: 689237e61d9Syasuoka /* first or next response decoration */ 690237e61d9Syasuoka for (;;) { 691237e61d9Syasuoka if (q->deco == NULL) 692237e61d9Syasuoka q->deco = TAILQ_FIRST(&q->authen->deco); 693237e61d9Syasuoka else 694237e61d9Syasuoka q->deco = TAILQ_NEXT(q->deco, next); 695237e61d9Syasuoka if (q->deco == NULL || MODULE_DO_RESDECO(q->deco->module)) 696237e61d9Syasuoka break; 697a7ca44b8Syasuoka } 698a7ca44b8Syasuoka 699237e61d9Syasuoka if (q->deco != NULL) { 700237e61d9Syasuoka radiusd_module_response_decoration(q->deco->module, q); 701237e61d9Syasuoka return; 702237e61d9Syasuoka } 703a7ca44b8Syasuoka 704ed1dc925Syasuoka if (q->prev != NULL) { 705ed1dc925Syasuoka if (MODULE_DO_NEXTRES(q->prev->authen->auth->module)) { 706ed1dc925Syasuoka if (radiusd_access_response_fixup(q->prev, q_last, 0) 707ed1dc925Syasuoka != 0) 708ed1dc925Syasuoka goto on_error; 709ed1dc925Syasuoka q0 = q; 710ed1dc925Syasuoka q = q->prev; 71130c0952cSyasuoka /* dissolve the relation */ 71230c0952cSyasuoka q0->prev = NULL; 71330c0952cSyasuoka q->hasnext = false; 714ed1dc925Syasuoka radiusd_module_next_response(q->authen->auth->module, 715ed1dc925Syasuoka q, q_last->res); 716ed1dc925Syasuoka radiusd_access_request_aborted(q0); 717ed1dc925Syasuoka return; 718ed1dc925Syasuoka } 719ed1dc925Syasuoka q = q->prev; 720ed1dc925Syasuoka goto next; 721ed1dc925Syasuoka } 722ed1dc925Syasuoka 723ed1dc925Syasuoka if (radiusd_access_response_fixup(q, q_last, 1) != 0) 724a7ca44b8Syasuoka goto on_error; 725a7ca44b8Syasuoka 726a7ca44b8Syasuoka res_id = radius_get_id(q->res); 727a7ca44b8Syasuoka res_code = radius_get_code(q->res); 728a7ca44b8Syasuoka 729d7548b59Syasuoka /* Reset response/message authenticator */ 730d7548b59Syasuoka if (radius_has_attr(q->res, RADIUS_TYPE_MESSAGE_AUTHENTICATOR)) 731d7548b59Syasuoka radius_del_attr_all(q->res, RADIUS_TYPE_MESSAGE_AUTHENTICATOR); 732d7548b59Syasuoka radius_put_message_authenticator(q->res, q->client->secret); 733a7ca44b8Syasuoka radius_set_response_authenticator(q->res, q->client->secret); 734a7ca44b8Syasuoka 735a7ca44b8Syasuoka log_info("Sending %s(code=%d) to %s id=%u q=%u", 736a7ca44b8Syasuoka radius_code_string(res_code), res_code, 737a7ca44b8Syasuoka addrport_tostring((struct sockaddr *)&q->clientaddr, 738a7ca44b8Syasuoka q->clientaddrlen, buf, sizeof(buf)), res_id, q->id); 739a7ca44b8Syasuoka 740a7ca44b8Syasuoka if ((sz = sendto(q->listen->sock, radius_get_data(q->res), 741a7ca44b8Syasuoka radius_get_length(q->res), 0, 742a7ca44b8Syasuoka (struct sockaddr *)&q->clientaddr, q->clientaddrlen)) <= 0) 743a7ca44b8Syasuoka log_warn("Sending a RADIUS response failed"); 744a7ca44b8Syasuoka on_error: 745a7ca44b8Syasuoka radiusd_access_request_aborted(q); 746237e61d9Syasuoka } 747237e61d9Syasuoka 748747da5e9Syasuoka static void 749747da5e9Syasuoka raidus_query_accounting_request(struct radiusd_accounting *accounting, 750747da5e9Syasuoka struct radius_query *q) 751747da5e9Syasuoka { 752747da5e9Syasuoka int req_code; 753747da5e9Syasuoka uint32_t acct_status; 754747da5e9Syasuoka char buf0[NI_MAXHOST + NI_MAXSERV + 30]; 755747da5e9Syasuoka 756747da5e9Syasuoka if (accounting != NULL) { 757747da5e9Syasuoka /* handle by the module */ 758747da5e9Syasuoka if (MODULE_DO_ACCTREQ(accounting->acct->module)) 759747da5e9Syasuoka radiusd_module_account_request(accounting->acct->module, 760747da5e9Syasuoka q); 761747da5e9Syasuoka return; 762747da5e9Syasuoka } 763747da5e9Syasuoka req_code = radius_get_code(q->req); 764747da5e9Syasuoka if (radius_get_uint32_attr(q->req, RADIUS_TYPE_ACCT_STATUS_TYPE, 765747da5e9Syasuoka &acct_status) != 0) 766747da5e9Syasuoka acct_status = 0; 767747da5e9Syasuoka log_info("Received %s(code=%d) type=%s(%lu) from %s id=%d username=%s " 768747da5e9Syasuoka "q=%u", radius_code_string(req_code), req_code, 769747da5e9Syasuoka radius_acct_status_type_string(acct_status), (unsigned long) 770747da5e9Syasuoka acct_status, addrport_tostring((struct sockaddr *)&q->clientaddr, 771747da5e9Syasuoka q->clientaddrlen, buf0, sizeof(buf0)), q->req_id, q->username, 772747da5e9Syasuoka q->id); 773747da5e9Syasuoka } 774747da5e9Syasuoka 775747da5e9Syasuoka static void 776747da5e9Syasuoka radius_query_accounting_response(struct radius_query *q) 777747da5e9Syasuoka { 778747da5e9Syasuoka int sz, res_id, res_code; 779747da5e9Syasuoka char buf[NI_MAXHOST + NI_MAXSERV + 30]; 780747da5e9Syasuoka 781747da5e9Syasuoka radius_set_response_authenticator(q->res, q->client->secret); 782747da5e9Syasuoka res_id = radius_get_id(q->res); 783747da5e9Syasuoka res_code = radius_get_code(q->res); 784747da5e9Syasuoka 785747da5e9Syasuoka log_info("Sending %s(code=%d) to %s id=%u q=%u", 786747da5e9Syasuoka radius_code_string(res_code), res_code, 787747da5e9Syasuoka addrport_tostring((struct sockaddr *)&q->clientaddr, 788747da5e9Syasuoka q->clientaddrlen, buf, sizeof(buf)), res_id, q->id); 789747da5e9Syasuoka 790747da5e9Syasuoka if ((sz = sendto(q->listen->sock, radius_get_data(q->res), 791747da5e9Syasuoka radius_get_length(q->res), 0, 792747da5e9Syasuoka (struct sockaddr *)&q->clientaddr, q->clientaddrlen)) <= 0) 793747da5e9Syasuoka log_warn("Sending a RADIUS response failed"); 794747da5e9Syasuoka } 795ed1dc925Syasuoka 796ed1dc925Syasuoka static const char * 797ed1dc925Syasuoka radius_query_client_secret(struct radius_query *q) 798ed1dc925Syasuoka { 799ed1dc925Syasuoka struct radius_query *q0; 800ed1dc925Syasuoka const char *client_secret = NULL; 801ed1dc925Syasuoka 802ed1dc925Syasuoka for (q0 = q; q0 != NULL && client_secret == NULL; q0 = q0->prev) { 803ed1dc925Syasuoka if (q0->client != NULL) 804ed1dc925Syasuoka client_secret = q0->client->secret; 805ed1dc925Syasuoka } 806ed1dc925Syasuoka RADIUSD_ASSERT(client_secret != NULL); 807ed1dc925Syasuoka 808ed1dc925Syasuoka return (client_secret); 809ed1dc925Syasuoka } 810237e61d9Syasuoka /*********************************************************************** 811237e61d9Syasuoka * Callback functions from the modules 812237e61d9Syasuoka ***********************************************************************/ 813237e61d9Syasuoka void 814237e61d9Syasuoka radiusd_access_request_answer(struct radius_query *q) 815237e61d9Syasuoka { 816237e61d9Syasuoka radius_set_request_packet(q->res, q->req); 817237e61d9Syasuoka RADIUSD_ASSERT(q->deco == NULL); 818237e61d9Syasuoka 819a449bbceSyasuoka radius_query_access_response(q); 820a7ca44b8Syasuoka } 821a7ca44b8Syasuoka 822a7ca44b8Syasuoka void 823ed1dc925Syasuoka radiusd_access_request_next(struct radius_query *q, RADIUS_PACKET *pkt) 824ed1dc925Syasuoka { 825ed1dc925Syasuoka struct radius_query *q_next = NULL; 826ed1dc925Syasuoka static char username[256]; 827ed1dc925Syasuoka struct radiusd_authentication *authen; 828ed1dc925Syasuoka int i; 829ed1dc925Syasuoka 830ed1dc925Syasuoka RADIUSD_ASSERT(q->deco == NULL); 831ed1dc925Syasuoka 832ed1dc925Syasuoka if (!q->authen->isfilter) { 833ed1dc925Syasuoka log_warnx("q=%u `%s' requested next authentication, but it's " 834ed1dc925Syasuoka "not authentication-filter", q->id, 835ed1dc925Syasuoka q->authen->auth->module->name); 836ed1dc925Syasuoka goto on_error; 837ed1dc925Syasuoka } 838ed1dc925Syasuoka if (radius_get_string_attr(pkt, RADIUS_TYPE_USER_NAME, username, 839ed1dc925Syasuoka sizeof(username)) != 0) 840ed1dc925Syasuoka username[0] = '\0'; 841ed1dc925Syasuoka 842ed1dc925Syasuoka for (authen = TAILQ_NEXT(q->authen, next); authen != NULL; 843ed1dc925Syasuoka authen = TAILQ_NEXT(authen, next)) { 844ed1dc925Syasuoka for (i = 0; authen->username[i] != NULL; i++) { 845ed1dc925Syasuoka if (fnmatch(authen->username[i], username, 0) 846ed1dc925Syasuoka == 0) 847ed1dc925Syasuoka goto found; 848ed1dc925Syasuoka } 849ed1dc925Syasuoka } 850ed1dc925Syasuoka found: 851ed1dc925Syasuoka if (authen == NULL) { /* no more authentication */ 852ed1dc925Syasuoka log_warnx("q=%u module `%s' requested next authentication " 853ed1dc925Syasuoka "no more `authenticate' matches", q->id, 854ed1dc925Syasuoka q->authen->auth->module->name); 855ed1dc925Syasuoka goto on_error; 856ed1dc925Syasuoka } 857ed1dc925Syasuoka 858ed1dc925Syasuoka if ((q_next = calloc(1, sizeof(struct radius_query))) == NULL) { 859ed1dc925Syasuoka log_warn("%s: q=%u calloc: %m", __func__, q->id); 860ed1dc925Syasuoka goto on_error; 861ed1dc925Syasuoka } 862ed1dc925Syasuoka q_next->id = ++radius_query_id_seq; 863ed1dc925Syasuoka q_next->radiusd = q->radiusd; 864ed1dc925Syasuoka q_next->req_id = q->req_id; 865ed1dc925Syasuoka q_next->req = pkt; 866ed1dc925Syasuoka radius_get_authenticator(pkt, q_next->req_auth); 867ed1dc925Syasuoka q_next->authen = authen; 868ed1dc925Syasuoka q_next->prev = q; 86930c0952cSyasuoka q->hasnext = true; 870ed1dc925Syasuoka strlcpy(q_next->username, username, sizeof(q_next->username)); 871ed1dc925Syasuoka TAILQ_INSERT_TAIL(&q->radiusd->query, q_next, next); 872ed1dc925Syasuoka 873ed1dc925Syasuoka raidus_query_access_request(q_next); 874ed1dc925Syasuoka return; 875ed1dc925Syasuoka on_error: 876ed1dc925Syasuoka RADIUSD_ASSERT(q_next == NULL); 877ed1dc925Syasuoka radius_delete_packet(pkt); 878ed1dc925Syasuoka radiusd_access_request_aborted(q); 879ed1dc925Syasuoka } 880ed1dc925Syasuoka 881ed1dc925Syasuoka void 882a7ca44b8Syasuoka radiusd_access_request_aborted(struct radius_query *q) 883a7ca44b8Syasuoka { 88430c0952cSyasuoka if (q->hasnext) /* don't abort if filtering */ 88530c0952cSyasuoka return; 88630c0952cSyasuoka if (q->prev != NULL) { 88730c0952cSyasuoka q->prev->hasnext = false; 888ed1dc925Syasuoka radiusd_access_request_aborted(q->prev); 88930c0952cSyasuoka } 890a7ca44b8Syasuoka if (q->req != NULL) 891a7ca44b8Syasuoka radius_delete_packet(q->req); 892a7ca44b8Syasuoka if (q->res != NULL) 893a7ca44b8Syasuoka radius_delete_packet(q->res); 894836eeaedSyasuoka TAILQ_REMOVE(&q->radiusd->query, q, next); 895a7ca44b8Syasuoka free(q); 896a7ca44b8Syasuoka } 897a7ca44b8Syasuoka 898a7ca44b8Syasuoka /*********************************************************************** 899a7ca44b8Syasuoka * Signal handlers 900a7ca44b8Syasuoka ***********************************************************************/ 901a7ca44b8Syasuoka static void 902a7ca44b8Syasuoka radiusd_on_sigterm(int fd, short evmask, void *ctx) 903a7ca44b8Syasuoka { 904a7ca44b8Syasuoka log_info("Received SIGTERM"); 905842565f2Syasuoka event_loopbreak(); 906a7ca44b8Syasuoka } 907a7ca44b8Syasuoka 908a7ca44b8Syasuoka static void 909a7ca44b8Syasuoka radiusd_on_sigint(int fd, short evmask, void *ctx) 910a7ca44b8Syasuoka { 911a7ca44b8Syasuoka log_info("Received SIGINT"); 912842565f2Syasuoka event_loopbreak(); 913a7ca44b8Syasuoka } 914a7ca44b8Syasuoka 915a7ca44b8Syasuoka static void 916a7ca44b8Syasuoka radiusd_on_sighup(int fd, short evmask, void *ctx) 917a7ca44b8Syasuoka { 918a7ca44b8Syasuoka log_info("Received SIGHUP"); 919a7ca44b8Syasuoka } 920a7ca44b8Syasuoka 921a7ca44b8Syasuoka static void 922a7ca44b8Syasuoka radiusd_on_sigchld(int fd, short evmask, void *ctx) 923a7ca44b8Syasuoka { 924a7ca44b8Syasuoka struct radiusd *radiusd = ctx; 925a7ca44b8Syasuoka struct radiusd_module *module; 926a7ca44b8Syasuoka pid_t pid; 92759396270Syasuoka int status, ndeath = 0; 928a7ca44b8Syasuoka 929a7ca44b8Syasuoka log_debug("Received SIGCHLD"); 930a7ca44b8Syasuoka while ((pid = wait3(&status, WNOHANG, NULL)) != 0) { 931a7ca44b8Syasuoka if (pid == -1) 932a7ca44b8Syasuoka break; 933a7ca44b8Syasuoka TAILQ_FOREACH(module, &radiusd->module, next) { 934a7ca44b8Syasuoka if (module->pid == pid) { 935a7ca44b8Syasuoka if (WIFEXITED(status)) 936a7ca44b8Syasuoka log_warnx("module `%s'(pid=%d) exited " 937a7ca44b8Syasuoka "with status %d", module->name, 938a7ca44b8Syasuoka (int)pid, WEXITSTATUS(status)); 939a7ca44b8Syasuoka else 940a7ca44b8Syasuoka log_warnx("module `%s'(pid=%d) exited " 941a7ca44b8Syasuoka "by signal %d", module->name, 942a7ca44b8Syasuoka (int)pid, WTERMSIG(status)); 94359396270Syasuoka ndeath++; 944a7ca44b8Syasuoka break; 945a7ca44b8Syasuoka } 946a7ca44b8Syasuoka } 947a7ca44b8Syasuoka if (!module) { 948a7ca44b8Syasuoka if (WIFEXITED(status)) 949*abc3a9caSyasuoka log_warnx("unknown child process pid=%d exited " 950a7ca44b8Syasuoka "with status %d", (int)pid, 951a7ca44b8Syasuoka WEXITSTATUS(status)); 952a7ca44b8Syasuoka else 953*abc3a9caSyasuoka log_warnx("unknown child process pid=%d exited " 954a7ca44b8Syasuoka "by signal %d", (int)pid, 955a7ca44b8Syasuoka WTERMSIG(status)); 956a7ca44b8Syasuoka } 957a7ca44b8Syasuoka } 95859396270Syasuoka if (ndeath > 0) { 95959396270Syasuoka radiusd->error++; 96059396270Syasuoka event_loopbreak(); 96159396270Syasuoka } 962a7ca44b8Syasuoka } 963a7ca44b8Syasuoka 964a7ca44b8Syasuoka static const char * 965a7ca44b8Syasuoka radius_code_string(int code) 966a7ca44b8Syasuoka { 967a7ca44b8Syasuoka int i; 968a7ca44b8Syasuoka struct _codestrings { 969a7ca44b8Syasuoka int code; 970a7ca44b8Syasuoka const char *string; 971a7ca44b8Syasuoka } codestrings[] = { 972a7ca44b8Syasuoka { RADIUS_CODE_ACCESS_REQUEST, "Access-Request" }, 973a7ca44b8Syasuoka { RADIUS_CODE_ACCESS_ACCEPT, "Access-Accept" }, 974a7ca44b8Syasuoka { RADIUS_CODE_ACCESS_REJECT, "Access-Reject" }, 975a7ca44b8Syasuoka { RADIUS_CODE_ACCOUNTING_REQUEST, "Accounting-Request" }, 976a7ca44b8Syasuoka { RADIUS_CODE_ACCOUNTING_RESPONSE, "Accounting-Response" }, 977a7ca44b8Syasuoka { RADIUS_CODE_ACCESS_CHALLENGE, "Access-Challenge" }, 978a7ca44b8Syasuoka { RADIUS_CODE_STATUS_SERVER, "Status-Server" }, 97905a7ce52Syasuoka { RADIUS_CODE_STATUS_CLIENT, "Status-Client" }, 980a7ca44b8Syasuoka { -1, NULL } 981a7ca44b8Syasuoka }; 982a7ca44b8Syasuoka 983a7ca44b8Syasuoka for (i = 0; codestrings[i].code != -1; i++) 984a7ca44b8Syasuoka if (codestrings[i].code == code) 985a7ca44b8Syasuoka return (codestrings[i].string); 986a7ca44b8Syasuoka 987240892bdSyasuoka return ("Unknown"); 988a7ca44b8Syasuoka } 989a7ca44b8Syasuoka 990747da5e9Syasuoka static const char * 991747da5e9Syasuoka radius_acct_status_type_string(uint32_t type) 992747da5e9Syasuoka { 993747da5e9Syasuoka int i; 994747da5e9Syasuoka struct _typestrings { 995747da5e9Syasuoka uint32_t type; 996747da5e9Syasuoka const char *string; 997747da5e9Syasuoka } typestrings[] = { 998747da5e9Syasuoka { RADIUS_ACCT_STATUS_TYPE_START, "Start" }, 999747da5e9Syasuoka { RADIUS_ACCT_STATUS_TYPE_STOP, "Stop" }, 1000747da5e9Syasuoka { RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE, "Interim-Update" }, 1001747da5e9Syasuoka { RADIUS_ACCT_STATUS_TYPE_ACCT_ON, "Accounting-On" }, 1002747da5e9Syasuoka { RADIUS_ACCT_STATUS_TYPE_ACCT_OFF, "Accounting-Off" }, 1003747da5e9Syasuoka { -1, NULL } 1004747da5e9Syasuoka }; 1005747da5e9Syasuoka 1006747da5e9Syasuoka for (i = 0; typestrings[i].string != NULL; i++) 1007747da5e9Syasuoka if (typestrings[i].type == type) 1008747da5e9Syasuoka return (typestrings[i].string); 1009747da5e9Syasuoka 1010747da5e9Syasuoka return ("Unknown"); 1011747da5e9Syasuoka } 1012747da5e9Syasuoka 1013a7ca44b8Syasuoka void 1014a7ca44b8Syasuoka radiusd_conf_init(struct radiusd *conf) 1015a7ca44b8Syasuoka { 1016a7ca44b8Syasuoka 1017a7ca44b8Syasuoka TAILQ_INIT(&conf->listen); 1018a7ca44b8Syasuoka TAILQ_INIT(&conf->module); 1019a7ca44b8Syasuoka TAILQ_INIT(&conf->authen); 1020747da5e9Syasuoka TAILQ_INIT(&conf->account); 1021a7ca44b8Syasuoka TAILQ_INIT(&conf->client); 1022a7ca44b8Syasuoka 1023a7ca44b8Syasuoka return; 1024a7ca44b8Syasuoka } 1025a7ca44b8Syasuoka 1026a7ca44b8Syasuoka /* 1027a7ca44b8Syasuoka * Fix some attributes which depend the secret value. 1028a7ca44b8Syasuoka */ 1029a7ca44b8Syasuoka static int 1030ed1dc925Syasuoka radiusd_access_response_fixup(struct radius_query *q, struct radius_query *q0, 1031ed1dc925Syasuoka bool islast) 1032a7ca44b8Syasuoka { 1033a7ca44b8Syasuoka int res_id; 1034a7ca44b8Syasuoka size_t attrlen; 1035ed1dc925Syasuoka u_char authen_req_auth[16], attrbuf[256]; 1036ed1dc925Syasuoka const char *client_req_auth; 1037ed1dc925Syasuoka const char *authen_secret, *client_secret; 1038a7ca44b8Syasuoka 1039ed1dc925Syasuoka authen_secret = q0->authen->auth->module->secret; 1040ed1dc925Syasuoka client_secret = (islast)? q->client->secret : 1041ed1dc925Syasuoka q->authen->auth->module->secret; 1042a7ca44b8Syasuoka 1043ed1dc925Syasuoka radius_get_authenticator(q0->req, authen_req_auth); 1044ed1dc925Syasuoka client_req_auth = q->req_auth; 1045a7ca44b8Syasuoka 1046ed1dc925Syasuoka if (client_secret == NULL && authen_secret == NULL) 1047ed1dc925Syasuoka return (0); 1048ed1dc925Syasuoka if (!(authen_secret != NULL && client_secret != NULL && 1049ed1dc925Syasuoka strcmp(authen_secret, client_secret) == 0 && 1050ed1dc925Syasuoka timingsafe_bcmp(authen_req_auth, client_req_auth, 16) == 0)) { 1051a7ca44b8Syasuoka /* RFC 2865 Tunnel-Password */ 10520fcb2422Syasuoka attrlen = sizeof(attrbuf); 1053ed1dc925Syasuoka if (radius_get_raw_attr(q0->res, RADIUS_TYPE_TUNNEL_PASSWORD, 1054a7ca44b8Syasuoka attrbuf, &attrlen) == 0) { 1055ed1dc925Syasuoka if (authen_secret != NULL) 1056ed1dc925Syasuoka radius_attr_unhide(authen_secret, 1057ed1dc925Syasuoka authen_req_auth, attrbuf, attrbuf + 3, 1058ed1dc925Syasuoka attrlen - 3); 1059ed1dc925Syasuoka if (client_secret != NULL) 1060ed1dc925Syasuoka radius_attr_hide(client_secret, client_req_auth, 1061a7ca44b8Syasuoka attrbuf, attrbuf + 3, attrlen - 3); 1062ed1dc925Syasuoka radius_del_attr_all(q0->res, 1063a7ca44b8Syasuoka RADIUS_TYPE_TUNNEL_PASSWORD); 1064ed1dc925Syasuoka radius_put_raw_attr(q0->res, 1065a7ca44b8Syasuoka RADIUS_TYPE_TUNNEL_PASSWORD, attrbuf, attrlen); 1066a7ca44b8Syasuoka } 1067a7ca44b8Syasuoka 1068a7ca44b8Syasuoka /* RFC 2548 Microsoft MPPE-{Send,Recv}-Key */ 10690fcb2422Syasuoka attrlen = sizeof(attrbuf); 1070ed1dc925Syasuoka if (radius_get_vs_raw_attr(q0->res, RADIUS_VENDOR_MICROSOFT, 1071a7ca44b8Syasuoka RADIUS_VTYPE_MPPE_SEND_KEY, attrbuf, &attrlen) == 0) { 1072ed1dc925Syasuoka if (authen_secret != NULL) 1073ed1dc925Syasuoka radius_attr_unhide(authen_secret, 1074ed1dc925Syasuoka authen_req_auth, attrbuf, attrbuf + 2, 1075ed1dc925Syasuoka attrlen - 2); 1076ed1dc925Syasuoka if (client_secret != NULL) 1077ed1dc925Syasuoka radius_attr_hide(client_secret, client_req_auth, 1078a7ca44b8Syasuoka attrbuf, attrbuf + 2, attrlen - 2); 1079ed1dc925Syasuoka radius_del_vs_attr_all(q0->res, RADIUS_VENDOR_MICROSOFT, 1080a7ca44b8Syasuoka RADIUS_VTYPE_MPPE_SEND_KEY); 1081ed1dc925Syasuoka radius_put_vs_raw_attr(q0->res, RADIUS_VENDOR_MICROSOFT, 1082a7ca44b8Syasuoka RADIUS_VTYPE_MPPE_SEND_KEY, attrbuf, attrlen); 1083a7ca44b8Syasuoka } 10840fcb2422Syasuoka attrlen = sizeof(attrbuf); 1085ed1dc925Syasuoka if (radius_get_vs_raw_attr(q0->res, RADIUS_VENDOR_MICROSOFT, 1086a7ca44b8Syasuoka RADIUS_VTYPE_MPPE_RECV_KEY, attrbuf, &attrlen) == 0) { 1087ed1dc925Syasuoka if (authen_secret != NULL) 1088ed1dc925Syasuoka radius_attr_unhide(authen_secret, 1089ed1dc925Syasuoka authen_req_auth, attrbuf, attrbuf + 2, 1090ed1dc925Syasuoka attrlen - 2); 1091ed1dc925Syasuoka if (client_secret != NULL) 1092ed1dc925Syasuoka radius_attr_hide(client_secret, client_req_auth, 1093a7ca44b8Syasuoka attrbuf, attrbuf + 2, attrlen - 2); 1094a7ca44b8Syasuoka 1095ed1dc925Syasuoka radius_del_vs_attr_all(q0->res, RADIUS_VENDOR_MICROSOFT, 1096a7ca44b8Syasuoka RADIUS_VTYPE_MPPE_RECV_KEY); 1097ed1dc925Syasuoka radius_put_vs_raw_attr(q0->res, RADIUS_VENDOR_MICROSOFT, 1098a7ca44b8Syasuoka RADIUS_VTYPE_MPPE_RECV_KEY, attrbuf, attrlen); 1099a7ca44b8Syasuoka } 1100a7ca44b8Syasuoka } 1101ed1dc925Syasuoka res_id = radius_get_id(q0->res); 1102a7ca44b8Syasuoka if (res_id != q->req_id) { 1103a7ca44b8Syasuoka /* authentication server change the id */ 1104ed1dc925Syasuoka radius_set_id(q0->res, q->req_id); 1105a7ca44b8Syasuoka } 1106a7ca44b8Syasuoka 1107a7ca44b8Syasuoka return (0); 1108a7ca44b8Syasuoka } 1109a7ca44b8Syasuoka 1110a7ca44b8Syasuoka static struct radius_query * 1111a7ca44b8Syasuoka radiusd_find_query(struct radiusd *radiusd, u_int q_id) 1112a7ca44b8Syasuoka { 1113a7ca44b8Syasuoka struct radius_query *q; 1114a7ca44b8Syasuoka 1115a7ca44b8Syasuoka TAILQ_FOREACH(q, &radiusd->query, next) { 1116a7ca44b8Syasuoka if (q->id == q_id) 1117a7ca44b8Syasuoka return (q); 1118a7ca44b8Syasuoka } 1119a7ca44b8Syasuoka return (NULL); 1120a7ca44b8Syasuoka } 1121a7ca44b8Syasuoka 1122842565f2Syasuoka int 1123842565f2Syasuoka radiusd_imsg_compose_module(struct radiusd *radiusd, const char *module_name, 1124842565f2Syasuoka uint32_t type, uint32_t id, pid_t pid, int fd, void *data, size_t datalen) 1125842565f2Syasuoka { 1126842565f2Syasuoka struct radiusd_module *module; 1127842565f2Syasuoka 1128842565f2Syasuoka TAILQ_FOREACH(module, &radiusd_s->module, next) { 1129842565f2Syasuoka if (strcmp(module->name, module_name) == 0) 1130842565f2Syasuoka break; 1131842565f2Syasuoka } 1132842565f2Syasuoka if (module == NULL || 1133842565f2Syasuoka (module->capabilities & RADIUSD_MODULE_CAP_CONTROL) == 0 || 1134842565f2Syasuoka module->fd < 0) 1135842565f2Syasuoka return (-1); 1136842565f2Syasuoka 1137842565f2Syasuoka if (imsg_compose(&module->ibuf, type, id, pid, fd, data, 1138842565f2Syasuoka datalen) == -1) 1139842565f2Syasuoka return (-1); 1140842565f2Syasuoka radiusd_module_reset_ev_handler(module); 1141842565f2Syasuoka 1142842565f2Syasuoka return (0); 1143842565f2Syasuoka } 1144842565f2Syasuoka 1145a7ca44b8Syasuoka /*********************************************************************** 1146a7ca44b8Syasuoka * radiusd module handling 1147a7ca44b8Syasuoka ***********************************************************************/ 1148a7ca44b8Syasuoka struct radiusd_module * 1149a7ca44b8Syasuoka radiusd_module_load(struct radiusd *radiusd, const char *path, const char *name) 1150a7ca44b8Syasuoka { 1151a7ca44b8Syasuoka struct radiusd_module *module = NULL; 1152a7ca44b8Syasuoka pid_t pid; 1153ab3cfbd9Syasuoka int ival, pairsock[] = { -1, -1 }; 1154a7ca44b8Syasuoka const char *av[3]; 1155a7ca44b8Syasuoka ssize_t n; 1156a7ca44b8Syasuoka struct imsg imsg; 1157a7ca44b8Syasuoka 1158a7ca44b8Syasuoka module = calloc(1, sizeof(struct radiusd_module)); 1159a7ca44b8Syasuoka if (module == NULL) 1160a7ca44b8Syasuoka fatal("Out of memory"); 1161a7ca44b8Syasuoka module->radiusd = radiusd; 1162a7ca44b8Syasuoka 1163ab3cfbd9Syasuoka if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pairsock) == -1) { 1164a7ca44b8Syasuoka log_warn("Could not load module `%s'(%s): pipe()", name, path); 1165a7ca44b8Syasuoka goto on_error; 1166a7ca44b8Syasuoka } 1167a7ca44b8Syasuoka 1168a7ca44b8Syasuoka pid = fork(); 1169a7ca44b8Syasuoka if (pid == -1) { 1170a7ca44b8Syasuoka log_warn("Could not load module `%s'(%s): fork()", name, path); 1171a7ca44b8Syasuoka goto on_error; 1172a7ca44b8Syasuoka } 1173a7ca44b8Syasuoka if (pid == 0) { 1174a7ca44b8Syasuoka setsid(); 1175a7ca44b8Syasuoka close(pairsock[0]); 1176a7ca44b8Syasuoka av[0] = path; 1177a7ca44b8Syasuoka av[1] = name; 1178a7ca44b8Syasuoka av[2] = NULL; 1179a7ca44b8Syasuoka dup2(pairsock[1], STDIN_FILENO); 1180a7ca44b8Syasuoka dup2(pairsock[1], STDOUT_FILENO); 1181a7ca44b8Syasuoka close(pairsock[1]); 1182a7ca44b8Syasuoka closefrom(STDERR_FILENO + 1); 1183a7ca44b8Syasuoka execv(path, (char * const *)av); 1184240892bdSyasuoka log_warn("Failed to execute %s", path); 1185a7ca44b8Syasuoka _exit(EXIT_FAILURE); 1186a7ca44b8Syasuoka } 1187a7ca44b8Syasuoka close(pairsock[1]); 1188a7ca44b8Syasuoka 1189a7ca44b8Syasuoka module->fd = pairsock[0]; 1190ab3cfbd9Syasuoka if ((ival = fcntl(module->fd, F_GETFL)) == -1) { 1191ab3cfbd9Syasuoka log_warn("Could not load module `%s': fcntl(F_GETFL)", 1192ab3cfbd9Syasuoka name); 1193ab3cfbd9Syasuoka goto on_error; 1194ab3cfbd9Syasuoka } 1195ab3cfbd9Syasuoka if (fcntl(module->fd, F_SETFL, ival | O_NONBLOCK) == -1) { 1196936475aaSyasuoka log_warn( 1197936475aaSyasuoka "Could not load module `%s': fcntl(F_SETFL,O_NONBLOCK)", 1198ab3cfbd9Syasuoka name); 1199ab3cfbd9Syasuoka goto on_error; 1200ab3cfbd9Syasuoka } 1201a7ca44b8Syasuoka strlcpy(module->name, name, sizeof(module->name)); 1202a7ca44b8Syasuoka module->pid = pid; 1203882428cdSclaudio if (imsgbuf_init(&module->ibuf, module->fd) == -1) { 1204882428cdSclaudio log_warn("Could not load module `%s': imsgbuf_init", name); 1205882428cdSclaudio goto on_error; 1206882428cdSclaudio } 1207a7ca44b8Syasuoka 1208a7ca44b8Syasuoka if (imsg_sync_read(&module->ibuf, MODULE_IO_TIMEOUT) <= 0 || 1209a7ca44b8Syasuoka (n = imsg_get(&module->ibuf, &imsg)) <= 0) { 1210a7ca44b8Syasuoka log_warnx("Could not load module `%s': module didn't " 1211a7ca44b8Syasuoka "respond", name); 1212a7ca44b8Syasuoka goto on_error; 1213a7ca44b8Syasuoka } 1214a7ca44b8Syasuoka if (imsg.hdr.type != IMSG_RADIUSD_MODULE_LOAD) { 1215a7ca44b8Syasuoka imsg_free(&imsg); 1216a7ca44b8Syasuoka log_warnx("Could not load module `%s': unknown imsg type=%d", 1217a7ca44b8Syasuoka name, imsg.hdr.type); 1218a7ca44b8Syasuoka goto on_error; 1219a7ca44b8Syasuoka } 1220a7ca44b8Syasuoka 1221a7ca44b8Syasuoka module->capabilities = 1222a7ca44b8Syasuoka ((struct radiusd_module_load_arg *)imsg.data)->cap; 1223a7ca44b8Syasuoka 1224a7ca44b8Syasuoka log_debug("Loaded module `%s' successfully. pid=%d", module->name, 1225a7ca44b8Syasuoka module->pid); 1226a7ca44b8Syasuoka imsg_free(&imsg); 1227a7ca44b8Syasuoka 1228a7ca44b8Syasuoka return (module); 1229a7ca44b8Syasuoka 1230a7ca44b8Syasuoka on_error: 1231a7ca44b8Syasuoka free(module); 1232a7ca44b8Syasuoka if (pairsock[0] >= 0) 1233a7ca44b8Syasuoka close(pairsock[0]); 1234a7ca44b8Syasuoka if (pairsock[1] >= 0) 1235a7ca44b8Syasuoka close(pairsock[1]); 1236a7ca44b8Syasuoka 1237a7ca44b8Syasuoka return (NULL); 1238a7ca44b8Syasuoka } 1239a7ca44b8Syasuoka 1240a7ca44b8Syasuoka void 1241a7ca44b8Syasuoka radiusd_module_start(struct radiusd_module *module) 1242a7ca44b8Syasuoka { 1243a7ca44b8Syasuoka int datalen; 1244a7ca44b8Syasuoka struct imsg imsg; 124544c4cd84Syasuoka struct timeval tv = { 0, 0 }; 1246a7ca44b8Syasuoka 1247a7ca44b8Syasuoka RADIUSD_ASSERT(module->fd >= 0); 1248a7ca44b8Syasuoka imsg_compose(&module->ibuf, IMSG_RADIUSD_MODULE_START, 0, 0, -1, 1249a7ca44b8Syasuoka NULL, 0); 1250a7ca44b8Syasuoka imsg_sync_flush(&module->ibuf, MODULE_IO_TIMEOUT); 1251a7ca44b8Syasuoka if (imsg_sync_read(&module->ibuf, MODULE_IO_TIMEOUT) <= 0 || 1252a7ca44b8Syasuoka imsg_get(&module->ibuf, &imsg) <= 0) { 1253a7ca44b8Syasuoka log_warnx("Module `%s' could not start: no response", 1254a7ca44b8Syasuoka module->name); 1255a7ca44b8Syasuoka goto on_fail; 1256a7ca44b8Syasuoka } 1257a7ca44b8Syasuoka 1258a7ca44b8Syasuoka datalen = imsg.hdr.len - IMSG_HEADER_SIZE; 1259a7ca44b8Syasuoka if (imsg.hdr.type != IMSG_OK) { 1260a7ca44b8Syasuoka if (imsg.hdr.type == IMSG_NG) { 1261a7ca44b8Syasuoka if (datalen > 0) 1262a7ca44b8Syasuoka log_warnx("Module `%s' could not start: %s", 1263a7ca44b8Syasuoka module->name, (char *)imsg.data); 1264a7ca44b8Syasuoka else 1265a7ca44b8Syasuoka log_warnx("Module `%s' could not start", 1266a7ca44b8Syasuoka module->name); 1267a7ca44b8Syasuoka } else 1268a7ca44b8Syasuoka log_warnx("Module `%s' could not started: module " 12693a50f0a9Sjmc "returned unknown message type %d", module->name, 1270a7ca44b8Syasuoka imsg.hdr.type); 1271a7ca44b8Syasuoka goto on_fail; 1272a7ca44b8Syasuoka } 1273a7ca44b8Syasuoka 1274c469475aSyasuoka event_set(&module->ev, module->fd, EV_READ, radiusd_module_on_imsg_io, 1275c469475aSyasuoka module); 127644c4cd84Syasuoka event_add(&module->ev, &tv); 1277a7ca44b8Syasuoka log_debug("Module `%s' started successfully", module->name); 1278c469475aSyasuoka 1279a7ca44b8Syasuoka return; 1280a7ca44b8Syasuoka on_fail: 1281a7ca44b8Syasuoka radiusd_module_close(module); 1282a7ca44b8Syasuoka return; 1283a7ca44b8Syasuoka } 1284a7ca44b8Syasuoka 1285a7ca44b8Syasuoka void 1286a7ca44b8Syasuoka radiusd_module_stop(struct radiusd_module *module) 1287a7ca44b8Syasuoka { 1288a7ca44b8Syasuoka module->stopped = true; 1289a7ca44b8Syasuoka 1290fda17215Smestre if (module->secret != NULL) { 129129b4e2eaSderaadt freezero(module->secret, strlen(module->secret)); 1292a7ca44b8Syasuoka module->secret = NULL; 1293fda17215Smestre } 1294a7ca44b8Syasuoka 1295d0175656Syasuoka if (module->fd >= 0) { 1296a7ca44b8Syasuoka imsg_compose(&module->ibuf, IMSG_RADIUSD_MODULE_STOP, 0, 0, -1, 1297a7ca44b8Syasuoka NULL, 0); 1298a7ca44b8Syasuoka radiusd_module_reset_ev_handler(module); 1299a7ca44b8Syasuoka } 1300d0175656Syasuoka } 1301a7ca44b8Syasuoka 1302a7ca44b8Syasuoka static void 1303a7ca44b8Syasuoka radiusd_module_close(struct radiusd_module *module) 1304a7ca44b8Syasuoka { 1305a7ca44b8Syasuoka if (module->fd >= 0) { 1306a7ca44b8Syasuoka event_del(&module->ev); 1307dd7efffeSclaudio imsgbuf_clear(&module->ibuf); 1308a7ca44b8Syasuoka close(module->fd); 1309a7ca44b8Syasuoka module->fd = -1; 1310a7ca44b8Syasuoka } 1311a7ca44b8Syasuoka } 1312a7ca44b8Syasuoka 1313a7ca44b8Syasuoka void 1314a7ca44b8Syasuoka radiusd_module_unload(struct radiusd_module *module) 1315a7ca44b8Syasuoka { 1316a7ca44b8Syasuoka free(module->radpkt); 1317a7ca44b8Syasuoka radiusd_module_close(module); 1318a7ca44b8Syasuoka free(module); 1319a7ca44b8Syasuoka } 1320a7ca44b8Syasuoka 1321a7ca44b8Syasuoka static void 1322a7ca44b8Syasuoka radiusd_module_on_imsg_io(int fd, short evmask, void *ctx) 1323a7ca44b8Syasuoka { 1324a7ca44b8Syasuoka struct radiusd_module *module = ctx; 1325a7ca44b8Syasuoka 1326c1aa9554Sclaudio if (evmask & EV_WRITE) { 1327a7ca44b8Syasuoka module->writeready = true; 1328dd7efffeSclaudio if (imsgbuf_write(&module->ibuf) == -1) { 1329dd7efffeSclaudio log_warn("Failed to write to module `%s': " 1330dd7efffeSclaudio "imsgbuf_write()", module->name); 1331c1aa9554Sclaudio goto on_error; 1332c1aa9554Sclaudio } 1333c1aa9554Sclaudio module->writeready = false; 1334c1aa9554Sclaudio } 1335a7ca44b8Syasuoka 13364a4dce94Syasuoka if (evmask & EV_READ) { 13374a4dce94Syasuoka if (radiusd_module_imsg_read(module) == -1) 1338a7ca44b8Syasuoka goto on_error; 1339a7ca44b8Syasuoka } 1340a7ca44b8Syasuoka 1341a7ca44b8Syasuoka radiusd_module_reset_ev_handler(module); 1342a7ca44b8Syasuoka 1343a7ca44b8Syasuoka return; 1344a7ca44b8Syasuoka on_error: 1345a7ca44b8Syasuoka radiusd_module_close(module); 1346a7ca44b8Syasuoka } 1347a7ca44b8Syasuoka 1348a7ca44b8Syasuoka static void 1349a7ca44b8Syasuoka radiusd_module_reset_ev_handler(struct radiusd_module *module) 1350a7ca44b8Syasuoka { 1351a7ca44b8Syasuoka short evmask; 1352a7ca44b8Syasuoka struct timeval *tvp = NULL, tv = { 0, 0 }; 1353a7ca44b8Syasuoka 1354a7ca44b8Syasuoka RADIUSD_ASSERT(module->fd >= 0); 1355a7ca44b8Syasuoka event_del(&module->ev); 1356a7ca44b8Syasuoka 1357a7ca44b8Syasuoka evmask = EV_READ; 135831be28caSclaudio if (imsgbuf_queuelen(&module->ibuf) > 0) { 1359a7ca44b8Syasuoka if (!module->writeready) 1360a7ca44b8Syasuoka evmask |= EV_WRITE; 1361a7ca44b8Syasuoka else 1362a7ca44b8Syasuoka tvp = &tv; /* fire immediately */ 13634a4dce94Syasuoka } 1364a7ca44b8Syasuoka 1365a7ca44b8Syasuoka /* module stopped and no event handler is set */ 1366a7ca44b8Syasuoka if (evmask & EV_WRITE && tvp == NULL && module->stopped) { 1367a7ca44b8Syasuoka /* stop requested and no more to write */ 1368a7ca44b8Syasuoka radiusd_module_close(module); 1369a7ca44b8Syasuoka return; 1370a7ca44b8Syasuoka } 1371a7ca44b8Syasuoka 1372a7ca44b8Syasuoka event_set(&module->ev, module->fd, evmask, radiusd_module_on_imsg_io, 1373a7ca44b8Syasuoka module); 1374a7ca44b8Syasuoka if (event_add(&module->ev, tvp) == -1) { 1375a7ca44b8Syasuoka log_warn("Could not set event handlers for module `%s': " 1376a7ca44b8Syasuoka "event_add()", module->name); 1377a7ca44b8Syasuoka radiusd_module_close(module); 1378a7ca44b8Syasuoka } 1379a7ca44b8Syasuoka } 1380a7ca44b8Syasuoka 1381a7ca44b8Syasuoka static int 13824a4dce94Syasuoka radiusd_module_imsg_read(struct radiusd_module *module) 1383a7ca44b8Syasuoka { 1384a7ca44b8Syasuoka int n; 1385a7ca44b8Syasuoka struct imsg imsg; 1386a7ca44b8Syasuoka 13874f3fb1ffSclaudio if ((n = imsgbuf_read(&module->ibuf)) != 1) { 1388a7ca44b8Syasuoka if (n == -1) 1389a7ca44b8Syasuoka log_warn("Receiving a message from module `%s' " 1390dd7efffeSclaudio "failed: imsgbuf_read", module->name); 1391a7ca44b8Syasuoka /* else closed */ 1392a7ca44b8Syasuoka radiusd_module_close(module); 1393a7ca44b8Syasuoka return (-1); 1394a7ca44b8Syasuoka } 1395a7ca44b8Syasuoka for (;;) { 1396a7ca44b8Syasuoka if ((n = imsg_get(&module->ibuf, &imsg)) == -1) { 1397a7ca44b8Syasuoka log_warn("Receiving a message from module `%s' failed: " 1398a7ca44b8Syasuoka "imsg_get", module->name); 1399a7ca44b8Syasuoka return (-1); 1400a7ca44b8Syasuoka } 1401a7ca44b8Syasuoka if (n == 0) 1402a7ca44b8Syasuoka return (0); 1403a7ca44b8Syasuoka radiusd_module_imsg(module, &imsg); 140458911fd1Syasuoka imsg_free(&imsg); 1405a7ca44b8Syasuoka } 1406a7ca44b8Syasuoka 1407a7ca44b8Syasuoka return (0); 1408a7ca44b8Syasuoka } 1409a7ca44b8Syasuoka 1410a7ca44b8Syasuoka static void 1411a7ca44b8Syasuoka radiusd_module_imsg(struct radiusd_module *module, struct imsg *imsg) 1412a7ca44b8Syasuoka { 1413a7ca44b8Syasuoka int datalen; 1414a7ca44b8Syasuoka struct radius_query *q; 1415a7ca44b8Syasuoka u_int q_id; 1416a7ca44b8Syasuoka 1417a7ca44b8Syasuoka datalen = imsg->hdr.len - IMSG_HEADER_SIZE; 1418a7ca44b8Syasuoka switch (imsg->hdr.type) { 1419a7ca44b8Syasuoka case IMSG_RADIUSD_MODULE_NOTIFY_SECRET: 1420a7ca44b8Syasuoka if (datalen > 0) { 1421a7ca44b8Syasuoka module->secret = strdup(imsg->data); 1422a7ca44b8Syasuoka if (module->secret == NULL) 1423a7ca44b8Syasuoka log_warn("Could not handle NOTIFY_SECRET " 1424a7ca44b8Syasuoka "from `%s'", module->name); 1425a7ca44b8Syasuoka } 1426a7ca44b8Syasuoka break; 1427a7ca44b8Syasuoka case IMSG_RADIUSD_MODULE_USERPASS_OK: 1428a7ca44b8Syasuoka case IMSG_RADIUSD_MODULE_USERPASS_FAIL: 1429a7ca44b8Syasuoka { 1430a7ca44b8Syasuoka char *msg = NULL; 1431a7ca44b8Syasuoka const char *msgtypestr; 1432a7ca44b8Syasuoka 1433a7ca44b8Syasuoka msgtypestr = (imsg->hdr.type == IMSG_RADIUSD_MODULE_USERPASS_OK) 1434a7ca44b8Syasuoka ? "USERPASS_OK" : "USERPASS_NG"; 1435a7ca44b8Syasuoka 1436a7ca44b8Syasuoka q_id = *(u_int *)imsg->data; 1437a7ca44b8Syasuoka if (datalen > (ssize_t)sizeof(u_int)) 1438a7ca44b8Syasuoka msg = (char *)(((u_int *)imsg->data) + 1); 1439a7ca44b8Syasuoka 1440a7ca44b8Syasuoka q = radiusd_find_query(module->radiusd, q_id); 1441a7ca44b8Syasuoka if (q == NULL) { 1442a7ca44b8Syasuoka log_warnx("Received %s from `%s', but query id=%u " 1443a7ca44b8Syasuoka "unknown", msgtypestr, module->name, q_id); 1444a7ca44b8Syasuoka break; 1445a7ca44b8Syasuoka } 1446a7ca44b8Syasuoka 1447a7ca44b8Syasuoka if ((q->res = radius_new_response_packet( 1448a7ca44b8Syasuoka (imsg->hdr.type == IMSG_RADIUSD_MODULE_USERPASS_OK) 1449a7ca44b8Syasuoka ? RADIUS_CODE_ACCESS_ACCEPT : RADIUS_CODE_ACCESS_REJECT, 1450a7ca44b8Syasuoka q->req)) == NULL) { 1451a7ca44b8Syasuoka log_warn("radius_new_response_packet() failed"); 1452a7ca44b8Syasuoka radiusd_access_request_aborted(q); 1453a7ca44b8Syasuoka } else { 1454a7ca44b8Syasuoka if (msg) 1455a7ca44b8Syasuoka radius_put_string_attr(q->res, 1456a7ca44b8Syasuoka RADIUS_TYPE_REPLY_MESSAGE, msg); 1457a7ca44b8Syasuoka radius_set_response_authenticator(q->res, 1458ed1dc925Syasuoka radius_query_client_secret(q)); 1459a7ca44b8Syasuoka radiusd_access_request_answer(q); 146099ace5a5Sjsg } 1461a7ca44b8Syasuoka break; 1462a7ca44b8Syasuoka } 1463a7ca44b8Syasuoka case IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER: 1464ed1dc925Syasuoka case IMSG_RADIUSD_MODULE_ACCSREQ_NEXT: 1465237e61d9Syasuoka case IMSG_RADIUSD_MODULE_REQDECO_DONE: 1466237e61d9Syasuoka case IMSG_RADIUSD_MODULE_RESDECO_DONE: 1467a7ca44b8Syasuoka { 1468a7ca44b8Syasuoka static struct radiusd_module_radpkt_arg *ans; 1469237e61d9Syasuoka const char *typestr = "unknown"; 1470237e61d9Syasuoka 1471237e61d9Syasuoka switch (imsg->hdr.type) { 1472237e61d9Syasuoka case IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER: 1473237e61d9Syasuoka typestr = "ACCSREQ_ANSWER"; 1474237e61d9Syasuoka break; 1475ed1dc925Syasuoka case IMSG_RADIUSD_MODULE_ACCSREQ_NEXT: 1476ed1dc925Syasuoka typestr = "ACCSREQ_NEXT"; 1477ed1dc925Syasuoka break; 1478237e61d9Syasuoka case IMSG_RADIUSD_MODULE_REQDECO_DONE: 1479237e61d9Syasuoka typestr = "REQDECO_DONE"; 1480237e61d9Syasuoka break; 1481237e61d9Syasuoka case IMSG_RADIUSD_MODULE_RESDECO_DONE: 1482237e61d9Syasuoka typestr = "RESDECO_DONE"; 1483237e61d9Syasuoka break; 1484237e61d9Syasuoka } 1485237e61d9Syasuoka 1486a7ca44b8Syasuoka if (datalen < 1487a7ca44b8Syasuoka (ssize_t)sizeof(struct radiusd_module_radpkt_arg)) { 1488237e61d9Syasuoka log_warnx("Received %s message, but length is wrong", 1489237e61d9Syasuoka typestr); 1490a7ca44b8Syasuoka break; 1491a7ca44b8Syasuoka } 1492a7ca44b8Syasuoka q_id = ((struct radiusd_module_radpkt_arg *)imsg->data)->q_id; 1493a7ca44b8Syasuoka q = radiusd_find_query(module->radiusd, q_id); 1494a7ca44b8Syasuoka if (q == NULL) { 1495237e61d9Syasuoka log_warnx("Received %s from %s, but query id=%u " 1496237e61d9Syasuoka "unknown", typestr, module->name, q_id); 1497a7ca44b8Syasuoka break; 1498a7ca44b8Syasuoka } 1499a7ca44b8Syasuoka if ((ans = radiusd_module_recv_radpkt(module, imsg, 1500237e61d9Syasuoka imsg->hdr.type, typestr)) != NULL) { 1501237e61d9Syasuoka RADIUS_PACKET *radpkt = NULL; 1502237e61d9Syasuoka 1503237e61d9Syasuoka if (module->radpktoff > 0 && 1504237e61d9Syasuoka (radpkt = radius_convert_packet( 1505237e61d9Syasuoka module->radpkt, module->radpktoff)) == NULL) { 1506237e61d9Syasuoka log_warn("q=%u radius_convert_packet() failed", 1507237e61d9Syasuoka q->id); 1508237e61d9Syasuoka radiusd_access_request_aborted(q); 1509237e61d9Syasuoka break; 1510237e61d9Syasuoka } 1511a7ca44b8Syasuoka module->radpktoff = 0; 1512237e61d9Syasuoka switch (imsg->hdr.type) { 1513237e61d9Syasuoka case IMSG_RADIUSD_MODULE_REQDECO_DONE: 1514c9e9c1c9Syasuoka if (q->deco == NULL || q->deco->type != 1515c9e9c1c9Syasuoka IMSG_RADIUSD_MODULE_REQDECO) { 1516a449bbceSyasuoka log_warnx("q=%u received %s but not " 1517a449bbceSyasuoka "requested", q->id, typestr); 1518c9e9c1c9Syasuoka if (radpkt != NULL) 1519c9e9c1c9Syasuoka radius_delete_packet(radpkt); 1520c9e9c1c9Syasuoka break; 1521c9e9c1c9Syasuoka } 1522237e61d9Syasuoka if (radpkt != NULL) { 1523237e61d9Syasuoka radius_delete_packet(q->req); 1524237e61d9Syasuoka q->req = radpkt; 1525237e61d9Syasuoka } 1526936475aaSyasuoka raidus_query_access_request(q); 1527237e61d9Syasuoka break; 1528237e61d9Syasuoka case IMSG_RADIUSD_MODULE_ACCSREQ_ANSWER: 1529237e61d9Syasuoka if (radpkt == NULL) { 1530c9e9c1c9Syasuoka log_warnx("q=%u wrong pkt from module", 1531237e61d9Syasuoka q->id); 1532237e61d9Syasuoka radiusd_access_request_aborted(q); 15335e763c0bSjsg break; 1534237e61d9Syasuoka } 1535237e61d9Syasuoka q->res = radpkt; 1536237e61d9Syasuoka radiusd_access_request_answer(q); 1537237e61d9Syasuoka break; 1538ed1dc925Syasuoka case IMSG_RADIUSD_MODULE_ACCSREQ_NEXT: 1539ed1dc925Syasuoka if (radpkt == NULL) { 1540ed1dc925Syasuoka log_warnx("q=%u wrong pkt from module", 1541ed1dc925Syasuoka q->id); 1542ed1dc925Syasuoka radiusd_access_request_aborted(q); 1543ed1dc925Syasuoka break; 1544ed1dc925Syasuoka } 1545ed1dc925Syasuoka radiusd_access_request_next(q, radpkt); 1546ed1dc925Syasuoka break; 1547237e61d9Syasuoka case IMSG_RADIUSD_MODULE_RESDECO_DONE: 1548c9e9c1c9Syasuoka if (q->deco == NULL || q->deco->type != 1549c9e9c1c9Syasuoka IMSG_RADIUSD_MODULE_RESDECO) { 1550c9e9c1c9Syasuoka log_warnx("q=%u received %s but not " 1551c9e9c1c9Syasuoka "requested", q->id, typestr); 1552c9e9c1c9Syasuoka if (radpkt != NULL) 1553c9e9c1c9Syasuoka radius_delete_packet(radpkt); 1554c9e9c1c9Syasuoka break; 1555c9e9c1c9Syasuoka } 1556237e61d9Syasuoka if (radpkt != NULL) { 1557237e61d9Syasuoka radius_delete_packet(q->res); 1558237e61d9Syasuoka radius_set_request_packet(radpkt, 1559237e61d9Syasuoka q->req); 1560237e61d9Syasuoka q->res = radpkt; 1561237e61d9Syasuoka } 1562936475aaSyasuoka radius_query_access_response(q); 1563237e61d9Syasuoka break; 1564237e61d9Syasuoka } 1565a7ca44b8Syasuoka } 1566a7ca44b8Syasuoka break; 1567a7ca44b8Syasuoka } 1568a7ca44b8Syasuoka case IMSG_RADIUSD_MODULE_ACCSREQ_ABORTED: 1569a7ca44b8Syasuoka { 15702e151769Syasuoka if (datalen < (ssize_t)sizeof(u_int)) { 15712e151769Syasuoka log_warnx("Received ACCSREQ_ABORTED message, but " 15722e151769Syasuoka "length is wrong"); 15732e151769Syasuoka break; 15742e151769Syasuoka } 1575a7ca44b8Syasuoka q_id = *((u_int *)imsg->data); 1576a7ca44b8Syasuoka q = radiusd_find_query(module->radiusd, q_id); 1577a7ca44b8Syasuoka if (q == NULL) { 1578a7ca44b8Syasuoka log_warnx("Received ACCSREQ_ABORT from %s, but query " 1579a7ca44b8Syasuoka "id=%u unknown", module->name, q_id); 1580a7ca44b8Syasuoka break; 1581a7ca44b8Syasuoka } 1582a7ca44b8Syasuoka radiusd_access_request_aborted(q); 1583a7ca44b8Syasuoka break; 1584a7ca44b8Syasuoka } 1585842565f2Syasuoka case IMSG_RADIUSD_MODULE_CTRL_BIND: 1586842565f2Syasuoka control_conn_bind(imsg->hdr.peerid, module->name); 1587842565f2Syasuoka break; 1588a7ca44b8Syasuoka default: 1589842565f2Syasuoka if (imsg->hdr.peerid != 0) 1590842565f2Syasuoka control_imsg_relay(imsg); 1591842565f2Syasuoka else 1592842565f2Syasuoka RADIUSD_DBG(("Unhandled imsg type=%d from %s", 1593842565f2Syasuoka imsg->hdr.type, module->name)); 1594a7ca44b8Syasuoka } 1595a7ca44b8Syasuoka } 1596a7ca44b8Syasuoka 1597a7ca44b8Syasuoka static struct radiusd_module_radpkt_arg * 1598a7ca44b8Syasuoka radiusd_module_recv_radpkt(struct radiusd_module *module, struct imsg *imsg, 1599a7ca44b8Syasuoka uint32_t imsg_type, const char *type_str) 1600a7ca44b8Syasuoka { 1601a7ca44b8Syasuoka struct radiusd_module_radpkt_arg *ans; 1602a7ca44b8Syasuoka int datalen, chunklen; 1603a7ca44b8Syasuoka 1604a7ca44b8Syasuoka datalen = imsg->hdr.len - IMSG_HEADER_SIZE; 1605a7ca44b8Syasuoka ans = (struct radiusd_module_radpkt_arg *)imsg->data; 16063f8258c8Syasuoka if (module->radpktsiz < ans->pktlen) { 1607a7ca44b8Syasuoka u_char *nradpkt; 16083f8258c8Syasuoka if ((nradpkt = realloc(module->radpkt, ans->pktlen)) == NULL) { 1609a7ca44b8Syasuoka log_warn("Could not handle received %s message from " 1610a7ca44b8Syasuoka "`%s'", type_str, module->name); 1611a7ca44b8Syasuoka goto on_fail; 1612a7ca44b8Syasuoka } 1613a7ca44b8Syasuoka module->radpkt = nradpkt; 16143f8258c8Syasuoka module->radpktsiz = ans->pktlen; 1615a7ca44b8Syasuoka } 1616a7ca44b8Syasuoka chunklen = datalen - sizeof(struct radiusd_module_radpkt_arg); 1617a7ca44b8Syasuoka if (chunklen > module->radpktsiz - module->radpktoff) { 1618a7ca44b8Syasuoka log_warnx("Could not handle received %s message from `%s': " 1619a7ca44b8Syasuoka "received length is too big", type_str, module->name); 1620a7ca44b8Syasuoka goto on_fail; 1621a7ca44b8Syasuoka } 1622237e61d9Syasuoka if (chunklen > 0) { 1623a7ca44b8Syasuoka memcpy(module->radpkt + module->radpktoff, 1624a7ca44b8Syasuoka (caddr_t)(ans + 1), chunklen); 1625a7ca44b8Syasuoka module->radpktoff += chunklen; 1626237e61d9Syasuoka } 1627a7ca44b8Syasuoka if (!ans->final) 1628a7ca44b8Syasuoka return (NULL); /* again */ 16293f8258c8Syasuoka if (module->radpktoff != ans->pktlen) { 1630a7ca44b8Syasuoka log_warnx("Could not handle received %s message from `%s': " 1631a7ca44b8Syasuoka "length is mismatch", type_str, module->name); 1632a7ca44b8Syasuoka goto on_fail; 1633a7ca44b8Syasuoka } 1634a7ca44b8Syasuoka 1635a7ca44b8Syasuoka return (ans); 1636a7ca44b8Syasuoka on_fail: 1637a7ca44b8Syasuoka module->radpktoff = 0; 1638a7ca44b8Syasuoka return (NULL); 1639a7ca44b8Syasuoka } 1640a7ca44b8Syasuoka 1641a7ca44b8Syasuoka int 1642a7ca44b8Syasuoka radiusd_module_set(struct radiusd_module *module, const char *name, 1643a7ca44b8Syasuoka int argc, char * const * argv) 1644a7ca44b8Syasuoka { 1645a7ca44b8Syasuoka struct radiusd_module_set_arg arg; 1646a7ca44b8Syasuoka struct radiusd_module_object *val; 1647a7ca44b8Syasuoka int i, niov = 0; 1648a7ca44b8Syasuoka u_char *buf = NULL, *buf0; 1649a7ca44b8Syasuoka ssize_t n; 1650a7ca44b8Syasuoka size_t bufsiz = 0, bufoff = 0, bufsiz0; 1651a7ca44b8Syasuoka size_t vallen, valsiz; 1652a7ca44b8Syasuoka struct iovec iov[2]; 1653a7ca44b8Syasuoka struct imsg imsg; 1654a7ca44b8Syasuoka 1655a7ca44b8Syasuoka memset(&arg, 0, sizeof(arg)); 1656a7ca44b8Syasuoka arg.nparamval = argc; 1657a7ca44b8Syasuoka strlcpy(arg.paramname, name, sizeof(arg.paramname)); 1658a7ca44b8Syasuoka 1659a7ca44b8Syasuoka iov[niov].iov_base = &arg; 1660a7ca44b8Syasuoka iov[niov].iov_len = sizeof(struct radiusd_module_set_arg); 1661a7ca44b8Syasuoka niov++; 1662a7ca44b8Syasuoka 1663a7ca44b8Syasuoka for (i = 0; i < argc; i++) { 1664a7ca44b8Syasuoka vallen = strlen(argv[i]) + 1; 1665a7ca44b8Syasuoka valsiz = sizeof(struct radiusd_module_object) + vallen; 1666a7ca44b8Syasuoka if (bufsiz < bufoff + valsiz) { 1667a7ca44b8Syasuoka bufsiz0 = bufoff + valsiz + 128; 1668a7ca44b8Syasuoka if ((buf0 = realloc(buf, bufsiz0)) == NULL) { 1669a7ca44b8Syasuoka log_warn("Failed to set config parameter to " 1670a7ca44b8Syasuoka "module `%s': realloc", module->name); 1671a7ca44b8Syasuoka goto on_error; 1672a7ca44b8Syasuoka } 1673a7ca44b8Syasuoka buf = buf0; 1674a7ca44b8Syasuoka bufsiz = bufsiz0; 1675a7ca44b8Syasuoka memset(buf + bufoff, 0, bufsiz - bufoff); 1676a7ca44b8Syasuoka } 1677a7ca44b8Syasuoka val = (struct radiusd_module_object *)(buf + bufoff); 1678a7ca44b8Syasuoka val->size = valsiz; 1679a7ca44b8Syasuoka memcpy(val + 1, argv[i], vallen); 1680a7ca44b8Syasuoka 1681a7ca44b8Syasuoka bufoff += valsiz; 1682a7ca44b8Syasuoka } 1683a7ca44b8Syasuoka iov[niov].iov_base = buf; 1684a7ca44b8Syasuoka iov[niov].iov_len = bufoff; 1685a7ca44b8Syasuoka niov++; 1686a7ca44b8Syasuoka 1687a7ca44b8Syasuoka if (imsg_composev(&module->ibuf, IMSG_RADIUSD_MODULE_SET_CONFIG, 0, 0, 1688a7ca44b8Syasuoka -1, iov, niov) == -1) { 1689a7ca44b8Syasuoka log_warn("Failed to set config parameter to module `%s': " 1690a7ca44b8Syasuoka "imsg_composev", module->name); 1691a7ca44b8Syasuoka goto on_error; 1692a7ca44b8Syasuoka } 1693a7ca44b8Syasuoka if (imsg_sync_flush(&module->ibuf, MODULE_IO_TIMEOUT) == -1) { 1694a7ca44b8Syasuoka log_warn("Failed to set config parameter to module `%s': " 1695a7ca44b8Syasuoka "imsg_flush_timeout", module->name); 1696a7ca44b8Syasuoka goto on_error; 1697a7ca44b8Syasuoka } 1698a7ca44b8Syasuoka for (;;) { 1699a7ca44b8Syasuoka if (imsg_sync_read(&module->ibuf, MODULE_IO_TIMEOUT) <= 0) { 1700a7ca44b8Syasuoka log_warn("Failed to get reply from module `%s': " 1701a7ca44b8Syasuoka "imsg_sync_read", module->name); 1702a7ca44b8Syasuoka goto on_error; 1703a7ca44b8Syasuoka } 1704a7ca44b8Syasuoka if ((n = imsg_get(&module->ibuf, &imsg)) > 0) 1705a7ca44b8Syasuoka break; 1706a7ca44b8Syasuoka if (n < 0) { 1707a7ca44b8Syasuoka log_warn("Failed to get reply from module `%s': " 1708a7ca44b8Syasuoka "imsg_get", module->name); 1709a7ca44b8Syasuoka goto on_error; 1710a7ca44b8Syasuoka } 1711a7ca44b8Syasuoka } 1712a7ca44b8Syasuoka if (imsg.hdr.type == IMSG_NG) { 1713a7ca44b8Syasuoka log_warnx("Could not set `%s' for module `%s': %s", name, 1714a7ca44b8Syasuoka module->name, (char *)imsg.data); 1715a7ca44b8Syasuoka goto on_error; 1716a7ca44b8Syasuoka } else if (imsg.hdr.type != IMSG_OK) { 1717a7ca44b8Syasuoka imsg_free(&imsg); 1718a7ca44b8Syasuoka log_warnx("Failed to get reply from module `%s': " 1719a7ca44b8Syasuoka "unknown imsg type=%d", module->name, imsg.hdr.type); 1720a7ca44b8Syasuoka goto on_error; 1721a7ca44b8Syasuoka } 1722a7ca44b8Syasuoka imsg_free(&imsg); 1723a7ca44b8Syasuoka 1724a7ca44b8Syasuoka free(buf); 1725a7ca44b8Syasuoka return (0); 1726a7ca44b8Syasuoka 1727a7ca44b8Syasuoka on_error: 1728a7ca44b8Syasuoka free(buf); 1729a7ca44b8Syasuoka return (-1); 1730a7ca44b8Syasuoka } 1731a7ca44b8Syasuoka 1732a7ca44b8Syasuoka static void 1733a7ca44b8Syasuoka radiusd_module_userpass(struct radiusd_module *module, struct radius_query *q) 1734a7ca44b8Syasuoka { 1735a7ca44b8Syasuoka struct radiusd_module_userpass_arg userpass; 1736a7ca44b8Syasuoka 1737a7ca44b8Syasuoka memset(&userpass, 0, sizeof(userpass)); 1738a7ca44b8Syasuoka userpass.q_id = q->id; 1739a7ca44b8Syasuoka 1740a7ca44b8Syasuoka if (radius_get_user_password_attr(q->req, userpass.pass, 1741ed1dc925Syasuoka sizeof(userpass.pass), radius_query_client_secret(q)) == 0) 1742a7ca44b8Syasuoka userpass.has_pass = true; 1743a7ca44b8Syasuoka else 1744a7ca44b8Syasuoka userpass.has_pass = false; 1745237e61d9Syasuoka if (radius_get_string_attr(q->req, RADIUS_TYPE_USER_NAME, 1746237e61d9Syasuoka userpass.user, sizeof(userpass.user)) != 0) { 1747237e61d9Syasuoka log_warnx("q=%u no User-Name attribute", q->id); 1748a7ca44b8Syasuoka goto on_error; 1749a7ca44b8Syasuoka } 1750a7ca44b8Syasuoka imsg_compose(&module->ibuf, IMSG_RADIUSD_MODULE_USERPASS, 0, 0, -1, 1751a7ca44b8Syasuoka &userpass, sizeof(userpass)); 1752a7ca44b8Syasuoka radiusd_module_reset_ev_handler(module); 1753a7ca44b8Syasuoka return; 1754a7ca44b8Syasuoka on_error: 1755a7ca44b8Syasuoka radiusd_access_request_aborted(q); 1756a7ca44b8Syasuoka } 1757a7ca44b8Syasuoka 1758a7ca44b8Syasuoka static void 1759a7ca44b8Syasuoka radiusd_module_access_request(struct radiusd_module *module, 1760a7ca44b8Syasuoka struct radius_query *q) 1761a7ca44b8Syasuoka { 1762a7ca44b8Syasuoka RADIUS_PACKET *radpkt; 1763a7ca44b8Syasuoka char pass[256]; 1764a7ca44b8Syasuoka 1765a7ca44b8Syasuoka if ((radpkt = radius_convert_packet(radius_get_data(q->req), 1766a7ca44b8Syasuoka radius_get_length(q->req))) == NULL) { 1767237e61d9Syasuoka log_warn("q=%u Could not send ACCSREQ to `%s'", q->id, 1768237e61d9Syasuoka module->name); 1769237e61d9Syasuoka radiusd_access_request_aborted(q); 1770a7ca44b8Syasuoka return; 1771a7ca44b8Syasuoka } 1772a449bbceSyasuoka if (radius_get_user_password_attr(radpkt, pass, sizeof(pass), 1773a7ca44b8Syasuoka q->client->secret) == 0) { 1774a7ca44b8Syasuoka radius_del_attr_all(radpkt, RADIUS_TYPE_USER_PASSWORD); 1775a7ca44b8Syasuoka (void)radius_put_raw_attr(radpkt, RADIUS_TYPE_USER_PASSWORD, 1776a7ca44b8Syasuoka pass, strlen(pass)); 1777a7ca44b8Syasuoka } 1778237e61d9Syasuoka if (imsg_compose_radius_packet(&module->ibuf, 1779237e61d9Syasuoka IMSG_RADIUSD_MODULE_ACCSREQ, q->id, radpkt) == -1) { 1780237e61d9Syasuoka log_warn("q=%u Could not send ACCSREQ to `%s'", q->id, 1781237e61d9Syasuoka module->name); 1782237e61d9Syasuoka radiusd_access_request_aborted(q); 1783a7ca44b8Syasuoka } 1784a7ca44b8Syasuoka radiusd_module_reset_ev_handler(module); 1785a7ca44b8Syasuoka radius_delete_packet(radpkt); 1786237e61d9Syasuoka } 1787a7ca44b8Syasuoka 1788237e61d9Syasuoka static void 1789ed1dc925Syasuoka radiusd_module_next_response(struct radiusd_module *module, 1790ed1dc925Syasuoka struct radius_query *q, RADIUS_PACKET *pkt) 1791ed1dc925Syasuoka { 1792ed1dc925Syasuoka if (imsg_compose_radius_packet(&module->ibuf, 1793ed1dc925Syasuoka IMSG_RADIUSD_MODULE_NEXTRES, q->id, pkt) == -1) { 1794ed1dc925Syasuoka log_warn("q=%u Could not send NEXTRES to `%s'", q->id, 1795ed1dc925Syasuoka module->name); 1796ed1dc925Syasuoka radiusd_access_request_aborted(q); 1797ed1dc925Syasuoka } 1798ed1dc925Syasuoka radiusd_module_reset_ev_handler(module); 1799ed1dc925Syasuoka } 1800ed1dc925Syasuoka 1801ed1dc925Syasuoka static void 1802237e61d9Syasuoka radiusd_module_request_decoration(struct radiusd_module *module, 1803237e61d9Syasuoka struct radius_query *q) 1804237e61d9Syasuoka { 1805237e61d9Syasuoka if (module->fd < 0) { 1806237e61d9Syasuoka log_warnx("q=%u Could not send REQDECO to `%s': module is " 1807237e61d9Syasuoka "not running?", q->id, module->name); 1808237e61d9Syasuoka radiusd_access_request_aborted(q); 1809a7ca44b8Syasuoka return; 1810a7ca44b8Syasuoka } 1811237e61d9Syasuoka if (imsg_compose_radius_packet(&module->ibuf, 1812237e61d9Syasuoka IMSG_RADIUSD_MODULE_REQDECO, q->id, q->req) == -1) { 1813237e61d9Syasuoka log_warn("q=%u Could not send REQDECO to `%s'", q->id, 1814237e61d9Syasuoka module->name); 1815237e61d9Syasuoka radiusd_access_request_aborted(q); 1816237e61d9Syasuoka return; 1817237e61d9Syasuoka } 1818c9e9c1c9Syasuoka RADIUSD_ASSERT(q->deco != NULL); 1819c9e9c1c9Syasuoka q->deco->type = IMSG_RADIUSD_MODULE_REQDECO; 1820237e61d9Syasuoka radiusd_module_reset_ev_handler(module); 1821237e61d9Syasuoka } 1822237e61d9Syasuoka 1823237e61d9Syasuoka static void 1824237e61d9Syasuoka radiusd_module_response_decoration(struct radiusd_module *module, 1825237e61d9Syasuoka struct radius_query *q) 1826237e61d9Syasuoka { 1827237e61d9Syasuoka if (module->fd < 0) { 1828237e61d9Syasuoka log_warnx("q=%u Could not send RESDECO to `%s': module is " 1829237e61d9Syasuoka "not running?", q->id, module->name); 1830237e61d9Syasuoka radiusd_access_request_aborted(q); 1831237e61d9Syasuoka return; 1832237e61d9Syasuoka } 1833237e61d9Syasuoka if (imsg_compose_radius_packet(&module->ibuf, 183476e157acSyasuoka IMSG_RADIUSD_MODULE_RESDECO0_REQ, q->id, q->req) == -1) { 183576e157acSyasuoka log_warn("q=%u Could not send RESDECO0_REQ to `%s'", q->id, 183676e157acSyasuoka module->name); 183776e157acSyasuoka radiusd_access_request_aborted(q); 183876e157acSyasuoka return; 183976e157acSyasuoka } 184076e157acSyasuoka if (imsg_compose_radius_packet(&module->ibuf, 1841237e61d9Syasuoka IMSG_RADIUSD_MODULE_RESDECO, q->id, q->res) == -1) { 1842237e61d9Syasuoka log_warn("q=%u Could not send RESDECO to `%s'", q->id, 1843237e61d9Syasuoka module->name); 1844237e61d9Syasuoka radiusd_access_request_aborted(q); 1845237e61d9Syasuoka return; 1846237e61d9Syasuoka } 1847c9e9c1c9Syasuoka RADIUSD_ASSERT(q->deco != NULL); 1848c9e9c1c9Syasuoka q->deco->type = IMSG_RADIUSD_MODULE_RESDECO; 1849237e61d9Syasuoka radiusd_module_reset_ev_handler(module); 1850237e61d9Syasuoka } 1851237e61d9Syasuoka 1852747da5e9Syasuoka static void 1853747da5e9Syasuoka radiusd_module_account_request(struct radiusd_module *module, 1854747da5e9Syasuoka struct radius_query *q) 1855747da5e9Syasuoka { 1856747da5e9Syasuoka RADIUS_PACKET *radpkt; 1857747da5e9Syasuoka 1858747da5e9Syasuoka if ((radpkt = radius_convert_packet(radius_get_data(q->req), 1859747da5e9Syasuoka radius_get_length(q->req))) == NULL) { 1860747da5e9Syasuoka log_warn("q=%u Could not send ACCSREQ to `%s'", q->id, 1861747da5e9Syasuoka module->name); 1862747da5e9Syasuoka radiusd_access_request_aborted(q); 1863747da5e9Syasuoka return; 1864747da5e9Syasuoka } 1865747da5e9Syasuoka if (imsg_compose_radius_packet(&module->ibuf, 1866747da5e9Syasuoka IMSG_RADIUSD_MODULE_ACCTREQ, q->id, radpkt) == -1) { 1867747da5e9Syasuoka log_warn("q=%u Could not send ACCTREQ to `%s'", q->id, 1868747da5e9Syasuoka module->name); 1869747da5e9Syasuoka radiusd_access_request_aborted(q); 1870747da5e9Syasuoka } 1871747da5e9Syasuoka radiusd_module_reset_ev_handler(module); 1872747da5e9Syasuoka radius_delete_packet(radpkt); 1873747da5e9Syasuoka } 1874747da5e9Syasuoka 1875237e61d9Syasuoka static int 1876237e61d9Syasuoka imsg_compose_radius_packet(struct imsgbuf *ibuf, uint32_t type, u_int q_id, 1877237e61d9Syasuoka RADIUS_PACKET *radpkt) 1878237e61d9Syasuoka { 1879237e61d9Syasuoka struct radiusd_module_radpkt_arg arg; 1880237e61d9Syasuoka int off = 0, len, siz; 1881237e61d9Syasuoka struct iovec iov[2]; 1882237e61d9Syasuoka const u_char *pkt; 1883237e61d9Syasuoka 1884237e61d9Syasuoka pkt = radius_get_data(radpkt); 1885237e61d9Syasuoka len = radius_get_length(radpkt); 1886237e61d9Syasuoka memset(&arg, 0, sizeof(arg)); 1887237e61d9Syasuoka arg.q_id = q_id; 1888237e61d9Syasuoka arg.pktlen = len; 1889237e61d9Syasuoka while (off < len) { 1890237e61d9Syasuoka siz = MAX_IMSGSIZE - sizeof(arg); 1891237e61d9Syasuoka if (len - off > siz) 1892237e61d9Syasuoka arg.final = false; 1893237e61d9Syasuoka else { 1894237e61d9Syasuoka arg.final = true; 1895237e61d9Syasuoka siz = len - off; 1896237e61d9Syasuoka } 1897237e61d9Syasuoka iov[0].iov_base = &arg; 1898237e61d9Syasuoka iov[0].iov_len = sizeof(arg); 1899237e61d9Syasuoka iov[1].iov_base = (caddr_t)pkt + off; 1900237e61d9Syasuoka iov[1].iov_len = siz; 1901237e61d9Syasuoka if (imsg_composev(ibuf, type, 0, 0, -1, iov, 2) == -1) 1902237e61d9Syasuoka return (-1); 1903237e61d9Syasuoka off += siz; 1904237e61d9Syasuoka } 1905237e61d9Syasuoka return (0); 1906237e61d9Syasuoka } 19078c9be245Syasuoka 19088c9be245Syasuoka static void 19098c9be245Syasuoka close_stdio(void) 19108c9be245Syasuoka { 19118c9be245Syasuoka int fd; 19128c9be245Syasuoka 19138c9be245Syasuoka if ((fd = open(_PATH_DEVNULL, O_RDWR)) != -1) { 19148c9be245Syasuoka dup2(fd, STDIN_FILENO); 19158c9be245Syasuoka dup2(fd, STDOUT_FILENO); 19168c9be245Syasuoka dup2(fd, STDERR_FILENO); 19178c9be245Syasuoka if (fd > STDERR_FILENO) 19188c9be245Syasuoka close(fd); 19198c9be245Syasuoka } 19208c9be245Syasuoka } 1921842565f2Syasuoka 1922842565f2Syasuoka /*********************************************************************** 1923842565f2Syasuoka * imsg_event 1924842565f2Syasuoka ***********************************************************************/ 1925842565f2Syasuoka struct iovec; 1926842565f2Syasuoka 1927842565f2Syasuoka void 1928842565f2Syasuoka imsg_event_add(struct imsgev *iev) 1929842565f2Syasuoka { 1930842565f2Syasuoka iev->events = EV_READ; 193131be28caSclaudio if (imsgbuf_queuelen(&iev->ibuf) > 0) 1932842565f2Syasuoka iev->events |= EV_WRITE; 1933842565f2Syasuoka 1934842565f2Syasuoka event_del(&iev->ev); 1935842565f2Syasuoka event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev); 1936842565f2Syasuoka event_add(&iev->ev, NULL); 1937842565f2Syasuoka } 1938842565f2Syasuoka 1939842565f2Syasuoka int 1940842565f2Syasuoka imsg_compose_event(struct imsgev *iev, uint32_t type, uint32_t peerid, 1941842565f2Syasuoka pid_t pid, int fd, void *data, size_t datalen) 1942842565f2Syasuoka { 1943842565f2Syasuoka int ret; 1944842565f2Syasuoka 1945842565f2Syasuoka if ((ret = imsg_compose(&iev->ibuf, type, peerid, 1946842565f2Syasuoka pid, fd, data, datalen)) != -1) 1947842565f2Syasuoka imsg_event_add(iev); 1948842565f2Syasuoka return (ret); 1949842565f2Syasuoka } 1950842565f2Syasuoka 1951842565f2Syasuoka int 1952842565f2Syasuoka imsg_composev_event(struct imsgev *iev, uint32_t type, uint32_t peerid, 1953842565f2Syasuoka pid_t pid, int fd, struct iovec *iov, int niov) 1954842565f2Syasuoka { 1955842565f2Syasuoka int ret; 1956842565f2Syasuoka 1957842565f2Syasuoka if ((ret = imsg_composev(&iev->ibuf, type, peerid, 1958842565f2Syasuoka pid, fd, iov, niov)) != -1) 1959842565f2Syasuoka imsg_event_add(iev); 1960842565f2Syasuoka return (ret); 1961842565f2Syasuoka } 1962