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