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

Line Branch Exec Source
1
/*	$OpenBSD: vmd.c,v 1.74 2017/11/11 02:50:07 mlarkin Exp $	*/
2
3
/*
4
 * Copyright (c) 2015 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/wait.h>
22
#include <sys/cdefs.h>
23
#include <sys/stat.h>
24
#include <sys/tty.h>
25
#include <sys/ioctl.h>
26
27
#include <stdio.h>
28
#include <stdlib.h>
29
#include <string.h>
30
#include <termios.h>
31
#include <errno.h>
32
#include <event.h>
33
#include <fcntl.h>
34
#include <pwd.h>
35
#include <signal.h>
36
#include <syslog.h>
37
#include <unistd.h>
38
#include <ctype.h>
39
#include <pwd.h>
40
#include <grp.h>
41
42
#include <machine/specialreg.h>
43
#include <machine/vmmvar.h>
44
45
#include "proc.h"
46
#include "atomicio.h"
47
#include "vmd.h"
48
49
__dead void usage(void);
50
51
int	 main(int, char **);
52
int	 vmd_configure(void);
53
void	 vmd_sighdlr(int sig, short event, void *arg);
54
void	 vmd_shutdown(void);
55
int	 vmd_control_run(void);
56
int	 vmd_dispatch_control(int, struct privsep_proc *, struct imsg *);
57
int	 vmd_dispatch_vmm(int, struct privsep_proc *, struct imsg *);
58
int	 check_vmh(struct vm_dump_header *);
59
60
struct vmd	*env;
61
62
static struct privsep_proc procs[] = {
63
	/* Keep "priv" on top as procs[0] */
64
	{ "priv",	PROC_PRIV,	NULL, priv },
65
	{ "control",	PROC_CONTROL,	vmd_dispatch_control, control },
66
	{ "vmm",	PROC_VMM,	vmd_dispatch_vmm, vmm, vmm_shutdown },
