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
54enum 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
64void	 hostapd_priv(int, short, void *);
65struct hostapd_apme *hostapd_priv_getapme(int, struct hostapd_config *);
66void	 hostapd_sig_relay(int, short, void *);
67void	 hostapd_sig_chld(int, short, void *);
68int	 hostapd_may_read(int, void *, size_t);
69void	 hostapd_must_read(int, void *, size_t);
70void	 hostapd_must_write(int, void *, size_t);
71
72static int priv_fd = -1;
73static volatile pid_t child_pid = -1;
74
75/*
76 * Main privsep functions
77 */
78
79void
80hostapd_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
186struct hostapd_apme *
187hostapd_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
203void
204hostapd_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 */
332int
333hostapd_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
356int
357hostapd_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
388void
389hostapd_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
412int
413hostapd_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
430int
431hostapd_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 */
459void
460hostapd_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
471void
472hostapd_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.  */
487int
488hostapd_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 */
513void
514hostapd_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 */
539void
540hostapd_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