GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/hostapd/privsep.c Lines: 0 232 0.0 %
Date: 2017-11-13 Branches: 0 133 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: privsep.c,v 1.25 2016/02/02 17:51:11 sthen Exp $	*/
2
3
/*
4
 * Copyright (c) 2004, 2005 Reyk Floeter <reyk@openbsd.org>
5
 * Copyright (c) 1995, 1999 Theo de Raadt
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/socket.h>
23
#include <sys/time.h>
24
25
#include <net/if.h>
26
#include <net/if_dl.h>
27
#include <net/if_media.h>
28
#include <net/if_arp.h>
29
#include <net/if_llc.h>
30
#include <net/bpf.h>
31
32
#include <netinet/in.h>
33
#include <netinet/if_ether.h>
34
#include <arpa/inet.h>
35
36
#include <net80211/ieee80211.h>
37
#include <net80211/ieee80211_ioctl.h>
38
39
#include <errno.h>
40
#include <event.h>
41
#include <fcntl.h>
42
#include <netdb.h>
43
#include <pwd.h>
44
#include <signal.h>
45
#include <stdarg.h>
46
#include <stdio.h>
47
#include <stdlib.h>
48
#include <string.h>
49
#include <unistd.h>
50
#include <limits.h>
51
52
#include "hostapd.h"
53
#include "iapp.h"
54
55
enum hostapd_cmd_types {
56
	PRIV_APME_BSSID,	/* Get the Host AP's BSSID */
57
	PRIV_APME_GETNODE,	/* Get a node from the Host AP */
58
	PRIV_APME_ADDNODE,	/* Delete a node from the Host AP */
59
	PRIV_APME_DELNODE,	/* Delete a node from the Host AP */
60
	PRIV_APME_ADDROAMING,	/* Add a route to the kernel */
61
	PRIV_APME_DELROAMING,	/* Delete a route from the kernel */
62
	PRIV_LLC_SEND_XID	/* Send IEEE 802.3 LLC XID frame */
63
};
64
65
void	 hostapd_priv(int, short, void *);
66
struct hostapd_apme *hostapd_priv_getapme(int, struct hostapd_config *);
67
void	 hostapd_sig_relay(int, short, void *);
68
void	 hostapd_sig_chld(int, short, void *);
69
int	 hostapd_may_read(int, void *, size_t);
70
void	 hostapd_must_read(int, void *, size_t);
71
void	 hostapd_must_write(int, void *, size_t);
72
73
static int priv_fd = -1;
74
static volatile pid_t child_pid = -1;
75
76
/*
77
 * Main privsep functions
78
 */