67
};
68
69
/* For the privileged process */
70
static struct privsep_proc *proc_priv = &procs[0];
71
static struct passwd proc_privpw;
72
73
int
74
vmd_dispatch_control(int fd, struct privsep_proc *p, struct imsg *imsg)
75
{
76
	struct privsep			*ps = p->p_ps;
77
	int				 res = 0, ret = 0, cmd = 0, verbose;
78
	unsigned int			 v = 0;
79
	struct vmop_create_params	 vmc;
80
	struct vmop_id			 vid;
81
	struct vm_terminate_params	 vtp;
82
	struct vmop_result		 vmr;
83
	struct vm_dump_header		 vmh;
84
	struct vmd_vm			*vm = NULL;
85
	char				*str = NULL;
86
	uint32_t			 id = 0;
87
88
	switch (imsg->hdr.type) {
89
	case IMSG_VMDOP_START_VM_REQUEST:
90
		IMSG_SIZE_CHECK(imsg, &vmc);
91
		memcpy(&vmc, imsg->data, sizeof(vmc));
92
		ret = vm_register(ps, &vmc, &vm, 0, vmc.vmc_uid);
93
		if (vmc.vmc_flags == 0) {
94
			/* start an existing VM with pre-configured options */
95
			if (!(ret == -1 && errno == EALREADY &&
96
			    vm->vm_running == 0)) {
97
				res = errno;
98
				cmd = IMSG_VMDOP_START_VM_RESPONSE;
99
			}
100
		} else if (ret != 0) {
101
			res = errno;
102
			cmd = IMSG_VMDOP_START_VM_RESPONSE;
103
		}
104
		if (res == 0 &&
105
		    config_setvm(ps, vm, imsg->hdr.peerid, vmc.vmc_uid) == -1) {
106
			res = errno;
107
			cmd = IMSG_VMDOP_START_VM_RESPONSE;
108
		}
109
		break;
110
	case IMSG_VMDOP_TERMINATE_VM_REQUEST:
111
		IMSG_SIZE_CHECK(imsg, &vid);
112
		memcpy(&vid, imsg->data, sizeof(vid));
113
		if ((id = vid.vid_id) == 0) {
114
			/* Lookup vm (id) by name */
115
			if ((vm = vm_getbyname(vid.vid_name)) == NULL) {
116
				res = ENOENT;
117
				cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE;
118
				break;
119
			} else if (vm->vm_shutdown) {
120
				res = EALREADY;
121
				cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE;
122
				break;
123
			} else if (vm->vm_running == 0) {
124
				res = EINVAL;
125
				cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE;
126
				break;
127
			}
128
			id = vm->vm_vmid;
129
		} else if ((vm = vm_getbyvmid(id)) == NULL) {
130
			res = ENOENT;
131
			cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE;
132
			break;
133
		}
134
		if (vm_checkperm(vm, vid.vid_uid) != 0) {
135
			res = EPERM;
136
			cmd = IMSG_VMDOP_TERMINATE_VM_RESPONSE;
137
			break;
138
		}
139
		memset(&vtp, 0, sizeof(vtp));
140
		vtp.vtp_vm_id = id;
141
		if (proc_compose_imsg(ps, PROC_VMM, -1, imsg->hdr.type,
142
		    imsg->hdr.peerid, -1, &vtp, sizeof(vtp)) == -1)
143
			return (-1);
144
		break;
145
	case IMSG_VMDOP_GET_INFO_VM_REQUEST:
146
		proc_forward_imsg(ps, imsg, PROC_VMM, -1);
147
		break;
148
	case IMSG_VMDOP_LOAD:
149
		IMSG_SIZE_CHECK(imsg, str); /* at least one byte for path */
150
		str = get_string((uint8_t *)imsg->data,
151
		    IMSG_DATA_SIZE(imsg));
152
	case IMSG_VMDOP_RELOAD:
153
		if (vmd_reload(0, str) == -1)
154
			cmd = IMSG_CTL_FAIL;
155
		else
156
			cmd = IMSG_CTL_OK;
157
		free(str);
158
		break;
159
	case IMSG_CTL_RESET:
160
		IMSG_SIZE_CHECK(imsg, &v);
161
		memcpy(&v, imsg->data, sizeof(v));
162
		if (vmd_reload(v, NULL) == -1)
163
			cmd = IMSG_CTL_FAIL;
164
		else
165
			cmd = IMSG_CTL_OK;
166
		break;
167
	case IMSG_CTL_VERBOSE:
168
		IMSG_SIZE_CHECK(imsg, &verbose);
169
		memcpy(&verbose, imsg->data, sizeof(verbose));
170
		log_setverbose(verbose);
171
172
		proc_forward_imsg(ps, imsg, PROC_VMM, -1);
173
		proc_forward_imsg(ps, imsg, PROC_PRIV, -1);
174
		cmd = IMSG_CTL_OK;
175
		break;
176
	case IMSG_VMDOP_PAUSE_VM:
177
	case IMSG_VMDOP_UNPAUSE_VM:
178
		IMSG_SIZE_CHECK(imsg, &vid);
179
		memcpy(&vid, imsg->data, sizeof(vid));
180
		if (vid.vid_id == 0) {
181
			if ((vm = vm_getbyname(vid.vid_name)) == NULL) {
182
				res = ENOENT;
183
				cmd = IMSG_VMDOP_PAUSE_VM_RESPONSE;
184
				break;
185
			} else {
186
				vid.vid_id = vm->vm_vmid;
187
			}
188
		}
189
		proc_compose_imsg(ps, PROC_VMM, -1, imsg->hdr.type,
190
		    imsg->hdr.peerid, -1, &vid, sizeof(vid));
191
		break;
192
	case IMSG_VMDOP_SEND_VM_REQUEST:
193
		IMSG_SIZE_CHECK(imsg, &vid);
194
		memcpy(&vid, imsg->data, sizeof(vid));
195
		id = vid.vid_id;
196
		if (vid.vid_id == 0) {
197
			if ((vm = vm_getbyname(vid.vid_name)) == NULL) {
198
				res = ENOENT;
199
				cmd = IMSG_VMDOP_SEND_VM_RESPONSE;
200
				close(imsg->fd);
201
				break;
202
			} else {
203
				vid.vid_id = vm->vm_vmid;
204
			}
205
		} else if ((vm = vm_getbyvmid(vid.vid_id)) == NULL) {
206
			res = ENOENT;
207
			cmd = IMSG_VMDOP_SEND_VM_RESPONSE;
208
			close(imsg->fd);
209
			break;
210
		} else {
211
		}
212
		vmr.vmr_id = vid.vid_id;
213
		log_debug("%s: sending fd to vmm", __func__);
214
		proc_compose_imsg(ps, PROC_VMM, -1, imsg->hdr.type,
215
		    imsg->hdr.peerid, imsg->fd, &vid, sizeof(vid));
216
		break;
217
	case IMSG_VMDOP_RECEIVE_VM_REQUEST:
218
		IMSG_SIZE_CHECK(imsg, &vid);
219
		memcpy(&vid, imsg->data, sizeof(vid));
220
		if (imsg->fd == -1) {
221
			log_warnx("%s: invalid fd", __func__);
222
			return (-1);
223
		}
224
		if (atomicio(read, imsg->fd, &vmh, sizeof(vmh)) !=
225
		    sizeof(vmh)) {
226
			log_warnx("%s: error reading vmh from recevied vm",
227
			    __func__);
228
			res = EIO;
229
			close(imsg->fd);
230
			cmd = IMSG_VMDOP_START_VM_RESPONSE;
231
			break;
232
		}
233
		if(check_vmh(&vmh)) {
234
			res = ENOENT;
235
			close(imsg->fd);
236
			cmd = IMSG_VMDOP_START_VM_RESPONSE;
237
			break;
238
		}
239
		if (atomicio(read, imsg->fd, &vmc, sizeof(vmc)) !=
240
		    sizeof(vmc)) {
241
			log_warnx("%s: error reading vmc from recevied vm",
242
			    __func__);
243
			res = EIO;
244
			close(imsg->fd);
245
			cmd = IMSG_VMDOP_START_VM_RESPONSE;
246
			break;
247
		}
248
		strlcpy(vmc.vmc_params.vcp_name, vid.vid_name,
249
		    sizeof(vmc.vmc_params.vcp_name));
250
		vmc.vmc_params.vcp_id = 0;
251
252
		ret = vm_register(ps, &vmc, &vm, 0, vmc.vmc_uid);
253
		if (ret != 0) {
254
			res = errno;
255
			cmd = IMSG_VMDOP_START_VM_RESPONSE;
256
			close(imsg->fd);
257
		} else {
258
			vm->vm_received = 1;
259
			config_setvm(ps, vm, imsg->hdr.peerid, vmc.vmc_uid);
260
			log_debug("%s: sending fd to vmm", __func__);
261
			proc_compose_imsg(ps, PROC_VMM, -1,
262
			    IMSG_VMDOP_RECEIVE_VM_END, vm->vm_vmid, imsg->fd,
263
			    NULL, 0);
264
		}
265
		break;
266
	default:
267
		return (-1);
268
	}
269
270
	switch (cmd) {
271
	case 0:
272
		break;
273
	case IMSG_VMDOP_START_VM_RESPONSE:
274
	case IMSG_VMDOP_TERMINATE_VM_RESPONSE:
275
		memset(&vmr, 0, sizeof(vmr));
276
		vmr.vmr_result = res;
277
		vmr.vmr_id = id;
278
		if (proc_compose_imsg(ps, PROC_CONTROL, -1, cmd,
279
		    imsg->hdr.peerid, -1, &vmr, sizeof(vmr)) == -1)
280
			return (-1);
281
		break;
282
	default:
283
		if (proc_compose_imsg(ps, PROC_CONTROL, -1, cmd,
284
		    imsg->hdr.peerid, -1, &res, sizeof(res)) == -1)
285
			return (-1);
286
		break;
287
	}
288
289
	return (0);
290
}
291
292
int
293
vmd_dispatch_vmm(int fd, struct privsep_proc *p, struct imsg *imsg)
294
{
295
	struct vmop_result	 vmr;
296
	struct privsep		*ps = p->p_ps;
297
	int			 res = 0;
298
	struct vmd_vm		*vm;
299
	struct vm_create_params	*vcp;
300
	struct vmop_info_result	 vir;
301
302
	switch (imsg->hdr.type) {
303
	case IMSG_VMDOP_PAUSE_VM_RESPONSE:
304
		IMSG_SIZE_CHECK(imsg, &vmr);
305
		memcpy(&vmr, imsg->data, sizeof(vmr));
306
		if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL)
307
			break;
308
		proc_compose_imsg(ps, PROC_CONTROL, -1,
309
		    imsg->hdr.type, imsg->hdr.peerid, -1,
310
		    imsg->data, sizeof(imsg->data));
311
		log_info("%s: paused vm %d successfully",
312
		    vm->vm_params.vmc_params.vcp_name,
313
		    vm->vm_vmid);
314
		break;
315
	case IMSG_VMDOP_UNPAUSE_VM_RESPONSE:
316
		IMSG_SIZE_CHECK(imsg, &vmr);
317
		memcpy(&vmr, imsg->data, sizeof(vmr));
318
		if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL)
319
			break;
320
		proc_compose_imsg(ps, PROC_CONTROL, -1,
321
		    imsg->hdr.type, imsg->hdr.peerid, -1,
322
		    imsg->data, sizeof(imsg->data));
323
		log_info("%s: unpaused vm %d successfully.",
324
		    vm->vm_params.vmc_params.vcp_name,
325
		    vm->vm_vmid);
326
		break;
327
	case IMSG_VMDOP_START_VM_RESPONSE:
328
		IMSG_SIZE_CHECK(imsg, &vmr);
