xref: /openbsd-src/usr.sbin/hostapd/hostapd.c (revision 99fd087599a8791921855f21bd7e36130f39aadc)
1 /*	$OpenBSD: hostapd.c,v 1.41 2019/07/03 03:24:03 deraadt 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 /* ARGSUSED */
295 void
296 hostapd_sig_handler(int sig, short event, void *arg)
297 {
298 	switch (sig) {
299 	case SIGALRM:
300 	case SIGTERM:
301 	case SIGQUIT:
302 	case SIGINT:
303 		(void)event_loopexit(NULL);
304 	}
305 }
306 
307 void
308 hostapd_cleanup(struct hostapd_config *cfg)
309 {
310 	struct hostapd_iapp *iapp = &cfg->c_iapp;
311 	struct ip_mreq mreq;
312 	struct hostapd_apme *apme;
313 	struct hostapd_table *table;
314 	struct hostapd_entry *entry;
315 
316 	/* Release all Host APs */
317 	if (cfg->c_flags & HOSTAPD_CFG_F_APME) {
318 		while ((apme = TAILQ_FIRST(&cfg->c_apmes)) != NULL)
319 			hostapd_apme_term(apme);
320 	}
321 
322 	if (cfg->c_flags & HOSTAPD_CFG_F_PRIV &&
323 	    (cfg->c_flags & HOSTAPD_CFG_F_BRDCAST) == 0) {
324 		/*
325 		 * Disable multicast and let the kernel unsubscribe
326 		 * from the multicast group.
327 		 */
328 
329 		bzero(&mreq, sizeof(mreq));
330 
331 		mreq.imr_multiaddr.s_addr =
332 		    inet_addr(IAPP_MCASTADDR);
333 		mreq.imr_interface.s_addr =
334 		    iapp->i_addr.sin_addr.s_addr;
335 
336 		if (setsockopt(iapp->i_udp, IPPROTO_IP,
337 		    IP_DROP_MEMBERSHIP, &mreq, sizeof(mreq)) == -1)
338 			hostapd_log(HOSTAPD_LOG, "failed to remove multicast"
339 			    " membership to %s: %s",
340 			    IAPP_MCASTADDR, strerror(errno));
341 	}
342 
343 	if ((cfg->c_flags & HOSTAPD_CFG_F_PRIV) == 0 &&
344 	    cfg->c_flags & HOSTAPD_CFG_F_APME) {
345 		/* Shutdown the Host AP protocol handler */
346 		hostapd_iapp_term(&hostapd_cfg);
347 	}
348 
349 	/* Cleanup tables */
350 	while ((table = TAILQ_FIRST(&cfg->c_tables)) != NULL) {
351 		while ((entry = RB_MIN(hostapd_tree, &table->t_tree)) != NULL) {
352 			RB_REMOVE(hostapd_tree, &table->t_tree, entry);
353 			free(entry);
354 		}
355 		while ((entry = TAILQ_FIRST(&table->t_mask_head)) != NULL) {
356 			TAILQ_REMOVE(&table->t_mask_head, entry, e_entries);
357 			free(entry);
358 		}
359 		TAILQ_REMOVE(&cfg->c_tables, table, t_entries);
360 		free(table);
361 	}
362 
363 	hostapd_log(HOSTAPD_LOG_VERBOSE, "bye!");
364 }
365 
366 int
367 main(int argc, char *argv[])
368 {
369 	struct event ev_sigalrm;
370 	struct event ev_sigterm;
371 	struct event ev_sigquit;
372 	struct event ev_sigint;
373 	struct hostapd_config *cfg = &hostapd_cfg;
374 	struct hostapd_iapp *iapp;
375 	struct hostapd_apme *apme;
376 	char *config = NULL;
377 	u_int debug = 0, ret;
378 	int ch;
379 
380 	/* Set startup logging */
381 	cfg->c_debug = 1;
382 
383 	/*
384 	 * Get and parse command line options
385 	 */
386 	while ((ch = getopt(argc, argv, "f:D:dv")) != -1) {
387 		switch (ch) {
388 		case 'f':
389 			config = optarg;
390 			break;
391 		case 'D':
392 			if (hostapd_parse_symset(optarg) < 0)
393 				hostapd_fatal("could not parse macro "
394 				    "definition %s\n", optarg);
395 			break;
396 		case 'd':
397 			debug++;
398 			break;
399 		case 'v':
400 			cfg->c_verbose++;
401 			break;
402 		default:
403 			hostapd_usage();
404 		}
405 	}
406 
407 	argc -= optind;
408 	argv += optind;
409 	if (argc > 0)
410 		hostapd_usage();
411 
412 	if (config == NULL)
413 		ret = strlcpy(cfg->c_config, HOSTAPD_CONFIG, sizeof(cfg->c_config));
414 	else
415 		ret = strlcpy(cfg->c_config, config, sizeof(cfg->c_config));
416 	if (ret >= sizeof(cfg->c_config))
417 		hostapd_fatal("invalid configuration file\n");
418 
419 	if (geteuid())
420 		hostapd_fatal("need root privileges\n");
421 
422 	/* Parse the configuration file */
423 	if (hostapd_parse_file(cfg) != 0)
424 		hostapd_fatal("invalid configuration in %s\n", cfg->c_config);
425 
426 	iapp = &cfg->c_iapp;
427 
428 	if ((cfg->c_flags & HOSTAPD_CFG_F_IAPP) == 0)
429 		hostapd_fatal("IAPP interface not specified\n");
430 
431 	if (cfg->c_apme_dlt == 0)
432 		cfg->c_apme_dlt = HOSTAPD_DLT;
433 
434 	/*
435 	 * Setup the hostapd handlers
436 	 */
437 	hostapd_udp_init(cfg);
438 	hostapd_llc_init(cfg);
439 
440 	/*
441 	 * Set runtime logging and detach as daemon
442 	 */
443 	if ((cfg->c_debug = debug) == 0) {
444 		openlog(__progname, LOG_PID | LOG_NDELAY, LOG_DAEMON);
445 		tzset();
446 		if (daemon(0, 0) == -1)
447 			hostapd_fatal("failed to daemonize\n");
448 	}
449 
450 	if (cfg->c_flags & HOSTAPD_CFG_F_APME) {
451 		TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries)
452 			hostapd_apme_init(apme);
453 	} else
454 		hostapd_log(HOSTAPD_LOG, "%s: running without a Host AP",
455 		    iapp->i_iface);
456 
457 	/* Drop all privileges in an unprivileged child process */
458 	hostapd_priv_init(cfg);
459 
460 	if (cfg->c_flags & HOSTAPD_CFG_F_APME)
461 		setproctitle("IAPP: %s, Host AP", iapp->i_iface);
462 	else
463 		setproctitle("IAPP: %s", iapp->i_iface);
464 
465 	/*
466 	 * Unprivileged child process
467 	 */
468 
469 	(void)event_init();
470 
471 	/*
472 	 * Set signal handlers
473 	 */
474 	signal_set(&ev_sigalrm, SIGALRM, hostapd_sig_handler, NULL);
475 	signal_set(&ev_sigterm, SIGTERM, hostapd_sig_handler, NULL);
476 	signal_set(&ev_sigquit, SIGQUIT, hostapd_sig_handler, NULL);
477 	signal_set(&ev_sigint, SIGINT, hostapd_sig_handler, NULL);
478 	signal_add(&ev_sigalrm, NULL);
479 	signal_add(&ev_sigterm, NULL);
480 	signal_add(&ev_sigquit, NULL);
481 	signal_add(&ev_sigint, NULL);
482 	signal(SIGHUP, SIG_IGN);
483 	signal(SIGCHLD, SIG_IGN);
484 
485 	/* Initialize the IAPP protocol handler */
486 	hostapd_iapp_init(cfg);
487 
488 	/*
489 	 * Schedule the Host AP listener
490 	 */
491 	if (cfg->c_flags & HOSTAPD_CFG_F_APME) {
492 		TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries) {
493 			event_set(&apme->a_ev, apme->a_raw,
494 			    EV_READ | EV_PERSIST, hostapd_apme_input, apme);
495 			if (event_add(&apme->a_ev, NULL) == -1)
496 				hostapd_fatal("failed to add APME event");
497 		}
498 	}
499 
500 	/*
501 	 * Schedule the IAPP listener
502 	 */
503 	event_set(&iapp->i_udp_ev, iapp->i_udp, EV_READ | EV_PERSIST,
504 	    hostapd_iapp_input, cfg);
505 	if (event_add(&iapp->i_udp_ev, NULL) == -1)
506 		hostapd_fatal("failed to add IAPP event");
507 
508 	hostapd_log(HOSTAPD_LOG, "starting hostapd with pid %u",
509 	    getpid());
510 
511 	/* Run event loop */
512 	if (event_dispatch() == -1)
513 		hostapd_fatal("failed to dispatch hostapd");
514 
515 	/* Executed after the event loop has been terminated */
516 	hostapd_cleanup(cfg);
517 	return (EXIT_SUCCESS);
518 }
519 
520 void
521 hostapd_randval(u_int8_t *buf, const u_int len)
522 {
523 	u_int32_t data = 0;
524 	u_int i;
525 
526 	for (i = 0; i < len; i++) {
527 		if ((i % sizeof(data)) == 0)
528 			data = arc4random();
529 		buf[i] = data & 0xff;
530 		data >>= 8;
531 	}
532 }
533 
534 struct hostapd_table *
535 hostapd_table_add(struct hostapd_config *cfg, const char *name)
536 {
537 	struct hostapd_table *table;
538 
539 	if (hostapd_table_lookup(cfg, name) != NULL)
540 		return (NULL);
541 	if ((table = (struct hostapd_table *)
542 	    calloc(1, sizeof(struct hostapd_table))) == NULL)
543 		return (NULL);
544 	if (strlcpy(table->t_name, name, sizeof(table->t_name)) >=
545 	    sizeof(table->t_name)) {
546 		free(table);
547 		return (NULL);
548 	}
549 	RB_INIT(&table->t_tree);
550 	TAILQ_INIT(&table->t_mask_head);
551 	TAILQ_INSERT_TAIL(&cfg->c_tables, table, t_entries);
552 
553 	return (table);
554 }
555 
556 struct hostapd_table *
557 hostapd_table_lookup(struct hostapd_config *cfg, const char *name)
558 {
559 	struct hostapd_table *table;
560 
561 	TAILQ_FOREACH(table, &cfg->c_tables, t_entries) {
562 		if (strcmp(name, table->t_name) == 0)
563 			return (table);
564 	}
565 
566 	return (NULL);
567 }
568 
569 struct hostapd_entry *
570 hostapd_entry_add(struct hostapd_table *table, u_int8_t *lladdr)
571 {
572 	struct hostapd_entry *entry;
573 
574 	if (hostapd_entry_lookup(table, lladdr) != NULL)
575 		return (NULL);
576 
577 	if ((entry = (struct hostapd_entry *)
578 	    calloc(1, sizeof(struct hostapd_entry))) == NULL)
579 		return (NULL);
580 
581 	bcopy(lladdr, entry->e_lladdr, IEEE80211_ADDR_LEN);
582 	RB_INSERT(hostapd_tree, &table->t_tree, entry);
583 
584 	return (entry);
585 }
586 
587 struct hostapd_entry *
588 hostapd_entry_lookup(struct hostapd_table *table, u_int8_t *lladdr)
589 {
590 	struct hostapd_entry *entry, key;
591 
592 	bcopy(lladdr, key.e_lladdr, IEEE80211_ADDR_LEN);
593 	if ((entry = RB_FIND(hostapd_tree, &table->t_tree, &key)) != NULL)
594 		return (entry);
595 
596 	/* Masked entries can't be handled by the red-black tree */
597 	TAILQ_FOREACH(entry, &table->t_mask_head, e_entries) {
598 		if (HOSTAPD_ENTRY_MASK_MATCH(entry, lladdr))
599 			return (entry);
600 	}
601 
602 	return (NULL);
603 }
604 
605 void
606 hostapd_entry_update(struct hostapd_table *table, struct hostapd_entry *entry)
607 {
608 	RB_REMOVE(hostapd_tree, &table->t_tree, entry);
609 
610 	/* Apply mask to entry */
611 	if (entry->e_flags & HOSTAPD_ENTRY_F_MASK) {
612 		HOSTAPD_ENTRY_MASK_ADD(entry->e_lladdr, entry->e_mask);
613 		TAILQ_INSERT_TAIL(&table->t_mask_head, entry, e_entries);
614 	} else {
615 		RB_INSERT(hostapd_tree, &table->t_tree, entry);
616 	}
617 }
618 
619 static __inline int
620 hostapd_entry_cmp(struct hostapd_entry *a, struct hostapd_entry *b)
621 {
622 	return (memcmp(a->e_lladdr, b->e_lladdr, IEEE80211_ADDR_LEN));
623 }
624 
625 RB_GENERATE(hostapd_tree, hostapd_entry, e_nodes, hostapd_entry_cmp);
626