79
80
void
81
hostapd_priv_init(struct hostapd_config *cfg)
82
{
83
	struct event ev_sigalrm;
84
	struct event ev_sigterm;
85
	struct event ev_sigint;
86
	struct event ev_sighup;
87
	struct event ev_sigchld;
88
	struct hostapd_iapp *iapp = &cfg->c_iapp;
89
	struct hostapd_apme *apme;
90
	int i, socks[2];
91
	struct passwd *pw;
92
	struct servent *se;
93
94
	for (i = 1; i < _NSIG; i++)
95
		signal(i, SIG_DFL);
96
97
	if ((se = getservbyname("iapp", "udp")) == NULL) {
98
		iapp->i_udp_port = IAPP_PORT;
99
	} else
100
		iapp->i_udp_port = se->s_port;
101
102
	if ((pw = getpwnam(HOSTAPD_USER)) == NULL)
103
		hostapd_fatal("failed to get user \"%s\"\n", HOSTAPD_USER);
104
105
	endservent();
106
107
	/* Create sockets */
108
	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
109
		hostapd_fatal("failed to get socket pair\n");
110
111
	if ((child_pid = fork()) < 0)
112
		hostapd_fatal("failed to fork child process\n");
113
114
	/*
115
	 * Unprivileged child process
116
	 */
117
	if (child_pid == 0) {
118
		cfg->c_flags &= ~HOSTAPD_CFG_F_PRIV;
119
120
		/*
121
		 * Change the child's root directory to the unprivileged
122
		 * user's home directory
123
		 */
124
		if (chroot(pw->pw_dir) == -1)
125
			hostapd_fatal("failed to change root directory\n");
126
		if (chdir("/") == -1)
127
			hostapd_fatal("failed to change directory\n");
128
129
		/*
130
		 * Drop privileges and clear the group access list
131
		 */
132
		if (setgroups(1, &pw->pw_gid) == -1 ||
133
		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
134
		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
135
			hostapd_fatal("can't drop privileges\n");
136
137
		(void)close(socks[0]);
138
		priv_fd = socks[1];
139
		return;
140
	}
141
142
	/*
143
	 * Privileged mother process
144
	 */
145
	cfg->c_flags |= HOSTAPD_CFG_F_PRIV;
146
147
	(void)event_init();
148
149
	/* Pass ALRM/TERM/INT/HUP through to child, and accept CHLD */
150
	signal_set(&ev_sigalrm, SIGALRM, hostapd_sig_relay, NULL);
151
	signal_set(&ev_sigterm, SIGTERM, hostapd_sig_relay, NULL);
152
	signal_set(&ev_sigint, SIGINT, hostapd_sig_relay, NULL);
153
	signal_set(&ev_sighup, SIGHUP, hostapd_sig_relay, NULL);
154
	signal_set(&ev_sigchld, SIGCHLD, hostapd_sig_chld, NULL);
155
	signal_add(&ev_sigalrm, NULL);
156
	signal_add(&ev_sigterm, NULL);
157
	signal_add(&ev_sigint, NULL);
158
	signal_add(&ev_sighup, NULL);
159
	signal_add(&ev_sigchld, NULL);
160
161
	(void)close(socks[1]);
162
163
	if (cfg->c_flags & HOSTAPD_CFG_F_APME) {
164
		if ((cfg->c_apme_ctl = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
165
			hostapd_fatal("unable to open ioctl socket\n");
166
		TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries)
167
			if (apme->a_chanavail != NULL)
168
				hostapd_apme_sethopper(apme, 0);
169
	}
170
171
	hostapd_roaming_init(cfg);
172
173
	/* Start a new event listener */
174
	event_set(&cfg->c_priv_ev, socks[0], EV_READ, hostapd_priv, cfg);
175
	if (event_add(&cfg->c_priv_ev, NULL) == -1)
176
		hostapd_fatal("failed to add priv event");
177
178
	/* Run privileged event loop */
179
	if (event_dispatch() == -1)
180
		hostapd_fatal("failed to dispatch priv hostapd");
181
182
	/* Executed after the event loop has been terminated */
183
	hostapd_cleanup(cfg);
184
	_exit(EXIT_SUCCESS);
185
}
186
187
struct hostapd_apme *
188
hostapd_priv_getapme(int fd, struct hostapd_config *cfg)
189
{
190
	struct hostapd_apme *apme;
191
	char name[IFNAMSIZ];
192
	int n;
193
194
	hostapd_must_read(fd, name, IFNAMSIZ);
195
	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0 ||
196
	    (apme = hostapd_apme_lookup(cfg, name)) == NULL) {
197
		n = ENXIO;
198
		hostapd_must_write(fd, &n, sizeof(int));
199
		return (NULL);
200
	}
201
	return (apme);
202
}
203
204
void
205
hostapd_priv(int fd, short sig, void *arg)
206
{
207
	struct hostapd_config *cfg = (struct hostapd_config *)arg;
208
	struct hostapd_apme *apme;
209
	struct hostapd_node node;
210
	struct ieee80211_bssid bssid;
211
	struct ieee80211_nodereq nr;
212
	struct ifreq ifr;
213
	unsigned long request;
214
	int ret = 0, cmd;
215
216
	/* Terminate the event if we got an invalid signal */
217
	if (sig != EV_READ)
218
		return;
219
220
	bzero(&node, sizeof(struct hostapd_node));
221
	bzero(&nr, sizeof(struct ieee80211_nodereq));
222
223
	/* Get privsep command */
224
	if (hostapd_may_read(fd, &cmd, sizeof(int)))
225
		return;
226
227
	switch (cmd) {
228
	case PRIV_APME_BSSID:
229
		hostapd_log(HOSTAPD_LOG_DEBUG,
230
		    "[priv]: msg PRIV_APME_BSSID received");
231
232
		if ((apme = hostapd_priv_getapme(fd, cfg)) == NULL)
233
			break;
234
		(void)strlcpy(bssid.i_name, apme->a_iface, sizeof(bssid.i_name));
235
236
		/* Try to get the APME's BSSID */
237
		if ((ret = ioctl(cfg->c_apme_ctl,
238
		    SIOCG80211BSSID, &bssid)) != 0)
239
			ret = errno;
240
241
		hostapd_must_write(fd, &ret, sizeof(int));
242
		if (ret == 0)
243
			hostapd_must_write(fd, &bssid.i_bssid,
244
			    IEEE80211_ADDR_LEN);
245
		break;
246
247
	case PRIV_APME_GETNODE:
248
		hostapd_log(HOSTAPD_LOG_DEBUG,
249
		    "[priv]: msg PRIV_APME_GETNODE received");
250
251
		hostapd_must_read(fd, &node, sizeof(struct hostapd_node));
252
		bcopy(node.ni_macaddr, nr.nr_macaddr, IEEE80211_ADDR_LEN);
253
254
		if ((apme = hostapd_priv_getapme(fd, cfg)) == NULL)
255
			break;
256
		(void)strlcpy(nr.nr_ifname, apme->a_iface, sizeof(ifr.ifr_name));
257
258
		/* Try to get a station from the APME */
259
		if ((ret = ioctl(cfg->c_apme_ctl,
260
		    SIOCG80211NODE, &nr)) != 0)
261
			ret = errno;
262
263
		hostapd_must_write(fd, &ret, sizeof(int));
264
		if (ret == 0) {
265
			node.ni_associd = nr.nr_associd;
266
			node.ni_flags = IEEE80211_NODEREQ_STATE(nr.nr_state);
267
			node.ni_rssi = nr.nr_rssi;
268
			node.ni_capinfo = nr.nr_capinfo;
269
270
			hostapd_must_write(fd, &node,
271
			    sizeof(struct hostapd_node));
272
		}
273
		break;
274
275
	case PRIV_APME_ADDNODE:
276
	case PRIV_APME_DELNODE:
277
		hostapd_log(HOSTAPD_LOG_DEBUG,
278
		    "[priv]: msg PRIV_APME_[ADD|DEL]NODE received");
279
280
		hostapd_must_read(fd, &node, sizeof(struct hostapd_node));
281
		bcopy(node.ni_macaddr, nr.nr_macaddr, IEEE80211_ADDR_LEN);
282
283
		if ((apme = hostapd_priv_getapme(fd, cfg)) == NULL)
284
			break;
285
		(void)strlcpy(nr.nr_ifname, apme->a_iface, sizeof(ifr.ifr_name));
286
287
		request = cmd == PRIV_APME_ADDNODE ?
288
		    SIOCS80211NODE : SIOCS80211DELNODE;
289
290
		/* Try to add/delete a station from the APME */
291
		if ((ret = ioctl(cfg->c_apme_ctl, request, &nr)) != 0)
292
			ret = errno;
293
294
		hostapd_must_write(fd, &ret, sizeof(int));
295
		break;
296
297
	case PRIV_LLC_SEND_XID:
298
		hostapd_log(HOSTAPD_LOG_DEBUG,
299
		    "[priv]: msg PRIV_LLC_SEND_XID received");
300
301
		hostapd_must_read(fd, &node, sizeof(struct hostapd_node));
302
303
		/* Send a LLC XID frame to reset possible switch ports */
304
		ret = hostapd_llc_send_xid(cfg, &node);
305
		hostapd_must_write(fd, &ret, sizeof(int));
306
		break;
307
308
	case PRIV_APME_ADDROAMING:
309
	case PRIV_APME_DELROAMING:
310
		hostapd_log(HOSTAPD_LOG_DEBUG,
311
		    "[priv]: msg PRIV_APME_[ADD|DEL]ROAMING received");
312
313
		hostapd_must_read(fd, &node, sizeof(struct hostapd_node));
314
315
		if ((apme = hostapd_priv_getapme(fd, cfg)) == NULL)
316
			break;
317
		ret = hostapd_roaming(apme, &node, cmd == PRIV_APME_ADDROAMING);
318
		hostapd_must_write(fd, &ret, sizeof(int));
319
		break;
320
321
	default:
322
		hostapd_fatal("[priv]: unknown command %d\n", cmd);
323
	}
324
	if (event_add(&cfg->c_priv_ev, NULL) == -1)
325
		hostapd_fatal("failed to schedult priv event");
326
327
	return;
328
}
329
330
/*
331
 * Unprivileged callers
332
 */
333
int
334
hostapd_priv_apme_getnode(struct hostapd_apme *apme, struct hostapd_node *node)
335
{
336
	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
337
	int ret, cmd;
338
339
	if (priv_fd < 0)
340
		hostapd_fatal("%s: called from privileged portion\n", __func__);
341
342
	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
343
		hostapd_fatal("%s: Host AP is not available\n", __func__);
344
345
	cmd = PRIV_APME_GETNODE;
346
	hostapd_must_write(priv_fd, &cmd, sizeof(int));
347
	hostapd_must_write(priv_fd, node, sizeof(struct hostapd_node));
348
	hostapd_must_write(priv_fd, &apme->a_iface, IFNAMSIZ);
349
	hostapd_must_read(priv_fd, &ret, sizeof(int));
350
	if (ret != 0)
351
		return (ret);
352
353
	hostapd_must_read(priv_fd, node, sizeof(struct hostapd_node));
354
	return (ret);
355
}
356
357
int
358
hostapd_priv_apme_setnode(struct hostapd_apme *apme, struct hostapd_node *node,
359
    int add)
360
{
361
	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
362
	struct hostapd_iapp *iapp = &cfg->c_iapp;
363
	int ret, cmd;
364
365
	if (priv_fd < 0)
366
		hostapd_fatal("%s: called from privileged portion\n", __func__);
367
368
	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
369
		hostapd_fatal("%s: Host AP is not available\n", __func__);
370
371
	if (add)
372
		cmd = PRIV_APME_ADDNODE;
373
	else
374
		cmd = PRIV_APME_DELNODE;
375
	hostapd_must_write(priv_fd, &cmd, sizeof(int));
376
	hostapd_must_write(priv_fd, node, sizeof(struct hostapd_node));
377
	hostapd_must_write(priv_fd, &apme->a_iface, IFNAMSIZ);
378
379
	hostapd_must_read(priv_fd, &ret, sizeof(int));
380
	if (ret == 0)
381
		hostapd_log(HOSTAPD_LOG_VERBOSE, "%s/%s: %s node %s",
382
		    apme->a_iface, iapp->i_iface,
383
		    add ? "added" : "removed",
384
		    etheraddr_string(node->ni_macaddr));
385
386
	return (ret);
387
}
388
389
void
390
hostapd_priv_apme_bssid(struct hostapd_apme *apme)
391
{
392
	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
393
	int ret, cmd;
394
395
	if (priv_fd < 0)
396
		hostapd_fatal("%s: called from privileged portion\n", __func__);
397
398
	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
399
		hostapd_fatal("%s: Host AP is not available\n", __func__);
400
401
	cmd = PRIV_APME_BSSID;
402
	hostapd_must_write(priv_fd, &cmd, sizeof(int));
403
	hostapd_must_write(priv_fd, &apme->a_iface, IFNAMSIZ);
404
	hostapd_must_read(priv_fd, &ret, sizeof(int));
405
	if (ret != 0)
406
		hostapd_fatal("failed to get Host AP's BSSID on"
407
		    " \"%s\": %s\n", apme->a_iface, strerror(errno));
408
409
	hostapd_must_read(priv_fd, &apme->a_bssid, IEEE80211_ADDR_LEN);
410
	cfg->c_stats.cn_tx_apme++;
411
}
412
413
int
414
hostapd_priv_llc_xid(struct hostapd_config *cfg, struct hostapd_node *node)
415
{
416
	int ret, cmd;
417
418
	if (priv_fd < 0)
419
		hostapd_fatal("%s: called from privileged portion\n", __func__);
420
421
	cmd = PRIV_LLC_SEND_XID;
422
	hostapd_must_write(priv_fd, &cmd, sizeof(int));
423
	hostapd_must_write(priv_fd, node, sizeof(struct hostapd_node));
424
	hostapd_must_read(priv_fd, &ret, sizeof(int));
425
426
	if (ret == 0)
427
		cfg->c_stats.cn_tx_llc++;
428
	return (ret);
429
}
430
431
int
432
hostapd_priv_roaming(struct hostapd_apme *apme, struct hostapd_node *node,
433
    int add)
434
{
435
	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
436
	int ret, cmd;
437
438
	if (priv_fd < 0)
439
		hostapd_fatal("%s: called from privileged portion\n", __func__);
440
441
	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
442
		hostapd_fatal("%s: Host AP is not available\n", __func__);
443
444
	if (add)
445
		cmd = PRIV_APME_ADDROAMING;
446
	else
447
		cmd = PRIV_APME_DELROAMING;
448
	hostapd_must_write(priv_fd, &cmd, sizeof(int));
449
	hostapd_must_write(priv_fd, node, sizeof(struct hostapd_node));
450
	hostapd_must_write(priv_fd, &apme->a_iface, IFNAMSIZ);
451
452
	hostapd_must_read(priv_fd, &ret, sizeof(int));
453
454
	return (ret);
455
}
456
457
/*
458
 * If priv parent gets a TERM or HUP, pass it through to child instead.
459
 */
460
/* ARGSUSED */
461
void
462
hostapd_sig_relay(int sig, short event, void *arg)
463
{
464
	int oerrno = errno;
465
466
	if (child_pid != -1)
467
		if (kill(child_pid, sig) == -1)
468
			hostapd_fatal("hostapd_sig_relay: kill(%d, %d)",
469
			    child_pid, sig);
470
	errno = oerrno;
471
}
472
473
/* ARGSUSED */
474
void
475
hostapd_sig_chld(int sig, short event, void *arg)
476
{
477
	/*
478
	 * If parent gets a SIGCHLD, it will exit.
479
	 */
480
481
	if (sig == SIGCHLD)
482
		(void)event_loopexit(NULL);
483
}
484
485
/*
486
 * privsep I/O functions
487
 */
488
489
/* Read all data or return 1 for error.  */
490
int
491
hostapd_may_read(int fd, void *buf, size_t n)
492
{
493
	char *s = buf;
494
	ssize_t res, pos = 0;
495
496
	while ((ssize_t)n > pos) {
497
		res = read(fd, s + pos, n - pos);
498
		switch (res) {
499
		case -1:
500
			if (errno == EINTR || errno == EAGAIN)
501
				continue;
502
			/* FALLTHROUGH */
503
		case 0:
504
			return (1);
505
		default:
506
			pos += res;
507
		}
508
	}
509
	return (0);
510
}
511
512
/*
513
 * Read data with the assertion that it all must come through, or
514
 * else abort the process.  Based on atomicio() from openssh.
515
 */
516
void
517
hostapd_must_read(int fd, void *buf, size_t n)
518
{
519
	char *s = buf;
520
	ssize_t res, pos = 0;
521
522
	while ((ssize_t)n > pos) {
523
		res = read(fd, s + pos, n - pos);
524
		switch (res) {
525
		case -1:
526
			if (errno == EINTR || errno == EAGAIN)
527
				continue;
528
			/* FALLTHROUGH */
529
		case 0:
530
			_exit(0);
531
			break;
532
		default:
533
			pos += res;
534
		}
535
	}
536
}
537
538
/*
539
 * Write data with the assertion that it all has to be written, or
540
 * else abort the process.  Based on atomicio() from openssh.
541
 */
542
void
543
hostapd_must_write(int fd, void *buf, size_t n)
544
{
545
	char *s = buf;
546
	ssize_t res, pos = 0;
547
548
	while ((ssize_t)n > pos) {
549
		res = write(fd, s + pos, n - pos);
550
		switch (res) {
551
		case -1:
552
			if (errno == EINTR || errno == EAGAIN)
553
				continue;
554
			/* FALLTHROUGH */
555
		case 0:
556
			_exit(0);
557
			break;
558
		default:
559
			pos += res;
560
		}
561
	}
562
}
563