329
		memcpy(&vmr, imsg->data, sizeof(vmr));
330
		if ((vm = vm_getbyvmid(imsg->hdr.peerid)) == NULL)
331
			break;
332
		vm->vm_pid = vmr.vmr_pid;
333
		vcp = &vm->vm_params.vmc_params;
334
		vcp->vcp_id = vmr.vmr_id;
335
336
		/*
337
		 * If the peerid is not -1, forward the response back to the
338
		 * the control socket.  If it is -1, the request originated
339
		 * from the parent, not the control socket.
340
		 */
341
		if (vm->vm_peerid != (uint32_t)-1) {
342
			(void)strlcpy(vmr.vmr_ttyname, vm->vm_ttyname,
343
			    sizeof(vmr.vmr_ttyname));
344
			if (proc_compose_imsg(ps, PROC_CONTROL, -1,
345
			    imsg->hdr.type, vm->vm_peerid, -1,
346
			    &vmr, sizeof(vmr)) == -1) {
347
				errno = vmr.vmr_result;
348
				log_warn("%s: failed to foward vm result",
349
				    vcp->vcp_name);
350
				vm_remove(vm);
351
				return (-1);
352
			}
353
		}
354
355
		if (vmr.vmr_result) {
356
			errno = vmr.vmr_result;
357
			log_warn("%s: failed to start vm", vcp->vcp_name);
358
			vm_remove(vm);
359
			break;
360
		}
361
362
		/* Now configure all the interfaces */
363
		if (vm_priv_ifconfig(ps, vm) == -1) {
364
			log_warn("%s: failed to configure vm", vcp->vcp_name);
365
			vm_remove(vm);
366
			break;
367
		}
368
369
		log_info("%s: started vm %d successfully, tty %s",
370
		    vcp->vcp_name, vm->vm_vmid, vm->vm_ttyname);
371
		break;
372
	case IMSG_VMDOP_TERMINATE_VM_RESPONSE:
373
		IMSG_SIZE_CHECK(imsg, &vmr);
374
		memcpy(&vmr, imsg->data, sizeof(vmr));
375
		log_debug("%s: forwarding TERMINATE VM for vm id %d",
376
		    __func__, vmr.vmr_id);
377
		proc_forward_imsg(ps, imsg, PROC_CONTROL, -1);
378
		if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL)
379
			break;
380
		if (vmr.vmr_result == 0) {
381
			/* Mark VM as shutting down */
382
			vm->vm_shutdown = 1;
383
		}
384
		break;
385
	case IMSG_VMDOP_SEND_VM_RESPONSE:
386
		IMSG_SIZE_CHECK(imsg, &vmr);
387
		memcpy(&vmr, imsg->data, sizeof(vmr));
388
		if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL)
389
			break;
390
		if (!vmr.vmr_result)
391
			log_info("%s: sent vm %d successfully.",
392
			    vm->vm_params.vmc_params.vcp_name,
393
			    vm->vm_vmid);
394
	case IMSG_VMDOP_TERMINATE_VM_EVENT:
395
		IMSG_SIZE_CHECK(imsg, &vmr);
396
		memcpy(&vmr, imsg->data, sizeof(vmr));
397
		log_debug("%s: handling TERMINATE_EVENT for vm id %d ret %d",
398
		    __func__, vmr.vmr_id, vmr.vmr_result);
399
		if ((vm = vm_getbyvmid(vmr.vmr_id)) == NULL) {
400
			log_debug("%s: vm %d is no longer available",
401
			    __func__, vmr.vmr_id);
402
			break;
403
		}
404
		if (vmr.vmr_result != EAGAIN) {
405
			if (vm->vm_from_config) {
406
				log_debug("%s: about to stop vm id %d",
407
				    __func__, vm->vm_vmid);
408
				vm_stop(vm, 0);
409
			} else {
410
				log_debug("%s: about to remove vm %d",
411
				    __func__, vm->vm_vmid);
412
				vm_remove(vm);
413
			}
414
		} else {
415
			/* Stop VM instance but keep the tty open */
416
			log_debug("%s: about to stop vm id %d with tty open",
417
			    __func__, vm->vm_vmid);
418
			vm_stop(vm, 1);
419
			config_setvm(ps, vm, (uint32_t)-1, vm->vm_uid);
420
		}
421
		break;
422
	case IMSG_VMDOP_GET_INFO_VM_DATA:
423
		IMSG_SIZE_CHECK(imsg, &vir);
424
		memcpy(&vir, imsg->data, sizeof(vir));
425
		if ((vm = vm_getbyvmid(vir.vir_info.vir_id)) != NULL) {
426
			memset(vir.vir_ttyname, 0, sizeof(vir.vir_ttyname));
427
			if (vm->vm_ttyname != NULL)
428
				strlcpy(vir.vir_ttyname, vm->vm_ttyname,
429
				    sizeof(vir.vir_ttyname));
430
			if (vm->vm_shutdown) {
431
				/* XXX there might be a nicer way */
432
				(void)strlcat(vir.vir_info.vir_name,
433
				    " - stopping",
434
				    sizeof(vir.vir_info.vir_name));
435
			}
436
			/* get the user id who started the vm */
437
			vir.vir_uid = vm->vm_uid;
438
			vir.vir_gid = vm->vm_params.vmc_gid;
439
		}
440
		if (proc_compose_imsg(ps, PROC_CONTROL, -1, imsg->hdr.type,
441
		    imsg->hdr.peerid, -1, &vir, sizeof(vir)) == -1) {
442
			log_debug("%s: GET_INFO_VM failed for vm %d, removing",
443
			    __func__, vm->vm_vmid);
444
			vm_remove(vm);
445
			return (-1);
446
		}
447
		break;
448
	case IMSG_VMDOP_GET_INFO_VM_END_DATA:
449
		/*
450
		 * PROC_VMM has responded with the *running* VMs, now we
451
		 * append the others. These use the special value 0 for their
452
		 * kernel id to indicate that they are not running.
453
		 */
454
		TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
455
			if (!vm->vm_running) {
456
				memset(&vir, 0, sizeof(vir));
457
				vir.vir_info.vir_id = vm->vm_vmid;
458
				strlcpy(vir.vir_info.vir_name,
459
				    vm->vm_params.vmc_params.vcp_name,
460
				    VMM_MAX_NAME_LEN);
461
				vir.vir_info.vir_memory_size =
462
				    vm->vm_params.vmc_params.vcp_memranges[0].vmr_size;
463
				vir.vir_info.vir_ncpus =
464
				    vm->vm_params.vmc_params.vcp_ncpus;
465
				/* get the configured user id for this vm */
466
				vir.vir_uid = vm->vm_params.vmc_uid;
467
				vir.vir_gid = vm->vm_params.vmc_gid;
468
				if (proc_compose_imsg(ps, PROC_CONTROL, -1,
469
				    IMSG_VMDOP_GET_INFO_VM_DATA,
470
				    imsg->hdr.peerid, -1, &vir,
471
				    sizeof(vir)) == -1) {
472
					log_debug("%s: GET_INFO_VM_END failed",
473
					    __func__);
474
					vm_remove(vm);
475
					return (-1);
476
				}
477
			}
