GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.sbin/vmd/priv.c Lines: 0 195 0.0 %
Date: 2017-11-13 Branches: 0 130 0.0 %

Line Branch Exec Source
1
/*	$OpenBSD: priv.c,v 1.13 2017/11/11 02:50:07 mlarkin 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 <arpa/inet.h>
33
34
#include <errno.h>
35
#include <event.h>
36
#include <fcntl.h>
37
#include <stdlib.h>
38
#include <stdio.h>
39
#include <string.h>
40
#include <unistd.h>
41
#include <signal.h>
42
#include <ctype.h>
43
44
#include "proc.h"
45
#include "vmd.h"
46
47
int	 priv_dispatch_parent(int, struct privsep_proc *, struct imsg *);
48
void	 priv_run(struct privsep *, struct privsep_proc *, void *);
49
50
static struct privsep_proc procs[] = {
51
	{ "parent",	PROC_PARENT,	priv_dispatch_parent }
52
};
53
54
void
55
priv(struct privsep *ps, struct privsep_proc *p)
56
{
57
	proc_run(ps, p, procs, nitems(procs), priv_run, NULL);
58
}
59
60
void
61
priv_run(struct privsep *ps, struct privsep_proc *p, void *arg)
62
{
63
	struct vmd		*env = ps->ps_env;
64
65
	/*
66
	 * no pledge(2) in the "priv" process:
67
	 * write ioctls are not permitted by pledge.
68
	 */
69
70
	/* Open our own socket for generic interface ioctls */
