xref: /openbsd-src/usr.sbin/vmd/control.c (revision 097a140d792de8b2bbe59ad827d39eabf9b4280a)
1 /*	$OpenBSD: control.c,v 1.35 2021/04/26 22:58:27 dv Exp $	*/
2 
3 /*
4  * Copyright (c) 2010-2015 Reyk Floeter <reyk@openbsd.org>
5  * Copyright (c) 2003, 2004 Henning Brauer <henning@openbsd.org>
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #include <sys/param.h>	/* nitems */
21 #include <sys/queue.h>
22 #include <sys/stat.h>
23 #include <sys/socket.h>
24 #include <sys/un.h>
25 #include <sys/tree.h>
26 
27 #include <net/if.h>
28 
29 #include <errno.h>
30 #include <event.h>
31 #include <fcntl.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 #include <signal.h>
36 
37 #include "proc.h"
38 #include "vmd.h"
39 
40 #define	CONTROL_BACKLOG	5
41 
42 struct ctl_connlist ctl_conns = TAILQ_HEAD_INITIALIZER(ctl_conns);
43 
44 struct ctl_notify {
45 	int			ctl_fd;
46 	uint32_t		ctl_vmid;
47 	TAILQ_ENTRY(ctl_notify)	entry;
48 };
49 TAILQ_HEAD(ctl_notify_q, ctl_notify) ctl_notify_q =
50 	TAILQ_HEAD_INITIALIZER(ctl_notify_q);
51 void
52 	 control_accept(int, short, void *);
53 struct ctl_conn
54 	*control_connbyfd(int);
55 void	 control_close(int, struct control_sock *);
56 void	 control_dispatch_imsg(int, short, void *);
57 int	 control_dispatch_vmd(int, struct privsep_proc *, struct imsg *);
58 void	 control_run(struct privsep *, struct privsep_proc *, void *);
59 
60 static struct privsep_proc procs[] = {
61 	{ "parent",	PROC_PARENT,	control_dispatch_vmd }
62 };
63 
64 void
65 control(struct privsep *ps, struct privsep_proc *p)
66 {
67 	proc_run(ps, p, procs, nitems(procs), control_run, NULL);
68 }
69 
70 void
71 control_run(struct privsep *ps, struct privsep_proc *p, void *arg)
72 {
73 	/*
74 	 * pledge in the control process:
75 	 * stdio - for malloc and basic I/O including events.
76 	 * unix - for the control socket.
77 	 * recvfd - for the proc fd exchange.
78 	 * sendfd - for send and receive.
79 	 */
80 	if (pledge("stdio unix recvfd sendfd", NULL) == -1)
81 		fatal("pledge");
82 }
83 
84 int
85 control_dispatch_vmd(int fd, struct privsep_proc *p, struct imsg *imsg)
86 {
87 	struct ctl_conn		*c;
88 	struct ctl_notify	*notify = NULL, *notify_next;
89 	struct privsep		*ps = p->p_ps;
90 	struct vmop_result	 vmr;
91 	int			 waiting = 0;
92 
93 	switch (imsg->hdr.type) {
94 	case IMSG_VMDOP_START_VM_RESPONSE:
95 	case IMSG_VMDOP_PAUSE_VM_RESPONSE:
96 	case IMSG_VMDOP_SEND_VM_RESPONSE:
97 	case IMSG_VMDOP_RECEIVE_VM_RESPONSE:
98 	case IMSG_VMDOP_UNPAUSE_VM_RESPONSE:
99 	case IMSG_VMDOP_GET_INFO_VM_DATA:
100 	case IMSG_VMDOP_GET_INFO_VM_END_DATA:
101 	case IMSG_CTL_FAIL:
102 	case IMSG_CTL_OK:
103 		/* Provide basic response back to a specific control client */
104 		if ((c = control_connbyfd(imsg->hdr.peerid)) == NULL) {
105 			log_warnx("%s: lost control connection: fd %d",
106 			    __func__, imsg->hdr.peerid);
107 			return (0);
108 		}
109 		imsg_compose_event(&c->iev, imsg->hdr.type,
110 		    0, 0, -1, imsg->data, IMSG_DATA_SIZE(imsg));
111 		break;
112 	case IMSG_VMDOP_TERMINATE_VM_RESPONSE:
113 		IMSG_SIZE_CHECK(imsg, &vmr);
114 		memcpy(&vmr, imsg->data, sizeof(vmr));
115 
116 		if ((c = control_connbyfd(imsg->hdr.peerid)) == NULL) {
117 			log_warnx("%s: lost control connection: fd %d",
118 			    __func__, imsg->hdr.peerid);
119 			return (0);
120 		}
121 
122 		TAILQ_FOREACH(notify, &ctl_notify_q, entry) {
123 			if (notify->ctl_fd == (int) imsg->hdr.peerid) {
124 				/*
125 				 * Update if waiting by vm name. This is only
126 				 * supported when stopping a single vm. If
127 				 * stopping all vms, vmctl(8) sends the request
128 				 * using the vmid.
129 				 */
130 				if (notify->ctl_vmid < 1)
131 					notify->ctl_vmid = vmr.vmr_id;
132 				waiting = 1;
133 				break;
134 			}
135 		}
136 
137 		/* An error needs to be relayed to the client immediately */
138 		if (!waiting || vmr.vmr_result) {
139 			imsg_compose_event(&c->iev, imsg->hdr.type,
140 			    0, 0, -1, imsg->data, IMSG_DATA_SIZE(imsg));
141 
142 			if (notify) {
143 				TAILQ_REMOVE(&ctl_notify_q, notify, entry);
144 				free(notify);
145 			}
146 		}
147 		break;
148 	case IMSG_VMDOP_TERMINATE_VM_EVENT:
149 		/* Notify any waiting clients that a VM terminated */
150 		IMSG_SIZE_CHECK(imsg, &vmr);
151 		memcpy(&vmr, imsg->data, sizeof(vmr));
152 
153 		TAILQ_FOREACH_SAFE(notify, &ctl_notify_q, entry, notify_next) {
154 			if (notify->ctl_vmid != vmr.vmr_id)
155 				continue;
156 			if ((c = control_connbyfd(notify->ctl_fd)) != NULL) {
157 				/* XXX vmctl expects *_RESPONSE, not *_EVENT */
158 				imsg_compose_event(&c->iev,
159 				    IMSG_VMDOP_TERMINATE_VM_RESPONSE,
160 				    0, 0, -1, imsg->data, IMSG_DATA_SIZE(imsg));
161 				TAILQ_REMOVE(&ctl_notify_q, notify, entry);
162 				free(notify);
163 			}
164 		}
165 		break;
166 	case IMSG_VMDOP_CONFIG:
167 		config_getconfig(ps->ps_env, imsg);
168 		proc_compose(ps, PROC_PARENT, IMSG_VMDOP_DONE, NULL, 0);
169 		break;
170 	case IMSG_CTL_RESET:
171 		config_getreset(ps->ps_env, imsg);
172 		break;
173 	default:
174 		return (-1);
175 	}
176 
177 	return (0);
178 }
179 
180 int
181 control_init(struct privsep *ps, struct control_sock *cs)
182 {
183 	struct sockaddr_un	 sun;
184 	int			 fd;
185 	mode_t			 old_umask, mode;
186 
187 	if (cs->cs_name == NULL)
188 		return (0);
189 
190 	if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) {
191 		log_warn("%s: socket", __func__);
192 		return (-1);
193 	}
194 
195 	sun.sun_family = AF_UNIX;
196 	if (strlcpy(sun.sun_path, cs->cs_name,
197 	    sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) {
198 		log_warn("%s: %s name too long", __func__, cs->cs_name);
199 		close(fd);
200 		return (-1);
201 	}
202 
203 	if (unlink(cs->cs_name) == -1)
204 		if (errno != ENOENT) {
205 			log_warn("%s: unlink %s", __func__, cs->cs_name);
206 			close(fd);
207 			return (-1);
208 		}
209 
210 	if (cs->cs_restricted) {
211 		old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
212 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
213 	} else {
214 		old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
215 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
216 	}
217 
218 	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
219 		log_warn("%s: bind: %s", __func__, cs->cs_name);
220 		close(fd);
221 		(void)umask(old_umask);
222 		return (-1);
223 	}
224 	(void)umask(old_umask);
225 
226 	if (chmod(cs->cs_name, mode) == -1) {
227 		log_warn("%s: chmod", __func__);
228 		close(fd);
229 		(void)unlink(cs->cs_name);
230 		return (-1);
231 	}
232 
233 	cs->cs_fd = fd;
234 	cs->cs_env = ps;
235 
236 	proc_compose(ps, PROC_PARENT, IMSG_VMDOP_DONE, NULL, 0);
237 
238 	return (0);
239 }
240 
241 int
242 control_reset(struct control_sock *cs)
243 {
244 	/* Updating owner of the control socket */
245 	if (chown(cs->cs_name, cs->cs_uid, cs->cs_gid) == -1)
246 		return (-1);
247 
248 	return (0);
249 }
250 
251 int
252 control_listen(struct control_sock *cs)
253 {
254 	if (cs->cs_name == NULL)
255 		return (0);
256 
257 	if (listen(cs->cs_fd, CONTROL_BACKLOG) == -1) {
258 		log_warn("%s: listen", __func__);
259 		return (-1);
260 	}
261 
262 	event_set(&cs->cs_ev, cs->cs_fd, EV_READ,
263 	    control_accept, cs);
264 	event_add(&cs->cs_ev, NULL);
265 	evtimer_set(&cs->cs_evt, control_accept, cs);
266 
267 	return (0);
268 }
269 
270 /* ARGSUSED */
271 void
272 control_accept(int listenfd, short event, void *arg)
273 {
274 	struct control_sock	*cs = arg;
275 	int			 connfd;
276 	socklen_t		 len;
277 	struct sockaddr_un	 sun;
278 	struct ctl_conn		*c;
279 
280 	event_add(&cs->cs_ev, NULL);
281 	if ((event & EV_TIMEOUT))
282 		return;
283 
284 	len = sizeof(sun);
285 	if ((connfd = accept4(listenfd,
286 	    (struct sockaddr *)&sun, &len, SOCK_NONBLOCK)) == -1) {
287 		/*
288 		 * Pause accept if we are out of file descriptors, or
289 		 * libevent will haunt us here too.
290 		 */
291 		if (errno == ENFILE || errno == EMFILE) {
292 			struct timeval evtpause = { 1, 0 };
293 
294 			event_del(&cs->cs_ev);
295 			evtimer_add(&cs->cs_evt, &evtpause);
296 		} else if (errno != EWOULDBLOCK && errno != EINTR &&
297 		    errno != ECONNABORTED)
298 			log_warn("%s: accept", __func__);
299 		return;
300 	}
301 
302 	if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
303 		log_warn("%s", __func__);
304 		close(connfd);
305 		return;
306 	}
307 
308 	if (getsockopt(connfd, SOL_SOCKET, SO_PEERCRED,
309 	    &c->peercred, &len) != 0) {
310 		log_warn("%s: failed to get peer credentials", __func__);
311 		close(connfd);
312 		free(c);
313 		return;
314 	}
315 
316 	imsg_init(&c->iev.ibuf, connfd);
317 	c->iev.handler = control_dispatch_imsg;
318 	c->iev.events = EV_READ;
319 	c->iev.data = cs;
320 	event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
321 	    c->iev.handler, c->iev.data);
322 	event_add(&c->iev.ev, NULL);
323 
324 	TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
325 }
326 
327 struct ctl_conn *
328 control_connbyfd(int fd)
329 {
330 	struct ctl_conn	*c;
331 
332 	TAILQ_FOREACH(c, &ctl_conns, entry) {
333 		if (c->iev.ibuf.fd == fd)
334 			break;
335 	}
336 
337 	return (c);
338 }
339 
340 void
341 control_close(int fd, struct control_sock *cs)
342 {
343 	struct ctl_conn		*c;
344 	struct ctl_notify	*notify, *notify_next;
345 
346 	if ((c = control_connbyfd(fd)) == NULL) {
347 		log_warn("%s: fd %d: not found", __func__, fd);
348 		return;
349 	}
350 
351 	msgbuf_clear(&c->iev.ibuf.w);
352 	TAILQ_REMOVE(&ctl_conns, c, entry);
353 
354 	TAILQ_FOREACH_SAFE(notify, &ctl_notify_q, entry, notify_next) {
355 		if (notify->ctl_fd == fd) {
356 			TAILQ_REMOVE(&ctl_notify_q, notify, entry);
357 			free(notify);
358 			break;
359 		}
360 	}
361 
362 	event_del(&c->iev.ev);
363 	close(c->iev.ibuf.fd);
364 
365 	/* Some file descriptors are available again. */
366 	if (evtimer_pending(&cs->cs_evt, NULL)) {
367 		evtimer_del(&cs->cs_evt);
368 		event_add(&cs->cs_ev, NULL);
369 	}
370 
371 	free(c);
372 }
373 
374 /* ARGSUSED */
375 void
376 control_dispatch_imsg(int fd, short event, void *arg)
377 {
378 	struct control_sock		*cs = arg;
379 	struct privsep			*ps = cs->cs_env;
380 	struct ctl_conn			*c;
381 	struct imsg			 imsg;
382 	struct vmop_create_params	 vmc;
383 	struct vmop_id			 vid;
384 	struct ctl_notify		*notify;
385 	int				 n, v, wait = 0, ret = 0;
386 
387 	if ((c = control_connbyfd(fd)) == NULL) {
388 		log_warn("%s: fd %d: not found", __func__, fd);
389 		return;
390 	}
391 
392 	if (event & EV_READ) {
393 		if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) ||
394 		    n == 0) {
395 			control_close(fd, cs);
396 			return;
397 		}
398 	}
399 	if (event & EV_WRITE) {
400 		if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) {
401 			control_close(fd, cs);
402 			return;
403 		}
404 	}
405 
406 	for (;;) {
407 		if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
408 			control_close(fd, cs);
409 			return;
410 		}
411 
412 		if (n == 0)
413 			break;
414 
415 		switch (imsg.hdr.type) {
416 		case IMSG_VMDOP_GET_INFO_VM_REQUEST:
417 		case IMSG_VMDOP_WAIT_VM_REQUEST:
418 		case IMSG_VMDOP_TERMINATE_VM_REQUEST:
419 		case IMSG_VMDOP_START_VM_REQUEST:
420 		case IMSG_VMDOP_PAUSE_VM:
421 		case IMSG_VMDOP_UNPAUSE_VM:
422 			break;
423 		default:
424 			if (c->peercred.uid != 0) {
425 				log_warnx("denied request %d from uid %d",
426 				    imsg.hdr.type, c->peercred.uid);
427 				ret = EPERM;
428 				goto fail;
429 			}
430 			break;
431 		}
432 
433 		switch (imsg.hdr.type) {
434 		case IMSG_CTL_VERBOSE:
435 			if (IMSG_DATA_SIZE(&imsg) < sizeof(v))
436 				goto fail;
437 			memcpy(&v, imsg.data, sizeof(v));
438 			log_setverbose(v);
439 
440 			/* FALLTHROUGH */
441 		case IMSG_VMDOP_RECEIVE_VM_REQUEST:
442 		case IMSG_VMDOP_SEND_VM_REQUEST:
443 		case IMSG_VMDOP_LOAD:
444 		case IMSG_VMDOP_RELOAD:
445 		case IMSG_CTL_RESET:
446 			if (proc_compose_imsg(ps, PROC_PARENT, -1,
447 			    imsg.hdr.type, fd, imsg.fd,
448 			    imsg.data, IMSG_DATA_SIZE(&imsg)) == -1)
449 				goto fail;
450 			break;
451 		case IMSG_VMDOP_START_VM_REQUEST:
452 			if (IMSG_DATA_SIZE(&imsg) < sizeof(vmc))
453 				goto fail;
454 			memcpy(&vmc, imsg.data, sizeof(vmc));
455 			vmc.vmc_owner.uid = c->peercred.uid;
456 			vmc.vmc_owner.gid = -1;
457 
458 			if (proc_compose_imsg(ps, PROC_PARENT, -1,
459 			    imsg.hdr.type, fd, -1, &vmc, sizeof(vmc)) == -1) {
460 				control_close(fd, cs);
461 				return;
462 			}
463 			break;
464 		case IMSG_VMDOP_WAIT_VM_REQUEST:
465 			wait = 1;
466 			/* FALLTHROUGH */
467 		case IMSG_VMDOP_TERMINATE_VM_REQUEST:
468 			if (IMSG_DATA_SIZE(&imsg) < sizeof(vid))
469 				goto fail;
470 			memcpy(&vid, imsg.data, sizeof(vid));
471 			vid.vid_uid = c->peercred.uid;
472 
473 			if (wait || vid.vid_flags & VMOP_WAIT) {
474 				vid.vid_flags |= VMOP_WAIT;
475 				notify = calloc(1, sizeof(struct ctl_notify));
476 				if (notify == NULL)
477 					fatal("%s: calloc", __func__);
478 				notify->ctl_vmid = vid.vid_id;
479 				notify->ctl_fd = fd;
480 				TAILQ_INSERT_TAIL(&ctl_notify_q, notify, entry);
481 				log_debug("%s: registered wait for peer %d",
482 				    __func__, fd);
483 			}
484 
485 			if (proc_compose_imsg(ps, PROC_PARENT, -1,
486 			    imsg.hdr.type, fd, -1, &vid, sizeof(vid)) == -1) {
487 				log_debug("%s: proc_compose_imsg failed",
488 				    __func__);
489 				control_close(fd, cs);
490 				return;
491 			}
492 			break;
493 		case IMSG_VMDOP_GET_INFO_VM_REQUEST:
494 			if (IMSG_DATA_SIZE(&imsg) != 0)
495 				goto fail;
496 			if (proc_compose_imsg(ps, PROC_PARENT, -1,
497 			    imsg.hdr.type, fd, -1, NULL, 0) == -1) {
498 				control_close(fd, cs);
499 				return;
500 			}
501 			break;
502 		case IMSG_VMDOP_PAUSE_VM:
503 		case IMSG_VMDOP_UNPAUSE_VM:
504 			if (IMSG_DATA_SIZE(&imsg) < sizeof(vid))
505 				goto fail;
506 			memcpy(&vid, imsg.data, sizeof(vid));
507 			vid.vid_uid = c->peercred.uid;
508 			log_debug("%s id: %d, name: %s, uid: %d",
509 			    __func__, vid.vid_id, vid.vid_name,
510 			    vid.vid_uid);
511 
512 			if (proc_compose_imsg(ps, PROC_PARENT, -1,
513 			    imsg.hdr.type, fd, imsg.fd,
514 			    &vid, sizeof(vid)) == -1)
515 				goto fail;
516 			break;
517 		default:
518 			log_debug("%s: error handling imsg %d",
519 			    __func__, imsg.hdr.type);
520 			control_close(fd, cs);
521 			break;
522 		}
523 		imsg_free(&imsg);
524 	}
525 
526 	imsg_event_add(&c->iev);
527 	return;
528 
529  fail:
530 	if (ret == 0)
531 		ret = EINVAL;
532 	imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
533 	    0, 0, -1, &ret, sizeof(ret));
534 	imsg_flush(&c->iev.ibuf);
535 	control_close(fd, cs);
536 }
537