478
		}
479
		IMSG_SIZE_CHECK(imsg, &res);
480
		proc_forward_imsg(ps, imsg, PROC_CONTROL, -1);
481
		break;
482
	default:
483
		return (-1);
484
	}
485
486
	return (0);
487
}
488
489
int
490
check_vmh(struct vm_dump_header *vmh) {
491
	int i;
492
	unsigned int code, leaf;
493
	unsigned int a, b, c, d;
494
495
496
	if (vmh->vmh_version != VM_DUMP_VERSION) {
497
		log_warnx("%s: incompatible dump version", __func__);
498
		return (-1);
499
	}
500
501
	for (i = 0; i < VM_DUMP_HEADER_CPUID_COUNT; i++) {
502
		code = vmh->vmh_cpuids[i].code;
503
		leaf = vmh->vmh_cpuids[i].leaf;
504
		if (leaf != 0x00) {
505
			log_debug("%s: invalid leaf 0x%x for code 0x%x",
506
			    __func__, leaf, code);
507
			return (-1);
508
		}
509
510
		switch(code) {
511
		case 0x00:
512
		CPUID_LEAF(code, leaf, a, b, c, d);
513
		if (vmh->vmh_cpuids[i].a > a) {
514
			log_debug("%s: incompatible cpuid level", __func__);
515
			return (-1);
516
		}
517
		if (!(vmh->vmh_cpuids[i].b == b &&
518
		    vmh->vmh_cpuids[i].c == c &&
519
		    vmh->vmh_cpuids[i].d == d)) {
520
			log_debug("%s: incompatible cpu brand", __func__);
521
			return (-1);
522
		}
523
		break;
524
525
		case 0x01:
526
		CPUID_LEAF(code, leaf, a, b, c, d);
527
		if ((vmh->vmh_cpuids[i].c & c & VMM_CPUIDECX_MASK) !=
528
		    (vmh->vmh_cpuids[i].c & VMM_CPUIDECX_MASK)) {
529
			log_debug("%s: incompatible cpu features "
530
			    "code: 0x%x leaf: 0x%x  reg: c", __func__,
531
			    code, leaf);
532
			return (-1);
533
		}
534
		if ((vmh->vmh_cpuids[i].d & d & VMM_CPUIDEDX_MASK) !=
535
		    (vmh->vmh_cpuids[i].d & VMM_CPUIDEDX_MASK)) {
536
			log_debug("%s: incompatible cpu features "
537
			    "code: 0x%x leaf: 0x%x  reg: d", __func__,
538
			    code, leaf);
539
			return (-1);
540
		}
541
		break;
542
543
		case 0x07:
544
		CPUID_LEAF(code, leaf, a, b, c, d);
545
		if ((vmh->vmh_cpuids[i].b & b & VMM_SEFF0EBX_MASK) !=
546
		    (vmh->vmh_cpuids[i].b & VMM_SEFF0EBX_MASK)) {
547
			log_debug("%s: incompatible cpu features "
548
			    "code: 0x%x leaf: 0x%x  reg: c", __func__,
549
			    code, leaf);
550
			return (-1);
551
		}
552
		if ((vmh->vmh_cpuids[i].c & c & VMM_SEFF0ECX_MASK) !=
553
		    (vmh->vmh_cpuids[i].c & VMM_SEFF0ECX_MASK)) {
554
			log_debug("%s: incompatible cpu features "
555
			    "code: 0x%x leaf: 0x%x  reg: d", __func__,
556
			    code, leaf);
557
			return (-1);
558
		}
559
		break;
560
561
		case 0x0d:
562
		CPUID_LEAF(code, leaf, a, b, c, d);
563
		if (vmh->vmh_cpuids[i].b > b) {
564
			log_debug("%s: incompatible cpu: insufficient "
565
			    "max save area for enabled XCR0 features",
566
			    __func__);
567
			return (-1);
568
		}
569
		if (vmh->vmh_cpuids[i].c > c) {
570
			log_debug("%s: incompatible cpu: insufficient "
571
			    "max save area for supported XCR0 features",
572
			    __func__);
573
			return (-1);
574
		}
575
		break;
576
577
		case 0x80000001:
578
		CPUID_LEAF(code, leaf, a, b, c, d);
579
		if ((vmh->vmh_cpuids[i].a & a) != vmh->vmh_cpuids[i].a) {
580
			log_debug("%s: incompatible cpu features "
581
			    "code: 0x%x leaf: 0x%x  reg: a", __func__,
582
			    code, leaf);
583
			return (-1);
584
		}
585
		if ((vmh->vmh_cpuids[i].c & c) != vmh->vmh_cpuids[i].c) {
586
			log_debug("%s: incompatible cpu features "
587
			    "code: 0x%x leaf: 0x%x  reg: c", __func__,
588
			    code, leaf);
589
			return (-1);
590
		}
591
		if ((vmh->vmh_cpuids[i].d & d) != vmh->vmh_cpuids[i].d) {
592
			log_debug("%s: incompatible cpu features "
593
			    "code: 0x%x leaf: 0x%x  reg: d", __func__,
594
			    code, leaf);
595
			return (-1);
596
		}
597
		break;
598
599
		default:
600
		log_debug("%s: unknown code 0x%x", __func__, code);
601
		return (-1);
602
		}
603
	}
604
605
	return (0);
606
}
607
608
void
609
vmd_sighdlr(int sig, short event, void *arg)
610
{
611
	if (privsep_process != PROC_PARENT)
612
		return;
613
	log_debug("%s: handling signal", __func__);
614
615
	switch (sig) {
616
	case SIGHUP:
617
		log_info("%s: reload requested with SIGHUP", __func__);
618
619
		/*
620
		 * This is safe because libevent uses async signal handlers
621
		 * that run in the event loop and not in signal context.
622
		 */
623
		(void)vmd_reload(0, NULL);
624
		break;
625
	case SIGPIPE:
626
		log_info("%s: ignoring SIGPIPE", __func__);
627
		break;
628
	case SIGUSR1:
629
		log_info("%s: ignoring SIGUSR1", __func__);
630
		break;
631
	case SIGTERM:
632
	case SIGINT:
633
		vmd_shutdown();
634
		break;
635
	default:
636
		fatalx("unexpected signal");
637
	}
638
}
639
640
__dead void
641
usage(void)
642
{
643
	extern char *__progname;
644
	fprintf(stderr, "usage: %s [-dnv] [-D macro=value] [-f file]\n",
645
	    __progname);
646
	exit(1);
647
}
648
649
int
650
main(int argc, char **argv)
651
{
652
	struct privsep		*ps;
653
	int			 ch;
654
	const char		*conffile = VMD_CONF;
655
	enum privsep_procid	 proc_id = PROC_PARENT;
656
	int			 proc_instance = 0;
657
	const char		*errp, *title = NULL;
658
	int			 argc0 = argc;
659
660
	/* log to stderr until daemonized */
661
	log_init(1, LOG_DAEMON);
662
663
	if ((env = calloc(1, sizeof(*env))) == NULL)
664
		fatal("calloc: env");
665
666
	while ((ch = getopt(argc, argv, "D:P:I:df:vn")) != -1) {
667
		switch (ch) {
668
		case 'D':
669
			if (cmdline_symset(optarg) < 0)
670
				log_warnx("could not parse macro definition %s",
671
				    optarg);
672
			break;
673
		case 'd':
674
			env->vmd_debug = 2;
675
			break;
676
		case 'f':
677
			conffile = optarg;
678
			break;
679
		case 'v':
680
			env->vmd_verbose++;
681
			break;
682
		case 'n':
683
			env->vmd_noaction = 1;
684
			break;
685
		case 'P':
686
			title = optarg;
687
			proc_id = proc_getid(procs, nitems(procs), title);
688
			if (proc_id == PROC_MAX)
689
				fatalx("invalid process name");
690
			break;
691
		case 'I':
692
			proc_instance = strtonum(optarg, 0,
693
			    PROC_MAX_INSTANCES, &errp);
694
			if (errp)
695
				fatalx("invalid process instance");
696
			break;
697
		default:
698
			usage();
699
		}
700
	}
701
702
	argc -= optind;
703
	if (argc > 0)
704
		usage();
705
706
	if (env->vmd_noaction && !env->vmd_debug)
707
		env->vmd_debug = 1;
708
709
	/* check for root privileges */
710
	if (env->vmd_noaction == 0) {
711
		if (geteuid())
712
			fatalx("need root privileges");
713
	}
714
715
	ps = &env->vmd_ps;
716
	ps->ps_env = env;
717
	env->vmd_fd = -1;
718
719
	if (config_init(env) == -1)
720
		fatal("failed to initialize configuration");
721
722
	if ((ps->ps_pw = getpwnam(VMD_USER)) == NULL)
723
		fatal("unknown user %s", VMD_USER);
724
725
	/* First proc runs as root without pledge but in default chroot */
726
	proc_priv->p_pw = &proc_privpw; /* initialized to all 0 */
727
	proc_priv->p_chroot = ps->ps_pw->pw_dir; /* from VMD_USER */
728
729
	/* Open /dev/vmm */
730
	if (env->vmd_noaction == 0) {
731
		env->vmd_fd = open(VMM_NODE, O_RDWR);
732
		if (env->vmd_fd == -1)
733
			fatal("%s", VMM_NODE);
734
	}
735
736
	/* Configure the control socket */
737
	ps->ps_csock.cs_name = SOCKET_NAME;
738
	TAILQ_INIT(&ps->ps_rcsocks);
739
740
	/* Configuration will be parsed after forking the children */
741
	env->vmd_conffile = conffile;
742
743
	log_init(env->vmd_debug, LOG_DAEMON);
744
	log_setverbose(env->vmd_verbose);
745
746
	if (env->vmd_noaction)
747
		ps->ps_noaction = 1;
748
	ps->ps_instance = proc_instance;
749
	if (title != NULL)
750
		ps->ps_title[proc_id] = title;
751
752
	/* only the parent returns */
753
	proc_init(ps, procs, nitems(procs), argc0, argv, proc_id);
754
755
	log_procinit("parent");
756
	if (!env->vmd_debug && daemon(0, 0) == -1)
757
		fatal("can't daemonize");
758
759
	if (ps->ps_noaction == 0)
760
		log_info("startup");
761
762
	event_init();
763
764
	signal_set(&ps->ps_evsigint, SIGINT, vmd_sighdlr, ps);
765
	signal_set(&ps->ps_evsigterm, SIGTERM, vmd_sighdlr, ps);
766
	signal_set(&ps->ps_evsighup, SIGHUP, vmd_sighdlr, ps);
767
	signal_set(&ps->ps_evsigpipe, SIGPIPE, vmd_sighdlr, ps);
768
	signal_set(&ps->ps_evsigusr1, SIGUSR1, vmd_sighdlr, ps);
769
770
	signal_add(&ps->ps_evsigint, NULL);
771
	signal_add(&ps->ps_evsigterm, NULL);
772
	signal_add(&ps->ps_evsighup, NULL);
773
	signal_add(&ps->ps_evsigpipe, NULL);
774
	signal_add(&ps->ps_evsigusr1, NULL);
775
776
	if (!env->vmd_noaction)
777
		proc_connect(ps);
778
779
	if (vmd_configure() == -1)
780
		fatalx("configuration failed");
781
782
	event_dispatch();
783
784
	log_debug("parent exiting");
785
786
	return (0);
787
}
788
789
int
790
vmd_configure(void)
791
{
792
	struct vmd_vm		*vm;
793
	struct vmd_switch	*vsw;
794
795
	if ((env->vmd_ptmfd = open(PATH_PTMDEV, O_RDWR|O_CLOEXEC)) == -1)
796
		fatal("open %s", PATH_PTMDEV);
797
798
	/*
799
	 * pledge in the parent process:
800
	 * stdio - for malloc and basic I/O including events.
801
	 * rpath - for reload to open and read the configuration files.
802
	 * wpath - for opening disk images and tap devices.
803
	 * tty - for openpty.
804
	 * proc - run kill to terminate its children safely.
805
	 * sendfd - for disks, interfaces and other fds.
806
	 * recvfd - for send and receive.
807
	 * getpw - lookup user or group id by name.
808
	 * chown, fattr - change tty ownership
809
	 */
810
	if (pledge("stdio rpath wpath proc tty recvfd sendfd getpw"
811
	    " chown fattr", NULL) == -1)
812
		fatal("pledge");
813
814
	if (parse_config(env->vmd_conffile) == -1) {
815
		proc_kill(&env->vmd_ps);
816
		exit(1);
817
	}
818
819
	if (env->vmd_noaction) {
820
		fprintf(stderr, "configuration OK\n");
821
		proc_kill(&env->vmd_ps);
822
		exit(0);
823
	}
824
825
	TAILQ_FOREACH(vsw, env->vmd_switches, sw_entry) {
826
		if (vsw->sw_running)
827
			continue;
828
		if (vm_priv_brconfig(&env->vmd_ps, vsw) == -1) {
829
			log_warn("%s: failed to create switch %s",
830
			    __func__, vsw->sw_name);
831
			switch_remove(vsw);
832
			return (-1);
833
		}
834
	}
835
836
	TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
837
		if (vm->vm_disabled) {
838
			log_debug("%s: not creating vm %s (disabled)",
839
			    __func__,
840
			    vm->vm_params.vmc_params.vcp_name);
841
			continue;
842
		}
843
		if (config_setvm(&env->vmd_ps, vm, -1, 0) == -1)
844
			return (-1);
845
	}
