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