71
	if ((env->vmd_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
72
		fatal("socket");
73
}
74
75
int
76
priv_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
77
{
78
	const char		*desct[] = { "tap", "switch", "bridge", NULL };
79
	struct privsep		*ps = p->p_ps;
80
	struct vmop_ifreq	 vfr;
81
	struct vmd		*env = ps->ps_env;
82
	struct ifreq		 ifr;
83
	struct ifbreq		 ifbr;
84
	struct ifgroupreq	 ifgr;
85
	struct ifaliasreq	 ifra;
86
	char			 type[IF_NAMESIZE];
87
88
	switch (imsg->hdr.type) {
89
	case IMSG_VMDOP_PRIV_IFDESCR:
90
	case IMSG_VMDOP_PRIV_IFRDOMAIN:
91
	case IMSG_VMDOP_PRIV_IFEXISTS:
92
	case IMSG_VMDOP_PRIV_IFADD:
93
	case IMSG_VMDOP_PRIV_IFUP:
94
	case IMSG_VMDOP_PRIV_IFDOWN:
95
	case IMSG_VMDOP_PRIV_IFGROUP:
96
	case IMSG_VMDOP_PRIV_IFADDR:
97
		IMSG_SIZE_CHECK(imsg, &vfr);
98
		memcpy(&vfr, imsg->data, sizeof(vfr));
99
100
		/* We should not get malicious requests from the parent */
101
		if (priv_getiftype(vfr.vfr_name, type, NULL) == -1 ||
102
		    priv_findname(type, desct) == -1)
103
			fatalx("%s: rejected priv operation on interface: %s",
104
			    __func__, vfr.vfr_name);
105
		break;
106
	case IMSG_VMDOP_CONFIG:
107
	case IMSG_CTL_RESET:
108
		break;
109
	default:
110
		return (-1);
111
	}
112
113
	switch (imsg->hdr.type) {
114
	case IMSG_VMDOP_PRIV_IFDESCR:
115
		/* Set the interface description */
116
		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
117
		ifr.ifr_data = (caddr_t)vfr.vfr_value;
118
		if (ioctl(env->vmd_fd, SIOCSIFDESCR, &ifr) < 0)
119
			log_warn("SIOCSIFDESCR");
120
		break;
121
	case IMSG_VMDOP_PRIV_IFRDOMAIN:
122
		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
123
		ifr.ifr_rdomainid = vfr.vfr_id;
124
		if (ioctl(env->vmd_fd, SIOCSIFRDOMAIN, &ifr) < 0)
125
			log_warn("SIOCSIFRDOMAIN");
126
		break;
127
	case IMSG_VMDOP_PRIV_IFADD:
128
		if (priv_getiftype(vfr.vfr_value, type, NULL) == -1)
129
			fatalx("%s: rejected to add interface: %s",
130
			    __func__, vfr.vfr_value);
131
132
		/* Attach the device to the bridge */
133
		strlcpy(ifbr.ifbr_name, vfr.vfr_name,
134
		    sizeof(ifbr.ifbr_name));
135
		strlcpy(ifbr.ifbr_ifsname, vfr.vfr_value,
136
		    sizeof(ifbr.ifbr_ifsname));
137
		if (ioctl(env->vmd_fd, SIOCBRDGADD, &ifbr) < 0 &&
138
		    errno != EEXIST)
139
			log_warn("SIOCBRDGADD");
140
		break;
141
	case IMSG_VMDOP_PRIV_IFEXISTS:
142
		/* Determine if bridge/switch exists */
143
		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
144
		if (ioctl(env->vmd_fd, SIOCGIFFLAGS, &ifr) < 0)
145
			fatalx("%s: bridge \"%s\" does not exist",
146
			    __func__, vfr.vfr_name);
147
		break;
148
	case IMSG_VMDOP_PRIV_IFUP:
149
	case IMSG_VMDOP_PRIV_IFDOWN:
150
		/* Set the interface status */
151
		strlcpy(ifr.ifr_name, vfr.vfr_name, sizeof(ifr.ifr_name));
152
		if (ioctl(env->vmd_fd, SIOCGIFFLAGS, &ifr) < 0) {
153
			log_warn("SIOCGIFFLAGS");
154
			break;
155
		}
156
		if (imsg->hdr.type == IMSG_VMDOP_PRIV_IFUP)
157
			ifr.ifr_flags |= IFF_UP;
158
		else
159
			ifr.ifr_flags &= ~IFF_UP;
160
		if (ioctl(env->vmd_fd, SIOCSIFFLAGS, &ifr) < 0)
161
			log_warn("SIOCSIFFLAGS");
162
		break;
163
	case IMSG_VMDOP_PRIV_IFGROUP:
164
		if (priv_validgroup(vfr.vfr_value) == -1)
165
			fatalx("%s: invalid group name", __func__);
166
167
		if (strlcpy(ifgr.ifgr_name, vfr.vfr_name,
168
		    sizeof(ifgr.ifgr_name)) >= sizeof(ifgr.ifgr_name) ||
169
		    strlcpy(ifgr.ifgr_group, vfr.vfr_value,
170
		    sizeof(ifgr.ifgr_group)) >= sizeof(ifgr.ifgr_group))
171
			fatalx("%s: group name too long", __func__);
172
173
		if (ioctl(env->vmd_fd, SIOCAIFGROUP, &ifgr) < 0 &&
174
		    errno != EEXIST)
175
			log_warn("SIOCAIFGROUP");
176
		break;
177
	case IMSG_VMDOP_PRIV_IFADDR:
178
		memset(&ifra, 0, sizeof(ifra));
179
180
		/* Set the interface address */
181
		strlcpy(ifra.ifra_name, vfr.vfr_name, sizeof(ifra.ifra_name));
182
183
		memcpy(&ifra.ifra_addr, &vfr.vfr_ifra.ifra_addr,
184
		    sizeof(ifra.ifra_addr));
185
		ifra.ifra_addr.sa_family = AF_INET;
186
		ifra.ifra_addr.sa_len = sizeof(struct sockaddr_in);
187
188
		memcpy(&ifra.ifra_mask, &vfr.vfr_ifra.ifra_mask,
189
		    sizeof(ifra.ifra_mask));
190
		ifra.ifra_mask.sa_family = AF_INET;
191
		ifra.ifra_mask.sa_len = sizeof(struct sockaddr_in);
192
193
		if (ioctl(env->vmd_fd, SIOCAIFADDR, &ifra) < 0)
194
			log_warn("SIOCAIFADDR");
195
		break;
196
	case IMSG_VMDOP_CONFIG:
197
		config_getconfig(env, imsg);
198
		break;
199
	case IMSG_CTL_RESET:
200
		config_getreset(env, imsg);
201
		break;
202
	default:
203
		return (-1);
204
	}
205
206
	return (0);
207
}
208
209
int
210
priv_getiftype(char *ifname, char *type, unsigned int *unitptr)
211
{
212
	const char	*errstr;
213
	size_t		 span;
214
	unsigned int	 unit;
215
216
	/* Extract the name part */
217
	span = strcspn(ifname, "0123456789");
218
	if (span == 0 || span >= strlen(ifname) || span >= (IF_NAMESIZE - 1))
219
		return (-1);
220
	memcpy(type, ifname, span);
221
	type[span] = 0;
222
223
	/* Now parse the unit (we don't strictly validate the format here) */
224
	unit = strtonum(ifname + span, 0, UINT_MAX, &errstr);
225
	if (errstr != NULL)
226
		return (-1);
227
	if (unitptr != NULL)
228
		*unitptr = unit;
229
230
	return (0);
231
}
232
233
int
234
priv_findname(const char *name, const char **names)
235
{
236
	unsigned int	 i;
237
238
	for (i = 0; names[i] != NULL; i++) {
239
		if (strcmp(name, names[i]) == 0)
240
			return (0);
241
	}
242
243
	return (-1);
244
}
245
246
int
247
priv_validgroup(const char *name)
248
{
249
	if (strlen(name) >= IF_NAMESIZE)
250
		return (-1);
251
	/* Group can not end with a digit */
252
	if (name[0] && isdigit(name[strlen(name) - 1]))
253
		return (-1);
254
	return (0);
255
}
256
257
/*
258
 * Called from the Parent process to setup vm interface(s)
259
 * - ensure the interface has the description set (tracking purposes)
260
 * - if interface is to be attached to a switch, attach it
261
 * - check if rdomain is set on interface and switch
262
 *   - if interface only or both, use interface rdomain
263
 *   - if switch only, use switch rdomain
264
 * - check if group is set on interface and switch
265
 *   - if interface, add it
266
 *   - if switch, add it
267
 * - ensure the interface is up/down
268
 * - if local interface, set address
269
 */
270
int
271
vm_priv_ifconfig(struct privsep *ps, struct vmd_vm *vm)
272
{
273
	struct vmd		*env = ps->ps_env;
274
	struct vm_create_params	*vcp = &vm->vm_params.vmc_params;
275
	struct vmd_if		*vif;
276
	struct vmd_switch	*vsw;
277
	unsigned int		 i;
278
	struct vmop_ifreq	 vfr, vfbr;
279
	struct sockaddr_in	*sin4;
280
281
	for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) {
282
		vif = &vm->vm_ifs[i];
283
284
		if (vif->vif_name == NULL)
285
			break;
286
287
		if (strlcpy(vfr.vfr_name, vif->vif_name,
288
		    sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name))
289
			return (-1);
290
291
		/* Description can be truncated */
292
		(void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value),
293
		    "vm%u-if%u-%s", vm->vm_vmid, i, vcp->vcp_name);
294
295
		log_debug("%s: interface %s description %s", __func__,
296
		    vfr.vfr_name, vfr.vfr_value);
297
298
		proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR,
299
		    &vfr, sizeof(vfr));