846
847
	/* Send shared global configuration to all children */
848
	if (config_setconfig(env) == -1)
849
		return (-1);
850
851
	return (0);
852
}
853
854
int
855
vmd_reload(unsigned int reset, const char *filename)
856
{
857
	struct vmd_vm		*vm, *next_vm;
858
	struct vmd_switch	*vsw;
859
	int			 reload = 0;
860
861
	/* Switch back to the default config file */
862
	if (filename == NULL || *filename == '\0') {
863
		filename = env->vmd_conffile;
864
		reload = 1;
865
	}
866
867
	log_debug("%s: level %d config file %s", __func__, reset, filename);
868
869
	if (reset) {
870
		/* Purge the configuration */
871
		config_purge(env, reset);
872
		config_setreset(env, reset);
873
	} else {
874
		/*
875
		 * Load or reload the configuration.
876
		 *
877
		 * Reloading removes all non-running VMs before processing the
878
		 * config file, whereas loading only adds to the existing list
879
		 * of VMs.
880
		 */
881
882
		if (reload) {
883
			TAILQ_FOREACH_SAFE(vm, env->vmd_vms, vm_entry, next_vm) {
884
				if (vm->vm_running == 0) {
885
					log_debug("%s: calling vm_remove",
886
					    __func__);
887
					vm_remove(vm);
888
				}
889
			}
890
891
			/* Update shared global configuration in all children */
892
			if (config_setconfig(env) == -1)
893
				return (-1);
894
		}
895
896
		if (parse_config(filename) == -1) {
897
			log_debug("%s: failed to load config file %s",
898
			    __func__, filename);
899
			return (-1);
900
		}
901
902
		TAILQ_FOREACH(vsw, env->vmd_switches, sw_entry) {
903
			if (vsw->sw_running)
904
				continue;
905
			if (vm_priv_brconfig(&env->vmd_ps, vsw) == -1) {
906
				log_warn("%s: failed to create switch %s",
907
				    __func__, vsw->sw_name);
908
				switch_remove(vsw);
909
				return (-1);
910
			}
911
		}
912
913
		TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
914
			if (vm->vm_running == 0) {
915
				if (vm->vm_disabled) {
916
					log_debug("%s: not creating vm %s"
917
					    " (disabled)", __func__,
918
					    vm->vm_params.vmc_params.vcp_name);
919
					continue;
920
				}
921
				if (config_setvm(&env->vmd_ps, vm, -1, 0) == -1)
922
					return (-1);
923
			} else {
924
				log_debug("%s: not creating vm \"%s\": "
925
				    "(running)", __func__,
926
				    vm->vm_params.vmc_params.vcp_name);
927
			}
928
		}
929
	}
