GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/hostapd/hostapd.c Lines: 0 275 0.0 %
Date: 2017-11-07 Branches: 0 342 0.0 %

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