300
301
		/* set default rdomain */
302
		vfr.vfr_id = getrtable();
303
304
		vsw = switch_getbyname(vif->vif_switch);
305
306
		/* Check if switch should exist */
307
		if (vsw == NULL && vif->vif_switch != NULL)
308
			log_warnx("switch \"%s\" not found", vif->vif_switch);
309
310
		/* Add interface to switch and set proper rdomain */
311
		if (vsw != NULL) {
312
			memset(&vfbr, 0, sizeof(vfbr));
313
314
			if (strlcpy(vfbr.vfr_name, vsw->sw_ifname,
315
			    sizeof(vfbr.vfr_name)) >= sizeof(vfbr.vfr_name))
316
				return (-1);
317
			if (strlcpy(vfbr.vfr_value, vif->vif_name,
318
			    sizeof(vfbr.vfr_value)) >= sizeof(vfbr.vfr_value))
319
				return (-1);
320
321
			log_debug("%s: switch \"%s\" interface %s add %s",
322
			    __func__, vsw->sw_name, vfbr.vfr_name,
323
			    vfbr.vfr_value);
324
325
			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADD,
326
			    &vfbr, sizeof(vfbr));
327
328
			/* Check rdomain properties */
329
			if (vif->vif_flags & VMIFF_RDOMAIN)