930
931
	return (0);
932
}
933
934
void
935
vmd_shutdown(void)
936
{
937
	struct vmd_vm *vm, *vm_next;
938
939
	log_debug("%s: performing shutdown", __func__);
940
	TAILQ_FOREACH_SAFE(vm, env->vmd_vms, vm_entry, vm_next) {
941
		vm_remove(vm);
942
	}
943
944
	proc_kill(&env->vmd_ps);
945
	free(env);
946
947
	log_warnx("parent terminating");
948
	exit(0);
949
}
950
951
struct vmd_vm *
952
vm_getbyvmid(uint32_t vmid)
953
{
954
	struct vmd_vm	*vm;
955
956
	if (vmid == 0)
957
		return (NULL);
958
	TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
959
		if (vm->vm_vmid == vmid)
960
			return (vm);
961
	}
962
963
	return (NULL);
964
}
965
966
struct vmd_vm *
967
vm_getbyid(uint32_t id)
968
{
969
	struct vmd_vm	*vm;
970
971
	if (id == 0)
972
		return (NULL);
973
	TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
974
		if (vm->vm_params.vmc_params.vcp_id == id)
975
			return (vm);
976
	}
977
978
	return (NULL);
979
}
980
981
uint32_t
982
vm_id2vmid(uint32_t id, struct vmd_vm *vm)
983
{
984
	if (vm == NULL && (vm = vm_getbyid(id)) == NULL)
985
		return (0);
986
	dprintf("%s: vmm id %u is vmid %u", __func__,
987
	    id, vm->vm_vmid);
988
	return (vm->vm_vmid);
989
}
990
991
uint32_t
992
vm_vmid2id(uint32_t vmid, struct vmd_vm *vm)
993
{
994
	if (vm == NULL && (vm = vm_getbyvmid(vmid)) == NULL)
995
		return (0);
996
	dprintf("%s: vmid %u is vmm id %u", __func__,
997
	    vmid, vm->vm_params.vmc_params.vcp_id);
998
	return (vm->vm_params.vmc_params.vcp_id);
999
}
1000
1001
struct vmd_vm *
1002
vm_getbyname(const char *name)
1003
{
1004
	struct vmd_vm	*vm;
1005
1006
	if (name == NULL)
1007
		return (NULL);
1008
	TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
1009
		if (strcmp(vm->vm_params.vmc_params.vcp_name, name) == 0)
1010
			return (vm);
1011
	}
1012
1013
	return (NULL);
1014
}
1015
1016
struct vmd_vm *
1017
vm_getbypid(pid_t pid)
1018
{
1019
	struct vmd_vm	*vm;
1020
1021
	TAILQ_FOREACH(vm, env->vmd_vms, vm_entry) {
1022
		if (vm->vm_pid == pid)
1023
			return (vm);
1024
	}
1025
1026
	return (NULL);
1027
}
1028
1029
void
1030
vm_stop(struct vmd_vm *vm, int keeptty)
1031
{
1032
	unsigned int	 i;
1033
1034
	if (vm == NULL)
1035
		return;
1036
1037
	log_debug("%s: stopping vm %d", __func__, vm->vm_vmid);
1038
	vm->vm_running = 0;
1039
	vm->vm_shutdown = 0;
1040
1041
	if (vm->vm_iev.ibuf.fd != -1) {
1042
		event_del(&vm->vm_iev.ev);
1043
		close(vm->vm_iev.ibuf.fd);
1044
	}
1045
	for (i = 0; i < VMM_MAX_DISKS_PER_VM; i++) {
1046
		if (vm->vm_disks[i] != -1) {
1047
			close(vm->vm_disks[i]);
1048
			vm->vm_disks[i] = -1;
1049
		}
1050
	}
1051
	for (i = 0; i < VMM_MAX_NICS_PER_VM; i++) {
1052
		if (vm->vm_ifs[i].vif_fd != -1) {
1053
			close(vm->vm_ifs[i].vif_fd);
1054
			vm->vm_ifs[i].vif_fd = -1;
1055
		}
1056
		free(vm->vm_ifs[i].vif_name);
1057
		free(vm->vm_ifs[i].vif_switch);
1058
		free(vm->vm_ifs[i].vif_group);
1059
		vm->vm_ifs[i].vif_name = NULL;
1060
		vm->vm_ifs[i].vif_switch = NULL;
1061
		vm->vm_ifs[i].vif_group = NULL;
1062
	}
1063
	if (vm->vm_kernel != -1) {
1064
		close(vm->vm_kernel);
1065
		vm->vm_kernel = -1;
1066
	}
1067
	if (!keeptty) {
1068
		vm_closetty(vm);
1069
		vm->vm_uid = 0;
1070
	}
1071
}
1072
1073
void
1074
vm_remove(struct vmd_vm *vm)
1075
{
1076
	if (vm == NULL)
1077
		return;
1078
1079
	log_debug("%s: removing vm id %d from running config",
1080
	    __func__, vm->vm_vmid);
1081
	TAILQ_REMOVE(env->vmd_vms, vm, vm_entry);
1082
	log_debug("%s: calling vm_stop", __func__);
1083
	vm_stop(vm, 0);
1084
	free(vm);
1085
}
1086
1087
int
1088
vm_register(struct privsep *ps, struct vmop_create_params *vmc,
1089
    struct vmd_vm **ret_vm, uint32_t id, uid_t uid)
