priv.c revision 1.3
1/*	$OpenBSD: priv.c,v 1.3 2016/10/15 14:02:11 reyk Exp $	*/
2
3/*
4 * Copyright (c) 2016 Reyk Floeter <reyk@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19#include <sys/param.h>	/* nitems */
20#include <sys/queue.h>
21#include <sys/stat.h>
22#include <sys/socket.h>
23#include <sys/un.h>
24#include <sys/ioctl.h>
25#include <sys/tree.h>
26
27#include <net/if.h>
28#include <netinet/in.h>
29#include <netinet/if_ether.h>
30#include <net/if_bridge.h>
31
32#include <errno.h>
33#include <event.h>
34#include <fcntl.h>
35#include <stdlib.h>
36#include <stdio.h>
37#include <string.h>
38#include <unistd.h>
39#include <signal.h>
40#include <ctype.h>
41
42#include "proc.h"
43#include "vmd.h"
44
45int	 priv_dispatch_parent(int, struct privsep_proc *, struct imsg *);
46void	 priv_run(struct privsep *, struct privsep_proc *, void *);
47
48static struct privsep_proc procs[] = {
49	{ "parent",	PROC_PARENT,	priv_dispatch_parent }
50};
51
52void
53priv(struct privsep *ps, struct privsep_proc *p)
54{
55	proc_run(ps, p, procs, nitems(procs), priv_run, NULL);
56}
57
58void
59priv_run(struct privsep *ps, struct privsep_proc *p, void *arg)
60{
61	struct vmd		*env = ps->ps_env;
62
63	/*
64	 * no pledge(2) in the "priv" process:
65	 * write ioctls are not permitted by pledge.
66	 */
67
68	/* Open our own socket for generic interface ioctls */
69	if ((env->vmd_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
70		fatal("socket");
71}
72
73int
74priv_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
75{
76	const char		*desct[] = { "tap", "switch", "bridge", NULL };
77	struct privsep		*ps = p->p_ps;
78	struct vmop_ifreq	 vfr;
79	struct vmd		*env = ps->ps_env;
80	struct ifreq		 ifr;
81	struct ifbreq		 ifbr;
82	struct ifgroupreq	 ifgr;
83	char			 type[IF_NAMESIZE];
84
85	switch (imsg->hdr.type) {
86	case IMSG_VMDOP_PRIV_IFDESCR:
87	case IMSG_VMDOP_PRIV_IFCREATE:
88	case IMSG_VMDOP_PRIV_IFADD:
89	case IMSG_VMDOP_PRIV_IFUP:
90	case IMSG_VMDOP_PRIV_IFDOWN:
91	case IMSG_VMDOP_PRIV_IFGROUP:
92		IMSG_SIZE_CHECK(imsg, &vfr);
93		memcpy(&vfr, imsg->data, sizeof(vfr));
94
95		/* We should not get malicious requests from the parent */
96		if (priv_getiftype(vfr.vfr_name, type, NULL) == -1 ||
97		    priv_findname(type, desct) == -1)
98			fatalx("%s: rejected priv operation on interface: %s",
99			    __func__, vfr.vfr_name);
100		break;
101	default:
102		return (-1);
103	}
104
105	switch (imsg->hdr.type) {
106	case IMSG_VMDOP_PRIV_IFDESCR:
107		/* Set the interface description */
108		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
109		ifr.ifr_data = (caddr_t)vfr.vfr_value;
110		if (ioctl(env->vmd_fd, SIOCSIFDESCR, &ifr) < 0)
111			log_warn("SIOCSIFDESCR");
112		break;
113	case IMSG_VMDOP_PRIV_IFCREATE:
114		/* Create the bridge if it doesn't exist */
115		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
116		if (ioctl(env->vmd_fd, SIOCIFCREATE, &ifr) < 0 &&
117		    errno != EEXIST)
118			log_warn("SIOCIFCREATE");
119		break;
120	case IMSG_VMDOP_PRIV_IFADD:
121		if (priv_getiftype(vfr.vfr_value, type, NULL) == -1)
122			fatalx("%s: rejected to add interface: %s",
123			    __func__, vfr.vfr_value);
124
125		/* Attach the device to the bridge */
126		strlcpy(ifbr.ifbr_name, vfr.vfr_name,
127		    sizeof(ifbr.ifbr_name));
128		strlcpy(ifbr.ifbr_ifsname, vfr.vfr_value,
129		    sizeof(ifbr.ifbr_ifsname));
130		if (ioctl(env->vmd_fd, SIOCBRDGADD, &ifbr) < 0 &&
131		    errno != EEXIST)
132			log_warn("SIOCBRDGADD");
133		break;
134	case IMSG_VMDOP_PRIV_IFUP:
135	case IMSG_VMDOP_PRIV_IFDOWN:
136		/* Set the interface status */
137		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
138		if (ioctl(env->vmd_fd, SIOCGIFFLAGS, &ifr) < 0) {
139			log_warn("SIOCGIFFLAGS");
140			break;
141		}
142		if (imsg->hdr.type == IMSG_VMDOP_PRIV_IFUP)
143			ifr.ifr_flags |= IFF_UP;
144		else
145			ifr.ifr_flags &= ~IFF_UP;
146		if (ioctl(env->vmd_fd, SIOCSIFFLAGS, &ifr) < 0)
147			log_warn("SIOCSIFFLAGS");
148		break;
149	case IMSG_VMDOP_PRIV_IFGROUP:
150		if (priv_validgroup(vfr.vfr_value) == -1)
151			fatalx("%s: invalid group name", __func__);
152
153		if (strlcpy(ifgr.ifgr_name, vfr.vfr_name,
154		    sizeof(ifgr.ifgr_name)) >= sizeof(ifgr.ifgr_name) ||
155		    strlcpy(ifgr.ifgr_group, vfr.vfr_value,
156		    sizeof(ifgr.ifgr_group)) >= sizeof(ifgr.ifgr_group))
157			fatalx("%s: group name too long", __func__);
158
159		if (ioctl(env->vmd_fd, SIOCAIFGROUP, &ifgr) < 0 &&
160		    errno != EEXIST)
161			log_warn("SIOCAIFGROUP");
162		break;
163	default:
164		return (-1);
165	}
166
167	return (0);
168}
169
170int
171priv_getiftype(char *ifname, char *type, unsigned int *unitptr)
172{
173	const char	*errstr;
174	size_t		 span;
175	unsigned int	 unit;
176
177	/* Extract the name part */
178	span = strcspn(ifname, "0123456789");
179	if (span == 0 || span >= strlen(ifname) || span >= (IF_NAMESIZE - 1))
180		return (-1);
181	memcpy(type, ifname, span);
182	type[span] = 0;
183
184	/* Now parse the unit (we don't strictly validate the format here) */
185	unit = strtonum(ifname + span, 0, UINT_MAX, &errstr);
186	if (errstr != NULL)
187		return (-1);
188	if (unitptr != NULL)
189		*unitptr = unit;
190
191	return (0);
192}
193
194int
195priv_findname(const char *name, const char **names)
196{
197	unsigned int	 i;
198
199	for (i = 0; names[i] != NULL; i++) {
200		if (strcmp(name, names[i]) == 0)
201			return (0);
202	}
203
204	return (-1);
205}
206
207int
208priv_validgroup(const char *name)
209{
210	if (strlen(name) >= IF_NAMESIZE)
211		return (-1);
212	/* Group can not end with a digit */
213	if (name[0] && isdigit(name[strlen(name) - 1]))
214		return (-1);
215	return (0);
216}
217
218/*
219 * Called from the process peer
220 */
221
222int
223vm_priv_ifconfig(struct privsep *ps, struct vmd_vm *vm)
224{
225	struct vm_create_params	*vcp = &vm->vm_params;
226	struct vmd_if		*vif;
227	struct vmd_switch	*vsw;
228	unsigned int		 i;
229	struct vmop_ifreq	 vfr, vfbr;
230
231	for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) {
232		vif = &vm->vm_ifs[i];
233
234		if (vif->vif_name == NULL)
235			break;
236
237		if (strlcpy(vfr.vfr_name, vif->vif_name,
238		    sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name))
239			return (-1);
240
241		/* Description can be truncated */
242		(void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value),
243		    "vm%u-if%u-%s", vm->vm_vmid, i, vcp->vcp_name);
244
245		log_debug("%s: interface %s description %s", __func__,
246		    vfr.vfr_name, vfr.vfr_value);
247
248		proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR,
249		    &vfr, sizeof(vfr));
250
251		if (vif->vif_group) {
252			if (strlcpy(vfr.vfr_value, vif->vif_group,
253			    sizeof(vfr.vfr_value)) >= sizeof(vfr.vfr_value))
254				return (-1);
255
256			log_debug("%s: interface %s group %s", __func__,
257			    vfr.vfr_name, vfr.vfr_value);
258
259			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFGROUP,
260			    &vfr, sizeof(vfr));
261		}
262
263		/* Add interface to bridge/switch */
264		if ((vsw = switch_getbyname(vif->vif_switch)) != NULL) {
265			if (strlcpy(vfbr.vfr_name, vsw->sw_ifname,
266			    sizeof(vfbr.vfr_name)) >= sizeof(vfbr.vfr_name))
267				return (-1);
268			if (strlcpy(vfbr.vfr_value, vif->vif_name,
269			    sizeof(vfbr.vfr_value)) >= sizeof(vfbr.vfr_value))
270				return (-1);
271
272			log_debug("%s: interface %s add %s", __func__,
273			    vfbr.vfr_name, vfbr.vfr_value);
274
275			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFCREATE,
276			    &vfbr, sizeof(vfbr));
277			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADD,
278			    &vfbr, sizeof(vfbr));
279		} else if (vif->vif_switch != NULL)
280			log_warnx("switch %s not found", vif->vif_switch);
281
282		/* Set the new interface status to up or down */
283		proc_compose(ps, PROC_PRIV, (vif->vif_flags & IFF_UP) ?
284		    IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN,
285		    &vfr, sizeof(vfr));
286	}
287
288	return (0);
289}
290
291int
292vm_priv_brconfig(struct privsep *ps, struct vmd_switch *vsw)
293{
294	struct vmd_if		*vif;
295	struct vmop_ifreq	 vfr;
296
297	if (strlcpy(vfr.vfr_name, vsw->sw_ifname,
298	    sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name))
299		return (-1);
300
301	proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFCREATE,
302	    &vfr, sizeof(vfr));
303
304	/* Description can be truncated */
305	(void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value),
306	    "switch%u-%s", vsw->sw_id, vsw->sw_name);
307
308	log_debug("%s: interface %s description %s", __func__,
309	    vfr.vfr_name, vfr.vfr_value);
310
311	proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR,
312	    &vfr, sizeof(vfr));
313
314	TAILQ_FOREACH(vif, &vsw->sw_ifs, vif_entry) {
315		if (strlcpy(vfr.vfr_value, vif->vif_name,
316		    sizeof(vfr.vfr_value)) >= sizeof(vfr.vfr_value))
317			return (-1);
318
319		log_debug("%s: interface %s add %s", __func__,
320		    vfr.vfr_name, vfr.vfr_value);
321
322		proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADD,
323		    &vfr, sizeof(vfr));
324	}
325
326	/* Set the new interface status to up or down */
327	proc_compose(ps, PROC_PRIV, (vsw->sw_flags & IFF_UP) ?
328	    IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN,
329	    &vfr, sizeof(vfr));
330
331	return (0);
332}
333