330
				vfr.vfr_id = vif->vif_rdomain;
331
			else if (vsw->sw_flags & VMIFF_RDOMAIN)
332
				vfr.vfr_id = vsw->sw_rdomain;
333
		} else {
334
			/* No switch to attach case */
335
			if (vif->vif_flags & VMIFF_RDOMAIN)
336
				vfr.vfr_id = vif->vif_rdomain;
337
		}
338
339
		/* Set rdomain on interface */
340
		if (vfr.vfr_id != 0)
341
			log_debug("%s: interface %s rdomain %u", __func__,
342
			    vfr.vfr_name, vfr.vfr_id);
343
344
		proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFRDOMAIN,
345
		    &vfr, sizeof(vfr));
346
347
		/* First group is defined per-interface */
348
		if (vif->vif_group) {
349
			if (strlcpy(vfr.vfr_value, vif->vif_group,
350
			    sizeof(vfr.vfr_value)) >= sizeof(vfr.vfr_value))
351
				return (-1);
352
353
			log_debug("%s: interface %s group %s", __func__,
354
			    vfr.vfr_name, vfr.vfr_value);
355
356
			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFGROUP,
357
			    &vfr, sizeof(vfr));
358
		}
359
360
		/* The second group is defined per-switch */
361
		if (vsw != NULL && vsw->sw_group != NULL) {
362
			if (strlcpy(vfr.vfr_value, vsw->sw_group,
363
			    sizeof(vfr.vfr_value)) >= sizeof(vfr.vfr_value))
364
				return (-1);
365
366
			log_debug("%s: interface %s group %s switch \"%s\"",
367
			    __func__, vfr.vfr_name, vfr.vfr_value,
368
			    vsw->sw_name);
369
370
			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFGROUP,
371
			    &vfr, sizeof(vfr));
372
		}
373
374
		/* Set the new interface status to up or down */
375
		proc_compose(ps, PROC_PRIV, (vif->vif_flags & VMIFF_UP) ?
376
		    IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN,
377
		    &vfr, sizeof(vfr));
378
379
		/* Set interface address if it is a local interface */
380
		if (vm->vm_params.vmc_ifflags[i] & VMIFF_LOCAL) {
381
			sin4 = (struct sockaddr_in *)&vfr.vfr_ifra.ifra_mask;
382
			sin4->sin_family = AF_INET;
383
			sin4->sin_len = sizeof(*sin4);
384
			sin4->sin_addr.s_addr = htonl(0xfffffffe);
385
386
			sin4 = (struct sockaddr_in *)&vfr.vfr_ifra.ifra_addr;
387
			sin4->sin_family = AF_INET;
388
			sin4->sin_len = sizeof(*sin4);
389
			if ((sin4->sin_addr.s_addr =
390
			    vm_priv_addr(&env->vmd_cfg.cfg_localprefix,
391
			    vm->vm_vmid, i, 0)) == 0)
392
				return (-1);
393
394
			log_debug("%s: interface %s address %s/31",
395
			    __func__, vfr.vfr_name,
396
			    inet_ntoa(sin4->sin_addr));
397
398
			proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFADDR,
399
			    &vfr, sizeof(vfr));
400
		}
401
	}
402
403
	return (0);