1090
{
1091
	struct vmd_vm		*vm = NULL;
1092
	struct vm_create_params	*vcp = &vmc->vmc_params;
1093
	static const uint8_t	 zero_mac[ETHER_ADDR_LEN];
1094
	uint32_t		 rng;
1095
	unsigned int		 i;
1096
	struct vmd_switch	*sw;
1097
	char			*s;
1098
1099
	errno = 0;
1100
	*ret_vm = NULL;
1101
1102
	if ((vm = vm_getbyname(vcp->vcp_name)) != NULL ||
1103
	    (vm = vm_getbyvmid(vcp->vcp_id)) != NULL) {
1104
		if (vm_checkperm(vm, uid) != 0 || vmc->vmc_flags != 0) {
1105
			errno = EPERM;
1106
			goto fail;
1107
		}
1108
		*ret_vm = vm;
1109
		errno = EALREADY;
1110
		goto fail;
1111
	}
1112
1113
	/*
1114
	 * non-root users can only start existing VMs
1115
	 * XXX there could be a mechanism to allow overriding some options
1116
	 */
1117
	if (vm_checkperm(NULL, uid) != 0) {
1118
		errno = EPERM;
1119
		goto fail;
1120
	}
1121
	if (vmc->vmc_flags == 0) {
1122
		errno = ENOENT;
1123
		goto fail;
1124
	}
1125
	if (vcp->vcp_ncpus == 0)
1126
		vcp->vcp_ncpus = 1;
1127
	if (vcp->vcp_memranges[0].vmr_size == 0)
1128
		vcp->vcp_memranges[0].vmr_size = VM_DEFAULT_MEMORY;
1129
	if (vcp->vcp_ncpus > VMM_MAX_VCPUS_PER_VM) {
1130
		log_warnx("invalid number of CPUs");
1131
		goto fail;
1132
	} else if (vcp->vcp_ndisks > VMM_MAX_DISKS_PER_VM) {
1133
		log_warnx("invalid number of disks");
1134
		goto fail;
1135
	} else if (vcp->vcp_nnics > VMM_MAX_NICS_PER_VM) {
1136
		log_warnx("invalid number of interfaces");
1137
		goto fail;
1138
	} else if (strlen(vcp->vcp_kernel) == 0 && vcp->vcp_ndisks == 0) {
1139
		log_warnx("no kernel or disk specified");
1140
		goto fail;
1141
	} else if (strlen(vcp->vcp_name) == 0) {
1142
		log_warnx("invalid VM name");
1143
		goto fail;
1144
	} else if (*vcp->vcp_name == '-' || *vcp->vcp_name == '.' ||
1145
		   *vcp->vcp_name == '_') {
1146
		log_warnx("Invalid VM name");
1147
		goto fail;
1148
	} else {
1149
		for (s = vcp->vcp_name; *s != '\0'; ++s) {
1150
			if (!(isalnum(*s) || *s == '.' || *s == '-' ||
1151
			      *s == '_')) {
1152
				log_warnx("Invalid VM name");
1153
				goto fail;
1154
			}
1155
		}
1156
	}
1157
1158
	if ((vm = calloc(1, sizeof(*vm))) == NULL)
1159
		goto fail;
1160
1161
	memcpy(&vm->vm_params, vmc, sizeof(vm->vm_params));
1162
	vmc = &vm->vm_params;
1163
	vcp = &vmc->vmc_params;
1164
	vm->vm_pid = -1;
1165
	vm->vm_tty = -1;
1166
	vm->vm_receive_fd = -1;
1167
	vm->vm_paused = 0;
1168
1169
	for (i = 0; i < vcp->vcp_ndisks; i++)
1170
		vm->vm_disks[i] = -1;
1171
	for (i = 0; i < vcp->vcp_nnics; i++) {
1172
		vm->vm_ifs[i].vif_fd = -1;
1173
1174
		if ((sw = switch_getbyname(vmc->vmc_ifswitch[i])) != NULL) {
1175
			/* inherit per-interface flags from the switch */
1176
			vmc->vmc_ifflags[i] |= (sw->sw_flags & VMIFF_OPTMASK);
1177
		}
1178
1179
		/*
1180
		 * If the MAC address is zero, always randomize it in vmd(8)
1181
		 * because we cannot rely on the guest OS to do the right
1182
		 * thing like OpenBSD does.  Based on ether_fakeaddr()
1183
		 * from the kernel, incremented by one to differentiate
1184
		 * the source.
1185
		 */
1186
		if (memcmp(zero_mac, &vcp->vcp_macs[i], ETHER_ADDR_LEN) == 0) {
1187
			rng = arc4random();
1188
			vcp->vcp_macs[i][0] = 0xfe;
1189
			vcp->vcp_macs[i][1] = 0xe1;
1190
			vcp->vcp_macs[i][2] = 0xba + 1;
1191
			vcp->vcp_macs[i][3] = 0xd0 | ((i + 1) & 0xf);
1192
			vcp->vcp_macs[i][4] = rng;
1193
			vcp->vcp_macs[i][5] = rng >> 8;
1194
		}
1195
	}
1196
	vm->vm_kernel = -1;
1197
	vm->vm_iev.ibuf.fd = -1;
1198
1199
	if (++env->vmd_nvm == 0)
1200
		fatalx("too many vms");
1201
1202
	/* Assign a new internal Id if not specified */
1203
	vm->vm_vmid = id == 0 ? env->vmd_nvm : id;
1204
1205
	log_debug("%s: registering vm %d", __func__, vm->vm_vmid);
1206
	TAILQ_INSERT_TAIL(env->vmd_vms, vm, vm_entry);
1207
1208
	*ret_vm = vm;
1209
	return (0);
1210
 fail:
1211
	if (errno == 0)
1212
		errno = EINVAL;
1213
	return (-1);
1214
}
1215
1216
/*
1217
 * vm_checkperm
1218
 *
1219
 * Checks if the user represented by the 'uid' parameter is allowed to
1220
 * manipulate the VM described by the 'vm' parameter (or connect to said VM's
1221
 * console.)
1222
 *
1223
 * Parameters:
1224
 *  vm: the VM whose permission is to be checked
1225
 *  uid: the user ID of the user making the request
1226
 *
1227
 * Return values:
1228
 *   0: the permission should be granted
1229
 *  -1: the permission check failed (also returned if vm == null)
1230
 */
