1*6676295fSclaudio /* $OpenBSD: proc.c,v 1.52 2024/11/21 13:38:45 claudio Exp $ */ 2b7b6a941Sreyk 3b7b6a941Sreyk /* 41b81e077Sreyk * Copyright (c) 2010 - 2016 Reyk Floeter <reyk@openbsd.org> 5b7b6a941Sreyk * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 6b7b6a941Sreyk * 7b7b6a941Sreyk * Permission to use, copy, modify, and distribute this software for any 8b7b6a941Sreyk * purpose with or without fee is hereby granted, provided that the above 9b7b6a941Sreyk * copyright notice and this permission notice appear in all copies. 10b7b6a941Sreyk * 11b7b6a941Sreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12b7b6a941Sreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13b7b6a941Sreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14b7b6a941Sreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15b7b6a941Sreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16b7b6a941Sreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17b7b6a941Sreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18b7b6a941Sreyk */ 19b7b6a941Sreyk 20b7b6a941Sreyk #include <sys/types.h> 21b7b6a941Sreyk #include <sys/queue.h> 22b7b6a941Sreyk #include <sys/socket.h> 23b7b6a941Sreyk #include <sys/wait.h> 24b7b6a941Sreyk 252584ffbfSrzalamena #include <fcntl.h> 26b7b6a941Sreyk #include <stdio.h> 27b7b6a941Sreyk #include <stdlib.h> 28b7b6a941Sreyk #include <unistd.h> 29b7b6a941Sreyk #include <string.h> 30b7b6a941Sreyk #include <errno.h> 31b7b6a941Sreyk #include <signal.h> 3238b34180Sbluhm #include <paths.h> 33b7b6a941Sreyk #include <pwd.h> 34b7b6a941Sreyk #include <event.h> 3586f952e4Sreyk #include <imsg.h> 36b7b6a941Sreyk 37b7b6a941Sreyk #include "httpd.h" 38b7b6a941Sreyk 3938b34180Sbluhm void proc_exec(struct privsep *, struct privsep_proc *, unsigned int, int, 402269e292Stobhe char **); 41887f279cSrzalamena void proc_setup(struct privsep *, struct privsep_proc *, unsigned int); 42887f279cSrzalamena void proc_open(struct privsep *, int, int); 4371b294e4Sreyk void proc_accept(struct privsep *, int, enum privsep_procid, 44887f279cSrzalamena unsigned int); 45b7b6a941Sreyk void proc_close(struct privsep *); 46b7b6a941Sreyk void proc_shutdown(struct privsep_proc *); 47b7b6a941Sreyk void proc_sig_handler(int, short, void *); 48b7b6a941Sreyk void proc_range(struct privsep *, enum privsep_procid, int *, int *); 49970e4754Sreyk int proc_dispatch_null(int, struct privsep_proc *, struct imsg *); 50b7b6a941Sreyk 51887f279cSrzalamena enum privsep_procid 52887f279cSrzalamena proc_getid(struct privsep_proc *procs, unsigned int nproc, 53887f279cSrzalamena const char *proc_name) 54b7b6a941Sreyk { 55887f279cSrzalamena struct privsep_proc *p; 56887f279cSrzalamena unsigned int proc; 57887f279cSrzalamena 58887f279cSrzalamena for (proc = 0; proc < nproc; proc++) { 59887f279cSrzalamena p = &procs[proc]; 60887f279cSrzalamena if (strcmp(p->p_title, proc_name)) 61887f279cSrzalamena continue; 62887f279cSrzalamena 63887f279cSrzalamena return (p->p_id); 64887f279cSrzalamena } 65887f279cSrzalamena 66887f279cSrzalamena return (PROC_MAX); 67887f279cSrzalamena } 68887f279cSrzalamena 69887f279cSrzalamena void 70887f279cSrzalamena proc_exec(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc, 712269e292Stobhe int argc, char **argv) 72887f279cSrzalamena { 73887f279cSrzalamena unsigned int proc, nargc, i, proc_i; 74887f279cSrzalamena char **nargv; 75887f279cSrzalamena struct privsep_proc *p; 76887f279cSrzalamena char num[32]; 77887f279cSrzalamena int fd; 78887f279cSrzalamena 79887f279cSrzalamena /* Prepare the new process argv. */ 80887f279cSrzalamena nargv = calloc(argc + 5, sizeof(char *)); 81887f279cSrzalamena if (nargv == NULL) 82887f279cSrzalamena fatal("%s: calloc", __func__); 83887f279cSrzalamena 84887f279cSrzalamena /* Copy call argument first. */ 85887f279cSrzalamena nargc = 0; 86887f279cSrzalamena nargv[nargc++] = argv[0]; 87887f279cSrzalamena 88887f279cSrzalamena /* Set process name argument and save the position. */ 89887f279cSrzalamena nargv[nargc++] = "-P"; 90887f279cSrzalamena proc_i = nargc; 91887f279cSrzalamena nargc++; 92887f279cSrzalamena 93887f279cSrzalamena /* Point process instance arg to stack and copy the original args. */ 94887f279cSrzalamena nargv[nargc++] = "-I"; 95887f279cSrzalamena nargv[nargc++] = num; 96887f279cSrzalamena for (i = 1; i < (unsigned int) argc; i++) 97887f279cSrzalamena nargv[nargc++] = argv[i]; 98887f279cSrzalamena 99887f279cSrzalamena nargv[nargc] = NULL; 100887f279cSrzalamena 101887f279cSrzalamena for (proc = 0; proc < nproc; proc++) { 102887f279cSrzalamena p = &procs[proc]; 103887f279cSrzalamena 104887f279cSrzalamena /* Update args with process title. */ 10569b8d8bcSreyk nargv[proc_i] = (char *)(uintptr_t)p->p_title; 106887f279cSrzalamena 107887f279cSrzalamena /* Fire children processes. */ 108887f279cSrzalamena for (i = 0; i < ps->ps_instances[p->p_id]; i++) { 109887f279cSrzalamena /* Update the process instance number. */ 110887f279cSrzalamena snprintf(num, sizeof(num), "%u", i); 111887f279cSrzalamena 112887f279cSrzalamena fd = ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0]; 113887f279cSrzalamena ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0] = -1; 114887f279cSrzalamena 115887f279cSrzalamena switch (fork()) { 116887f279cSrzalamena case -1: 117887f279cSrzalamena fatal("%s: fork", __func__); 118887f279cSrzalamena break; 119887f279cSrzalamena case 0: 120887f279cSrzalamena /* Prepare parent socket. */ 1212584ffbfSrzalamena if (fd != PROC_PARENT_SOCK_FILENO) { 1222584ffbfSrzalamena if (dup2(fd, PROC_PARENT_SOCK_FILENO) 1232584ffbfSrzalamena == -1) 1242584ffbfSrzalamena fatal("dup2"); 1252584ffbfSrzalamena } else if (fcntl(fd, F_SETFD, 0) == -1) 1262584ffbfSrzalamena fatal("fcntl"); 127887f279cSrzalamena 128887f279cSrzalamena execvp(argv[0], nargv); 129887f279cSrzalamena fatal("%s: execvp", __func__); 130887f279cSrzalamena break; 131887f279cSrzalamena default: 132887f279cSrzalamena /* Close child end. */ 133887f279cSrzalamena close(fd); 134887f279cSrzalamena break; 135887f279cSrzalamena } 136887f279cSrzalamena } 137887f279cSrzalamena } 138887f279cSrzalamena free(nargv); 139887f279cSrzalamena } 140887f279cSrzalamena 141887f279cSrzalamena void 142887f279cSrzalamena proc_connect(struct privsep *ps) 143887f279cSrzalamena { 144887f279cSrzalamena struct imsgev *iev; 14504617fabSrzalamena unsigned int src, dst, inst; 146887f279cSrzalamena 14704617fabSrzalamena /* Don't distribute any sockets if we are not really going to run. */ 14804617fabSrzalamena if (ps->ps_noaction) 14904617fabSrzalamena return; 150887f279cSrzalamena 15104617fabSrzalamena for (dst = 0; dst < PROC_MAX; dst++) { 15204617fabSrzalamena /* We don't communicate with ourselves. */ 15304617fabSrzalamena if (dst == PROC_PARENT) 154887f279cSrzalamena continue; 155887f279cSrzalamena 15604617fabSrzalamena for (inst = 0; inst < ps->ps_instances[dst]; inst++) { 15704617fabSrzalamena iev = &ps->ps_ievs[dst][inst]; 158*6676295fSclaudio if (imsgbuf_init(&iev->ibuf, 159*6676295fSclaudio ps->ps_pp->pp_pipes[dst][inst]) == -1) 160*6676295fSclaudio fatal(NULL); 161*6676295fSclaudio imsgbuf_allow_fdpass(&iev->ibuf); 162887f279cSrzalamena event_set(&iev->ev, iev->ibuf.fd, iev->events, 163887f279cSrzalamena iev->handler, iev->data); 164887f279cSrzalamena event_add(&iev->ev, NULL); 165887f279cSrzalamena } 166887f279cSrzalamena } 167887f279cSrzalamena 16804617fabSrzalamena /* Distribute the socketpair()s for everyone. */ 16904617fabSrzalamena for (src = 0; src < PROC_MAX; src++) 17004617fabSrzalamena for (dst = src; dst < PROC_MAX; dst++) { 17104617fabSrzalamena /* Parent already distributed its fds. */ 17204617fabSrzalamena if (src == PROC_PARENT || dst == PROC_PARENT) 173887f279cSrzalamena continue; 174887f279cSrzalamena 17504617fabSrzalamena proc_open(ps, src, dst); 176887f279cSrzalamena } 177887f279cSrzalamena } 178887f279cSrzalamena 179887f279cSrzalamena void 18071b294e4Sreyk proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc, 18138b34180Sbluhm int debug, int argc, char **argv, enum privsep_procid proc_id) 18271b294e4Sreyk { 18371b294e4Sreyk struct privsep_proc *p = NULL; 18404617fabSrzalamena struct privsep_pipes *pa, *pb; 18571b294e4Sreyk unsigned int proc; 18604617fabSrzalamena unsigned int dst; 18704617fabSrzalamena int fds[2]; 18804617fabSrzalamena 18904617fabSrzalamena /* Don't initiate anything if we are not really going to run. */ 19004617fabSrzalamena if (ps->ps_noaction) 19104617fabSrzalamena return; 19271b294e4Sreyk 19371b294e4Sreyk if (proc_id == PROC_PARENT) { 19471b294e4Sreyk privsep_process = PROC_PARENT; 19571b294e4Sreyk proc_setup(ps, procs, nproc); 19671b294e4Sreyk 1974c131d56Stobhe if (!debug && daemon(1, 0) == -1) 1984c131d56Stobhe fatal("failed to daemonize"); 1994c131d56Stobhe 20004617fabSrzalamena /* 20104617fabSrzalamena * Create the children sockets so we can use them 20204617fabSrzalamena * to distribute the rest of the socketpair()s using 20304617fabSrzalamena * proc_connect() later. 20404617fabSrzalamena */ 20504617fabSrzalamena for (dst = 0; dst < PROC_MAX; dst++) { 20604617fabSrzalamena /* Don't create socket for ourselves. */ 20704617fabSrzalamena if (dst == PROC_PARENT) 20804617fabSrzalamena continue; 20904617fabSrzalamena 21004617fabSrzalamena for (proc = 0; proc < ps->ps_instances[dst]; proc++) { 21104617fabSrzalamena pa = &ps->ps_pipes[PROC_PARENT][0]; 21204617fabSrzalamena pb = &ps->ps_pipes[dst][proc]; 21304617fabSrzalamena if (socketpair(AF_UNIX, 21404617fabSrzalamena SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 21504617fabSrzalamena PF_UNSPEC, fds) == -1) 21604617fabSrzalamena fatal("%s: socketpair", __func__); 21704617fabSrzalamena 21804617fabSrzalamena pa->pp_pipes[dst][proc] = fds[0]; 21904617fabSrzalamena pb->pp_pipes[PROC_PARENT][0] = fds[1]; 22004617fabSrzalamena } 22104617fabSrzalamena } 22271b294e4Sreyk 22371b294e4Sreyk /* Engage! */ 2242269e292Stobhe proc_exec(ps, procs, nproc, argc, argv); 22571b294e4Sreyk return; 22671b294e4Sreyk } 22771b294e4Sreyk 22871b294e4Sreyk /* Initialize a child */ 22971b294e4Sreyk for (proc = 0; proc < nproc; proc++) { 23071b294e4Sreyk if (procs[proc].p_id != proc_id) 23171b294e4Sreyk continue; 23271b294e4Sreyk p = &procs[proc]; 23371b294e4Sreyk break; 23471b294e4Sreyk } 23571b294e4Sreyk if (p == NULL || p->p_init == NULL) 23671b294e4Sreyk fatalx("%s: process %d missing process initialization", 23771b294e4Sreyk __func__, proc_id); 23871b294e4Sreyk 23971b294e4Sreyk p->p_init(ps, p); 24071b294e4Sreyk 24171b294e4Sreyk fatalx("failed to initiate child process"); 24271b294e4Sreyk } 24371b294e4Sreyk 24471b294e4Sreyk void 24571b294e4Sreyk proc_accept(struct privsep *ps, int fd, enum privsep_procid dst, 2468ea811cdSreyk unsigned int n) 247887f279cSrzalamena { 248887f279cSrzalamena struct privsep_pipes *pp = ps->ps_pp; 249887f279cSrzalamena struct imsgev *iev; 250887f279cSrzalamena 251887f279cSrzalamena if (ps->ps_ievs[dst] == NULL) { 252887f279cSrzalamena #if DEBUG > 1 2539409a5efSreyk log_debug("%s: %s src %d %d to dst %d %d not connected", 2549409a5efSreyk __func__, ps->ps_title[privsep_process], 2559409a5efSreyk privsep_process, ps->ps_instance + 1, 2569409a5efSreyk dst, n + 1); 2579409a5efSreyk #endif 258887f279cSrzalamena close(fd); 259887f279cSrzalamena return; 260887f279cSrzalamena } 261887f279cSrzalamena 262887f279cSrzalamena if (pp->pp_pipes[dst][n] != -1) { 263887f279cSrzalamena log_warnx("%s: duplicated descriptor", __func__); 264887f279cSrzalamena close(fd); 265887f279cSrzalamena return; 266887f279cSrzalamena } else 267887f279cSrzalamena pp->pp_pipes[dst][n] = fd; 268887f279cSrzalamena 269887f279cSrzalamena iev = &ps->ps_ievs[dst][n]; 270*6676295fSclaudio if (imsgbuf_init(&iev->ibuf, fd) == -1) 271*6676295fSclaudio fatal(NULL); 272*6676295fSclaudio imsgbuf_allow_fdpass(&iev->ibuf); 273887f279cSrzalamena event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data); 274887f279cSrzalamena event_add(&iev->ev, NULL); 275887f279cSrzalamena } 276887f279cSrzalamena 277887f279cSrzalamena void 278887f279cSrzalamena proc_setup(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc) 279887f279cSrzalamena { 280887f279cSrzalamena unsigned int i, j, src, dst, id; 281b7b6a941Sreyk struct privsep_pipes *pp; 282b7b6a941Sreyk 283887f279cSrzalamena /* Initialize parent title, ps_instances and procs. */ 284887f279cSrzalamena ps->ps_title[PROC_PARENT] = "parent"; 285887f279cSrzalamena 286887f279cSrzalamena for (src = 0; src < PROC_MAX; src++) 28740f5ae16Srzalamena /* Default to 1 process instance */ 28840f5ae16Srzalamena if (ps->ps_instances[src] < 1) 28940f5ae16Srzalamena ps->ps_instances[src] = 1; 290887f279cSrzalamena 291887f279cSrzalamena for (src = 0; src < nproc; src++) { 292887f279cSrzalamena procs[src].p_ps = ps; 293887f279cSrzalamena if (procs[src].p_cb == NULL) 294887f279cSrzalamena procs[src].p_cb = proc_dispatch_null; 295887f279cSrzalamena 296887f279cSrzalamena id = procs[src].p_id; 297887f279cSrzalamena ps->ps_title[id] = procs[src].p_title; 298887f279cSrzalamena if ((ps->ps_ievs[id] = calloc(ps->ps_instances[id], 299887f279cSrzalamena sizeof(struct imsgev))) == NULL) 300e9f9523eSrzalamena fatal("%s: calloc", __func__); 301887f279cSrzalamena 302dd7efffeSclaudio /* With this set up, we are ready to call imsgbuf_init(). */ 303887f279cSrzalamena for (i = 0; i < ps->ps_instances[id]; i++) { 304887f279cSrzalamena ps->ps_ievs[id][i].handler = proc_dispatch; 305887f279cSrzalamena ps->ps_ievs[id][i].events = EV_READ; 306887f279cSrzalamena ps->ps_ievs[id][i].proc = &procs[src]; 307887f279cSrzalamena ps->ps_ievs[id][i].data = &ps->ps_ievs[id][i]; 308887f279cSrzalamena } 30940f5ae16Srzalamena } 31040f5ae16Srzalamena 311b7b6a941Sreyk /* 312b7b6a941Sreyk * Allocate pipes for all process instances (incl. parent) 313b7b6a941Sreyk * 314b7b6a941Sreyk * - ps->ps_pipes: N:M mapping 315b7b6a941Sreyk * N source processes connected to M destination processes: 316b7b6a941Sreyk * [src][instances][dst][instances], for example 317b7b6a941Sreyk * [PROC_RELAY][3][PROC_CA][3] 318b7b6a941Sreyk * 319b7b6a941Sreyk * - ps->ps_pp: per-process 1:M part of ps->ps_pipes 320b7b6a941Sreyk * Each process instance has a destination array of socketpair fds: 321b7b6a941Sreyk * [dst][instances], for example 322b7b6a941Sreyk * [PROC_PARENT][0] 323b7b6a941Sreyk */ 324b7b6a941Sreyk for (src = 0; src < PROC_MAX; src++) { 325b7b6a941Sreyk /* Allocate destination array for each process */ 32640f5ae16Srzalamena if ((ps->ps_pipes[src] = calloc(ps->ps_instances[src], 327b7b6a941Sreyk sizeof(struct privsep_pipes))) == NULL) 328887f279cSrzalamena fatal("%s: calloc", __func__); 329b7b6a941Sreyk 33040f5ae16Srzalamena for (i = 0; i < ps->ps_instances[src]; i++) { 331b7b6a941Sreyk pp = &ps->ps_pipes[src][i]; 332b7b6a941Sreyk 333b7b6a941Sreyk for (dst = 0; dst < PROC_MAX; dst++) { 334b7b6a941Sreyk /* Allocate maximum fd integers */ 335b7b6a941Sreyk if ((pp->pp_pipes[dst] = 33640f5ae16Srzalamena calloc(ps->ps_instances[dst], 337b7b6a941Sreyk sizeof(int))) == NULL) 338887f279cSrzalamena fatal("%s: calloc", __func__); 339b7b6a941Sreyk 340b7b6a941Sreyk /* Mark fd as unused */ 34140f5ae16Srzalamena for (j = 0; j < ps->ps_instances[dst]; j++) 342b7b6a941Sreyk pp->pp_pipes[dst][j] = -1; 343b7b6a941Sreyk } 344b7b6a941Sreyk } 345b7b6a941Sreyk } 346b7b6a941Sreyk 347887f279cSrzalamena ps->ps_pp = &ps->ps_pipes[privsep_process][ps->ps_instance]; 348887f279cSrzalamena } 349887f279cSrzalamena 350887f279cSrzalamena void 351b7b6a941Sreyk proc_kill(struct privsep *ps) 352b7b6a941Sreyk { 3536ac0e468Srzalamena char *cause; 354b7b6a941Sreyk pid_t pid; 3556ac0e468Srzalamena int len, status; 356b7b6a941Sreyk 357b7b6a941Sreyk if (privsep_process != PROC_PARENT) 358b7b6a941Sreyk return; 359b7b6a941Sreyk 3606ac0e468Srzalamena proc_close(ps); 361b7b6a941Sreyk 362b7b6a941Sreyk do { 3636ac0e468Srzalamena pid = waitpid(WAIT_ANY, &status, 0); 3646ac0e468Srzalamena if (pid <= 0) 3656ac0e468Srzalamena continue; 366b7b6a941Sreyk 3676ac0e468Srzalamena if (WIFSIGNALED(status)) { 3686ac0e468Srzalamena len = asprintf(&cause, "terminated; signal %d", 3696ac0e468Srzalamena WTERMSIG(status)); 3706ac0e468Srzalamena } else if (WIFEXITED(status)) { 3716ac0e468Srzalamena if (WEXITSTATUS(status) != 0) 3726ac0e468Srzalamena len = asprintf(&cause, "exited abnormally"); 3736ac0e468Srzalamena else 374160c0f4dSreyk len = 0; 3756ac0e468Srzalamena } else 3766ac0e468Srzalamena len = -1; 3776ac0e468Srzalamena 378160c0f4dSreyk if (len == 0) { 379160c0f4dSreyk /* child exited OK, don't print a warning message */ 380160c0f4dSreyk } else if (len != -1) { 3816ac0e468Srzalamena log_warnx("lost child: pid %u %s", pid, cause); 3826ac0e468Srzalamena free(cause); 3836ac0e468Srzalamena } else 3846ac0e468Srzalamena log_warnx("lost child: pid %u", pid); 3856ab3e2c7Sbenno } while (pid != -1 || errno == EINTR); 386b7b6a941Sreyk } 387b7b6a941Sreyk 388b7b6a941Sreyk void 389887f279cSrzalamena proc_open(struct privsep *ps, int src, int dst) 390b7b6a941Sreyk { 391b7b6a941Sreyk struct privsep_pipes *pa, *pb; 39204617fabSrzalamena struct privsep_fd pf; 393b7b6a941Sreyk int fds[2]; 394887f279cSrzalamena unsigned int i, j; 395b7b6a941Sreyk 39604617fabSrzalamena /* Exchange pipes between process. */ 397b7b6a941Sreyk for (i = 0; i < ps->ps_instances[src]; i++) { 398887f279cSrzalamena for (j = 0; j < ps->ps_instances[dst]; j++) { 399887f279cSrzalamena /* Don't create sockets for ourself. */ 400887f279cSrzalamena if (src == dst && i == j) 401887f279cSrzalamena continue; 402b7b6a941Sreyk 403f2de6a3fSflorian /* Servers don't talk to each other. */ 404f2de6a3fSflorian if (src == PROC_SERVER && dst == PROC_SERVER) 405f2de6a3fSflorian continue; 406f2de6a3fSflorian 407887f279cSrzalamena pa = &ps->ps_pipes[src][i]; 408887f279cSrzalamena pb = &ps->ps_pipes[dst][j]; 409838637bcSreyk if (socketpair(AF_UNIX, 410887f279cSrzalamena SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 411b7b6a941Sreyk PF_UNSPEC, fds) == -1) 412e9f9523eSrzalamena fatal("%s: socketpair", __func__); 413b7b6a941Sreyk 414887f279cSrzalamena pa->pp_pipes[dst][j] = fds[0]; 415b7b6a941Sreyk pb->pp_pipes[src][i] = fds[1]; 41604617fabSrzalamena 41704617fabSrzalamena pf.pf_procid = src; 41804617fabSrzalamena pf.pf_instance = i; 41904617fabSrzalamena if (proc_compose_imsg(ps, dst, j, IMSG_CTL_PROCFD, 42004617fabSrzalamena -1, pb->pp_pipes[src][i], &pf, sizeof(pf)) == -1) 42104617fabSrzalamena fatal("%s: proc_compose_imsg", __func__); 42204617fabSrzalamena 42304617fabSrzalamena pf.pf_procid = dst; 42404617fabSrzalamena pf.pf_instance = j; 42504617fabSrzalamena if (proc_compose_imsg(ps, src, i, IMSG_CTL_PROCFD, 42604617fabSrzalamena -1, pa->pp_pipes[dst][j], &pf, sizeof(pf)) == -1) 42704617fabSrzalamena fatal("%s: proc_compose_imsg", __func__); 42804617fabSrzalamena 42904617fabSrzalamena /* 43004617fabSrzalamena * We have to flush to send the descriptors and close 43104617fabSrzalamena * them to avoid the fd ramp on startup. 43204617fabSrzalamena */ 43393c3ddf9Sreyk if (proc_flush_imsg(ps, src, i) == -1 || 43493c3ddf9Sreyk proc_flush_imsg(ps, dst, j) == -1) 435dd7efffeSclaudio fatal("%s: imsgbuf_flush", __func__); 436b7b6a941Sreyk } 437b7b6a941Sreyk } 438b7b6a941Sreyk } 439b7b6a941Sreyk 440b7b6a941Sreyk void 441b7b6a941Sreyk proc_close(struct privsep *ps) 442b7b6a941Sreyk { 4434703e0faSreyk unsigned int dst, n; 444b7b6a941Sreyk struct privsep_pipes *pp; 445b7b6a941Sreyk 446b7b6a941Sreyk if (ps == NULL) 447b7b6a941Sreyk return; 448b7b6a941Sreyk 449b7b6a941Sreyk pp = ps->ps_pp; 450b7b6a941Sreyk 451b7b6a941Sreyk for (dst = 0; dst < PROC_MAX; dst++) { 452b7b6a941Sreyk if (ps->ps_ievs[dst] == NULL) 453b7b6a941Sreyk continue; 454b7b6a941Sreyk 455b7b6a941Sreyk for (n = 0; n < ps->ps_instances[dst]; n++) { 456b7b6a941Sreyk if (pp->pp_pipes[dst][n] == -1) 457b7b6a941Sreyk continue; 458b7b6a941Sreyk 459b7b6a941Sreyk /* Cancel the fd, close and invalidate the fd */ 460b7b6a941Sreyk event_del(&(ps->ps_ievs[dst][n].ev)); 461dd7efffeSclaudio imsgbuf_clear(&(ps->ps_ievs[dst][n].ibuf)); 462b7b6a941Sreyk close(pp->pp_pipes[dst][n]); 463b7b6a941Sreyk pp->pp_pipes[dst][n] = -1; 464b7b6a941Sreyk } 465b7b6a941Sreyk free(ps->ps_ievs[dst]); 466b7b6a941Sreyk } 467b7b6a941Sreyk } 468b7b6a941Sreyk 469b7b6a941Sreyk void 470b7b6a941Sreyk proc_shutdown(struct privsep_proc *p) 471b7b6a941Sreyk { 472b7b6a941Sreyk struct privsep *ps = p->p_ps; 473b7b6a941Sreyk 474b7b6a941Sreyk if (p->p_id == PROC_CONTROL && ps) 475b7b6a941Sreyk control_cleanup(&ps->ps_csock); 476b7b6a941Sreyk 477b7b6a941Sreyk if (p->p_shutdown != NULL) 478b7b6a941Sreyk (*p->p_shutdown)(); 479b7b6a941Sreyk 480b7b6a941Sreyk proc_close(ps); 481b7b6a941Sreyk 482b7b6a941Sreyk log_info("%s exiting, pid %d", p->p_title, getpid()); 483b7b6a941Sreyk 484887f279cSrzalamena exit(0); 485b7b6a941Sreyk } 486b7b6a941Sreyk 487b7b6a941Sreyk void 488b7b6a941Sreyk proc_sig_handler(int sig, short event, void *arg) 489b7b6a941Sreyk { 490b7b6a941Sreyk struct privsep_proc *p = arg; 491b7b6a941Sreyk 492b7b6a941Sreyk switch (sig) { 493b7b6a941Sreyk case SIGINT: 494b7b6a941Sreyk case SIGTERM: 495b7b6a941Sreyk proc_shutdown(p); 496b7b6a941Sreyk break; 497b7b6a941Sreyk case SIGCHLD: 498b7b6a941Sreyk case SIGHUP: 499b7b6a941Sreyk case SIGPIPE: 500844c3615Sreyk case SIGUSR1: 501b7b6a941Sreyk /* ignore */ 502b7b6a941Sreyk break; 503b7b6a941Sreyk default: 504a3d8d4e4Sbenno fatalx("%s: unexpected signal", __func__); 505b7b6a941Sreyk /* NOTREACHED */ 506b7b6a941Sreyk } 507b7b6a941Sreyk } 508b7b6a941Sreyk 509887f279cSrzalamena void 510b7b6a941Sreyk proc_run(struct privsep *ps, struct privsep_proc *p, 5114703e0faSreyk struct privsep_proc *procs, unsigned int nproc, 512970e4754Sreyk void (*run)(struct privsep *, struct privsep_proc *, void *), void *arg) 513b7b6a941Sreyk { 5141b81e077Sreyk struct passwd *pw; 515b7b6a941Sreyk const char *root; 516b7b6a941Sreyk struct control_sock *rcs; 517b7b6a941Sreyk 5180f12961aSreyk log_procinit(p->p_title); 5190f12961aSreyk 520b7b6a941Sreyk if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) { 521b7b6a941Sreyk if (control_init(ps, &ps->ps_csock) == -1) 522e9f9523eSrzalamena fatalx("%s: control_init", __func__); 523b7b6a941Sreyk TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry) 524b7b6a941Sreyk if (control_init(ps, rcs) == -1) 525e9f9523eSrzalamena fatalx("%s: control_init", __func__); 526b7b6a941Sreyk } 527b7b6a941Sreyk 5281b81e077Sreyk /* Use non-standard user */ 5291b81e077Sreyk if (p->p_pw != NULL) 5301b81e077Sreyk pw = p->p_pw; 5311b81e077Sreyk else 5321b81e077Sreyk pw = ps->ps_pw; 5331b81e077Sreyk 534b7b6a941Sreyk /* Change root directory */ 535b7b6a941Sreyk if (p->p_chroot != NULL) 536b7b6a941Sreyk root = p->p_chroot; 537b7b6a941Sreyk else 538b7b6a941Sreyk root = pw->pw_dir; 539b7b6a941Sreyk 540b7b6a941Sreyk if (chroot(root) == -1) 541a3d8d4e4Sbenno fatal("%s: chroot", __func__); 542b7b6a941Sreyk if (chdir("/") == -1) 543a3d8d4e4Sbenno fatal("%s: chdir(\"/\")", __func__); 544b7b6a941Sreyk 545b7b6a941Sreyk privsep_process = p->p_id; 546b7b6a941Sreyk 547b7b6a941Sreyk setproctitle("%s", p->p_title); 548b7b6a941Sreyk 549b7b6a941Sreyk if (setgroups(1, &pw->pw_gid) || 550b7b6a941Sreyk setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 551b7b6a941Sreyk setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 552a3d8d4e4Sbenno fatal("%s: cannot drop privileges", __func__); 553b7b6a941Sreyk 554b7b6a941Sreyk event_init(); 555b7b6a941Sreyk 556b7b6a941Sreyk signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p); 557b7b6a941Sreyk signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p); 558b7b6a941Sreyk signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p); 559b7b6a941Sreyk signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p); 560b7b6a941Sreyk signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p); 561844c3615Sreyk signal_set(&ps->ps_evsigusr1, SIGUSR1, proc_sig_handler, p); 562b7b6a941Sreyk 563b7b6a941Sreyk signal_add(&ps->ps_evsigint, NULL); 564b7b6a941Sreyk signal_add(&ps->ps_evsigterm, NULL); 565b7b6a941Sreyk signal_add(&ps->ps_evsigchld, NULL); 566b7b6a941Sreyk signal_add(&ps->ps_evsighup, NULL); 567b7b6a941Sreyk signal_add(&ps->ps_evsigpipe, NULL); 568844c3615Sreyk signal_add(&ps->ps_evsigusr1, NULL); 569b7b6a941Sreyk 570887f279cSrzalamena proc_setup(ps, procs, nproc); 5718e01c6e3Sreyk proc_accept(ps, PROC_PARENT_SOCK_FILENO, PROC_PARENT, 0); 572b7b6a941Sreyk if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) { 573b7b6a941Sreyk if (control_listen(&ps->ps_csock) == -1) 574e9f9523eSrzalamena fatalx("%s: control_listen", __func__); 575b7b6a941Sreyk TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry) 576b7b6a941Sreyk if (control_listen(rcs) == -1) 577e9f9523eSrzalamena fatalx("%s: control_listen", __func__); 578b7b6a941Sreyk } 579b7b6a941Sreyk 580aa2d7030Sreyk DPRINTF("%s: %s %d/%d, pid %d", __func__, p->p_title, 581887f279cSrzalamena ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid()); 582887f279cSrzalamena 583970e4754Sreyk if (run != NULL) 584970e4754Sreyk run(ps, p, arg); 585b7b6a941Sreyk 586b7b6a941Sreyk event_dispatch(); 587b7b6a941Sreyk 588b7b6a941Sreyk proc_shutdown(p); 589b7b6a941Sreyk } 590b7b6a941Sreyk 591b7b6a941Sreyk void 592b7b6a941Sreyk proc_dispatch(int fd, short event, void *arg) 593b7b6a941Sreyk { 594b7b6a941Sreyk struct imsgev *iev = arg; 595b7b6a941Sreyk struct privsep_proc *p = iev->proc; 596b7b6a941Sreyk struct privsep *ps = p->p_ps; 597b7b6a941Sreyk struct imsgbuf *ibuf; 598b7b6a941Sreyk struct imsg imsg; 599b7b6a941Sreyk ssize_t n; 600b7b6a941Sreyk int verbose; 601b7b6a941Sreyk const char *title; 602887f279cSrzalamena struct privsep_fd pf; 603b7b6a941Sreyk 604b7b6a941Sreyk title = ps->ps_title[privsep_process]; 605b7b6a941Sreyk ibuf = &iev->ibuf; 606b7b6a941Sreyk 607b7b6a941Sreyk if (event & EV_READ) { 608668e5ba9Sclaudio if ((n = imsgbuf_read(ibuf)) == -1) 609dd7efffeSclaudio fatal("%s: imsgbuf_read", __func__); 610b7b6a941Sreyk if (n == 0) { 611b7b6a941Sreyk /* this pipe is dead, so remove the event handler */ 612b7b6a941Sreyk event_del(&iev->ev); 613b7b6a941Sreyk event_loopexit(NULL); 614b7b6a941Sreyk return; 615b7b6a941Sreyk } 616b7b6a941Sreyk } 617b7b6a941Sreyk 618b7b6a941Sreyk if (event & EV_WRITE) { 619dd7efffeSclaudio if (imsgbuf_write(ibuf) == -1) { 620c1aa9554Sclaudio if (errno == EPIPE) { /* connection closed */ 621c1aa9554Sclaudio /* remove the event handler */ 6226296db85Srzalamena event_del(&iev->ev); 6236296db85Srzalamena event_loopexit(NULL); 6246296db85Srzalamena return; 625c1aa9554Sclaudio } else 626dd7efffeSclaudio fatal("%s: imsgbuf_write", __func__); 6276296db85Srzalamena } 628b7b6a941Sreyk } 629b7b6a941Sreyk 630b7b6a941Sreyk for (;;) { 631b7b6a941Sreyk if ((n = imsg_get(ibuf, &imsg)) == -1) 632e9f9523eSrzalamena fatal("%s: imsg_get", __func__); 633b7b6a941Sreyk if (n == 0) 634b7b6a941Sreyk break; 635b7b6a941Sreyk 636b7b6a941Sreyk #if DEBUG > 1 63766094308Sreyk log_debug("%s: %s %d got imsg %d peerid %d from %s %d", 638b7b6a941Sreyk __func__, title, ps->ps_instance + 1, 639b607ef5aSrzalamena imsg.hdr.type, imsg.hdr.peerid, p->p_title, imsg.hdr.pid); 640b7b6a941Sreyk #endif 641b7b6a941Sreyk 642b7b6a941Sreyk /* 643b7b6a941Sreyk * Check the message with the program callback 644b7b6a941Sreyk */ 645b7b6a941Sreyk if ((p->p_cb)(fd, p, &imsg) == 0) { 646b7b6a941Sreyk /* Message was handled by the callback, continue */ 647b7b6a941Sreyk imsg_free(&imsg); 648b7b6a941Sreyk continue; 649b7b6a941Sreyk } 650b7b6a941Sreyk 651b7b6a941Sreyk /* 652b7b6a941Sreyk * Generic message handling 653b7b6a941Sreyk */ 654b7b6a941Sreyk switch (imsg.hdr.type) { 655b7b6a941Sreyk case IMSG_CTL_VERBOSE: 656b7b6a941Sreyk IMSG_SIZE_CHECK(&imsg, &verbose); 657b7b6a941Sreyk memcpy(&verbose, imsg.data, sizeof(verbose)); 658871fc12cSreyk log_setverbose(verbose); 659b7b6a941Sreyk break; 660887f279cSrzalamena case IMSG_CTL_PROCFD: 661887f279cSrzalamena IMSG_SIZE_CHECK(&imsg, &pf); 662887f279cSrzalamena memcpy(&pf, imsg.data, sizeof(pf)); 6633cc21533Sclaudio proc_accept(ps, imsg_get_fd(&imsg), pf.pf_procid, 664887f279cSrzalamena pf.pf_instance); 665887f279cSrzalamena break; 666b7b6a941Sreyk default: 667e9f9523eSrzalamena fatalx("%s: %s %d got invalid imsg %d peerid %d " 66866094308Sreyk "from %s %d", 669b7b6a941Sreyk __func__, title, ps->ps_instance + 1, 67066094308Sreyk imsg.hdr.type, imsg.hdr.peerid, 671b607ef5aSrzalamena p->p_title, imsg.hdr.pid); 672b7b6a941Sreyk } 673b7b6a941Sreyk imsg_free(&imsg); 674b7b6a941Sreyk } 675b7b6a941Sreyk imsg_event_add(iev); 676b7b6a941Sreyk } 677b7b6a941Sreyk 678970e4754Sreyk int 679970e4754Sreyk proc_dispatch_null(int fd, struct privsep_proc *p, struct imsg *imsg) 680970e4754Sreyk { 681970e4754Sreyk return (-1); 682970e4754Sreyk } 683970e4754Sreyk 684b7b6a941Sreyk /* 685b7b6a941Sreyk * imsg helper functions 686b7b6a941Sreyk */ 687b7b6a941Sreyk 688b7b6a941Sreyk void 689b7b6a941Sreyk imsg_event_add(struct imsgev *iev) 690b7b6a941Sreyk { 691b7b6a941Sreyk if (iev->handler == NULL) { 692dd7efffeSclaudio imsgbuf_flush(&iev->ibuf); 693b7b6a941Sreyk return; 694b7b6a941Sreyk } 695b7b6a941Sreyk 696b7b6a941Sreyk iev->events = EV_READ; 69731be28caSclaudio if (imsgbuf_queuelen(&iev->ibuf) > 0) 698b7b6a941Sreyk iev->events |= EV_WRITE; 699b7b6a941Sreyk 700b7b6a941Sreyk event_del(&iev->ev); 701b7b6a941Sreyk event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data); 702b7b6a941Sreyk event_add(&iev->ev, NULL); 703b7b6a941Sreyk } 704b7b6a941Sreyk 705b7b6a941Sreyk int 7064703e0faSreyk imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid, 7074703e0faSreyk pid_t pid, int fd, void *data, uint16_t datalen) 708b7b6a941Sreyk { 709b7b6a941Sreyk int ret; 710b7b6a941Sreyk 711b7b6a941Sreyk if ((ret = imsg_compose(&iev->ibuf, type, peerid, 712b7b6a941Sreyk pid, fd, data, datalen)) == -1) 713b7b6a941Sreyk return (ret); 714b7b6a941Sreyk imsg_event_add(iev); 715b7b6a941Sreyk return (ret); 716b7b6a941Sreyk } 717b7b6a941Sreyk 718b7b6a941Sreyk int 7194703e0faSreyk imsg_composev_event(struct imsgev *iev, uint16_t type, uint32_t peerid, 720b7b6a941Sreyk pid_t pid, int fd, const struct iovec *iov, int iovcnt) 721b7b6a941Sreyk { 722b7b6a941Sreyk int ret; 723b7b6a941Sreyk 724b7b6a941Sreyk if ((ret = imsg_composev(&iev->ibuf, type, peerid, 725b7b6a941Sreyk pid, fd, iov, iovcnt)) == -1) 726b7b6a941Sreyk return (ret); 727b7b6a941Sreyk imsg_event_add(iev); 728b7b6a941Sreyk return (ret); 729b7b6a941Sreyk } 730b7b6a941Sreyk 731b7b6a941Sreyk void 732b7b6a941Sreyk proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m) 733b7b6a941Sreyk { 734b7b6a941Sreyk if (*n == -1) { 735b7b6a941Sreyk /* Use a range of all target instances */ 736b7b6a941Sreyk *n = 0; 737b7b6a941Sreyk *m = ps->ps_instances[id]; 738b7b6a941Sreyk } else { 739b7b6a941Sreyk /* Use only a single slot of the specified peer process */ 740b7b6a941Sreyk *m = *n + 1; 741b7b6a941Sreyk } 742b7b6a941Sreyk } 743b7b6a941Sreyk 744b7b6a941Sreyk int 745b7b6a941Sreyk proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n, 746f19e65beSreyk uint16_t type, uint32_t peerid, int fd, void *data, uint16_t datalen) 747b7b6a941Sreyk { 748b7b6a941Sreyk int m; 749b7b6a941Sreyk 750b7b6a941Sreyk proc_range(ps, id, &n, &m); 751b7b6a941Sreyk for (; n < m; n++) { 752b7b6a941Sreyk if (imsg_compose_event(&ps->ps_ievs[id][n], 753b607ef5aSrzalamena type, peerid, ps->ps_instance + 1, fd, data, datalen) == -1) 754b7b6a941Sreyk return (-1); 755b7b6a941Sreyk } 756b7b6a941Sreyk 757b7b6a941Sreyk return (0); 758b7b6a941Sreyk } 759b7b6a941Sreyk 760b7b6a941Sreyk int 761f19e65beSreyk proc_compose(struct privsep *ps, enum privsep_procid id, 762f19e65beSreyk uint16_t type, void *data, uint16_t datalen) 763f19e65beSreyk { 764f19e65beSreyk return (proc_compose_imsg(ps, id, -1, type, -1, -1, data, datalen)); 765f19e65beSreyk } 766f19e65beSreyk 767f19e65beSreyk int 768b7b6a941Sreyk proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n, 769f19e65beSreyk uint16_t type, uint32_t peerid, int fd, const struct iovec *iov, int iovcnt) 770b7b6a941Sreyk { 771b7b6a941Sreyk int m; 772b7b6a941Sreyk 773b7b6a941Sreyk proc_range(ps, id, &n, &m); 774b7b6a941Sreyk for (; n < m; n++) 775b7b6a941Sreyk if (imsg_composev_event(&ps->ps_ievs[id][n], 776b607ef5aSrzalamena type, peerid, ps->ps_instance + 1, fd, iov, iovcnt) == -1) 777b7b6a941Sreyk return (-1); 778b7b6a941Sreyk 779b7b6a941Sreyk return (0); 780b7b6a941Sreyk } 781b7b6a941Sreyk 782b7b6a941Sreyk int 783f19e65beSreyk proc_composev(struct privsep *ps, enum privsep_procid id, 784f19e65beSreyk uint16_t type, const struct iovec *iov, int iovcnt) 785f19e65beSreyk { 786f19e65beSreyk return (proc_composev_imsg(ps, id, -1, type, -1, -1, iov, iovcnt)); 787f19e65beSreyk } 788f19e65beSreyk 789f19e65beSreyk int 790b7b6a941Sreyk proc_forward_imsg(struct privsep *ps, struct imsg *imsg, 791b7b6a941Sreyk enum privsep_procid id, int n) 792b7b6a941Sreyk { 793b7b6a941Sreyk return (proc_compose_imsg(ps, id, n, imsg->hdr.type, 7943cc21533Sclaudio imsg->hdr.peerid, -1, imsg->data, IMSG_DATA_SIZE(imsg))); 795b7b6a941Sreyk } 796b7b6a941Sreyk 797b7b6a941Sreyk struct imsgbuf * 798b7b6a941Sreyk proc_ibuf(struct privsep *ps, enum privsep_procid id, int n) 799b7b6a941Sreyk { 800b7b6a941Sreyk int m; 801b7b6a941Sreyk 802b7b6a941Sreyk proc_range(ps, id, &n, &m); 803b7b6a941Sreyk return (&ps->ps_ievs[id][n].ibuf); 804b7b6a941Sreyk } 805b7b6a941Sreyk 806b7b6a941Sreyk struct imsgev * 807b7b6a941Sreyk proc_iev(struct privsep *ps, enum privsep_procid id, int n) 808b7b6a941Sreyk { 809b7b6a941Sreyk int m; 810b7b6a941Sreyk 811b7b6a941Sreyk proc_range(ps, id, &n, &m); 812b7b6a941Sreyk return (&ps->ps_ievs[id][n]); 813b7b6a941Sreyk } 81493c3ddf9Sreyk 81593c3ddf9Sreyk /* This function should only be called with care as it breaks async I/O */ 81693c3ddf9Sreyk int 81793c3ddf9Sreyk proc_flush_imsg(struct privsep *ps, enum privsep_procid id, int n) 81893c3ddf9Sreyk { 81993c3ddf9Sreyk struct imsgbuf *ibuf; 82093c3ddf9Sreyk int m, ret = 0; 82193c3ddf9Sreyk 82293c3ddf9Sreyk proc_range(ps, id, &n, &m); 82393c3ddf9Sreyk for (; n < m; n++) { 82493c3ddf9Sreyk if ((ibuf = proc_ibuf(ps, id, n)) == NULL) 82593c3ddf9Sreyk return (-1); 826dd7efffeSclaudio if ((ret = imsgbuf_flush(ibuf)) == -1) 82793c3ddf9Sreyk break; 82893c3ddf9Sreyk imsg_event_add(&ps->ps_ievs[id][n]); 82993c3ddf9Sreyk } 83093c3ddf9Sreyk 83193c3ddf9Sreyk return (ret); 83293c3ddf9Sreyk } 833