404
}
405
406
/*
407
 * Called from the Parent process to setup underlying switch interface
408
 * - ensure the interface exists
409
 * - ensure the interface has the correct rdomain set
410
 * - ensure the interface has the description set (tracking purposes)
411
 * - ensure the interface is up/down
412
 */
413
int
414
vm_priv_brconfig(struct privsep *ps, struct vmd_switch *vsw)
415
{
416
	struct vmop_ifreq	 vfr;
417
418
	memset(&vfr, 0, sizeof(vfr));
419
420
	if (strlcpy(vfr.vfr_name, vsw->sw_ifname,
421
	    sizeof(vfr.vfr_name)) >= sizeof(vfr.vfr_name))
422
		return (-1);
423
424
	/* ensure bridge/switch exists */
425
	proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFEXISTS,
426
	    &vfr, sizeof(vfr));
427
428
	/* Use the configured rdomain or get it from the process */
429
	if (vsw->sw_flags & VMIFF_RDOMAIN)
430
		vfr.vfr_id = vsw->sw_rdomain;
431
	else
432
		vfr.vfr_id = getrtable();
433
	if (vfr.vfr_id != 0)
434
		log_debug("%s: interface %s rdomain %u", __func__,
435
		    vfr.vfr_name, vfr.vfr_id);
436
437
	/* ensure switch has the correct rodmain */
438
	proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFRDOMAIN,
439
	    &vfr, sizeof(vfr));
440
441
	/* Description can be truncated */
442
	(void)snprintf(vfr.vfr_value, sizeof(vfr.vfr_value),
443
	    "switch%u-%s", vsw->sw_id, vsw->sw_name);
444
445
	log_debug("%s: interface %s description %s", __func__,
446
	    vfr.vfr_name, vfr.vfr_value);
447
448
	proc_compose(ps, PROC_PRIV, IMSG_VMDOP_PRIV_IFDESCR,
449
	    &vfr, sizeof(vfr));
450
451
	/* Set the new interface status to up or down */
452
	proc_compose(ps, PROC_PRIV, (vsw->sw_flags & VMIFF_UP) ?
453
	    IMSG_VMDOP_PRIV_IFUP : IMSG_VMDOP_PRIV_IFDOWN,
454
	    &vfr, sizeof(vfr));
455
456
	vsw->sw_running = 1;
457
	return (0);
458
}
459
460
uint32_t
461
vm_priv_addr(struct address *h, uint32_t vmid, int idx, int isvm)
462
{
463
	in_addr_t		prefix, mask, addr;
464
465
	/*
466
	 * 1. Set the address prefix and mask, 100.64.0.0/10 by default.
467
	 */
468
	if (h->ss.ss_family != AF_INET ||
469
	    h->prefixlen < 0 || h->prefixlen > 32)
470
		fatal("local prefix");
471
	prefix = ss2sin(&h->ss)->sin_addr.s_addr;
472
	mask = prefixlen2mask(h->prefixlen);
473
474
	/* 2. Encode the VM ID as a per-VM subnet range N, 100.64.N.0/24. */
475
	addr = vmid << 8;
476
477
	/*
478
	 * 3. Assign a /31 subnet M per VM interface, 100.64.N.M/31.
479
	 * Each subnet contains exactly two IP addresses; skip the
480
	 * first subnet to avoid a gateway address ending with .0.
481
	 */
482
	addr |= (idx + 1) * 2;
483
484
	/* 4. Use the first address for the gateway, the second for the VM. */
485
	if (isvm)
486
		addr++;
487
488
	/* 5. Convert to network byte order and add the prefix. */
489
	addr = htonl(addr) | prefix;
490
491
	/*
492
	 * Validate the results:
493
	 * - the address should not exceed the prefix (eg. VM ID to high).
494
	 * - up to 126 interfaces can be encoded per VM.
495
	 */
496
	if (prefix != (addr & mask) || idx >= 0x7f) {
497
		log_warnx("%s: dhcp address range exceeded,"
498
		    " vm id %u interface %d", __func__, vmid, idx);
499
		return (0);
500
	}
501
502
	return (addr);
503
}