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