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