1231
int
1232
vm_checkperm(struct vmd_vm *vm, uid_t uid)
1233
{
1234
	struct group	*gr;
1235
	struct passwd	*pw;
1236
	char		**grmem;
1237
1238
	/* root has no restrictions */
1239
	if (uid == 0)
1240
		return (0);
1241
1242
	if (vm == NULL)
1243
		return (-1);
1244
1245
	/* check supplementary groups */
1246
	if (vm->vm_params.vmc_gid != -1 &&
1247
	    (pw = getpwuid(uid)) != NULL &&
1248
	    (gr = getgrgid(vm->vm_params.vmc_gid)) != NULL) {
1249
		for (grmem = gr->gr_mem; *grmem; grmem++)
1250
			if (strcmp(*grmem, pw->pw_name) == 0)
1251
				return (0);
1252
	}
1253
1254
	/* check user */
1255
	if ((vm->vm_running && vm->vm_uid == uid) ||
1256
	    (!vm->vm_running && vm->vm_params.vmc_uid == uid))
1257
		return (0);
1258
1259
	return (-1);
1260
}
1261
1262
int
1263
vm_opentty(struct vmd_vm *vm)
1264
{
1265
	struct ptmget		 ptm;
1266
	struct stat		 st;
1267
	struct group		*gr;
1268
	uid_t			 uid;
1269
	gid_t			 gid;
1270
	mode_t			 mode;
1271
1272
	/*
1273
	 * Open tty with pre-opened PTM fd
1274
	 */
1275
	if ((ioctl(env->vmd_ptmfd, PTMGET, &ptm) == -1))
1276
		return (-1);
1277
1278
	vm->vm_tty = ptm.cfd;
1279
	close(ptm.sfd);
1280
	if ((vm->vm_ttyname = strdup(ptm.sn)) == NULL)
1281
		goto fail;
1282
1283
	uid = vm->vm_uid;
1284
	gid = vm->vm_params.vmc_gid;
1285
1286
	if (vm->vm_params.vmc_gid != -1) {
1287
		mode = 0660;
1288
	} else if ((gr = getgrnam("tty")) != NULL) {
1289
		gid = gr->gr_gid;
1290
		mode = 0620;
1291
	} else {
1292
		mode = 0600;
1293
		gid = 0;
1294
	}
1295
1296
	log_debug("%s: vm %s tty %s uid %d gid %d mode %o",
1297
	    __func__, vm->vm_params.vmc_params.vcp_name,
1298
	    vm->vm_ttyname, uid, gid, mode);
1299
1300
	/*
1301
	 * Change ownership and mode of the tty as required.
1302
	 * Loosely based on the implementation of sshpty.c
1303
	 */
1304
	if (stat(vm->vm_ttyname, &st) == -1)
1305
		goto fail;
1306
1307
	if (st.st_uid != uid || st.st_gid != gid) {
1308
		if (chown(vm->vm_ttyname, uid, gid) == -1) {
1309
			log_warn("chown %s %d %d failed, uid %d",
1310
			    vm->vm_ttyname, uid, gid, getuid());
1311
1312
			/* Ignore failure on read-only filesystems */
1313
			if (!((errno == EROFS) &&
1314
			    (st.st_uid == uid || st.st_uid == 0)))
1315
				goto fail;
1316
		}
1317
	}
1318
1319
	if ((st.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO)) != mode) {
1320
		if (chmod(vm->vm_ttyname, mode) == -1) {
1321
			log_warn("chmod %s %o failed, uid %d",
1322
			    vm->vm_ttyname, mode, getuid());
1323
1324
			/* Ignore failure on read-only filesystems */
1325
			if (!((errno == EROFS) &&
1326
			    (st.st_uid == uid || st.st_uid == 0)))
1327
				goto fail;
1328
		}
1329
	}
1330
1331
	return (0);
1332
 fail:
1333
	vm_closetty(vm);
1334
	return (-1);
1335
}
1336
1337
void
1338
vm_closetty(struct vmd_vm *vm)
1339
{
1340
	if (vm->vm_tty != -1) {
1341
		/* Release and close the tty */
1342
		if (fchown(vm->vm_tty, 0, 0) == -1)
1343
			log_warn("chown %s 0 0 failed", vm->vm_ttyname);
1344
		if (fchmod(vm->vm_tty, 0666) == -1)
1345
			log_warn("chmod %s 0666 failed", vm->vm_ttyname);
1346
		close(vm->vm_tty);
1347
		vm->vm_tty = -1;
1348
	}
1349
	free(vm->vm_ttyname);
1350
	vm->vm_ttyname = NULL;
1351
}
1352
1353
void
1354
switch_remove(struct vmd_switch *vsw)
1355
{
1356
	if (vsw == NULL)
1357
		return;
1358
1359
	TAILQ_REMOVE(env->vmd_switches, vsw, sw_entry);
1360
1361
	free(vsw->sw_group);
1362
	free(vsw->sw_name);
1363
	free(vsw);
1364
}
1365
1366
struct vmd_switch *
1367
switch_getbyname(const char *name)
1368
{
1369
	struct vmd_switch	*vsw;
1370
1371
	if (name == NULL)
1372
		return (NULL);
1373
	TAILQ_FOREACH(vsw, env->vmd_switches, sw_entry) {
1374
		if (strcmp(vsw->sw_name, name) == 0)
1375
			return (vsw);
1376
	}
1377
1378
	return (NULL);
1379
}
1380
1381
char *
1382
get_string(uint8_t *ptr, size_t len)
1383
{
1384
	size_t	 i;
1385
1386
	for (i = 0; i < len; i++)
1387
		if (!isprint(ptr[i]))
1388
			break;
1389
1390
	return strndup(ptr, i);
1391
}
1392
1393
uint32_t
1394
prefixlen2mask(uint8_t prefixlen)
1395
{
1396
	if (prefixlen == 0)
1397
		return (0);
1398
1399
	if (prefixlen > 32)
1400
		prefixlen = 32;
1401
1402
	return (htonl(0xffffffff << (32 - prefixlen)));
1403
}