xref: /openbsd-src/usr.sbin/hostapd/privsep.c (revision 5b133f3f277e80f096764111e64f3a1284acb179)
1*5b133f3fSguenther /*	$OpenBSD: privsep.c,v 1.28 2023/03/08 04:43:13 guenther Exp $	*/
2d2b2a2e3Sreyk 
3d2b2a2e3Sreyk /*
42c56d0d6Sreyk  * Copyright (c) 2004, 2005 Reyk Floeter <reyk@openbsd.org>
5663e37f5Sreyk  * Copyright (c) 1995, 1999 Theo de Raadt
6d2b2a2e3Sreyk  *
7d2b2a2e3Sreyk  * Permission to use, copy, modify, and distribute this software for any
8d2b2a2e3Sreyk  * purpose with or without fee is hereby granted, provided that the above
9d2b2a2e3Sreyk  * copyright notice and this permission notice appear in all copies.
10d2b2a2e3Sreyk  *
11d2b2a2e3Sreyk  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12d2b2a2e3Sreyk  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13d2b2a2e3Sreyk  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14d2b2a2e3Sreyk  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15d2b2a2e3Sreyk  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16d2b2a2e3Sreyk  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17d2b2a2e3Sreyk  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18d2b2a2e3Sreyk  */
19d2b2a2e3Sreyk 
20d2b2a2e3Sreyk #include <sys/ioctl.h>
21d2b2a2e3Sreyk #include <sys/types.h>
22d2b2a2e3Sreyk #include <sys/socket.h>
23d2b2a2e3Sreyk #include <sys/time.h>
24d2b2a2e3Sreyk 
25d2b2a2e3Sreyk #include <net/if.h>
26d2b2a2e3Sreyk #include <net/if_media.h>
27d2b2a2e3Sreyk #include <net/if_arp.h>
28d2b2a2e3Sreyk #include <net/if_llc.h>
29d2b2a2e3Sreyk #include <net/bpf.h>
30d2b2a2e3Sreyk 
31d2b2a2e3Sreyk #include <netinet/in.h>
32d2b2a2e3Sreyk #include <netinet/if_ether.h>
33d2b2a2e3Sreyk #include <arpa/inet.h>
34d2b2a2e3Sreyk 
3514207dadSreyk #include <net80211/ieee80211.h>
3614207dadSreyk #include <net80211/ieee80211_ioctl.h>
37d2b2a2e3Sreyk 
38d2b2a2e3Sreyk #include <errno.h>
39d2b2a2e3Sreyk #include <event.h>
40d2b2a2e3Sreyk #include <fcntl.h>
41d2b2a2e3Sreyk #include <netdb.h>
42d2b2a2e3Sreyk #include <pwd.h>
43d2b2a2e3Sreyk #include <signal.h>
44d2b2a2e3Sreyk #include <stdarg.h>
45d2b2a2e3Sreyk #include <stdio.h>
46d2b2a2e3Sreyk #include <stdlib.h>
47d2b2a2e3Sreyk #include <string.h>
48d2b2a2e3Sreyk #include <unistd.h>
49b9fc9a72Sderaadt #include <limits.h>
50d2b2a2e3Sreyk 
51d2b2a2e3Sreyk #include "hostapd.h"
529b9e1fddSreyk #include "iapp.h"
53d2b2a2e3Sreyk 
54d2b2a2e3Sreyk enum hostapd_cmd_types {
55d2b2a2e3Sreyk 	PRIV_APME_BSSID,	/* Get the Host AP's BSSID */
56d2b2a2e3Sreyk 	PRIV_APME_GETNODE,	/* Get a node from the Host AP */
5750ce650fSreyk 	PRIV_APME_ADDNODE,	/* Delete a node from the Host AP */
58d2b2a2e3Sreyk 	PRIV_APME_DELNODE,	/* Delete a node from the Host AP */
5974ff1540Sreyk 	PRIV_APME_ADDROAMING,	/* Add a route to the kernel */
6074ff1540Sreyk 	PRIV_APME_DELROAMING,	/* Delete a route from the kernel */
61d2b2a2e3Sreyk 	PRIV_LLC_SEND_XID	/* Send IEEE 802.3 LLC XID frame */
62d2b2a2e3Sreyk };
63d2b2a2e3Sreyk 
64d2b2a2e3Sreyk void	 hostapd_priv(int, short, void *);
65d01b6ac2Sreyk struct hostapd_apme *hostapd_priv_getapme(int, struct hostapd_config *);
667b37258aSclaudio void	 hostapd_sig_relay(int, short, void *);
677b37258aSclaudio void	 hostapd_sig_chld(int, short, void *);
68d2b2a2e3Sreyk int	 hostapd_may_read(int, void *, size_t);
69d2b2a2e3Sreyk void	 hostapd_must_read(int, void *, size_t);
70d2b2a2e3Sreyk void	 hostapd_must_write(int, void *, size_t);
71d2b2a2e3Sreyk 
72d2b2a2e3Sreyk static int priv_fd = -1;
73d2b2a2e3Sreyk static volatile pid_t child_pid = -1;
74d2b2a2e3Sreyk 
75d2b2a2e3Sreyk /*
76d2b2a2e3Sreyk  * Main privsep functions
77d2b2a2e3Sreyk  */
78d2b2a2e3Sreyk 
79d2b2a2e3Sreyk void
hostapd_priv_init(struct hostapd_config * cfg)80d2b2a2e3Sreyk hostapd_priv_init(struct hostapd_config *cfg)
81d2b2a2e3Sreyk {
827b37258aSclaudio 	struct event ev_sigalrm;
837b37258aSclaudio 	struct event ev_sigterm;
847b37258aSclaudio 	struct event ev_sigint;
857b37258aSclaudio 	struct event ev_sighup;
867b37258aSclaudio 	struct event ev_sigchld;
877c8c4753Sreyk 	struct hostapd_iapp *iapp = &cfg->c_iapp;
8801908219Sreyk 	struct hostapd_apme *apme;
89d2b2a2e3Sreyk 	int i, socks[2];
90d2b2a2e3Sreyk 	struct passwd *pw;
91d2b2a2e3Sreyk 	struct servent *se;
92d2b2a2e3Sreyk 
93d2b2a2e3Sreyk 	for (i = 1; i < _NSIG; i++)
94d2b2a2e3Sreyk 		signal(i, SIG_DFL);
95d2b2a2e3Sreyk 
96d2b2a2e3Sreyk 	if ((se = getservbyname("iapp", "udp")) == NULL) {
977c8c4753Sreyk 		iapp->i_udp_port = IAPP_PORT;
98d2b2a2e3Sreyk 	} else
997c8c4753Sreyk 		iapp->i_udp_port = se->s_port;
100d2b2a2e3Sreyk 
101d2b2a2e3Sreyk 	if ((pw = getpwnam(HOSTAPD_USER)) == NULL)
102d2b2a2e3Sreyk 		hostapd_fatal("failed to get user \"%s\"\n", HOSTAPD_USER);
103d2b2a2e3Sreyk 
104d2b2a2e3Sreyk 	endservent();
105d2b2a2e3Sreyk 
106d2b2a2e3Sreyk 	/* Create sockets */
107d2b2a2e3Sreyk 	if (socketpair(AF_LOCAL, SOCK_STREAM, PF_UNSPEC, socks) == -1)
1082dd2df06Sreyk 		hostapd_fatal("failed to get socket pair\n");
109d2b2a2e3Sreyk 
110df69c215Sderaadt 	if ((child_pid = fork()) == -1)
1112dd2df06Sreyk 		hostapd_fatal("failed to fork child process\n");
112d2b2a2e3Sreyk 
113d2b2a2e3Sreyk 	/*
114d2b2a2e3Sreyk 	 * Unprivileged child process
115d2b2a2e3Sreyk 	 */
116d2b2a2e3Sreyk 	if (child_pid == 0) {
117d2b2a2e3Sreyk 		cfg->c_flags &= ~HOSTAPD_CFG_F_PRIV;
118d2b2a2e3Sreyk 
119d2b2a2e3Sreyk 		/*
120d2b2a2e3Sreyk 		 * Change the child's root directory to the unprivileged
121d2b2a2e3Sreyk 		 * user's home directory
122d2b2a2e3Sreyk 		 */
123d2b2a2e3Sreyk 		if (chroot(pw->pw_dir) == -1)
124d2b2a2e3Sreyk 			hostapd_fatal("failed to change root directory\n");
125d2b2a2e3Sreyk 		if (chdir("/") == -1)
1262dd2df06Sreyk 			hostapd_fatal("failed to change directory\n");
127d2b2a2e3Sreyk 
128d2b2a2e3Sreyk 		/*
129d2b2a2e3Sreyk 		 * Drop privileges and clear the group access list
130d2b2a2e3Sreyk 		 */
13166cb9ac9Smoritz 		if (setgroups(1, &pw->pw_gid) == -1 ||
1321729d5ddSreyk 		    setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) == -1 ||
1331729d5ddSreyk 		    setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) == -1)
1342dd2df06Sreyk 			hostapd_fatal("can't drop privileges\n");
135d2b2a2e3Sreyk 
136ae7a93f5Sreyk 		(void)close(socks[0]);
137d2b2a2e3Sreyk 		priv_fd = socks[1];
138d2b2a2e3Sreyk 		return;
139d2b2a2e3Sreyk 	}
140d2b2a2e3Sreyk 
141d2b2a2e3Sreyk 	/*
142d2b2a2e3Sreyk 	 * Privileged mother process
143d2b2a2e3Sreyk 	 */
144d2b2a2e3Sreyk 	cfg->c_flags |= HOSTAPD_CFG_F_PRIV;
145d2b2a2e3Sreyk 
146ae7a93f5Sreyk 	(void)event_init();
147d2b2a2e3Sreyk 
148552e00c7Smoritz 	/* Pass ALRM/TERM/INT/HUP through to child, and accept CHLD */
1497b37258aSclaudio 	signal_set(&ev_sigalrm, SIGALRM, hostapd_sig_relay, NULL);
1507b37258aSclaudio 	signal_set(&ev_sigterm, SIGTERM, hostapd_sig_relay, NULL);
1517b37258aSclaudio 	signal_set(&ev_sigint, SIGINT, hostapd_sig_relay, NULL);
1527b37258aSclaudio 	signal_set(&ev_sighup, SIGHUP, hostapd_sig_relay, NULL);
1537b37258aSclaudio 	signal_set(&ev_sigchld, SIGCHLD, hostapd_sig_chld, NULL);
1547b37258aSclaudio 	signal_add(&ev_sigalrm, NULL);
1557b37258aSclaudio 	signal_add(&ev_sigterm, NULL);
1567b37258aSclaudio 	signal_add(&ev_sigint, NULL);
1577b37258aSclaudio 	signal_add(&ev_sighup, NULL);
1587b37258aSclaudio 	signal_add(&ev_sigchld, NULL);
159d2b2a2e3Sreyk 
160ae7a93f5Sreyk 	(void)close(socks[1]);
161d2b2a2e3Sreyk 
162d2b2a2e3Sreyk 	if (cfg->c_flags & HOSTAPD_CFG_F_APME) {
163d01b6ac2Sreyk 		if ((cfg->c_apme_ctl = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
164d2b2a2e3Sreyk 			hostapd_fatal("unable to open ioctl socket\n");
16501908219Sreyk 		TAILQ_FOREACH(apme, &cfg->c_apmes, a_entries)
16601908219Sreyk 			if (apme->a_chanavail != NULL)
16701908219Sreyk 				hostapd_apme_sethopper(apme, 0);
168d2b2a2e3Sreyk 	}
169d2b2a2e3Sreyk 
17074ff1540Sreyk 	hostapd_roaming_init(cfg);
17174ff1540Sreyk 
172d2b2a2e3Sreyk 	/* Start a new event listener */
173d2b2a2e3Sreyk 	event_set(&cfg->c_priv_ev, socks[0], EV_READ, hostapd_priv, cfg);
174ae7a93f5Sreyk 	if (event_add(&cfg->c_priv_ev, NULL) == -1)
175ae7a93f5Sreyk 		hostapd_fatal("failed to add priv event");
176d2b2a2e3Sreyk 
177d2b2a2e3Sreyk 	/* Run privileged event loop */
178ae7a93f5Sreyk 	if (event_dispatch() == -1)
179ae7a93f5Sreyk 		hostapd_fatal("failed to dispatch priv hostapd");
180d2b2a2e3Sreyk 
181d2b2a2e3Sreyk 	/* Executed after the event loop has been terminated */
182d2b2a2e3Sreyk 	hostapd_cleanup(cfg);
183d2b2a2e3Sreyk 	_exit(EXIT_SUCCESS);
184d2b2a2e3Sreyk }
185d2b2a2e3Sreyk 
186d01b6ac2Sreyk struct hostapd_apme *
hostapd_priv_getapme(int fd,struct hostapd_config * cfg)187d01b6ac2Sreyk hostapd_priv_getapme(int fd, struct hostapd_config *cfg)
188d01b6ac2Sreyk {
189d01b6ac2Sreyk 	struct hostapd_apme *apme;
190d01b6ac2Sreyk 	char name[IFNAMSIZ];
191d01b6ac2Sreyk 	int n;
192d01b6ac2Sreyk 
193d01b6ac2Sreyk 	hostapd_must_read(fd, name, IFNAMSIZ);
194d01b6ac2Sreyk 	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0 ||
195d01b6ac2Sreyk 	    (apme = hostapd_apme_lookup(cfg, name)) == NULL) {
196d01b6ac2Sreyk 		n = ENXIO;
197d01b6ac2Sreyk 		hostapd_must_write(fd, &n, sizeof(int));
198d01b6ac2Sreyk 		return (NULL);
199d01b6ac2Sreyk 	}
200d01b6ac2Sreyk 	return (apme);
201d01b6ac2Sreyk }
202d01b6ac2Sreyk 
203d2b2a2e3Sreyk void
hostapd_priv(int fd,short sig,void * arg)204d2b2a2e3Sreyk hostapd_priv(int fd, short sig, void *arg)
205d2b2a2e3Sreyk {
206d2b2a2e3Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)arg;
207d01b6ac2Sreyk 	struct hostapd_apme *apme;
208d2b2a2e3Sreyk 	struct hostapd_node node;
209d2b2a2e3Sreyk 	struct ieee80211_bssid bssid;
21014207dadSreyk 	struct ieee80211_nodereq nr;
211d2b2a2e3Sreyk 	struct ifreq ifr;
21250ce650fSreyk 	unsigned long request;
21374ff1540Sreyk 	int ret = 0, cmd;
214d2b2a2e3Sreyk 
215d2b2a2e3Sreyk 	/* Terminate the event if we got an invalid signal */
216d2b2a2e3Sreyk 	if (sig != EV_READ)
217d2b2a2e3Sreyk 		return;
218d2b2a2e3Sreyk 
219d2b2a2e3Sreyk 	bzero(&node, sizeof(struct hostapd_node));
22014207dadSreyk 	bzero(&nr, sizeof(struct ieee80211_nodereq));
221d2b2a2e3Sreyk 
222d2b2a2e3Sreyk 	/* Get privsep command */
223d2b2a2e3Sreyk 	if (hostapd_may_read(fd, &cmd, sizeof(int)))
224d2b2a2e3Sreyk 		return;
225d2b2a2e3Sreyk 
226d2b2a2e3Sreyk 	switch (cmd) {
227d2b2a2e3Sreyk 	case PRIV_APME_BSSID:
228d2b2a2e3Sreyk 		hostapd_log(HOSTAPD_LOG_DEBUG,
229f83005a6Sreyk 		    "[priv]: msg PRIV_APME_BSSID received");
230d2b2a2e3Sreyk 
231d01b6ac2Sreyk 		if ((apme = hostapd_priv_getapme(fd, cfg)) == NULL)
232d01b6ac2Sreyk 			break;
233ae7a93f5Sreyk 		(void)strlcpy(bssid.i_name, apme->a_iface, sizeof(bssid.i_name));
234d2b2a2e3Sreyk 
235d2b2a2e3Sreyk 		/* Try to get the APME's BSSID */
236d01b6ac2Sreyk 		if ((ret = ioctl(cfg->c_apme_ctl,
237d2b2a2e3Sreyk 		    SIOCG80211BSSID, &bssid)) != 0)
238d2b2a2e3Sreyk 			ret = errno;
239d2b2a2e3Sreyk 
240d2b2a2e3Sreyk 		hostapd_must_write(fd, &ret, sizeof(int));
241a8a19692Sderaadt 		if (ret == 0)
242d2b2a2e3Sreyk 			hostapd_must_write(fd, &bssid.i_bssid,
243d2b2a2e3Sreyk 			    IEEE80211_ADDR_LEN);
244d2b2a2e3Sreyk 		break;
245d2b2a2e3Sreyk 
246d2b2a2e3Sreyk 	case PRIV_APME_GETNODE:
247d2b2a2e3Sreyk 		hostapd_log(HOSTAPD_LOG_DEBUG,
248f83005a6Sreyk 		    "[priv]: msg PRIV_APME_GETNODE received");
249d2b2a2e3Sreyk 
250d2b2a2e3Sreyk 		hostapd_must_read(fd, &node, sizeof(struct hostapd_node));
25114207dadSreyk 		bcopy(node.ni_macaddr, nr.nr_macaddr, IEEE80211_ADDR_LEN);
252d2b2a2e3Sreyk 
253d01b6ac2Sreyk 		if ((apme = hostapd_priv_getapme(fd, cfg)) == NULL)
254d01b6ac2Sreyk 			break;
255ae7a93f5Sreyk 		(void)strlcpy(nr.nr_ifname, apme->a_iface, sizeof(ifr.ifr_name));
256d2b2a2e3Sreyk 
257d2b2a2e3Sreyk 		/* Try to get a station from the APME */
258d01b6ac2Sreyk 		if ((ret = ioctl(cfg->c_apme_ctl,
25914207dadSreyk 		    SIOCG80211NODE, &nr)) != 0)
260d2b2a2e3Sreyk 			ret = errno;
261d2b2a2e3Sreyk 
262d2b2a2e3Sreyk 		hostapd_must_write(fd, &ret, sizeof(int));
263d2b2a2e3Sreyk 		if (ret == 0) {
26414207dadSreyk 			node.ni_associd = nr.nr_associd;
26514207dadSreyk 			node.ni_flags = IEEE80211_NODEREQ_STATE(nr.nr_state);
26614207dadSreyk 			node.ni_rssi = nr.nr_rssi;
26714207dadSreyk 			node.ni_capinfo = nr.nr_capinfo;
268d2b2a2e3Sreyk 
269804d065bShenning 			hostapd_must_write(fd, &node,
270804d065bShenning 			    sizeof(struct hostapd_node));
271d2b2a2e3Sreyk 		}
272d2b2a2e3Sreyk 		break;
273d2b2a2e3Sreyk 
27450ce650fSreyk 	case PRIV_APME_ADDNODE:
275d2b2a2e3Sreyk 	case PRIV_APME_DELNODE:
276d2b2a2e3Sreyk 		hostapd_log(HOSTAPD_LOG_DEBUG,
277f83005a6Sreyk 		    "[priv]: msg PRIV_APME_[ADD|DEL]NODE received");
278d2b2a2e3Sreyk 
279d2b2a2e3Sreyk 		hostapd_must_read(fd, &node, sizeof(struct hostapd_node));
28014207dadSreyk 		bcopy(node.ni_macaddr, nr.nr_macaddr, IEEE80211_ADDR_LEN);
281d2b2a2e3Sreyk 
282d01b6ac2Sreyk 		if ((apme = hostapd_priv_getapme(fd, cfg)) == NULL)
283d01b6ac2Sreyk 			break;
284ae7a93f5Sreyk 		(void)strlcpy(nr.nr_ifname, apme->a_iface, sizeof(ifr.ifr_name));
28550ce650fSreyk 
28650ce650fSreyk 		request = cmd == PRIV_APME_ADDNODE ?
28750ce650fSreyk 		    SIOCS80211NODE : SIOCS80211DELNODE;
28850ce650fSreyk 
28950ce650fSreyk 		/* Try to add/delete a station from the APME */
290df69c215Sderaadt 		if ((ret = ioctl(cfg->c_apme_ctl, request, &nr)) == -1)
291d2b2a2e3Sreyk 			ret = errno;
292d01b6ac2Sreyk 
293d2b2a2e3Sreyk 		hostapd_must_write(fd, &ret, sizeof(int));
294d2b2a2e3Sreyk 		break;
295d2b2a2e3Sreyk 
296d2b2a2e3Sreyk 	case PRIV_LLC_SEND_XID:
297d2b2a2e3Sreyk 		hostapd_log(HOSTAPD_LOG_DEBUG,
298f83005a6Sreyk 		    "[priv]: msg PRIV_LLC_SEND_XID received");
299d2b2a2e3Sreyk 
300d2b2a2e3Sreyk 		hostapd_must_read(fd, &node, sizeof(struct hostapd_node));
301d2b2a2e3Sreyk 
302d2b2a2e3Sreyk 		/* Send a LLC XID frame to reset possible switch ports */
303d2b2a2e3Sreyk 		ret = hostapd_llc_send_xid(cfg, &node);
304d2b2a2e3Sreyk 		hostapd_must_write(fd, &ret, sizeof(int));
305d2b2a2e3Sreyk 		break;
306d2b2a2e3Sreyk 
30774ff1540Sreyk 	case PRIV_APME_ADDROAMING:
30874ff1540Sreyk 	case PRIV_APME_DELROAMING:
30974ff1540Sreyk 		hostapd_log(HOSTAPD_LOG_DEBUG,
310f83005a6Sreyk 		    "[priv]: msg PRIV_APME_[ADD|DEL]ROAMING received");
31174ff1540Sreyk 
31274ff1540Sreyk 		hostapd_must_read(fd, &node, sizeof(struct hostapd_node));
31374ff1540Sreyk 
31474ff1540Sreyk 		if ((apme = hostapd_priv_getapme(fd, cfg)) == NULL)
31574ff1540Sreyk 			break;
31674ff1540Sreyk 		ret = hostapd_roaming(apme, &node, cmd == PRIV_APME_ADDROAMING);
31774ff1540Sreyk 		hostapd_must_write(fd, &ret, sizeof(int));
31874ff1540Sreyk 		break;
31974ff1540Sreyk 
320d2b2a2e3Sreyk 	default:
321d2b2a2e3Sreyk 		hostapd_fatal("[priv]: unknown command %d\n", cmd);
322d2b2a2e3Sreyk 	}
323ae7a93f5Sreyk 	if (event_add(&cfg->c_priv_ev, NULL) == -1)
324ae7a93f5Sreyk 		hostapd_fatal("failed to schedult priv event");
325d01b6ac2Sreyk 
326d01b6ac2Sreyk 	return;
327d2b2a2e3Sreyk }
328d2b2a2e3Sreyk 
329d2b2a2e3Sreyk /*
330d2b2a2e3Sreyk  * Unprivileged callers
331d2b2a2e3Sreyk  */
332d2b2a2e3Sreyk int
hostapd_priv_apme_getnode(struct hostapd_apme * apme,struct hostapd_node * node)333d01b6ac2Sreyk hostapd_priv_apme_getnode(struct hostapd_apme *apme, struct hostapd_node *node)
334d2b2a2e3Sreyk {
335d01b6ac2Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
336d2b2a2e3Sreyk 	int ret, cmd;
337d2b2a2e3Sreyk 
338d2b2a2e3Sreyk 	if (priv_fd < 0)
339d2b2a2e3Sreyk 		hostapd_fatal("%s: called from privileged portion\n", __func__);
340d2b2a2e3Sreyk 
341d2b2a2e3Sreyk 	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
342d2b2a2e3Sreyk 		hostapd_fatal("%s: Host AP is not available\n", __func__);
343d2b2a2e3Sreyk 
344d2b2a2e3Sreyk 	cmd = PRIV_APME_GETNODE;
345d2b2a2e3Sreyk 	hostapd_must_write(priv_fd, &cmd, sizeof(int));
346d2b2a2e3Sreyk 	hostapd_must_write(priv_fd, node, sizeof(struct hostapd_node));
347d01b6ac2Sreyk 	hostapd_must_write(priv_fd, &apme->a_iface, IFNAMSIZ);
348d2b2a2e3Sreyk 	hostapd_must_read(priv_fd, &ret, sizeof(int));
349d2b2a2e3Sreyk 	if (ret != 0)
350d2b2a2e3Sreyk 		return (ret);
351d2b2a2e3Sreyk 
352d2b2a2e3Sreyk 	hostapd_must_read(priv_fd, node, sizeof(struct hostapd_node));
353d2b2a2e3Sreyk 	return (ret);
354d2b2a2e3Sreyk }
355d2b2a2e3Sreyk 
356d2b2a2e3Sreyk int
hostapd_priv_apme_setnode(struct hostapd_apme * apme,struct hostapd_node * node,int add)357d01b6ac2Sreyk hostapd_priv_apme_setnode(struct hostapd_apme *apme, struct hostapd_node *node,
35850ce650fSreyk     int add)
359d2b2a2e3Sreyk {
360d01b6ac2Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
3617c8c4753Sreyk 	struct hostapd_iapp *iapp = &cfg->c_iapp;
362d2b2a2e3Sreyk 	int ret, cmd;
363d2b2a2e3Sreyk 
364d2b2a2e3Sreyk 	if (priv_fd < 0)
365d2b2a2e3Sreyk 		hostapd_fatal("%s: called from privileged portion\n", __func__);
366d2b2a2e3Sreyk 
367d2b2a2e3Sreyk 	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
368d2b2a2e3Sreyk 		hostapd_fatal("%s: Host AP is not available\n", __func__);
369d2b2a2e3Sreyk 
37050ce650fSreyk 	if (add)
37150ce650fSreyk 		cmd = PRIV_APME_ADDNODE;
37250ce650fSreyk 	else
373d2b2a2e3Sreyk 		cmd = PRIV_APME_DELNODE;
374d2b2a2e3Sreyk 	hostapd_must_write(priv_fd, &cmd, sizeof(int));
375d2b2a2e3Sreyk 	hostapd_must_write(priv_fd, node, sizeof(struct hostapd_node));
376d01b6ac2Sreyk 	hostapd_must_write(priv_fd, &apme->a_iface, IFNAMSIZ);
377d01b6ac2Sreyk 
378d2b2a2e3Sreyk 	hostapd_must_read(priv_fd, &ret, sizeof(int));
379d01b6ac2Sreyk 	if (ret == 0)
380f83005a6Sreyk 		hostapd_log(HOSTAPD_LOG_VERBOSE, "%s/%s: %s node %s",
3817c8c4753Sreyk 		    apme->a_iface, iapp->i_iface,
3827c8c4753Sreyk 		    add ? "added" : "removed",
383d01b6ac2Sreyk 		    etheraddr_string(node->ni_macaddr));
384d2b2a2e3Sreyk 
385d2b2a2e3Sreyk 	return (ret);
386d2b2a2e3Sreyk }
387d2b2a2e3Sreyk 
388d2b2a2e3Sreyk void
hostapd_priv_apme_bssid(struct hostapd_apme * apme)389d01b6ac2Sreyk hostapd_priv_apme_bssid(struct hostapd_apme *apme)
390d2b2a2e3Sreyk {
391d01b6ac2Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
392d2b2a2e3Sreyk 	int ret, cmd;
393d2b2a2e3Sreyk 
394d2b2a2e3Sreyk 	if (priv_fd < 0)
395d2b2a2e3Sreyk 		hostapd_fatal("%s: called from privileged portion\n", __func__);
396d2b2a2e3Sreyk 
397d2b2a2e3Sreyk 	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
398d2b2a2e3Sreyk 		hostapd_fatal("%s: Host AP is not available\n", __func__);
399d2b2a2e3Sreyk 
400d2b2a2e3Sreyk 	cmd = PRIV_APME_BSSID;
401d2b2a2e3Sreyk 	hostapd_must_write(priv_fd, &cmd, sizeof(int));
402d01b6ac2Sreyk 	hostapd_must_write(priv_fd, &apme->a_iface, IFNAMSIZ);
403d2b2a2e3Sreyk 	hostapd_must_read(priv_fd, &ret, sizeof(int));
404d2b2a2e3Sreyk 	if (ret != 0)
405552e00c7Smoritz 		hostapd_fatal("failed to get Host AP's BSSID on"
406d01b6ac2Sreyk 		    " \"%s\": %s\n", apme->a_iface, strerror(errno));
407d2b2a2e3Sreyk 
408d01b6ac2Sreyk 	hostapd_must_read(priv_fd, &apme->a_bssid, IEEE80211_ADDR_LEN);
409d2b2a2e3Sreyk 	cfg->c_stats.cn_tx_apme++;
410d2b2a2e3Sreyk }
411d2b2a2e3Sreyk 
412d2b2a2e3Sreyk int
hostapd_priv_llc_xid(struct hostapd_config * cfg,struct hostapd_node * node)413d2b2a2e3Sreyk hostapd_priv_llc_xid(struct hostapd_config *cfg, struct hostapd_node *node)
414d2b2a2e3Sreyk {
415d2b2a2e3Sreyk 	int ret, cmd;
416d2b2a2e3Sreyk 
417d2b2a2e3Sreyk 	if (priv_fd < 0)
418d2b2a2e3Sreyk 		hostapd_fatal("%s: called from privileged portion\n", __func__);
419d2b2a2e3Sreyk 
420d2b2a2e3Sreyk 	cmd = PRIV_LLC_SEND_XID;
421d2b2a2e3Sreyk 	hostapd_must_write(priv_fd, &cmd, sizeof(int));
422d2b2a2e3Sreyk 	hostapd_must_write(priv_fd, node, sizeof(struct hostapd_node));
423d2b2a2e3Sreyk 	hostapd_must_read(priv_fd, &ret, sizeof(int));
424d2b2a2e3Sreyk 
425d2b2a2e3Sreyk 	if (ret == 0)
426d2b2a2e3Sreyk 		cfg->c_stats.cn_tx_llc++;
427d2b2a2e3Sreyk 	return (ret);
428d2b2a2e3Sreyk }
429d2b2a2e3Sreyk 
43074ff1540Sreyk int
hostapd_priv_roaming(struct hostapd_apme * apme,struct hostapd_node * node,int add)43174ff1540Sreyk hostapd_priv_roaming(struct hostapd_apme *apme, struct hostapd_node *node,
43274ff1540Sreyk     int add)
43374ff1540Sreyk {
43474ff1540Sreyk 	struct hostapd_config *cfg = (struct hostapd_config *)apme->a_cfg;
43574ff1540Sreyk 	int ret, cmd;
43674ff1540Sreyk 
43774ff1540Sreyk 	if (priv_fd < 0)
43874ff1540Sreyk 		hostapd_fatal("%s: called from privileged portion\n", __func__);
43974ff1540Sreyk 
44074ff1540Sreyk 	if ((cfg->c_flags & HOSTAPD_CFG_F_APME) == 0)
44174ff1540Sreyk 		hostapd_fatal("%s: Host AP is not available\n", __func__);
44274ff1540Sreyk 
44374ff1540Sreyk 	if (add)
44474ff1540Sreyk 		cmd = PRIV_APME_ADDROAMING;
44574ff1540Sreyk 	else
44674ff1540Sreyk 		cmd = PRIV_APME_DELROAMING;
44774ff1540Sreyk 	hostapd_must_write(priv_fd, &cmd, sizeof(int));
44874ff1540Sreyk 	hostapd_must_write(priv_fd, node, sizeof(struct hostapd_node));
44974ff1540Sreyk 	hostapd_must_write(priv_fd, &apme->a_iface, IFNAMSIZ);
45074ff1540Sreyk 
45174ff1540Sreyk 	hostapd_must_read(priv_fd, &ret, sizeof(int));
45274ff1540Sreyk 
45374ff1540Sreyk 	return (ret);
45474ff1540Sreyk }
45574ff1540Sreyk 
456d2b2a2e3Sreyk /*
457a8a19692Sderaadt  * If priv parent gets a TERM or HUP, pass it through to child instead.
458d2b2a2e3Sreyk  */
459d2b2a2e3Sreyk void
hostapd_sig_relay(int sig,short event,void * arg)4607b37258aSclaudio hostapd_sig_relay(int sig, short event, void *arg)
461d2b2a2e3Sreyk {
462d2b2a2e3Sreyk 	int oerrno = errno;
463d2b2a2e3Sreyk 
464d2b2a2e3Sreyk 	if (child_pid != -1)
465ae7a93f5Sreyk 		if (kill(child_pid, sig) == -1)
466ae7a93f5Sreyk 			hostapd_fatal("hostapd_sig_relay: kill(%d, %d)",
467ae7a93f5Sreyk 			    child_pid, sig);
468d2b2a2e3Sreyk 	errno = oerrno;
469d2b2a2e3Sreyk }
470d2b2a2e3Sreyk 
471d2b2a2e3Sreyk void
hostapd_sig_chld(int sig,short event,void * arg)4727b37258aSclaudio hostapd_sig_chld(int sig, short event, void *arg)
473d2b2a2e3Sreyk {
474d2b2a2e3Sreyk 	/*
475d2b2a2e3Sreyk 	 * If parent gets a SIGCHLD, it will exit.
476d2b2a2e3Sreyk 	 */
477d2b2a2e3Sreyk 
478ae7a93f5Sreyk 	if (sig == SIGCHLD)
4797b37258aSclaudio 		(void)event_loopexit(NULL);
480d2b2a2e3Sreyk }
481d2b2a2e3Sreyk 
482d2b2a2e3Sreyk /*
483d2b2a2e3Sreyk  * privsep I/O functions
484d2b2a2e3Sreyk  */
485d2b2a2e3Sreyk 
486d2b2a2e3Sreyk /* Read all data or return 1 for error.  */
487d2b2a2e3Sreyk int
hostapd_may_read(int fd,void * buf,size_t n)488d2b2a2e3Sreyk hostapd_may_read(int fd, void *buf, size_t n)
489d2b2a2e3Sreyk {
490d2b2a2e3Sreyk 	char *s = buf;
491d2b2a2e3Sreyk 	ssize_t res, pos = 0;
492d2b2a2e3Sreyk 
493d2b2a2e3Sreyk 	while ((ssize_t)n > pos) {
494d2b2a2e3Sreyk 		res = read(fd, s + pos, n - pos);
495d2b2a2e3Sreyk 		switch (res) {
496d2b2a2e3Sreyk 		case -1:
497d2b2a2e3Sreyk 			if (errno == EINTR || errno == EAGAIN)
498d2b2a2e3Sreyk 				continue;
499442bc206Sreyk 			/* FALLTHROUGH */
500d2b2a2e3Sreyk 		case 0:
501d2b2a2e3Sreyk 			return (1);
502d2b2a2e3Sreyk 		default:
503d2b2a2e3Sreyk 			pos += res;
504d2b2a2e3Sreyk 		}
505d2b2a2e3Sreyk 	}
506d2b2a2e3Sreyk 	return (0);
507d2b2a2e3Sreyk }
508d2b2a2e3Sreyk 
509d2b2a2e3Sreyk /*
510d2b2a2e3Sreyk  * Read data with the assertion that it all must come through, or
511d2b2a2e3Sreyk  * else abort the process.  Based on atomicio() from openssh.
512d2b2a2e3Sreyk  */
513d2b2a2e3Sreyk void
hostapd_must_read(int fd,void * buf,size_t n)514d2b2a2e3Sreyk hostapd_must_read(int fd, void *buf, size_t n)
515d2b2a2e3Sreyk {
516d2b2a2e3Sreyk 	char *s = buf;
517d2b2a2e3Sreyk 	ssize_t res, pos = 0;
518d2b2a2e3Sreyk 
519d2b2a2e3Sreyk 	while ((ssize_t)n > pos) {
520d2b2a2e3Sreyk 		res = read(fd, s + pos, n - pos);
521d2b2a2e3Sreyk 		switch (res) {
522d2b2a2e3Sreyk 		case -1:
523d2b2a2e3Sreyk 			if (errno == EINTR || errno == EAGAIN)
524d2b2a2e3Sreyk 				continue;
525442bc206Sreyk 			/* FALLTHROUGH */
526d2b2a2e3Sreyk 		case 0:
527d2b2a2e3Sreyk 			_exit(0);
528442bc206Sreyk 			break;
529d2b2a2e3Sreyk 		default:
530d2b2a2e3Sreyk 			pos += res;
531d2b2a2e3Sreyk 		}
532d2b2a2e3Sreyk 	}
533d2b2a2e3Sreyk }
534d2b2a2e3Sreyk 
535d2b2a2e3Sreyk /*
536d2b2a2e3Sreyk  * Write data with the assertion that it all has to be written, or
537d2b2a2e3Sreyk  * else abort the process.  Based on atomicio() from openssh.
538d2b2a2e3Sreyk  */
539d2b2a2e3Sreyk void
hostapd_must_write(int fd,void * buf,size_t n)540d2b2a2e3Sreyk hostapd_must_write(int fd, void *buf, size_t n)
541d2b2a2e3Sreyk {
542d2b2a2e3Sreyk 	char *s = buf;
543d2b2a2e3Sreyk 	ssize_t res, pos = 0;
544d2b2a2e3Sreyk 
545d2b2a2e3Sreyk 	while ((ssize_t)n > pos) {
546d2b2a2e3Sreyk 		res = write(fd, s + pos, n - pos);
547d2b2a2e3Sreyk 		switch (res) {
548d2b2a2e3Sreyk 		case -1:
549d2b2a2e3Sreyk 			if (errno == EINTR || errno == EAGAIN)
550d2b2a2e3Sreyk 				continue;
551442bc206Sreyk 			/* FALLTHROUGH */
552d2b2a2e3Sreyk 		case 0:
553d2b2a2e3Sreyk 			_exit(0);
554442bc206Sreyk 			break;
555d2b2a2e3Sreyk 		default:
556d2b2a2e3Sreyk 			pos += res;
557d2b2a2e3Sreyk 		}
558d2b2a2e3Sreyk 	}
559d2b2a2e3Sreyk }
560d2b2a2e3Sreyk 
561