priv.c revision 1.2
1/*	$OpenBSD: priv.c,v 1.2 2016/10/05 17:30:13 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
41#include "proc.h"
42#include "vmd.h"
43
44int	 priv_dispatch_parent(int, struct privsep_proc *, struct imsg *);
45void	 priv_run(struct privsep *, struct privsep_proc *, void *);
46
47static struct privsep_proc procs[] = {
48	{ "parent",	PROC_PARENT,	priv_dispatch_parent }
49};
50
51void
52priv(struct privsep *ps, struct privsep_proc *p)
53{
54	proc_run(ps, p, procs, nitems(procs), priv_run, NULL);
55}
56
57void
58priv_run(struct privsep *ps, struct privsep_proc *p, void *arg)
59{
60	struct vmd		*env = ps->ps_env;
61
62	/*
63	 * no pledge(2) in the "priv" process:
64	 * write ioctls are not permitted by pledge.
65	 */
66
67	/* Open our own socket for generic interface ioctls */
68	if ((env->vmd_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
69		fatal("socket");
70}
71
72int
73priv_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
74{
75	const char		*desct[] = { "tap", "switch", "bridge", NULL };
76	struct privsep		*ps = p->p_ps;
77	struct vmop_ifreq	 vfr;
78	struct vmd		*env = ps->ps_env;
79	struct ifreq		 ifr;
80	struct ifbreq		 ifbr;
81	char			 type[IF_NAMESIZE];
82
83	switch (imsg->hdr.type) {
84	case IMSG_VMDOP_PRIV_IFDESCR:
85	case IMSG_VMDOP_PRIV_IFCREATE:
86	case IMSG_VMDOP_PRIV_IFADD:
87	case IMSG_VMDOP_PRIV_IFUP:
88	case IMSG_VMDOP_PRIV_IFDOWN:
89		IMSG_SIZE_CHECK(imsg, &vfr);
90		memcpy(&vfr, imsg->data, sizeof(vfr));
91
92		/* We should not get malicious requests from the parent */
93		if (priv_getiftype(vfr.vfr_name, type, NULL) == -1 ||
94		    priv_findname(type, desct) == -1)
95			fatalx("%s: rejected priv operation on interface: %s",
96			    __func__, vfr.vfr_name);
97		break;
98	default:
99		return (-1);
100	}
101
102	switch (imsg->hdr.type) {
103	case IMSG_VMDOP_PRIV_IFDESCR:
104		/* Set the interface description */
105		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
106		ifr.ifr_data = (caddr_t)vfr.vfr_value;
107		if (ioctl(env->vmd_fd, SIOCSIFDESCR, &ifr) < 0)
108			log_warn("SIOCSIFDESCR");
109		break;
110	case IMSG_VMDOP_PRIV_IFCREATE:
111		/* Create the bridge if it doesn't exist */
112		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
113		if (ioctl(env->vmd_fd, SIOCIFCREATE, &ifr) < 0 &&
114		    errno != EEXIST)
115			log_warn("SIOCIFCREATE");
116		break;
117	case IMSG_VMDOP_PRIV_IFADD:
118		if (priv_getiftype(vfr.vfr_value, type, NULL) == -1)
119			fatalx("%s: rejected to add interface: %s",
120			    __func__, vfr.vfr_value);
121
122		/* Attach the device to the bridge */
123		strlcpy(ifbr.ifbr_name, vfr.vfr_name,
124		    sizeof(ifbr.ifbr_name));
125		strlcpy(ifbr.ifbr_ifsname, vfr.vfr_value,
126		    sizeof(ifbr.ifbr_ifsname));
127		if (ioctl(env->vmd_fd, SIOCBRDGADD, &ifbr) < 0 &&
128		    errno != EEXIST)
129			log_warn("SIOCBRDGADD");
130		break;
131	case IMSG_VMDOP_PRIV_IFUP:
132	case IMSG_VMDOP_PRIV_IFDOWN:
133		/* Set the interface status */
134		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
135		if (ioctl(env->vmd_fd, SIOCGIFFLAGS, &ifr) < 0) {
136			log_warn("SIOCGIFFLAGS");
137			break;
138		}
139		if (imsg->hdr.type == IMSG_VMDOP_PRIV_IFUP)
140			ifr.ifr_flags |= IFF_UP;
141		else
142			ifr.ifr_flags &= ~IFF_UP;
143		if (ioctl(env->vmd_fd, SIOCSIFFLAGS, &ifr) < 0)
144			log_warn("SIOCSIFFLAGS");
145		break;
146	default:
147		return (-1);
148	}
149
150	return (0);
151}
152
153int
154priv_getiftype(char *ifname, char *type, unsigned int *unitptr)
155{
156	const char	*errstr;
157	size_t		 span;
158	unsigned int	 unit;
159
160	/* Extract the name part */
161	span = strcspn(ifname, "0123456789");
162	if (span == 0 || span >= strlen(ifname) || span >= (IF_NAMESIZE - 1))
163		return (-1);
164	memcpy(type, ifname, span);
165	type[span] = 0;
166
167	/* Now parse the unit (we don't strictly validate the format here) */
168	unit = strtonum(ifname + span, 0, UINT_MAX, &errstr);
169	if (errstr != NULL)
170		return (-1);
171	if (unitptr != NULL)
172		*unitptr = unit;
173
174	return (0);
175}
176
177int
178priv_findname(const char *name, const char **names)
179{
180	unsigned int	 i;
181
182	for (i = 0; names[i] != NULL; i++) {
183		if (strcmp(name, names[i]) == 0)
184			return (0);
185	}
186
187	return (-1);
188}
189
190/*
191 * Called from the process peer
192 */
193
194int
195vm_priv_ifconfig(struct privsep *ps, struct vmd_vm *vm)
196{
197	struct vm_create_params	*vcp = &vm->vm_params;
198	struct vmd_if		*vif;
199	struct vmd_switch	*vsw;
200	unsigned int		 i;
201	struct vmop_ifreq	 vfr, vfbr;
202
203	for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) {
204		vif = &vm->vm_ifs[i];
205
206		if (vif->vif_name == NULL)
207			break;
208
209		if (strlcpy(vfr.vfr_name, vif->vif_name,
210		    sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name))
211			return (-1);
212
213		/* Description can be truncated */
214		(void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value),
215		    "vm%u-if%u-%s", vm->vm_vmid, i, vcp->vcp_name);
216
217		log_debug("%s: interface %s description %s", __func__,
218		    vfr.vfr_name, vfr.vfr_value);
219
220		proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR,
221		    &vfr, sizeof(vfr));
222
223		/* Add interface to bridge/switch */
224		if ((vsw = switch_getbyname(vif->vif_switch)) != NULL) {
225			if (strlcpy(vfbr.vfr_name, vsw->sw_ifname,
226			    sizeof(vfbr.vfr_name)) >= sizeof(vfbr.vfr_name))
227				return (-1);
228			if (strlcpy(vfbr.vfr_value, vif->vif_name,
229			    sizeof(vfbr.vfr_value)) >= sizeof(vfbr.vfr_value))
230				return (-1);
231
232			log_debug("%s: interface %s add %s", __func__,
233			    vfbr.vfr_name, vfbr.vfr_value);
234
235			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFCREATE,
236			    &vfbr, sizeof(vfbr));
237			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADD,
238			    &vfbr, sizeof(vfbr));
239		} else if (vif->vif_switch != NULL)
240			log_warnx("switch %s not found", vif->vif_switch);
241
242		/* Set the new interface status to up or down */
243		proc_compose(ps, PROC_PRIV, (vif->vif_flags & IFF_UP) ?
244		    IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN,
245		    &vfr, sizeof(vfr));
246	}
247
248	return (0);
249}
250
251int
252vm_priv_brconfig(struct privsep *ps, struct vmd_switch *vsw)
253{
254	struct vmd_if		*vif;
255	struct vmop_ifreq	 vfr;
256
257	if (strlcpy(vfr.vfr_name, vsw->sw_ifname,
258	    sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name))
259		return (-1);
260
261	proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFCREATE,
262	    &vfr, sizeof(vfr));
263
264	/* Description can be truncated */
265	(void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value),
266	    "switch%u-%s", vsw->sw_id, vsw->sw_name);
267
268	log_debug("%s: interface %s description %s", __func__,
269	    vfr.vfr_name, vfr.vfr_value);
270
271	proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR,
272	    &vfr, sizeof(vfr));
273
274	TAILQ_FOREACH(vif, &vsw->sw_ifs, vif_entry) {
275		if (strlcpy(vfr.vfr_value, vif->vif_name,
276		    sizeof(vfr.vfr_value)) >= sizeof(vfr.vfr_value))
277			return (-1);
278
279		log_debug("%s: interface %s add %s", __func__,
280		    vfr.vfr_name, vfr.vfr_value);
281
282		proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADD,
283		    &vfr, sizeof(vfr));
284	}
285
286	/* Set the new interface status to up or down */
287	proc_compose(ps, PROC_PRIV, (vsw->sw_flags & IFF_UP) ?
288	    IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN,
289	    &vfr, sizeof(vfr));
290
291	return (0);
292}
293