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
hostapd_usage(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
hostapd_log(u_int level,const char * fmt,...)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
hostapd_printf(const char * fmt,...)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
hostapd_fatal(const char * fmt,...)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
hostapd_check_file_secrecy(int fd,const char * fname)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
hostapd_bpf_open(u_int flags)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
hostapd_udp_init(struct hostapd_config * cfg)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
hostapd_sig_handler(int sig,short event,void * arg)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
hostapd_cleanup(struct hostapd_config * cfg)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
main(int argc,char * argv[])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
hostapd_randval(u_int8_t * buf,const u_int len)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 *
hostapd_table_add(struct hostapd_config * cfg,const char * name)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 *
hostapd_table_lookup(struct hostapd_config * cfg,const char * name)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 *
hostapd_entry_add(struct hostapd_table * table,u_int8_t * lladdr)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 *
hostapd_entry_lookup(struct hostapd_table * table,u_int8_t * lladdr)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
hostapd_entry_update(struct hostapd_table * table,struct hostapd_entry * entry)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
hostapd_entry_cmp(struct hostapd_entry * a,struct hostapd_entry * b)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