1 /* $OpenBSD: hostapd.c,v 1.42 2023/03/08 04:43:13 guenther Exp $ */ 2 3 /* 4 * Copyright (c) 2004, 2005 Reyk Floeter <reyk@openbsd.org> 5 * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/ioctl.h> 21 #include <sys/types.h> 22 #include <sys/signal.h> 23 #include <sys/socket.h> 24 #include <sys/time.h> 25 #include <sys/queue.h> 26 #include <sys/stat.h> 27 28 #include <net/if.h> 29 #include <net/if_media.h> 30 #include <net/if_arp.h> 31 #include <net/if_llc.h> 32 #include <net/bpf.h> 33 34 #include <netinet/in.h> 35 #include <netinet/if_ether.h> 36 #include <arpa/inet.h> 37 38 #include <errno.h> 39 #include <event.h> 40 #include <fcntl.h> 41 #include <stdio.h> 42 #include <stdlib.h> 43 #include <stdarg.h> 44 #include <string.h> 45 #include <unistd.h> 46 #include <limits.h> 47 #include <err.h> 48 49 #include "hostapd.h" 50 #include "iapp.h" 51 52 void hostapd_usage(void); 53 void hostapd_udp_init(struct hostapd_config *); 54 void hostapd_sig_handler(int, short, void *); 55 static __inline int 56 hostapd_entry_cmp(struct hostapd_entry *, struct hostapd_entry *); 57 58 struct hostapd_config hostapd_cfg; 59 60 extern char *__progname; 61 char printbuf[BUFSIZ]; 62 63 void 64 hostapd_usage(void) 65 { 66 fprintf(stderr, "usage: %s [-dv] [-D macro=value] [-f file]\n", 67 __progname); 68 exit(EXIT_FAILURE); 69 } 70 71 void 72 hostapd_log(u_int level, const char *fmt, ...) 73 { 74 char *nfmt = NULL; 75 va_list ap; 76 77 if (level > hostapd_cfg.c_verbose) 78 return; 79 80 va_start(ap, fmt); 81 if (hostapd_cfg.c_debug) { 82 if (asprintf(&nfmt, "%s\n", fmt) != -1) 83 vfprintf(stderr, nfmt, ap); 84 else { 85 vfprintf(stderr, fmt, ap); 86 fprintf(stderr, "\n"); 87 } 88 fflush(stderr); 89 } else 90 vsyslog(LOG_INFO, fmt, ap); 91 va_end(ap); 92 93 free(nfmt); 94 } 95 96 void 97 hostapd_printf(const char *fmt, ...) 98 { 99 char newfmt[BUFSIZ]; 100 va_list ap; 101 size_t n; 102 103 if (fmt == NULL) 104 goto flush; 105 106 va_start(ap, fmt); 107 bzero(newfmt, sizeof(newfmt)); 108 if ((n = strlcpy(newfmt, printbuf, sizeof(newfmt))) >= sizeof(newfmt)) 109 goto va_flush; 110 if (strlcpy(newfmt + n, fmt, sizeof(newfmt) - n) >= sizeof(newfmt) - n) 111 goto va_flush; 112 if (vsnprintf(printbuf, sizeof(printbuf), newfmt, ap) < 0) 113 goto va_flush; 114 va_end(ap); 115 116 return; 117 118 va_flush: 119 va_end(ap); 120 flush: 121 if (strlen(printbuf)) 122 hostapd_log(HOSTAPD_LOG, "%s", printbuf); 123 bzero(printbuf, sizeof(printbuf)); 124 } 125 126 void 127 hostapd_fatal(const char *fmt, ...) 128 { 129 va_list ap; 130 131 va_start(ap, fmt); 132 if (hostapd_cfg.c_debug) { 133 vfprintf(stderr, fmt, ap); 134 fflush(stderr); 135 } else 136 vsyslog(LOG_ERR, fmt, ap); 137 va_end(ap); 138 139 hostapd_cleanup(&hostapd_cfg); 140 exit(EXIT_FAILURE); 141 } 142 143 int 144 hostapd_check_file_secrecy(int fd, const char *fname) 145 { 146 struct stat st; 147 148 if (fstat(fd, &st)) { 149 hostapd_log(HOSTAPD_LOG, 150 "cannot stat %s", fname); 151 return (-1); 152 } 153 154 if (st.st_uid != 0 && st.st_uid != getuid()) { 155 hostapd_log(HOSTAPD_LOG, 156 "%s: owner not root or current user", fname); 157 return (-1); 158 } 159 160 if (st.st_mode & (S_IRWXG | S_IRWXO)) { 161 hostapd_log(HOSTAPD_LOG, 162 "%s: group/world readable/writeable", fname); 163 return (-1); 164 } 165 166 return (0); 167 } 168 169 int 170 hostapd_bpf_open(u_int flags) 171 { 172 int fd = -1; 173 struct bpf_version bpv; 174 175 if ((fd = open("/dev/bpf", flags)) == -1) { 176 hostapd_fatal("unable to open BPF device: %s\n", 177 strerror(errno)); 178 } 179 180 /* 181 * Get and validate the BPF version 182 */ 183 184 if (ioctl(fd, BIOCVERSION, &bpv) == -1) 185 hostapd_fatal("failed to get BPF version: %s\n", 186 strerror(errno)); 187 188 if (bpv.bv_major != BPF_MAJOR_VERSION || 189 bpv.bv_minor < BPF_MINOR_VERSION) 190 hostapd_fatal("invalid BPF version\n"); 191 192 return (fd); 193 } 194 195 void 196 hostapd_udp_init(struct hostapd_config *cfg) 197 { 198 struct hostapd_iapp *iapp = &cfg->c_iapp; 199 struct ifreq ifr; 200 struct sockaddr_in *addr, baddr; 201 struct ip_mreq mreq; 202 int brd = 1; 203 204 bzero(&ifr, sizeof(ifr)); 205 206 /* 207 * Open a listening UDP socket 208 */ 209 210 if ((iapp->i_udp = socket(AF_INET, SOCK_DGRAM, 0)) == -1) 211 hostapd_fatal("unable to open udp socket\n"); 212 213 cfg->c_flags |= HOSTAPD_CFG_F_UDP; 214 215 (void)strlcpy(ifr.ifr_name, iapp->i_iface, sizeof(ifr.ifr_name)); 216 217 if (ioctl(iapp->i_udp, SIOCGIFADDR, &ifr) == -1) 218 hostapd_fatal("UDP ioctl %s on \"%s\" failed: %s\n", 219 "SIOCGIFADDR", ifr.ifr_name, strerror(errno)); 220 221 addr = (struct sockaddr_in *)&ifr.ifr_addr; 222 iapp->i_addr.sin_family = AF_INET; 223 iapp->i_addr.sin_addr.s_addr = addr->sin_addr.s_addr; 224 if (iapp->i_addr.sin_port == 0) 225 iapp->i_addr.sin_port = htons(IAPP_PORT); 226 227 if (ioctl(iapp->i_udp, SIOCGIFBRDADDR, &ifr) == -1) 228 hostapd_fatal("UDP ioctl %s on \"%s\" failed: %s\n", 229 "SIOCGIFBRDADDR", ifr.ifr_name, strerror(errno)); 230 231 addr = (struct sockaddr_in *)&ifr.ifr_addr; 232 iapp->i_broadcast.sin_family = AF_INET; 233 iapp->i_broadcast.sin_addr.s_addr = addr->sin_addr.s_addr; 234 iapp->i_broadcast.sin_port = iapp->i_addr.sin_port; 235 236 baddr.sin_family = AF_INET; 237 baddr.sin_addr.s_addr = htonl(INADDR_ANY); 238 baddr.sin_port = iapp->i_addr.sin_port; 239 240 if (bind(iapp->i_udp, (struct sockaddr *)&baddr, 241 sizeof(baddr)) == -1) 242 hostapd_fatal("failed to bind UDP socket: %s\n", 243 strerror(errno)); 244 245 /* 246 * The revised 802.11F standard requires IAPP messages to be 247 * sent via multicast to the default group 224.0.1.178. 248 * Nevertheless, some implementations still use broadcasts 249 * for IAPP messages. 250 */ 251 if (cfg->c_flags & HOSTAPD_CFG_F_BRDCAST) { 252 /* 253 * Enable broadcast 254 */ 255 if (setsockopt(iapp->i_udp, SOL_SOCKET, SO_BROADCAST, 256 &brd, sizeof(brd)) == -1) 257 hostapd_fatal("failed to enable broadcast on socket\n"); 258 259 hostapd_log(HOSTAPD_LOG_DEBUG, "%s: using broadcast mode " 260 "(address %s)", iapp->i_iface, inet_ntoa(addr->sin_addr)); 261 } else { 262 /* 263 * Enable multicast 264 */ 265 bzero(&mreq, sizeof(mreq)); 266 267 iapp->i_multicast.sin_family = AF_INET; 268 if (iapp->i_multicast.sin_addr.s_addr == INADDR_ANY) 269 iapp->i_multicast.sin_addr.s_addr = 270 inet_addr(IAPP_MCASTADDR); 271 iapp->i_multicast.sin_port = iapp->i_addr.sin_port; 272 273 mreq.imr_multiaddr.s_addr = 274 iapp->i_multicast.sin_addr.s_addr; 275 mreq.imr_interface.s_addr = 276 iapp->i_addr.sin_addr.s_addr; 277 278 if (setsockopt(iapp->i_udp, IPPROTO_IP, 279 IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) 280 hostapd_fatal("failed to add multicast membership to " 281 "%s: %s\n", IAPP_MCASTADDR, strerror(errno)); 282 283 if (setsockopt(iapp->i_udp, IPPROTO_IP, IP_MULTICAST_TTL, 284 &iapp->i_ttl, sizeof(iapp->i_ttl)) == -1) 285 hostapd_fatal("failed to set multicast ttl to " 286 "%u: %s\n", iapp->i_ttl, strerror(errno)); 287 288 hostapd_log(HOSTAPD_LOG_DEBUG, "%s: using multicast mode " 289 "(ttl %u, group %s)", iapp->i_iface, iapp->i_ttl, 290 inet_ntoa(iapp->i_multicast.sin_addr)); 291 } 292 } 293 294 void 295 hostapd_sig_handler(int sig, short event, void *arg) 296 { 297 switch (sig) { 298 case SIGALRM: 299 case SIGTERM: 300 case SIGQUIT: 301 case SIGINT: 302 (void)event_loopexit(NULL); 303 } 304 } 305 306 void 307 hostapd_cleanup(struct hostapd_config *cfg) 308 { 309 struct hostapd_iapp *iapp = &cfg->c_iapp; 310 struct ip_mreq mreq; 311 struct hostapd_apme *apme; 312 struct hostapd_table *table; 313 struct hostapd_entry *entry; 314 315 /* Release all Host APs */ 316 if (cfg->c_flags & HOSTAPD_CFG_F_APME) { 317 while ((apme = TAILQ_FIRST(&cfg->c_apmes)) != NULL) 318 hostapd_apme_term(apme); 319 } 320 321 if (cfg->c_flags & HOSTAPD_CFG_F_PRIV && 322 (cfg->c_flags & HOSTAPD_CFG_F_BRDCAST) == 0) { 323 /* 324 * Disable multicast and let the kernel unsubscribe 325 * from the multicast group. 326 */ 327 328 bzero(&mreq, sizeof(mreq)); 329 330 mreq.imr_multiaddr.s_addr = 331 inet_addr(IAPP_MCASTADDR); 332 mreq.imr_interface.s_addr = 333 iapp->i_addr.sin_addr.s_addr; 334 335 if (setsockopt(iapp->i_udp, IPPROTO_IP, 336 IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) == -1) 337 hostapd_log(HOSTAPD_LOG, "failed to remove multicast" 338 " membership to %s: %s", 339 IAPP_MCASTADDR, strerror(errno)); 340 } 341 342 if ((cfg->c_flags & HOSTAPD_CFG_F_PRIV) == 0 && 343 cfg->c_flags & HOSTAPD_CFG_F_APME) { 344 /* Shutdown the Host AP protocol handler */ 345 hostapd_iapp_term(&hostapd_cfg); 346 } 347 348 /* Cleanup tables */ 349 while ((table = TAILQ_FIRST(&cfg->c_tables)) != NULL) { 350 while ((entry = RB_MIN(hostapd_tree, &table->t_tree)) != NULL) { 351 RB_REMOVE(hostapd_tree, &table->t_tree, entry); 352 free(entry); 353 } 354 while ((entry = TAILQ_FIRST(&table->t_mask_head)) != NULL) { 355 TAILQ_REMOVE(&table->t_mask_head, entry, e_entries); 356 free(entry); 357 } 358 TAILQ_REMOVE(&cfg->c_tables, table, t_entries); 359 free(table); 360 } 361 362 hostapd_log(HOSTAPD_LOG_VERBOSE, "bye!"); 363 } 364 365 int 366 main(int argc, char *argv[]) 367 { 368 struct event ev_sigalrm; 369 struct event ev_sigterm; 370 struct event ev_sigquit; 371 struct event ev_sigint; 372 struct hostapd_config *cfg = &hostapd_cfg; 373 struct hostapd_iapp *iapp; 374 struct hostapd_apme *apme; 375 char *config = NULL; 376 u_int debug = 0, ret; 377 int ch; 378 379 /* Set startup logging */ 380 cfg->c_debug = 1; 381 382 /* 383 * Get and parse command line options 384 */ 385 while ((ch = getopt(argc, argv, "f:D:dv")) != -1) { 386 switch (ch) { 387 case 'f': 388 config = optarg; 389 break; 390 case 'D': 391 if (hostapd_parse_symset(optarg) < 0) 392 hostapd_fatal("could not parse macro " 393 "definition %s\n", optarg); 394 break; 395 case 'd': 396 debug++; 397 break; 398 case 'v': 399 cfg->c_verbose++; 400 break; 401 default: 402 hostapd_usage(); 403 } 404 } 405 406 argc -= optind; 407 argv += optind; 408 if (argc > 0) 409 hostapd_usage(); 410 411 if (config == NULL) 412 ret = strlcpy(cfg->c_config, HOSTAPD_CONFIG, sizeof(cfg->c_config)); 413 else 414 ret = strlcpy(cfg->c_config, config, sizeof(cfg->c_config)); 415 if (ret >= sizeof(cfg->c_config)) 416 hostapd_fatal("invalid configuration file\n"); 417 418 if (geteuid()) 419 hostapd_fatal("need root privileges\n"); 420 421 /* Parse the configuration file */ 422 if (hostapd_parse_file(cfg) != 0) 423 hostapd_fatal("invalid configuration in %s\n", cfg->c_config); 424 425 iapp = &cfg->c_iapp; 426 427 if ((cfg->c_flags & HOSTAPD_CFG_F_IAPP) == 0) 428 hostapd_fatal("IAPP interface not specified\n"); 429 430 if (cfg->c_apme_dlt == 0) 431 cfg->c_apme_dlt = HOSTAPD_DLT; 432 433 /* 434 * Setup the hostapd handlers 435 */ 436 hostapd_udp_init(cfg); 437 hostapd_llc_init(cfg); 438 439 /* 440 * Set runtime logging and detach as daemon 441 */ 442 if ((cfg->c_debug = debug) == 0) { 443 openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON); 444 tzset(); 445 if (daemon(0, 0) == -1) 446 hostapd_fatal("failed to daemonize\n"); 447 } 448 449 if (cfg->c_flags & HOSTAPD_CFG_F_APME) { 450 TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) 451 hostapd_apme_init(apme); 452 } else 453 hostapd_log(HOSTAPD_LOG, "%s: running without a Host AP", 454 iapp->i_iface); 455 456 /* Drop all privileges in an unprivileged child process */ 457 hostapd_priv_init(cfg); 458 459 if (cfg->c_flags & HOSTAPD_CFG_F_APME) 460 setproctitle("IAPP: %s, Host AP", iapp->i_iface); 461 else 462 setproctitle("IAPP: %s", iapp->i_iface); 463 464 /* 465 * Unprivileged child process 466 */ 467 468 (void)event_init(); 469 470 /* 471 * Set signal handlers 472 */ 473 signal_set(&ev_sigalrm, SIGALRM, hostapd_sig_handler, NULL); 474 signal_set(&ev_sigterm, SIGTERM, hostapd_sig_handler, NULL); 475 signal_set(&ev_sigquit, SIGQUIT, hostapd_sig_handler, NULL); 476 signal_set(&ev_sigint, SIGINT, hostapd_sig_handler, NULL); 477 signal_add(&ev_sigalrm, NULL); 478 signal_add(&ev_sigterm, NULL); 479 signal_add(&ev_sigquit, NULL); 480 signal_add(&ev_sigint, NULL); 481 signal(SIGHUP, SIG_IGN); 482 signal(SIGCHLD, SIG_IGN); 483 484 /* Initialize the IAPP protocol handler */ 485 hostapd_iapp_init(cfg); 486 487 /* 488 * Schedule the Host AP listener 489 */ 490 if (cfg->c_flags & HOSTAPD_CFG_F_APME) { 491 TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) { 492 event_set(&apme->a_ev, apme->a_raw, 493 EV_READ | EV_PERSIST, hostapd_apme_input, apme); 494 if (event_add(&apme->a_ev, NULL) == -1) 495 hostapd_fatal("failed to add APME event"); 496 } 497 } 498 499 /* 500 * Schedule the IAPP listener 501 */ 502 event_set(&iapp->i_udp_ev, iapp->i_udp, EV_READ | EV_PERSIST, 503 hostapd_iapp_input, cfg); 504 if (event_add(&iapp->i_udp_ev, NULL) == -1) 505 hostapd_fatal("failed to add IAPP event"); 506 507 hostapd_log(HOSTAPD_LOG, "starting hostapd with pid %u", 508 getpid()); 509 510 /* Run event loop */ 511 if (event_dispatch() == -1) 512 hostapd_fatal("failed to dispatch hostapd"); 513 514 /* Executed after the event loop has been terminated */ 515 hostapd_cleanup(cfg); 516 return (EXIT_SUCCESS); 517 } 518 519 void 520 hostapd_randval(u_int8_t *buf, const u_int len) 521 { 522 u_int32_t data = 0; 523 u_int i; 524 525 for (i = 0; i < len; i++) { 526 if ((i % sizeof(data)) == 0) 527 data = arc4random(); 528 buf[i] = data & 0xff; 529 data >>= 8; 530 } 531 } 532 533 struct hostapd_table * 534 hostapd_table_add(struct hostapd_config *cfg, const char *name) 535 { 536 struct hostapd_table *table; 537 538 if (hostapd_table_lookup(cfg, name) != NULL) 539 return (NULL); 540 if ((table = (struct hostapd_table *) 541 calloc(1, sizeof(struct hostapd_table))) == NULL) 542 return (NULL); 543 if (strlcpy(table->t_name, name, sizeof(table->t_name)) >= 544 sizeof(table->t_name)) { 545 free(table); 546 return (NULL); 547 } 548 RB_INIT(&table->t_tree); 549 TAILQ_INIT(&table->t_mask_head); 550 TAILQ_INSERT_TAIL(&cfg->c_tables, table, t_entries); 551 552 return (table); 553 } 554 555 struct hostapd_table * 556 hostapd_table_lookup(struct hostapd_config *cfg, const char *name) 557 { 558 struct hostapd_table *table; 559 560 TAILQ_FOREACH(table, &cfg->c_tables, t_entries) { 561 if (strcmp(name, table->t_name) == 0) 562 return (table); 563 } 564 565 return (NULL); 566 } 567 568 struct hostapd_entry * 569 hostapd_entry_add(struct hostapd_table *table, u_int8_t *lladdr) 570 { 571 struct hostapd_entry *entry; 572 573 if (hostapd_entry_lookup(table, lladdr) != NULL) 574 return (NULL); 575 576 if ((entry = (struct hostapd_entry *) 577 calloc(1, sizeof(struct hostapd_entry))) == NULL) 578 return (NULL); 579 580 bcopy(lladdr, entry->e_lladdr, IEEE80211_ADDR_LEN); 581 RB_INSERT(hostapd_tree, &table->t_tree, entry); 582 583 return (entry); 584 } 585 586 struct hostapd_entry * 587 hostapd_entry_lookup(struct hostapd_table *table, u_int8_t *lladdr) 588 { 589 struct hostapd_entry *entry, key; 590 591 bcopy(lladdr, key.e_lladdr, IEEE80211_ADDR_LEN); 592 if ((entry = RB_FIND(hostapd_tree, &table->t_tree, &key)) != NULL) 593 return (entry); 594 595 /* Masked entries can't be handled by the red-black tree */ 596 TAILQ_FOREACH(entry, &table->t_mask_head, e_entries) { 597 if (HOSTAPD_ENTRY_MASK_MATCH(entry, lladdr)) 598 return (entry); 599 } 600 601 return (NULL); 602 } 603 604 void 605 hostapd_entry_update(struct hostapd_table *table, struct hostapd_entry *entry) 606 { 607 RB_REMOVE(hostapd_tree, &table->t_tree, entry); 608 609 /* Apply mask to entry */ 610 if (entry->e_flags & HOSTAPD_ENTRY_F_MASK) { 611 HOSTAPD_ENTRY_MASK_ADD(entry->e_lladdr, entry->e_mask); 612 TAILQ_INSERT_TAIL(&table->t_mask_head, entry, e_entries); 613 } else { 614 RB_INSERT(hostapd_tree, &table->t_tree, entry); 615 } 616 } 617 618 static __inline int 619 hostapd_entry_cmp(struct hostapd_entry *a, struct hostapd_entry *b) 620 { 621 return (memcmp(a->e_lladdr, b->e_lladdr, IEEE80211_ADDR_LEN)); 622 } 623 624 RB_GENERATE(hostapd_tree, hostapd_entry, e_nodes, hostapd_entry_cmp); 625