xref: /openbsd-src/usr.sbin/hostapd/privsep.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
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 
564