xref: /openbsd-src/usr.sbin/vmd/control.c (revision 24bb5fcea3ed904bc467217bdaadb5dfc618d5bf)
1 /*	$OpenBSD: control.c,v 1.37 2021/06/16 16:55:02 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 <signal.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <unistd.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 				/* Forward to the vmctl(8) client */
158 				imsg_compose_event(&c->iev, imsg->hdr.type,
159 				    0, 0, -1, imsg->data, IMSG_DATA_SIZE(imsg));
160 				TAILQ_REMOVE(&ctl_notify_q, notify, entry);
161 				free(notify);
162 			}
163 		}
164 		break;
165 	case IMSG_VMDOP_CONFIG:
166 		config_getconfig(ps->ps_env, imsg);
167 		proc_compose(ps, PROC_PARENT, IMSG_VMDOP_DONE, NULL, 0);
168 		break;
169 	case IMSG_CTL_RESET:
170 		config_getreset(ps->ps_env, imsg);
171 		break;
172 	default:
173 		return (-1);
174 	}
175 
176 	return (0);
177 }
178 
179 int
180 control_init(struct privsep *ps, struct control_sock *cs)
181 {
182 	struct sockaddr_un	 sun;
183 	int			 fd;
184 	mode_t			 old_umask, mode;
185 
186 	if (cs->cs_name == NULL)
187 		return (0);
188 
189 	if ((fd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0)) == -1) {
190 		log_warn("%s: socket", __func__);
191 		return (-1);
192 	}
193 
194 	sun.sun_family = AF_UNIX;
195 	if (strlcpy(sun.sun_path, cs->cs_name,
196 	    sizeof(sun.sun_path)) >= sizeof(sun.sun_path)) {
197 		log_warn("%s: %s name too long", __func__, cs->cs_name);
198 		close(fd);
199 		return (-1);
200 	}
201 
202 	if (unlink(cs->cs_name) == -1)
203 		if (errno != ENOENT) {
204 			log_warn("%s: unlink %s", __func__, cs->cs_name);
205 			close(fd);
206 			return (-1);
207 		}
208 
209 	if (cs->cs_restricted) {
210 		old_umask = umask(S_IXUSR|S_IXGRP|S_IXOTH);
211 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
212 	} else {
213 		old_umask = umask(S_IXUSR|S_IXGRP|S_IWOTH|S_IROTH|S_IXOTH);
214 		mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP;
215 	}
216 
217 	if (bind(fd, (struct sockaddr *)&sun, sizeof(sun)) == -1) {
218 		log_warn("%s: bind: %s", __func__, cs->cs_name);
219 		close(fd);
220 		(void)umask(old_umask);
221 		return (-1);
222 	}
223 	(void)umask(old_umask);
224 
225 	if (chmod(cs->cs_name, mode) == -1) {
226 		log_warn("%s: chmod", __func__);
227 		close(fd);
228 		(void)unlink(cs->cs_name);
229 		return (-1);
230 	}
231 
232 	cs->cs_fd = fd;
233 	cs->cs_env = ps;
234 
235 	proc_compose(ps, PROC_PARENT, IMSG_VMDOP_DONE, NULL, 0);
236 
237 	return (0);
238 }
239 
240 int
241 control_reset(struct control_sock *cs)
242 {
243 	/* Updating owner of the control socket */
244 	if (chown(cs->cs_name, cs->cs_uid, cs->cs_gid) == -1)
245 		return (-1);
246 
247 	return (0);
248 }
249 
250 int
251 control_listen(struct control_sock *cs)
252 {
253 	if (cs->cs_name == NULL)
254 		return (0);
255 
256 	if (listen(cs->cs_fd, CONTROL_BACKLOG) == -1) {
257 		log_warn("%s: listen", __func__);
258 		return (-1);
259 	}
260 
261 	event_set(&cs->cs_ev, cs->cs_fd, EV_READ,
262 	    control_accept, cs);
263 	event_add(&cs->cs_ev, NULL);
264 	evtimer_set(&cs->cs_evt, control_accept, cs);
265 
266 	return (0);
267 }
268 
269 /* ARGSUSED */
270 void
271 control_accept(int listenfd, short event, void *arg)
272 {
273 	struct control_sock	*cs = arg;
274 	int			 connfd;
275 	socklen_t		 len;
276 	struct sockaddr_un	 sun;
277 	struct ctl_conn		*c;
278 
279 	event_add(&cs->cs_ev, NULL);
280 	if ((event & EV_TIMEOUT))
281 		return;
282 
283 	len = sizeof(sun);
284 	if ((connfd = accept4(listenfd,
285 	    (struct sockaddr *)&sun, &len, SOCK_NONBLOCK)) == -1) {
286 		/*
287 		 * Pause accept if we are out of file descriptors, or
288 		 * libevent will haunt us here too.
289 		 */
290 		if (errno == ENFILE || errno == EMFILE) {
291 			struct timeval evtpause = { 1, 0 };
292 
293 			event_del(&cs->cs_ev);
294 			evtimer_add(&cs->cs_evt, &evtpause);
295 		} else if (errno != EWOULDBLOCK && errno != EINTR &&
296 		    errno != ECONNABORTED)
297 			log_warn("%s: accept", __func__);
298 		return;
299 	}
300 
301 	if ((c = calloc(1, sizeof(struct ctl_conn))) == NULL) {
302 		log_warn("%s", __func__);
303 		close(connfd);
304 		return;
305 	}
306 
307 	if (getsockopt(connfd, SOL_SOCKET, SO_PEERCRED,
308 	    &c->peercred, &len) != 0) {
309 		log_warn("%s: failed to get peer credentials", __func__);
310 		close(connfd);
311 		free(c);
312 		return;
313 	}
314 
315 	imsg_init(&c->iev.ibuf, connfd);
316 	c->iev.handler = control_dispatch_imsg;
317 	c->iev.events = EV_READ;
318 	c->iev.data = cs;
319 	event_set(&c->iev.ev, c->iev.ibuf.fd, c->iev.events,
320 	    c->iev.handler, c->iev.data);
321 	event_add(&c->iev.ev, NULL);
322 
323 	TAILQ_INSERT_TAIL(&ctl_conns, c, entry);
324 }
325 
326 struct ctl_conn *
327 control_connbyfd(int fd)
328 {
329 	struct ctl_conn	*c;
330 
331 	TAILQ_FOREACH(c, &ctl_conns, entry) {
332 		if (c->iev.ibuf.fd == fd)
333 			break;
334 	}
335 
336 	return (c);
337 }
338 
339 void
340 control_close(int fd, struct control_sock *cs)
341 {
342 	struct ctl_conn		*c;
343 	struct ctl_notify	*notify, *notify_next;
344 
345 	if ((c = control_connbyfd(fd)) == NULL) {
346 		log_warn("%s: fd %d: not found", __func__, fd);
347 		return;
348 	}
349 
350 	msgbuf_clear(&c->iev.ibuf.w);
351 	TAILQ_REMOVE(&ctl_conns, c, entry);
352 
353 	TAILQ_FOREACH_SAFE(notify, &ctl_notify_q, entry, notify_next) {
354 		if (notify->ctl_fd == fd) {
355 			TAILQ_REMOVE(&ctl_notify_q, notify, entry);
356 			free(notify);
357 			break;
358 		}
359 	}
360 
361 	event_del(&c->iev.ev);
362 	close(c->iev.ibuf.fd);
363 
364 	/* Some file descriptors are available again. */
365 	if (evtimer_pending(&cs->cs_evt, NULL)) {
366 		evtimer_del(&cs->cs_evt);
367 		event_add(&cs->cs_ev, NULL);
368 	}
369 
370 	free(c);
371 }
372 
373 /* ARGSUSED */
374 void
375 control_dispatch_imsg(int fd, short event, void *arg)
376 {
377 	struct control_sock		*cs = arg;
378 	struct privsep			*ps = cs->cs_env;
379 	struct ctl_conn			*c;
380 	struct imsg			 imsg;
381 	struct vmop_create_params	 vmc;
382 	struct vmop_id			 vid;
383 	struct ctl_notify		*notify;
384 	int				 n, v, wait = 0, ret = 0;
385 
386 	if ((c = control_connbyfd(fd)) == NULL) {
387 		log_warn("%s: fd %d: not found", __func__, fd);
388 		return;
389 	}
390 
391 	if (event & EV_READ) {
392 		if (((n = imsg_read(&c->iev.ibuf)) == -1 && errno != EAGAIN) ||
393 		    n == 0) {
394 			control_close(fd, cs);
395 			return;
396 		}
397 	}
398 	if (event & EV_WRITE) {
399 		if (msgbuf_write(&c->iev.ibuf.w) <= 0 && errno != EAGAIN) {
400 			control_close(fd, cs);
401 			return;
402 		}
403 	}
404 
405 	for (;;) {
406 		if ((n = imsg_get(&c->iev.ibuf, &imsg)) == -1) {
407 			control_close(fd, cs);
408 			return;
409 		}
410 
411 		if (n == 0)
412 			break;
413 
414 		switch (imsg.hdr.type) {
415 		case IMSG_VMDOP_GET_INFO_VM_REQUEST:
416 		case IMSG_VMDOP_WAIT_VM_REQUEST:
417 		case IMSG_VMDOP_TERMINATE_VM_REQUEST:
418 		case IMSG_VMDOP_START_VM_REQUEST:
419 		case IMSG_VMDOP_PAUSE_VM:
420 		case IMSG_VMDOP_UNPAUSE_VM:
421 			break;
422 		default:
423 			if (c->peercred.uid != 0) {
424 				log_warnx("denied request %d from uid %d",
425 				    imsg.hdr.type, c->peercred.uid);
426 				ret = EPERM;
427 				goto fail;
428 			}
429 			break;
430 		}
431 
432 		switch (imsg.hdr.type) {
433 		case IMSG_CTL_VERBOSE:
434 			if (IMSG_DATA_SIZE(&imsg) < sizeof(v))
435 				goto fail;
436 			memcpy(&v, imsg.data, sizeof(v));
437 			log_setverbose(v);
438 
439 			/* FALLTHROUGH */
440 		case IMSG_VMDOP_RECEIVE_VM_REQUEST:
441 		case IMSG_VMDOP_SEND_VM_REQUEST:
442 		case IMSG_VMDOP_LOAD:
443 		case IMSG_VMDOP_RELOAD:
444 		case IMSG_CTL_RESET:
445 			if (proc_compose_imsg(ps, PROC_PARENT, -1,
446 			    imsg.hdr.type, fd, imsg.fd,
447 			    imsg.data, IMSG_DATA_SIZE(&imsg)) == -1)
448 				goto fail;
449 			break;
450 		case IMSG_VMDOP_START_VM_REQUEST:
451 			if (IMSG_DATA_SIZE(&imsg) < sizeof(vmc))
452 				goto fail;
453 			memcpy(&vmc, imsg.data, sizeof(vmc));
454 			vmc.vmc_owner.uid = c->peercred.uid;
455 			vmc.vmc_owner.gid = -1;
456 
457 			if (proc_compose_imsg(ps, PROC_PARENT, -1,
458 			    imsg.hdr.type, fd, -1, &vmc, sizeof(vmc)) == -1) {
459 				control_close(fd, cs);
460 				return;
461 			}
462 			break;
463 		case IMSG_VMDOP_WAIT_VM_REQUEST:
464 			wait = 1;
465 			/* FALLTHROUGH */
466 		case IMSG_VMDOP_TERMINATE_VM_REQUEST:
467 			if (IMSG_DATA_SIZE(&imsg) < sizeof(vid))
468 				goto fail;
469 			memcpy(&vid, imsg.data, sizeof(vid));
470 			vid.vid_uid = c->peercred.uid;
471 
472 			if (wait || vid.vid_flags & VMOP_WAIT) {
473 				vid.vid_flags |= VMOP_WAIT;
474 				notify = calloc(1, sizeof(struct ctl_notify));
475 				if (notify == NULL)
476 					fatal("%s: calloc", __func__);
477 				notify->ctl_vmid = vid.vid_id;
478 				notify->ctl_fd = fd;
479 				TAILQ_INSERT_TAIL(&ctl_notify_q, notify, entry);
480 				log_debug("%s: registered wait for peer %d",
481 				    __func__, fd);
482 			}
483 
484 			if (proc_compose_imsg(ps, PROC_PARENT, -1,
485 			    imsg.hdr.type, fd, -1, &vid, sizeof(vid)) == -1) {
486 				log_debug("%s: proc_compose_imsg failed",
487 				    __func__);
488 				control_close(fd, cs);
489 				return;
490 			}
491 			break;
492 		case IMSG_VMDOP_GET_INFO_VM_REQUEST:
493 			if (IMSG_DATA_SIZE(&imsg) != 0)
494 				goto fail;
495 			if (proc_compose_imsg(ps, PROC_PARENT, -1,
496 			    imsg.hdr.type, fd, -1, NULL, 0) == -1) {
497 				control_close(fd, cs);
498 				return;
499 			}
500 			break;
501 		case IMSG_VMDOP_PAUSE_VM:
502 		case IMSG_VMDOP_UNPAUSE_VM:
503 			if (IMSG_DATA_SIZE(&imsg) < sizeof(vid))
504 				goto fail;
505 			memcpy(&vid, imsg.data, sizeof(vid));
506 			vid.vid_uid = c->peercred.uid;
507 			log_debug("%s id: %d, name: %s, uid: %d",
508 			    __func__, vid.vid_id, vid.vid_name,
509 			    vid.vid_uid);
510 
511 			if (proc_compose_imsg(ps, PROC_PARENT, -1,
512 			    imsg.hdr.type, fd, imsg.fd,
513 			    &vid, sizeof(vid)) == -1)
514 				goto fail;
515 			break;
516 		default:
517 			log_debug("%s: error handling imsg %d",
518 			    __func__, imsg.hdr.type);
519 			control_close(fd, cs);
520 			break;
521 		}
522 		imsg_free(&imsg);
523 	}
524 
525 	imsg_event_add(&c->iev);
526 	return;
527 
528  fail:
529 	if (ret == 0)
530 		ret = EINVAL;
531 	imsg_compose_event(&c->iev, IMSG_CTL_FAIL,
532 	    0, 0, -1, &ret, sizeof(ret));
533 	imsg_flush(&c->iev.ibuf);
534 	control_close(fd, cs);
535 }
536