1*6676295fSclaudio /* $OpenBSD: proc.c,v 1.39 2024/11/21 13:38:45 claudio Exp $ */ 260996bedSreyk 360996bedSreyk /* 47be2c8aaSrzalamena * Copyright (c) 2010 - 2016 Reyk Floeter <reyk@openbsd.org> 560996bedSreyk * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 660996bedSreyk * 760996bedSreyk * Permission to use, copy, modify, and distribute this software for any 860996bedSreyk * purpose with or without fee is hereby granted, provided that the above 960996bedSreyk * copyright notice and this permission notice appear in all copies. 1060996bedSreyk * 1160996bedSreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1260996bedSreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1360996bedSreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1460996bedSreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1560996bedSreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1660996bedSreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1760996bedSreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1860996bedSreyk */ 1960996bedSreyk 2060996bedSreyk #include <sys/socket.h> 2160996bedSreyk #include <sys/wait.h> 2260996bedSreyk 237be2c8aaSrzalamena #include <fcntl.h> 2460996bedSreyk #include <stdio.h> 2560996bedSreyk #include <stdlib.h> 26a9292d2aSmartijn #include <stdint.h> 2760996bedSreyk #include <unistd.h> 2821032f8aSreyk #include <string.h> 2921032f8aSreyk #include <errno.h> 3021032f8aSreyk #include <signal.h> 319f020842Sbluhm #include <paths.h> 3260996bedSreyk #include <pwd.h> 3321032f8aSreyk #include <event.h> 340f12961aSreyk #include <imsg.h> 3560996bedSreyk 36a9292d2aSmartijn #include "log.h" 3760996bedSreyk #include "snmpd.h" 3860996bedSreyk 399f020842Sbluhm void proc_exec(struct privsep *, struct privsep_proc *, unsigned int, int, 402269e292Stobhe char **); 417be2c8aaSrzalamena void proc_setup(struct privsep *, struct privsep_proc *, unsigned int); 427be2c8aaSrzalamena void proc_open(struct privsep *, int, int); 437be2c8aaSrzalamena void proc_accept(struct privsep *, int, enum privsep_procid, 447be2c8aaSrzalamena unsigned int); 4521032f8aSreyk void proc_close(struct privsep *); 4660996bedSreyk void proc_shutdown(struct privsep_proc *); 4760996bedSreyk void proc_sig_handler(int, short, void *); 4821032f8aSreyk void proc_range(struct privsep *, enum privsep_procid, int *, int *); 493f75f466Sreyk int proc_dispatch_null(int, struct privsep_proc *, struct imsg *); 5060996bedSreyk 517be2c8aaSrzalamena enum privsep_procid 527be2c8aaSrzalamena proc_getid(struct privsep_proc *procs, unsigned int nproc, 537be2c8aaSrzalamena const char *proc_name) 5421032f8aSreyk { 557be2c8aaSrzalamena struct privsep_proc *p; 567be2c8aaSrzalamena unsigned int proc; 577be2c8aaSrzalamena 587be2c8aaSrzalamena for (proc = 0; proc < nproc; proc++) { 597be2c8aaSrzalamena p = &procs[proc]; 607be2c8aaSrzalamena if (strcmp(p->p_title, proc_name)) 617be2c8aaSrzalamena continue; 627be2c8aaSrzalamena 637be2c8aaSrzalamena return (p->p_id); 647be2c8aaSrzalamena } 657be2c8aaSrzalamena 667be2c8aaSrzalamena return (PROC_MAX); 677be2c8aaSrzalamena } 687be2c8aaSrzalamena 697be2c8aaSrzalamena void 707be2c8aaSrzalamena proc_exec(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc, 712269e292Stobhe int argc, char **argv) 727be2c8aaSrzalamena { 737be2c8aaSrzalamena unsigned int proc, nargc, i, proc_i; 747be2c8aaSrzalamena char **nargv; 757be2c8aaSrzalamena struct privsep_proc *p; 767be2c8aaSrzalamena char num[32]; 777be2c8aaSrzalamena int fd; 787be2c8aaSrzalamena 797be2c8aaSrzalamena /* Prepare the new process argv. */ 807be2c8aaSrzalamena nargv = calloc(argc + 5, sizeof(char *)); 817be2c8aaSrzalamena if (nargv == NULL) 827be2c8aaSrzalamena fatal("%s: calloc", __func__); 837be2c8aaSrzalamena 847be2c8aaSrzalamena /* Copy call argument first. */ 857be2c8aaSrzalamena nargc = 0; 867be2c8aaSrzalamena nargv[nargc++] = argv[0]; 877be2c8aaSrzalamena 887be2c8aaSrzalamena /* Set process name argument and save the position. */ 897be2c8aaSrzalamena nargv[nargc++] = "-P"; 907be2c8aaSrzalamena proc_i = nargc; 917be2c8aaSrzalamena nargc++; 927be2c8aaSrzalamena 937be2c8aaSrzalamena /* Point process instance arg to stack and copy the original args. */ 947be2c8aaSrzalamena nargv[nargc++] = "-I"; 957be2c8aaSrzalamena nargv[nargc++] = num; 967be2c8aaSrzalamena for (i = 1; i < (unsigned int) argc; i++) 977be2c8aaSrzalamena nargv[nargc++] = argv[i]; 987be2c8aaSrzalamena 997be2c8aaSrzalamena nargv[nargc] = NULL; 1007be2c8aaSrzalamena 1017be2c8aaSrzalamena for (proc = 0; proc < nproc; proc++) { 1027be2c8aaSrzalamena p = &procs[proc]; 1037be2c8aaSrzalamena 1047be2c8aaSrzalamena /* Update args with process title. */ 1057be2c8aaSrzalamena nargv[proc_i] = (char *)(uintptr_t)p->p_title; 1067be2c8aaSrzalamena 1077be2c8aaSrzalamena /* Fire children processes. */ 1087be2c8aaSrzalamena for (i = 0; i < ps->ps_instances[p->p_id]; i++) { 1097be2c8aaSrzalamena /* Update the process instance number. */ 1107be2c8aaSrzalamena snprintf(num, sizeof(num), "%u", i); 1117be2c8aaSrzalamena 1127be2c8aaSrzalamena fd = ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0]; 1137be2c8aaSrzalamena ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0] = -1; 1147be2c8aaSrzalamena 1157be2c8aaSrzalamena switch (fork()) { 1167be2c8aaSrzalamena case -1: 1177be2c8aaSrzalamena fatal("%s: fork", __func__); 1187be2c8aaSrzalamena break; 1197be2c8aaSrzalamena case 0: 1207be2c8aaSrzalamena /* Prepare parent socket. */ 1217be2c8aaSrzalamena if (fd != PROC_PARENT_SOCK_FILENO) { 1227be2c8aaSrzalamena if (dup2(fd, PROC_PARENT_SOCK_FILENO) 1237be2c8aaSrzalamena == -1) 1247be2c8aaSrzalamena fatal("dup2"); 1257be2c8aaSrzalamena } else if (fcntl(fd, F_SETFD, 0) == -1) 1267be2c8aaSrzalamena fatal("fcntl"); 1277be2c8aaSrzalamena 1287be2c8aaSrzalamena execvp(argv[0], nargv); 1297be2c8aaSrzalamena fatal("%s: execvp", __func__); 1307be2c8aaSrzalamena break; 1317be2c8aaSrzalamena default: 1327be2c8aaSrzalamena /* Close child end. */ 1337be2c8aaSrzalamena close(fd); 1347be2c8aaSrzalamena break; 1357be2c8aaSrzalamena } 1367be2c8aaSrzalamena } 1377be2c8aaSrzalamena } 1387be2c8aaSrzalamena free(nargv); 1397be2c8aaSrzalamena } 1407be2c8aaSrzalamena 1417be2c8aaSrzalamena void 1427be2c8aaSrzalamena proc_connect(struct privsep *ps) 1437be2c8aaSrzalamena { 1447be2c8aaSrzalamena struct imsgev *iev; 1457be2c8aaSrzalamena unsigned int src, dst, inst; 1467be2c8aaSrzalamena 1477be2c8aaSrzalamena /* Don't distribute any sockets if we are not really going to run. */ 1487be2c8aaSrzalamena if (ps->ps_noaction) 1497be2c8aaSrzalamena return; 1507be2c8aaSrzalamena 1517be2c8aaSrzalamena for (dst = 0; dst < PROC_MAX; dst++) { 1527be2c8aaSrzalamena /* We don't communicate with ourselves. */ 1537be2c8aaSrzalamena if (dst == PROC_PARENT) 1547be2c8aaSrzalamena continue; 1557be2c8aaSrzalamena 1567be2c8aaSrzalamena for (inst = 0; inst < ps->ps_instances[dst]; inst++) { 1577be2c8aaSrzalamena 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("imsgbuf_init"); 161*6676295fSclaudio imsgbuf_allow_fdpass(&iev->ibuf); 1627be2c8aaSrzalamena event_set(&iev->ev, iev->ibuf.fd, iev->events, 1637be2c8aaSrzalamena iev->handler, iev->data); 1647be2c8aaSrzalamena event_add(&iev->ev, NULL); 1657be2c8aaSrzalamena } 1667be2c8aaSrzalamena } 1677be2c8aaSrzalamena 1687be2c8aaSrzalamena /* Distribute the socketpair()s for everyone. */ 1697be2c8aaSrzalamena for (src = 0; src < PROC_MAX; src++) 1707be2c8aaSrzalamena for (dst = src; dst < PROC_MAX; dst++) { 1717be2c8aaSrzalamena /* Parent already distributed its fds. */ 1727be2c8aaSrzalamena if (src == PROC_PARENT || dst == PROC_PARENT) 1737be2c8aaSrzalamena continue; 1747be2c8aaSrzalamena 1757be2c8aaSrzalamena proc_open(ps, src, dst); 1767be2c8aaSrzalamena } 1777be2c8aaSrzalamena } 1787be2c8aaSrzalamena 1797be2c8aaSrzalamena void 1807be2c8aaSrzalamena proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc, 1819f020842Sbluhm int debug, int argc, char **argv, enum privsep_procid proc_id) 1827be2c8aaSrzalamena { 1837be2c8aaSrzalamena struct privsep_proc *p = NULL; 1847be2c8aaSrzalamena struct privsep_pipes *pa, *pb; 1857be2c8aaSrzalamena unsigned int proc; 1867be2c8aaSrzalamena unsigned int dst; 1877be2c8aaSrzalamena int fds[2]; 1887be2c8aaSrzalamena 1897be2c8aaSrzalamena /* Don't initiate anything if we are not really going to run. */ 1907be2c8aaSrzalamena if (ps->ps_noaction) 1917be2c8aaSrzalamena return; 1927be2c8aaSrzalamena 1937be2c8aaSrzalamena if (proc_id == PROC_PARENT) { 1947be2c8aaSrzalamena privsep_process = PROC_PARENT; 1957be2c8aaSrzalamena proc_setup(ps, procs, nproc); 1967be2c8aaSrzalamena 19728fef5efStobhe if (!debug && daemon(0, 0) == -1) 19828fef5efStobhe fatal("failed to daemonize"); 19928fef5efStobhe 2007be2c8aaSrzalamena /* 2017be2c8aaSrzalamena * Create the children sockets so we can use them 2027be2c8aaSrzalamena * to distribute the rest of the socketpair()s using 2037be2c8aaSrzalamena * proc_connect() later. 2047be2c8aaSrzalamena */ 2057be2c8aaSrzalamena for (dst = 0; dst < PROC_MAX; dst++) { 2067be2c8aaSrzalamena /* Don't create socket for ourselves. */ 2077be2c8aaSrzalamena if (dst == PROC_PARENT) 2087be2c8aaSrzalamena continue; 2097be2c8aaSrzalamena 2107be2c8aaSrzalamena for (proc = 0; proc < ps->ps_instances[dst]; proc++) { 2117be2c8aaSrzalamena pa = &ps->ps_pipes[PROC_PARENT][0]; 2127be2c8aaSrzalamena pb = &ps->ps_pipes[dst][proc]; 2137be2c8aaSrzalamena if (socketpair(AF_UNIX, 2147be2c8aaSrzalamena SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 2157be2c8aaSrzalamena PF_UNSPEC, fds) == -1) 2167be2c8aaSrzalamena fatal("%s: socketpair", __func__); 2177be2c8aaSrzalamena 2187be2c8aaSrzalamena pa->pp_pipes[dst][proc] = fds[0]; 2197be2c8aaSrzalamena pb->pp_pipes[PROC_PARENT][0] = fds[1]; 2207be2c8aaSrzalamena } 2217be2c8aaSrzalamena } 2227be2c8aaSrzalamena 2237be2c8aaSrzalamena /* Engage! */ 2242269e292Stobhe proc_exec(ps, procs, nproc, argc, argv); 2257be2c8aaSrzalamena return; 2267be2c8aaSrzalamena } 2277be2c8aaSrzalamena 2287be2c8aaSrzalamena /* Initialize a child */ 2297be2c8aaSrzalamena for (proc = 0; proc < nproc; proc++) { 2307be2c8aaSrzalamena if (procs[proc].p_id != proc_id) 2317be2c8aaSrzalamena continue; 2327be2c8aaSrzalamena p = &procs[proc]; 2337be2c8aaSrzalamena break; 2347be2c8aaSrzalamena } 2357be2c8aaSrzalamena if (p == NULL || p->p_init == NULL) 2367be2c8aaSrzalamena fatalx("%s: process %d missing process initialization", 2377be2c8aaSrzalamena __func__, proc_id); 2387be2c8aaSrzalamena 2397be2c8aaSrzalamena p->p_init(ps, p); 2407be2c8aaSrzalamena 2417be2c8aaSrzalamena fatalx("failed to initiate child process"); 2427be2c8aaSrzalamena } 2437be2c8aaSrzalamena 2447be2c8aaSrzalamena void 2457be2c8aaSrzalamena proc_accept(struct privsep *ps, int fd, enum privsep_procid dst, 2467be2c8aaSrzalamena unsigned int n) 2477be2c8aaSrzalamena { 2487be2c8aaSrzalamena struct privsep_pipes *pp = ps->ps_pp; 2497be2c8aaSrzalamena struct imsgev *iev; 2507be2c8aaSrzalamena 2517be2c8aaSrzalamena if (ps->ps_ievs[dst] == NULL) { 2527be2c8aaSrzalamena #if DEBUG > 1 2537be2c8aaSrzalamena log_debug("%s: %s src %d %d to dst %d %d not connected", 2547be2c8aaSrzalamena __func__, ps->ps_title[privsep_process], 2557be2c8aaSrzalamena privsep_process, ps->ps_instance + 1, 2567be2c8aaSrzalamena dst, n + 1); 2577be2c8aaSrzalamena #endif 2587be2c8aaSrzalamena close(fd); 2597be2c8aaSrzalamena return; 2607be2c8aaSrzalamena } 2617be2c8aaSrzalamena 2627be2c8aaSrzalamena if (pp->pp_pipes[dst][n] != -1) { 2637be2c8aaSrzalamena log_warnx("%s: duplicated descriptor", __func__); 2647be2c8aaSrzalamena close(fd); 2657be2c8aaSrzalamena return; 2667be2c8aaSrzalamena } else 2677be2c8aaSrzalamena pp->pp_pipes[dst][n] = fd; 2687be2c8aaSrzalamena 2697be2c8aaSrzalamena iev = &ps->ps_ievs[dst][n]; 270*6676295fSclaudio if (imsgbuf_init(&iev->ibuf, fd) == -1) 271*6676295fSclaudio fatal("imsgbuf_init"); 272*6676295fSclaudio imsgbuf_allow_fdpass(&iev->ibuf); 2737be2c8aaSrzalamena event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data); 2747be2c8aaSrzalamena event_add(&iev->ev, NULL); 2757be2c8aaSrzalamena } 2767be2c8aaSrzalamena 2777be2c8aaSrzalamena void 2787be2c8aaSrzalamena proc_setup(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc) 2797be2c8aaSrzalamena { 2807be2c8aaSrzalamena unsigned int i, j, src, dst, id; 28121032f8aSreyk struct privsep_pipes *pp; 28221032f8aSreyk 2837be2c8aaSrzalamena /* Initialize parent title, ps_instances and procs. */ 2847be2c8aaSrzalamena ps->ps_title[PROC_PARENT] = "parent"; 2857be2c8aaSrzalamena 2867be2c8aaSrzalamena for (src = 0; src < PROC_MAX; src++) 2877be2c8aaSrzalamena /* Default to 1 process instance */ 2887be2c8aaSrzalamena if (ps->ps_instances[src] < 1) 2897be2c8aaSrzalamena ps->ps_instances[src] = 1; 2907be2c8aaSrzalamena 2917be2c8aaSrzalamena for (src = 0; src < nproc; src++) { 2927be2c8aaSrzalamena procs[src].p_ps = ps; 2937be2c8aaSrzalamena if (procs[src].p_cb == NULL) 2947be2c8aaSrzalamena procs[src].p_cb = proc_dispatch_null; 2957be2c8aaSrzalamena 2967be2c8aaSrzalamena id = procs[src].p_id; 2977be2c8aaSrzalamena ps->ps_title[id] = procs[src].p_title; 2987be2c8aaSrzalamena if ((ps->ps_ievs[id] = calloc(ps->ps_instances[id], 2997be2c8aaSrzalamena sizeof(struct imsgev))) == NULL) 3007be2c8aaSrzalamena fatal("%s: calloc", __func__); 3017be2c8aaSrzalamena 302dd7efffeSclaudio /* With this set up, we are ready to call imsgbuf_init(). */ 3037be2c8aaSrzalamena for (i = 0; i < ps->ps_instances[id]; i++) { 3047be2c8aaSrzalamena ps->ps_ievs[id][i].handler = proc_dispatch; 3057be2c8aaSrzalamena ps->ps_ievs[id][i].events = EV_READ; 3067be2c8aaSrzalamena ps->ps_ievs[id][i].proc = &procs[src]; 3077be2c8aaSrzalamena ps->ps_ievs[id][i].data = &ps->ps_ievs[id][i]; 3087be2c8aaSrzalamena } 3097be2c8aaSrzalamena } 3107be2c8aaSrzalamena 31160996bedSreyk /* 31221032f8aSreyk * Allocate pipes for all process instances (incl. parent) 31321032f8aSreyk * 31421032f8aSreyk * - ps->ps_pipes: N:M mapping 31521032f8aSreyk * N source processes connected to M destination processes: 31621032f8aSreyk * [src][instances][dst][instances], for example 31721032f8aSreyk * [PROC_RELAY][3][PROC_CA][3] 31821032f8aSreyk * 31921032f8aSreyk * - ps->ps_pp: per-process 1:M part of ps->ps_pipes 32021032f8aSreyk * Each process instance has a destination array of socketpair fds: 32121032f8aSreyk * [dst][instances], for example 32221032f8aSreyk * [PROC_PARENT][0] 32321032f8aSreyk */ 32421032f8aSreyk for (src = 0; src < PROC_MAX; src++) { 32521032f8aSreyk /* Allocate destination array for each process */ 3267be2c8aaSrzalamena if ((ps->ps_pipes[src] = calloc(ps->ps_instances[src], 32721032f8aSreyk sizeof(struct privsep_pipes))) == NULL) 3287be2c8aaSrzalamena fatal("%s: calloc", __func__); 32921032f8aSreyk 3307be2c8aaSrzalamena for (i = 0; i < ps->ps_instances[src]; i++) { 33121032f8aSreyk pp = &ps->ps_pipes[src][i]; 33221032f8aSreyk 33321032f8aSreyk for (dst = 0; dst < PROC_MAX; dst++) { 33421032f8aSreyk /* Allocate maximum fd integers */ 33521032f8aSreyk if ((pp->pp_pipes[dst] = 3367be2c8aaSrzalamena calloc(ps->ps_instances[dst], 33721032f8aSreyk sizeof(int))) == NULL) 3387be2c8aaSrzalamena fatal("%s: calloc", __func__); 33921032f8aSreyk 34021032f8aSreyk /* Mark fd as unused */ 3417be2c8aaSrzalamena for (j = 0; j < ps->ps_instances[dst]; j++) 34221032f8aSreyk pp->pp_pipes[dst][j] = -1; 34321032f8aSreyk } 34421032f8aSreyk } 34521032f8aSreyk } 34621032f8aSreyk 3477be2c8aaSrzalamena ps->ps_pp = &ps->ps_pipes[privsep_process][ps->ps_instance]; 34860996bedSreyk } 34960996bedSreyk 35060996bedSreyk void 35160996bedSreyk proc_kill(struct privsep *ps) 35260996bedSreyk { 3537be2c8aaSrzalamena char *cause; 35460996bedSreyk pid_t pid; 3557be2c8aaSrzalamena int len, status; 35660996bedSreyk 35760996bedSreyk if (privsep_process != PROC_PARENT) 35860996bedSreyk return; 35960996bedSreyk 3607be2c8aaSrzalamena proc_close(ps); 36160996bedSreyk 36260996bedSreyk do { 3637be2c8aaSrzalamena pid = waitpid(WAIT_ANY, &status, 0); 3647be2c8aaSrzalamena if (pid <= 0) 3657be2c8aaSrzalamena continue; 36621032f8aSreyk 3677be2c8aaSrzalamena if (WIFSIGNALED(status)) { 3687be2c8aaSrzalamena len = asprintf(&cause, "terminated; signal %d", 3697be2c8aaSrzalamena WTERMSIG(status)); 3707be2c8aaSrzalamena } else if (WIFEXITED(status)) { 3717be2c8aaSrzalamena if (WEXITSTATUS(status) != 0) 3727be2c8aaSrzalamena len = asprintf(&cause, "exited abnormally"); 3737be2c8aaSrzalamena else 3747be2c8aaSrzalamena len = 0; 3757be2c8aaSrzalamena } else 3767be2c8aaSrzalamena len = -1; 3777be2c8aaSrzalamena 3787be2c8aaSrzalamena if (len == 0) { 3797be2c8aaSrzalamena /* child exited OK, don't print a warning message */ 3807be2c8aaSrzalamena } else if (len != -1) { 3817be2c8aaSrzalamena log_warnx("lost child: pid %u %s", pid, cause); 3827be2c8aaSrzalamena free(cause); 3837be2c8aaSrzalamena } else 3847be2c8aaSrzalamena log_warnx("lost child: pid %u", pid); 3857be2c8aaSrzalamena } while (pid != -1 || (pid == -1 && errno == EINTR)); 38660996bedSreyk } 38760996bedSreyk 38860996bedSreyk void 3897be2c8aaSrzalamena proc_open(struct privsep *ps, int src, int dst) 39060996bedSreyk { 39121032f8aSreyk struct privsep_pipes *pa, *pb; 3927be2c8aaSrzalamena struct privsep_fd pf; 39321032f8aSreyk int fds[2]; 3947be2c8aaSrzalamena unsigned int i, j; 39560996bedSreyk 3967be2c8aaSrzalamena /* Exchange pipes between process. */ 39721032f8aSreyk for (i = 0; i < ps->ps_instances[src]; i++) { 3987be2c8aaSrzalamena for (j = 0; j < ps->ps_instances[dst]; j++) { 3997be2c8aaSrzalamena /* Don't create sockets for ourself. */ 4007be2c8aaSrzalamena if (src == dst && i == j) 4017be2c8aaSrzalamena continue; 4027be2c8aaSrzalamena 40321032f8aSreyk pa = &ps->ps_pipes[src][i]; 4047be2c8aaSrzalamena pb = &ps->ps_pipes[dst][j]; 4055f0d8540Sreyk if (socketpair(AF_UNIX, 4067be2c8aaSrzalamena SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 40721032f8aSreyk PF_UNSPEC, fds) == -1) 4087be2c8aaSrzalamena fatal("%s: socketpair", __func__); 40921032f8aSreyk 4107be2c8aaSrzalamena pa->pp_pipes[dst][j] = fds[0]; 41121032f8aSreyk pb->pp_pipes[src][i] = fds[1]; 41260996bedSreyk 4137be2c8aaSrzalamena pf.pf_procid = src; 4147be2c8aaSrzalamena pf.pf_instance = i; 4157be2c8aaSrzalamena if (proc_compose_imsg(ps, dst, j, IMSG_CTL_PROCFD, 4167be2c8aaSrzalamena -1, pb->pp_pipes[src][i], &pf, sizeof(pf)) == -1) 4177be2c8aaSrzalamena fatal("%s: proc_compose_imsg", __func__); 4187be2c8aaSrzalamena 4197be2c8aaSrzalamena pf.pf_procid = dst; 4207be2c8aaSrzalamena pf.pf_instance = j; 4217be2c8aaSrzalamena if (proc_compose_imsg(ps, src, i, IMSG_CTL_PROCFD, 4227be2c8aaSrzalamena -1, pa->pp_pipes[dst][j], &pf, sizeof(pf)) == -1) 4237be2c8aaSrzalamena fatal("%s: proc_compose_imsg", __func__); 42421032f8aSreyk 42521032f8aSreyk /* 4267be2c8aaSrzalamena * We have to flush to send the descriptors and close 4277be2c8aaSrzalamena * them to avoid the fd ramp on startup. 42821032f8aSreyk */ 4297be2c8aaSrzalamena if (proc_flush_imsg(ps, src, i) == -1 || 4307be2c8aaSrzalamena proc_flush_imsg(ps, dst, j) == -1) 431dd7efffeSclaudio fatal("%s: proc_flush_imsg", __func__); 43260996bedSreyk } 43360996bedSreyk } 43460996bedSreyk } 43560996bedSreyk 43621032f8aSreyk void 43721032f8aSreyk proc_close(struct privsep *ps) 43821032f8aSreyk { 43932c142bfSreyk unsigned int dst, n; 44021032f8aSreyk struct privsep_pipes *pp; 44160996bedSreyk 44221032f8aSreyk if (ps == NULL) 44321032f8aSreyk return; 44421032f8aSreyk 44521032f8aSreyk pp = ps->ps_pp; 44621032f8aSreyk 44721032f8aSreyk for (dst = 0; dst < PROC_MAX; dst++) { 44821032f8aSreyk if (ps->ps_ievs[dst] == NULL) 44921032f8aSreyk continue; 45021032f8aSreyk 45121032f8aSreyk for (n = 0; n < ps->ps_instances[dst]; n++) { 45221032f8aSreyk if (pp->pp_pipes[dst][n] == -1) 45321032f8aSreyk continue; 45421032f8aSreyk 45521032f8aSreyk /* Cancel the fd, close and invalidate the fd */ 45621032f8aSreyk event_del(&(ps->ps_ievs[dst][n].ev)); 457dd7efffeSclaudio imsgbuf_clear(&(ps->ps_ievs[dst][n].ibuf)); 45821032f8aSreyk close(pp->pp_pipes[dst][n]); 45921032f8aSreyk pp->pp_pipes[dst][n] = -1; 46021032f8aSreyk } 46121032f8aSreyk free(ps->ps_ievs[dst]); 46260996bedSreyk } 46360996bedSreyk } 46460996bedSreyk 46560996bedSreyk void 46660996bedSreyk proc_shutdown(struct privsep_proc *p) 46760996bedSreyk { 46860996bedSreyk struct privsep *ps = p->p_ps; 46921032f8aSreyk 47060996bedSreyk if (p->p_shutdown != NULL) 47121032f8aSreyk (*p->p_shutdown)(); 47260996bedSreyk 47321032f8aSreyk proc_close(ps); 47460996bedSreyk 47521032f8aSreyk log_info("%s exiting, pid %d", p->p_title, getpid()); 47621032f8aSreyk 4777be2c8aaSrzalamena exit(0); 47860996bedSreyk } 47960996bedSreyk 48060996bedSreyk void 48160996bedSreyk proc_sig_handler(int sig, short event, void *arg) 48260996bedSreyk { 48360996bedSreyk struct privsep_proc *p = arg; 48460996bedSreyk 48560996bedSreyk switch (sig) { 48660996bedSreyk case SIGINT: 48760996bedSreyk case SIGTERM: 48860996bedSreyk proc_shutdown(p); 48960996bedSreyk break; 49060996bedSreyk case SIGCHLD: 49160996bedSreyk case SIGHUP: 49260996bedSreyk case SIGPIPE: 4935f2be52bSreyk case SIGUSR1: 49460996bedSreyk /* ignore */ 49560996bedSreyk break; 49660996bedSreyk default: 497566d2cadSbenno fatalx("%s: unexpected signal", __func__); 49860996bedSreyk /* NOTREACHED */ 49960996bedSreyk } 50060996bedSreyk } 50160996bedSreyk 5027be2c8aaSrzalamena void 50360996bedSreyk proc_run(struct privsep *ps, struct privsep_proc *p, 50432c142bfSreyk struct privsep_proc *procs, unsigned int nproc, 5053f75f466Sreyk void (*run)(struct privsep *, struct privsep_proc *, void *), void *arg) 50660996bedSreyk { 50760996bedSreyk struct passwd *pw; 50860996bedSreyk const char *root; 50960996bedSreyk 5100f12961aSreyk log_procinit(p->p_title); 5110f12961aSreyk 5127be2c8aaSrzalamena /* Use non-standard user */ 5137be2c8aaSrzalamena if (p->p_pw != NULL) 5147be2c8aaSrzalamena pw = p->p_pw; 5157be2c8aaSrzalamena else 5167be2c8aaSrzalamena pw = ps->ps_pw; 5177be2c8aaSrzalamena 51860996bedSreyk /* Change root directory */ 51960996bedSreyk if (p->p_chroot != NULL) 52060996bedSreyk root = p->p_chroot; 52160996bedSreyk else 52260996bedSreyk root = pw->pw_dir; 52360996bedSreyk 52460996bedSreyk if (chroot(root) == -1) 525566d2cadSbenno fatal("%s: chroot", __func__); 52660996bedSreyk if (chdir("/") == -1) 527566d2cadSbenno fatal("%s: chdir(\"/\")", __func__); 52860996bedSreyk 52960996bedSreyk privsep_process = p->p_id; 53060996bedSreyk 53160996bedSreyk setproctitle("%s", p->p_title); 53260996bedSreyk 53360996bedSreyk if (setgroups(1, &pw->pw_gid) || 53460996bedSreyk setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 53560996bedSreyk setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 536566d2cadSbenno fatal("%s: cannot drop privileges", __func__); 53760996bedSreyk 53860996bedSreyk event_init(); 53960996bedSreyk 54060996bedSreyk signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p); 54160996bedSreyk signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p); 54260996bedSreyk signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p); 54360996bedSreyk signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p); 54460996bedSreyk signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p); 5455f2be52bSreyk signal_set(&ps->ps_evsigusr1, SIGUSR1, proc_sig_handler, p); 54660996bedSreyk 54760996bedSreyk signal_add(&ps->ps_evsigint, NULL); 54860996bedSreyk signal_add(&ps->ps_evsigterm, NULL); 54960996bedSreyk signal_add(&ps->ps_evsigchld, NULL); 55060996bedSreyk signal_add(&ps->ps_evsighup, NULL); 55160996bedSreyk signal_add(&ps->ps_evsigpipe, NULL); 5525f2be52bSreyk signal_add(&ps->ps_evsigusr1, NULL); 55360996bedSreyk 5547be2c8aaSrzalamena proc_setup(ps, procs, nproc); 5557be2c8aaSrzalamena proc_accept(ps, PROC_PARENT_SOCK_FILENO, PROC_PARENT, 0); 5567be2c8aaSrzalamena DPRINTF("%s: %s %d/%d, pid %d", __func__, p->p_title, 5577be2c8aaSrzalamena ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid()); 5587be2c8aaSrzalamena 5593f75f466Sreyk if (run != NULL) 5603f75f466Sreyk run(ps, p, arg); 56160996bedSreyk 56260996bedSreyk event_dispatch(); 56360996bedSreyk 56460996bedSreyk proc_shutdown(p); 56560996bedSreyk } 56660996bedSreyk 56760996bedSreyk void 56860996bedSreyk proc_dispatch(int fd, short event, void *arg) 56960996bedSreyk { 57021032f8aSreyk struct imsgev *iev = arg; 57121032f8aSreyk struct privsep_proc *p = iev->proc; 57260996bedSreyk struct privsep *ps = p->p_ps; 57360996bedSreyk struct imsgbuf *ibuf; 57460996bedSreyk struct imsg imsg; 57560996bedSreyk ssize_t n; 57660996bedSreyk int verbose; 57760996bedSreyk const char *title; 5787be2c8aaSrzalamena struct privsep_fd pf; 57960996bedSreyk 58060996bedSreyk title = ps->ps_title[privsep_process]; 58160996bedSreyk ibuf = &iev->ibuf; 58260996bedSreyk 58360996bedSreyk if (event & EV_READ) { 584668e5ba9Sclaudio if ((n = imsgbuf_read(ibuf)) == -1) 585dd7efffeSclaudio fatal("%s: imsgbuf_read", __func__); 58660996bedSreyk if (n == 0) { 58760996bedSreyk /* this pipe is dead, so remove the event handler */ 58860996bedSreyk event_del(&iev->ev); 58960996bedSreyk event_loopexit(NULL); 59060996bedSreyk return; 59160996bedSreyk } 59260996bedSreyk } 59360996bedSreyk 59460996bedSreyk if (event & EV_WRITE) { 595dd7efffeSclaudio if (imsgbuf_write(ibuf) == -1) { 596c1aa9554Sclaudio if (errno == EPIPE) { 597c1aa9554Sclaudio /* this pipe is dead, remove the handler */ 5987be2c8aaSrzalamena event_del(&iev->ev); 5997be2c8aaSrzalamena event_loopexit(NULL); 6007be2c8aaSrzalamena return; 6017be2c8aaSrzalamena } 602dd7efffeSclaudio fatal("%s: imsgbuf_write", __func__); 603c1aa9554Sclaudio } 60460996bedSreyk } 60560996bedSreyk 60660996bedSreyk for (;;) { 60760996bedSreyk if ((n = imsg_get(ibuf, &imsg)) == -1) 6087be2c8aaSrzalamena fatal("%s: imsg_get", __func__); 60960996bedSreyk if (n == 0) 61060996bedSreyk break; 61160996bedSreyk 61221032f8aSreyk #if DEBUG > 1 61366094308Sreyk log_debug("%s: %s %d got imsg %d peerid %d from %s %d", 61421032f8aSreyk __func__, title, ps->ps_instance + 1, 6157be2c8aaSrzalamena imsg.hdr.type, imsg.hdr.peerid, p->p_title, imsg.hdr.pid); 61621032f8aSreyk #endif 61721032f8aSreyk 61860996bedSreyk /* 61960996bedSreyk * Check the message with the program callback 62060996bedSreyk */ 62160996bedSreyk if ((p->p_cb)(fd, p, &imsg) == 0) { 62260996bedSreyk /* Message was handled by the callback, continue */ 62360996bedSreyk imsg_free(&imsg); 62460996bedSreyk continue; 62560996bedSreyk } 62660996bedSreyk 62760996bedSreyk /* 62860996bedSreyk * Generic message handling 62960996bedSreyk */ 63060996bedSreyk switch (imsg.hdr.type) { 63160996bedSreyk case IMSG_CTL_VERBOSE: 63260996bedSreyk IMSG_SIZE_CHECK(&imsg, &verbose); 63360996bedSreyk memcpy(&verbose, imsg.data, sizeof(verbose)); 634871fc12cSreyk log_setverbose(verbose); 63560996bedSreyk break; 6367be2c8aaSrzalamena case IMSG_CTL_PROCFD: 6377be2c8aaSrzalamena IMSG_SIZE_CHECK(&imsg, &pf); 6387be2c8aaSrzalamena memcpy(&pf, imsg.data, sizeof(pf)); 639e60163e5Sclaudio proc_accept(ps, imsg_get_fd(&imsg), pf.pf_procid, 6407be2c8aaSrzalamena pf.pf_instance); 6417be2c8aaSrzalamena break; 64260996bedSreyk default: 6437be2c8aaSrzalamena fatalx("%s: %s %d got invalid imsg %d peerid %d " 64466094308Sreyk "from %s %d", 64521032f8aSreyk __func__, title, ps->ps_instance + 1, 64666094308Sreyk imsg.hdr.type, imsg.hdr.peerid, 6477be2c8aaSrzalamena p->p_title, imsg.hdr.pid); 64860996bedSreyk } 64960996bedSreyk imsg_free(&imsg); 65060996bedSreyk } 65160996bedSreyk imsg_event_add(iev); 65260996bedSreyk } 65360996bedSreyk 6543f75f466Sreyk int 6553f75f466Sreyk proc_dispatch_null(int fd, struct privsep_proc *p, struct imsg *imsg) 6563f75f466Sreyk { 6573f75f466Sreyk return (-1); 6583f75f466Sreyk } 6593f75f466Sreyk 66021032f8aSreyk /* 66121032f8aSreyk * imsg helper functions 66221032f8aSreyk */ 66321032f8aSreyk 66460996bedSreyk void 66560996bedSreyk imsg_event_add(struct imsgev *iev) 66660996bedSreyk { 66760996bedSreyk if (iev->handler == NULL) { 668dd7efffeSclaudio imsgbuf_flush(&iev->ibuf); 66960996bedSreyk return; 67060996bedSreyk } 67160996bedSreyk 67260996bedSreyk iev->events = EV_READ; 67331be28caSclaudio if (imsgbuf_queuelen(&iev->ibuf) > 0) 67460996bedSreyk iev->events |= EV_WRITE; 67560996bedSreyk 67660996bedSreyk event_del(&iev->ev); 67760996bedSreyk event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data); 67860996bedSreyk event_add(&iev->ev, NULL); 67960996bedSreyk } 68060996bedSreyk 68160996bedSreyk int 68232c142bfSreyk imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid, 68332c142bfSreyk pid_t pid, int fd, void *data, uint16_t datalen) 68460996bedSreyk { 68560996bedSreyk int ret; 68660996bedSreyk 68760996bedSreyk if ((ret = imsg_compose(&iev->ibuf, type, peerid, 68860996bedSreyk pid, fd, data, datalen)) == -1) 68960996bedSreyk return (ret); 69060996bedSreyk imsg_event_add(iev); 69160996bedSreyk return (ret); 69260996bedSreyk } 69360996bedSreyk 69460996bedSreyk int 69532c142bfSreyk imsg_composev_event(struct imsgev *iev, uint16_t type, uint32_t peerid, 69660996bedSreyk pid_t pid, int fd, const struct iovec *iov, int iovcnt) 69760996bedSreyk { 69860996bedSreyk int ret; 69960996bedSreyk 70060996bedSreyk if ((ret = imsg_composev(&iev->ibuf, type, peerid, 70160996bedSreyk pid, fd, iov, iovcnt)) == -1) 70260996bedSreyk return (ret); 70360996bedSreyk imsg_event_add(iev); 70460996bedSreyk return (ret); 70560996bedSreyk } 70660996bedSreyk 70721032f8aSreyk void 70821032f8aSreyk proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m) 70960996bedSreyk { 71021032f8aSreyk if (*n == -1) { 71121032f8aSreyk /* Use a range of all target instances */ 71221032f8aSreyk *n = 0; 71321032f8aSreyk *m = ps->ps_instances[id]; 71421032f8aSreyk } else { 71521032f8aSreyk /* Use only a single slot of the specified peer process */ 71621032f8aSreyk *m = *n + 1; 71721032f8aSreyk } 71860996bedSreyk } 71960996bedSreyk 72060996bedSreyk int 72121032f8aSreyk proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n, 7229ac7219fSreyk uint16_t type, uint32_t peerid, int fd, void *data, uint16_t datalen) 72321032f8aSreyk { 72421032f8aSreyk int m; 72521032f8aSreyk 72621032f8aSreyk proc_range(ps, id, &n, &m); 72721032f8aSreyk for (; n < m; n++) { 72821032f8aSreyk if (imsg_compose_event(&ps->ps_ievs[id][n], 7297be2c8aaSrzalamena type, peerid, ps->ps_instance + 1, fd, data, datalen) == -1) 73021032f8aSreyk return (-1); 73121032f8aSreyk } 73221032f8aSreyk 73321032f8aSreyk return (0); 73421032f8aSreyk } 73521032f8aSreyk 73621032f8aSreyk int 7379ac7219fSreyk proc_compose(struct privsep *ps, enum privsep_procid id, 7389ac7219fSreyk uint16_t type, void *data, uint16_t datalen) 7399ac7219fSreyk { 7409ac7219fSreyk return (proc_compose_imsg(ps, id, -1, type, -1, -1, data, datalen)); 7419ac7219fSreyk } 7429ac7219fSreyk 7439ac7219fSreyk int 74421032f8aSreyk proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n, 7459ac7219fSreyk uint16_t type, uint32_t peerid, int fd, const struct iovec *iov, int iovcnt) 74660996bedSreyk { 74721032f8aSreyk int m; 74821032f8aSreyk 74921032f8aSreyk proc_range(ps, id, &n, &m); 75021032f8aSreyk for (; n < m; n++) 75121032f8aSreyk if (imsg_composev_event(&ps->ps_ievs[id][n], 7527be2c8aaSrzalamena type, peerid, ps->ps_instance + 1, fd, iov, iovcnt) == -1) 75321032f8aSreyk return (-1); 75421032f8aSreyk 75521032f8aSreyk return (0); 75660996bedSreyk } 75760996bedSreyk 75860996bedSreyk int 7599ac7219fSreyk proc_composev(struct privsep *ps, enum privsep_procid id, 7609ac7219fSreyk uint16_t type, const struct iovec *iov, int iovcnt) 7619ac7219fSreyk { 7629ac7219fSreyk return (proc_composev_imsg(ps, id, -1, type, -1, -1, iov, iovcnt)); 7639ac7219fSreyk } 7649ac7219fSreyk 76521032f8aSreyk struct imsgbuf * 76621032f8aSreyk proc_ibuf(struct privsep *ps, enum privsep_procid id, int n) 76721032f8aSreyk { 76821032f8aSreyk int m; 76921032f8aSreyk 77021032f8aSreyk proc_range(ps, id, &n, &m); 77121032f8aSreyk return (&ps->ps_ievs[id][n].ibuf); 77221032f8aSreyk } 77321032f8aSreyk 77421032f8aSreyk struct imsgev * 77521032f8aSreyk proc_iev(struct privsep *ps, enum privsep_procid id, int n) 77621032f8aSreyk { 77721032f8aSreyk int m; 77821032f8aSreyk 77921032f8aSreyk proc_range(ps, id, &n, &m); 78021032f8aSreyk return (&ps->ps_ievs[id][n]); 78121032f8aSreyk } 7827be2c8aaSrzalamena 7837be2c8aaSrzalamena /* This function should only be called with care as it breaks async I/O */ 7847be2c8aaSrzalamena int 7857be2c8aaSrzalamena proc_flush_imsg(struct privsep *ps, enum privsep_procid id, int n) 7867be2c8aaSrzalamena { 7877be2c8aaSrzalamena struct imsgbuf *ibuf; 7887be2c8aaSrzalamena int m, ret = 0; 7897be2c8aaSrzalamena 7907be2c8aaSrzalamena proc_range(ps, id, &n, &m); 7917be2c8aaSrzalamena for (; n < m; n++) { 7927be2c8aaSrzalamena if ((ibuf = proc_ibuf(ps, id, n)) == NULL) 7937be2c8aaSrzalamena return (-1); 794dd7efffeSclaudio if ((ret = imsgbuf_flush(ibuf)) == -1) 7957be2c8aaSrzalamena break; 7967be2c8aaSrzalamena imsg_event_add(&ps->ps_ievs[id][n]); 7977be2c8aaSrzalamena } 7987be2c8aaSrzalamena 7997be2c8aaSrzalamena return (ret); 8007be2c8aaSrzalamena } 801