1*2269e292Stobhe /* $OpenBSD: proc.c,v 1.32 2024/04/09 15:48:01 tobhe 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,
40*2269e292Stobhe 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
proc_getid(struct privsep_proc * procs,unsigned int nproc,const char * proc_name)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
proc_exec(struct privsep * ps,struct privsep_proc * procs,unsigned int nproc,int argc,char ** argv)707be2c8aaSrzalamena proc_exec(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc,
71*2269e292Stobhe 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
proc_connect(struct privsep * ps)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];
1587be2c8aaSrzalamena imsg_init(&iev->ibuf, ps->ps_pp->pp_pipes[dst][inst]);
1597be2c8aaSrzalamena event_set(&iev->ev, iev->ibuf.fd, iev->events,
1607be2c8aaSrzalamena iev->handler, iev->data);
1617be2c8aaSrzalamena event_add(&iev->ev, NULL);
1627be2c8aaSrzalamena }
1637be2c8aaSrzalamena }
1647be2c8aaSrzalamena
1657be2c8aaSrzalamena /* Distribute the socketpair()s for everyone. */
1667be2c8aaSrzalamena for (src = 0; src < PROC_MAX; src++)
1677be2c8aaSrzalamena for (dst = src; dst < PROC_MAX; dst++) {
1687be2c8aaSrzalamena /* Parent already distributed its fds. */
1697be2c8aaSrzalamena if (src == PROC_PARENT || dst == PROC_PARENT)
1707be2c8aaSrzalamena continue;
1717be2c8aaSrzalamena
1727be2c8aaSrzalamena proc_open(ps, src, dst);
1737be2c8aaSrzalamena }
1747be2c8aaSrzalamena }
1757be2c8aaSrzalamena
1767be2c8aaSrzalamena void
proc_init(struct privsep * ps,struct privsep_proc * procs,unsigned int nproc,int debug,int argc,char ** argv,enum privsep_procid proc_id)1777be2c8aaSrzalamena proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc,
1789f020842Sbluhm int debug, int argc, char **argv, enum privsep_procid proc_id)
1797be2c8aaSrzalamena {
1807be2c8aaSrzalamena struct privsep_proc *p = NULL;
1817be2c8aaSrzalamena struct privsep_pipes *pa, *pb;
1827be2c8aaSrzalamena unsigned int proc;
1837be2c8aaSrzalamena unsigned int dst;
1847be2c8aaSrzalamena int fds[2];
1857be2c8aaSrzalamena
1867be2c8aaSrzalamena /* Don't initiate anything if we are not really going to run. */
1877be2c8aaSrzalamena if (ps->ps_noaction)
1887be2c8aaSrzalamena return;
1897be2c8aaSrzalamena
1907be2c8aaSrzalamena if (proc_id == PROC_PARENT) {
1917be2c8aaSrzalamena privsep_process = PROC_PARENT;
1927be2c8aaSrzalamena proc_setup(ps, procs, nproc);
1937be2c8aaSrzalamena
19428fef5efStobhe if (!debug && daemon(0, 0) == -1)
19528fef5efStobhe fatal("failed to daemonize");
19628fef5efStobhe
1977be2c8aaSrzalamena /*
1987be2c8aaSrzalamena * Create the children sockets so we can use them
1997be2c8aaSrzalamena * to distribute the rest of the socketpair()s using
2007be2c8aaSrzalamena * proc_connect() later.
2017be2c8aaSrzalamena */
2027be2c8aaSrzalamena for (dst = 0; dst < PROC_MAX; dst++) {
2037be2c8aaSrzalamena /* Don't create socket for ourselves. */
2047be2c8aaSrzalamena if (dst == PROC_PARENT)
2057be2c8aaSrzalamena continue;
2067be2c8aaSrzalamena
2077be2c8aaSrzalamena for (proc = 0; proc < ps->ps_instances[dst]; proc++) {
2087be2c8aaSrzalamena pa = &ps->ps_pipes[PROC_PARENT][0];
2097be2c8aaSrzalamena pb = &ps->ps_pipes[dst][proc];
2107be2c8aaSrzalamena if (socketpair(AF_UNIX,
2117be2c8aaSrzalamena SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
2127be2c8aaSrzalamena PF_UNSPEC, fds) == -1)
2137be2c8aaSrzalamena fatal("%s: socketpair", __func__);
2147be2c8aaSrzalamena
2157be2c8aaSrzalamena pa->pp_pipes[dst][proc] = fds[0];
2167be2c8aaSrzalamena pb->pp_pipes[PROC_PARENT][0] = fds[1];
2177be2c8aaSrzalamena }
2187be2c8aaSrzalamena }
2197be2c8aaSrzalamena
2207be2c8aaSrzalamena /* Engage! */
221*2269e292Stobhe proc_exec(ps, procs, nproc, argc, argv);
2227be2c8aaSrzalamena return;
2237be2c8aaSrzalamena }
2247be2c8aaSrzalamena
2257be2c8aaSrzalamena /* Initialize a child */
2267be2c8aaSrzalamena for (proc = 0; proc < nproc; proc++) {
2277be2c8aaSrzalamena if (procs[proc].p_id != proc_id)
2287be2c8aaSrzalamena continue;
2297be2c8aaSrzalamena p = &procs[proc];
2307be2c8aaSrzalamena break;
2317be2c8aaSrzalamena }
2327be2c8aaSrzalamena if (p == NULL || p->p_init == NULL)
2337be2c8aaSrzalamena fatalx("%s: process %d missing process initialization",
2347be2c8aaSrzalamena __func__, proc_id);
2357be2c8aaSrzalamena
2367be2c8aaSrzalamena p->p_init(ps, p);
2377be2c8aaSrzalamena
2387be2c8aaSrzalamena fatalx("failed to initiate child process");
2397be2c8aaSrzalamena }
2407be2c8aaSrzalamena
2417be2c8aaSrzalamena void
proc_accept(struct privsep * ps,int fd,enum privsep_procid dst,unsigned int n)2427be2c8aaSrzalamena proc_accept(struct privsep *ps, int fd, enum privsep_procid dst,
2437be2c8aaSrzalamena unsigned int n)
2447be2c8aaSrzalamena {
2457be2c8aaSrzalamena struct privsep_pipes *pp = ps->ps_pp;
2467be2c8aaSrzalamena struct imsgev *iev;
2477be2c8aaSrzalamena
2487be2c8aaSrzalamena if (ps->ps_ievs[dst] == NULL) {
2497be2c8aaSrzalamena #if DEBUG > 1
2507be2c8aaSrzalamena log_debug("%s: %s src %d %d to dst %d %d not connected",
2517be2c8aaSrzalamena __func__, ps->ps_title[privsep_process],
2527be2c8aaSrzalamena privsep_process, ps->ps_instance + 1,
2537be2c8aaSrzalamena dst, n + 1);
2547be2c8aaSrzalamena #endif
2557be2c8aaSrzalamena close(fd);
2567be2c8aaSrzalamena return;
2577be2c8aaSrzalamena }
2587be2c8aaSrzalamena
2597be2c8aaSrzalamena if (pp->pp_pipes[dst][n] != -1) {
2607be2c8aaSrzalamena log_warnx("%s: duplicated descriptor", __func__);
2617be2c8aaSrzalamena close(fd);
2627be2c8aaSrzalamena return;
2637be2c8aaSrzalamena } else
2647be2c8aaSrzalamena pp->pp_pipes[dst][n] = fd;
2657be2c8aaSrzalamena
2667be2c8aaSrzalamena iev = &ps->ps_ievs[dst][n];
2677be2c8aaSrzalamena imsg_init(&iev->ibuf, fd);
2687be2c8aaSrzalamena event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
2697be2c8aaSrzalamena event_add(&iev->ev, NULL);
2707be2c8aaSrzalamena }
2717be2c8aaSrzalamena
2727be2c8aaSrzalamena void
proc_setup(struct privsep * ps,struct privsep_proc * procs,unsigned int nproc)2737be2c8aaSrzalamena proc_setup(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc)
2747be2c8aaSrzalamena {
2757be2c8aaSrzalamena unsigned int i, j, src, dst, id;
27621032f8aSreyk struct privsep_pipes *pp;
27721032f8aSreyk
2787be2c8aaSrzalamena /* Initialize parent title, ps_instances and procs. */
2797be2c8aaSrzalamena ps->ps_title[PROC_PARENT] = "parent";
2807be2c8aaSrzalamena
2817be2c8aaSrzalamena for (src = 0; src < PROC_MAX; src++)
2827be2c8aaSrzalamena /* Default to 1 process instance */
2837be2c8aaSrzalamena if (ps->ps_instances[src] < 1)
2847be2c8aaSrzalamena ps->ps_instances[src] = 1;
2857be2c8aaSrzalamena
2867be2c8aaSrzalamena for (src = 0; src < nproc; src++) {
2877be2c8aaSrzalamena procs[src].p_ps = ps;
2887be2c8aaSrzalamena if (procs[src].p_cb == NULL)
2897be2c8aaSrzalamena procs[src].p_cb = proc_dispatch_null;
2907be2c8aaSrzalamena
2917be2c8aaSrzalamena id = procs[src].p_id;
2927be2c8aaSrzalamena ps->ps_title[id] = procs[src].p_title;
2937be2c8aaSrzalamena if ((ps->ps_ievs[id] = calloc(ps->ps_instances[id],
2947be2c8aaSrzalamena sizeof(struct imsgev))) == NULL)
2957be2c8aaSrzalamena fatal("%s: calloc", __func__);
2967be2c8aaSrzalamena
2977be2c8aaSrzalamena /* With this set up, we are ready to call imsg_init(). */
2987be2c8aaSrzalamena for (i = 0; i < ps->ps_instances[id]; i++) {
2997be2c8aaSrzalamena ps->ps_ievs[id][i].handler = proc_dispatch;
3007be2c8aaSrzalamena ps->ps_ievs[id][i].events = EV_READ;
3017be2c8aaSrzalamena ps->ps_ievs[id][i].proc = &procs[src];
3027be2c8aaSrzalamena ps->ps_ievs[id][i].data = &ps->ps_ievs[id][i];
3037be2c8aaSrzalamena }
3047be2c8aaSrzalamena }
3057be2c8aaSrzalamena
30660996bedSreyk /*
30721032f8aSreyk * Allocate pipes for all process instances (incl. parent)
30821032f8aSreyk *
30921032f8aSreyk * - ps->ps_pipes: N:M mapping
31021032f8aSreyk * N source processes connected to M destination processes:
31121032f8aSreyk * [src][instances][dst][instances], for example
31221032f8aSreyk * [PROC_RELAY][3][PROC_CA][3]
31321032f8aSreyk *
31421032f8aSreyk * - ps->ps_pp: per-process 1:M part of ps->ps_pipes
31521032f8aSreyk * Each process instance has a destination array of socketpair fds:
31621032f8aSreyk * [dst][instances], for example
31721032f8aSreyk * [PROC_PARENT][0]
31821032f8aSreyk */
31921032f8aSreyk for (src = 0; src < PROC_MAX; src++) {
32021032f8aSreyk /* Allocate destination array for each process */
3217be2c8aaSrzalamena if ((ps->ps_pipes[src] = calloc(ps->ps_instances[src],
32221032f8aSreyk sizeof(struct privsep_pipes))) == NULL)
3237be2c8aaSrzalamena fatal("%s: calloc", __func__);
32421032f8aSreyk
3257be2c8aaSrzalamena for (i = 0; i < ps->ps_instances[src]; i++) {
32621032f8aSreyk pp = &ps->ps_pipes[src][i];
32721032f8aSreyk
32821032f8aSreyk for (dst = 0; dst < PROC_MAX; dst++) {
32921032f8aSreyk /* Allocate maximum fd integers */
33021032f8aSreyk if ((pp->pp_pipes[dst] =
3317be2c8aaSrzalamena calloc(ps->ps_instances[dst],
33221032f8aSreyk sizeof(int))) == NULL)
3337be2c8aaSrzalamena fatal("%s: calloc", __func__);
33421032f8aSreyk
33521032f8aSreyk /* Mark fd as unused */
3367be2c8aaSrzalamena for (j = 0; j < ps->ps_instances[dst]; j++)
33721032f8aSreyk pp->pp_pipes[dst][j] = -1;
33821032f8aSreyk }
33921032f8aSreyk }
34021032f8aSreyk }
34121032f8aSreyk
3427be2c8aaSrzalamena ps->ps_pp = &ps->ps_pipes[privsep_process][ps->ps_instance];
34360996bedSreyk }
34460996bedSreyk
34560996bedSreyk void
proc_kill(struct privsep * ps)34660996bedSreyk proc_kill(struct privsep *ps)
34760996bedSreyk {
3487be2c8aaSrzalamena char *cause;
34960996bedSreyk pid_t pid;
3507be2c8aaSrzalamena int len, status;
35160996bedSreyk
35260996bedSreyk if (privsep_process != PROC_PARENT)
35360996bedSreyk return;
35460996bedSreyk
3557be2c8aaSrzalamena proc_close(ps);
35660996bedSreyk
35760996bedSreyk do {
3587be2c8aaSrzalamena pid = waitpid(WAIT_ANY, &status, 0);
3597be2c8aaSrzalamena if (pid <= 0)
3607be2c8aaSrzalamena continue;
36121032f8aSreyk
3627be2c8aaSrzalamena if (WIFSIGNALED(status)) {
3637be2c8aaSrzalamena len = asprintf(&cause, "terminated; signal %d",
3647be2c8aaSrzalamena WTERMSIG(status));
3657be2c8aaSrzalamena } else if (WIFEXITED(status)) {
3667be2c8aaSrzalamena if (WEXITSTATUS(status) != 0)
3677be2c8aaSrzalamena len = asprintf(&cause, "exited abnormally");
3687be2c8aaSrzalamena else
3697be2c8aaSrzalamena len = 0;
3707be2c8aaSrzalamena } else
3717be2c8aaSrzalamena len = -1;
3727be2c8aaSrzalamena
3737be2c8aaSrzalamena if (len == 0) {
3747be2c8aaSrzalamena /* child exited OK, don't print a warning message */
3757be2c8aaSrzalamena } else if (len != -1) {
3767be2c8aaSrzalamena log_warnx("lost child: pid %u %s", pid, cause);
3777be2c8aaSrzalamena free(cause);
3787be2c8aaSrzalamena } else
3797be2c8aaSrzalamena log_warnx("lost child: pid %u", pid);
3807be2c8aaSrzalamena } while (pid != -1 || (pid == -1 && errno == EINTR));
38160996bedSreyk }
38260996bedSreyk
38360996bedSreyk void
proc_open(struct privsep * ps,int src,int dst)3847be2c8aaSrzalamena proc_open(struct privsep *ps, int src, int dst)
38560996bedSreyk {
38621032f8aSreyk struct privsep_pipes *pa, *pb;
3877be2c8aaSrzalamena struct privsep_fd pf;
38821032f8aSreyk int fds[2];
3897be2c8aaSrzalamena unsigned int i, j;
39060996bedSreyk
3917be2c8aaSrzalamena /* Exchange pipes between process. */
39221032f8aSreyk for (i = 0; i < ps->ps_instances[src]; i++) {
3937be2c8aaSrzalamena for (j = 0; j < ps->ps_instances[dst]; j++) {
3947be2c8aaSrzalamena /* Don't create sockets for ourself. */
3957be2c8aaSrzalamena if (src == dst && i == j)
3967be2c8aaSrzalamena continue;
3977be2c8aaSrzalamena
39821032f8aSreyk pa = &ps->ps_pipes[src][i];
3997be2c8aaSrzalamena pb = &ps->ps_pipes[dst][j];
4005f0d8540Sreyk if (socketpair(AF_UNIX,
4017be2c8aaSrzalamena SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC,
40221032f8aSreyk PF_UNSPEC, fds) == -1)
4037be2c8aaSrzalamena fatal("%s: socketpair", __func__);
40421032f8aSreyk
4057be2c8aaSrzalamena pa->pp_pipes[dst][j] = fds[0];
40621032f8aSreyk pb->pp_pipes[src][i] = fds[1];
40760996bedSreyk
4087be2c8aaSrzalamena pf.pf_procid = src;
4097be2c8aaSrzalamena pf.pf_instance = i;
4107be2c8aaSrzalamena if (proc_compose_imsg(ps, dst, j, IMSG_CTL_PROCFD,
4117be2c8aaSrzalamena -1, pb->pp_pipes[src][i], &pf, sizeof(pf)) == -1)
4127be2c8aaSrzalamena fatal("%s: proc_compose_imsg", __func__);
4137be2c8aaSrzalamena
4147be2c8aaSrzalamena pf.pf_procid = dst;
4157be2c8aaSrzalamena pf.pf_instance = j;
4167be2c8aaSrzalamena if (proc_compose_imsg(ps, src, i, IMSG_CTL_PROCFD,
4177be2c8aaSrzalamena -1, pa->pp_pipes[dst][j], &pf, sizeof(pf)) == -1)
4187be2c8aaSrzalamena fatal("%s: proc_compose_imsg", __func__);
41921032f8aSreyk
42021032f8aSreyk /*
4217be2c8aaSrzalamena * We have to flush to send the descriptors and close
4227be2c8aaSrzalamena * them to avoid the fd ramp on startup.
42321032f8aSreyk */
4247be2c8aaSrzalamena if (proc_flush_imsg(ps, src, i) == -1 ||
4257be2c8aaSrzalamena proc_flush_imsg(ps, dst, j) == -1)
4267be2c8aaSrzalamena fatal("%s: imsg_flush", __func__);
42760996bedSreyk }
42860996bedSreyk }
42960996bedSreyk }
43060996bedSreyk
43121032f8aSreyk void
proc_close(struct privsep * ps)43221032f8aSreyk proc_close(struct privsep *ps)
43321032f8aSreyk {
43432c142bfSreyk unsigned int dst, n;
43521032f8aSreyk struct privsep_pipes *pp;
43660996bedSreyk
43721032f8aSreyk if (ps == NULL)
43821032f8aSreyk return;
43921032f8aSreyk
44021032f8aSreyk pp = ps->ps_pp;
44121032f8aSreyk
44221032f8aSreyk for (dst = 0; dst < PROC_MAX; dst++) {
44321032f8aSreyk if (ps->ps_ievs[dst] == NULL)
44421032f8aSreyk continue;
44521032f8aSreyk
44621032f8aSreyk for (n = 0; n < ps->ps_instances[dst]; n++) {
44721032f8aSreyk if (pp->pp_pipes[dst][n] == -1)
44821032f8aSreyk continue;
44921032f8aSreyk
45021032f8aSreyk /* Cancel the fd, close and invalidate the fd */
45121032f8aSreyk event_del(&(ps->ps_ievs[dst][n].ev));
45221032f8aSreyk imsg_clear(&(ps->ps_ievs[dst][n].ibuf));
45321032f8aSreyk close(pp->pp_pipes[dst][n]);
45421032f8aSreyk pp->pp_pipes[dst][n] = -1;
45521032f8aSreyk }
45621032f8aSreyk free(ps->ps_ievs[dst]);
45760996bedSreyk }
45860996bedSreyk }
45960996bedSreyk
46060996bedSreyk void
proc_shutdown(struct privsep_proc * p)46160996bedSreyk proc_shutdown(struct privsep_proc *p)
46260996bedSreyk {
46360996bedSreyk struct privsep *ps = p->p_ps;
46421032f8aSreyk
46560996bedSreyk if (p->p_shutdown != NULL)
46621032f8aSreyk (*p->p_shutdown)();
46760996bedSreyk
46821032f8aSreyk proc_close(ps);
46960996bedSreyk
47021032f8aSreyk log_info("%s exiting, pid %d", p->p_title, getpid());
47121032f8aSreyk
4727be2c8aaSrzalamena exit(0);
47360996bedSreyk }
47460996bedSreyk
47560996bedSreyk void
proc_sig_handler(int sig,short event,void * arg)47660996bedSreyk proc_sig_handler(int sig, short event, void *arg)
47760996bedSreyk {
47860996bedSreyk struct privsep_proc *p = arg;
47960996bedSreyk
48060996bedSreyk switch (sig) {
48160996bedSreyk case SIGINT:
48260996bedSreyk case SIGTERM:
48360996bedSreyk proc_shutdown(p);
48460996bedSreyk break;
48560996bedSreyk case SIGCHLD:
48660996bedSreyk case SIGHUP:
48760996bedSreyk case SIGPIPE:
4885f2be52bSreyk case SIGUSR1:
48960996bedSreyk /* ignore */
49060996bedSreyk break;
49160996bedSreyk default:
492566d2cadSbenno fatalx("%s: unexpected signal", __func__);
49360996bedSreyk /* NOTREACHED */
49460996bedSreyk }
49560996bedSreyk }
49660996bedSreyk
4977be2c8aaSrzalamena void
proc_run(struct privsep * ps,struct privsep_proc * p,struct privsep_proc * procs,unsigned int nproc,void (* run)(struct privsep *,struct privsep_proc *,void *),void * arg)49860996bedSreyk proc_run(struct privsep *ps, struct privsep_proc *p,
49932c142bfSreyk struct privsep_proc *procs, unsigned int nproc,
5003f75f466Sreyk void (*run)(struct privsep *, struct privsep_proc *, void *), void *arg)
50160996bedSreyk {
50260996bedSreyk struct passwd *pw;
50360996bedSreyk const char *root;
50460996bedSreyk
5050f12961aSreyk log_procinit(p->p_title);
5060f12961aSreyk
5077be2c8aaSrzalamena /* Use non-standard user */
5087be2c8aaSrzalamena if (p->p_pw != NULL)
5097be2c8aaSrzalamena pw = p->p_pw;
5107be2c8aaSrzalamena else
5117be2c8aaSrzalamena pw = ps->ps_pw;
5127be2c8aaSrzalamena
51360996bedSreyk /* Change root directory */
51460996bedSreyk if (p->p_chroot != NULL)
51560996bedSreyk root = p->p_chroot;
51660996bedSreyk else
51760996bedSreyk root = pw->pw_dir;
51860996bedSreyk
51960996bedSreyk if (chroot(root) == -1)
520566d2cadSbenno fatal("%s: chroot", __func__);
52160996bedSreyk if (chdir("/") == -1)
522566d2cadSbenno fatal("%s: chdir(\"/\")", __func__);
52360996bedSreyk
52460996bedSreyk privsep_process = p->p_id;
52560996bedSreyk
52660996bedSreyk setproctitle("%s", p->p_title);
52760996bedSreyk
52860996bedSreyk if (setgroups(1, &pw->pw_gid) ||
52960996bedSreyk setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) ||
53060996bedSreyk setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid))
531566d2cadSbenno fatal("%s: cannot drop privileges", __func__);
53260996bedSreyk
53360996bedSreyk event_init();
53460996bedSreyk
53560996bedSreyk signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p);
53660996bedSreyk signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p);
53760996bedSreyk signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p);
53860996bedSreyk signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p);
53960996bedSreyk signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p);
5405f2be52bSreyk signal_set(&ps->ps_evsigusr1, SIGUSR1, proc_sig_handler, p);
54160996bedSreyk
54260996bedSreyk signal_add(&ps->ps_evsigint, NULL);
54360996bedSreyk signal_add(&ps->ps_evsigterm, NULL);
54460996bedSreyk signal_add(&ps->ps_evsigchld, NULL);
54560996bedSreyk signal_add(&ps->ps_evsighup, NULL);
54660996bedSreyk signal_add(&ps->ps_evsigpipe, NULL);
5475f2be52bSreyk signal_add(&ps->ps_evsigusr1, NULL);
54860996bedSreyk
5497be2c8aaSrzalamena proc_setup(ps, procs, nproc);
5507be2c8aaSrzalamena proc_accept(ps, PROC_PARENT_SOCK_FILENO, PROC_PARENT, 0);
5517be2c8aaSrzalamena DPRINTF("%s: %s %d/%d, pid %d", __func__, p->p_title,
5527be2c8aaSrzalamena ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid());
5537be2c8aaSrzalamena
5543f75f466Sreyk if (run != NULL)
5553f75f466Sreyk run(ps, p, arg);
55660996bedSreyk
55760996bedSreyk event_dispatch();
55860996bedSreyk
55960996bedSreyk proc_shutdown(p);
56060996bedSreyk }
56160996bedSreyk
56260996bedSreyk void
proc_dispatch(int fd,short event,void * arg)56360996bedSreyk proc_dispatch(int fd, short event, void *arg)
56460996bedSreyk {
56521032f8aSreyk struct imsgev *iev = arg;
56621032f8aSreyk struct privsep_proc *p = iev->proc;
56760996bedSreyk struct privsep *ps = p->p_ps;
56860996bedSreyk struct imsgbuf *ibuf;
56960996bedSreyk struct imsg imsg;
57060996bedSreyk ssize_t n;
57160996bedSreyk int verbose;
57260996bedSreyk const char *title;
5737be2c8aaSrzalamena struct privsep_fd pf;
57460996bedSreyk
57560996bedSreyk title = ps->ps_title[privsep_process];
57660996bedSreyk ibuf = &iev->ibuf;
57760996bedSreyk
57860996bedSreyk if (event & EV_READ) {
5792c684640Sclaudio if ((n = imsg_read(ibuf)) == -1 && errno != EAGAIN)
5807be2c8aaSrzalamena fatal("%s: imsg_read", __func__);
58160996bedSreyk if (n == 0) {
58260996bedSreyk /* this pipe is dead, so remove the event handler */
58360996bedSreyk event_del(&iev->ev);
58460996bedSreyk event_loopexit(NULL);
58560996bedSreyk return;
58660996bedSreyk }
58760996bedSreyk }
58860996bedSreyk
58960996bedSreyk if (event & EV_WRITE) {
5907be2c8aaSrzalamena if ((n = msgbuf_write(&ibuf->w)) == -1 && errno != EAGAIN)
5917be2c8aaSrzalamena fatal("%s: msgbuf_write", __func__);
5927be2c8aaSrzalamena if (n == 0) {
5937be2c8aaSrzalamena /* this pipe is dead, so remove the event handler */
5947be2c8aaSrzalamena event_del(&iev->ev);
5957be2c8aaSrzalamena event_loopexit(NULL);
5967be2c8aaSrzalamena return;
5977be2c8aaSrzalamena }
59860996bedSreyk }
59960996bedSreyk
60060996bedSreyk for (;;) {
60160996bedSreyk if ((n = imsg_get(ibuf, &imsg)) == -1)
6027be2c8aaSrzalamena fatal("%s: imsg_get", __func__);
60360996bedSreyk if (n == 0)
60460996bedSreyk break;
60560996bedSreyk
60621032f8aSreyk #if DEBUG > 1
60766094308Sreyk log_debug("%s: %s %d got imsg %d peerid %d from %s %d",
60821032f8aSreyk __func__, title, ps->ps_instance + 1,
6097be2c8aaSrzalamena imsg.hdr.type, imsg.hdr.peerid, p->p_title, imsg.hdr.pid);
61021032f8aSreyk #endif
61121032f8aSreyk
61260996bedSreyk /*
61360996bedSreyk * Check the message with the program callback
61460996bedSreyk */
61560996bedSreyk if ((p->p_cb)(fd, p, &imsg) == 0) {
61660996bedSreyk /* Message was handled by the callback, continue */
61760996bedSreyk imsg_free(&imsg);
61860996bedSreyk continue;
61960996bedSreyk }
62060996bedSreyk
62160996bedSreyk /*
62260996bedSreyk * Generic message handling
62360996bedSreyk */
62460996bedSreyk switch (imsg.hdr.type) {
62560996bedSreyk case IMSG_CTL_VERBOSE:
62660996bedSreyk IMSG_SIZE_CHECK(&imsg, &verbose);
62760996bedSreyk memcpy(&verbose, imsg.data, sizeof(verbose));
628871fc12cSreyk log_setverbose(verbose);
62960996bedSreyk break;
6307be2c8aaSrzalamena case IMSG_CTL_PROCFD:
6317be2c8aaSrzalamena IMSG_SIZE_CHECK(&imsg, &pf);
6327be2c8aaSrzalamena memcpy(&pf, imsg.data, sizeof(pf));
633e60163e5Sclaudio proc_accept(ps, imsg_get_fd(&imsg), pf.pf_procid,
6347be2c8aaSrzalamena pf.pf_instance);
6357be2c8aaSrzalamena break;
63660996bedSreyk default:
6377be2c8aaSrzalamena fatalx("%s: %s %d got invalid imsg %d peerid %d "
63866094308Sreyk "from %s %d",
63921032f8aSreyk __func__, title, ps->ps_instance + 1,
64066094308Sreyk imsg.hdr.type, imsg.hdr.peerid,
6417be2c8aaSrzalamena p->p_title, imsg.hdr.pid);
64260996bedSreyk }
64360996bedSreyk imsg_free(&imsg);
64460996bedSreyk }
64560996bedSreyk imsg_event_add(iev);
64660996bedSreyk }
64760996bedSreyk
6483f75f466Sreyk int
proc_dispatch_null(int fd,struct privsep_proc * p,struct imsg * imsg)6493f75f466Sreyk proc_dispatch_null(int fd, struct privsep_proc *p, struct imsg *imsg)
6503f75f466Sreyk {
6513f75f466Sreyk return (-1);
6523f75f466Sreyk }
6533f75f466Sreyk
65421032f8aSreyk /*
65521032f8aSreyk * imsg helper functions
65621032f8aSreyk */
65721032f8aSreyk
65860996bedSreyk void
imsg_event_add(struct imsgev * iev)65960996bedSreyk imsg_event_add(struct imsgev *iev)
66060996bedSreyk {
66160996bedSreyk if (iev->handler == NULL) {
66260996bedSreyk imsg_flush(&iev->ibuf);
66360996bedSreyk return;
66460996bedSreyk }
66560996bedSreyk
66660996bedSreyk iev->events = EV_READ;
66760996bedSreyk if (iev->ibuf.w.queued)
66860996bedSreyk iev->events |= EV_WRITE;
66960996bedSreyk
67060996bedSreyk event_del(&iev->ev);
67160996bedSreyk event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data);
67260996bedSreyk event_add(&iev->ev, NULL);
67360996bedSreyk }
67460996bedSreyk
67560996bedSreyk int
imsg_compose_event(struct imsgev * iev,uint16_t type,uint32_t peerid,pid_t pid,int fd,void * data,uint16_t datalen)67632c142bfSreyk imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
67732c142bfSreyk pid_t pid, int fd, void *data, uint16_t datalen)
67860996bedSreyk {
67960996bedSreyk int ret;
68060996bedSreyk
68160996bedSreyk if ((ret = imsg_compose(&iev->ibuf, type, peerid,
68260996bedSreyk pid, fd, data, datalen)) == -1)
68360996bedSreyk return (ret);
68460996bedSreyk imsg_event_add(iev);
68560996bedSreyk return (ret);
68660996bedSreyk }
68760996bedSreyk
68860996bedSreyk int
imsg_composev_event(struct imsgev * iev,uint16_t type,uint32_t peerid,pid_t pid,int fd,const struct iovec * iov,int iovcnt)68932c142bfSreyk imsg_composev_event(struct imsgev *iev, uint16_t type, uint32_t peerid,
69060996bedSreyk pid_t pid, int fd, const struct iovec *iov, int iovcnt)
69160996bedSreyk {
69260996bedSreyk int ret;
69360996bedSreyk
69460996bedSreyk if ((ret = imsg_composev(&iev->ibuf, type, peerid,
69560996bedSreyk pid, fd, iov, iovcnt)) == -1)
69660996bedSreyk return (ret);
69760996bedSreyk imsg_event_add(iev);
69860996bedSreyk return (ret);
69960996bedSreyk }
70060996bedSreyk
70121032f8aSreyk void
proc_range(struct privsep * ps,enum privsep_procid id,int * n,int * m)70221032f8aSreyk proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m)
70360996bedSreyk {
70421032f8aSreyk if (*n == -1) {
70521032f8aSreyk /* Use a range of all target instances */
70621032f8aSreyk *n = 0;
70721032f8aSreyk *m = ps->ps_instances[id];
70821032f8aSreyk } else {
70921032f8aSreyk /* Use only a single slot of the specified peer process */
71021032f8aSreyk *m = *n + 1;
71121032f8aSreyk }
71260996bedSreyk }
71360996bedSreyk
71460996bedSreyk int
proc_compose_imsg(struct privsep * ps,enum privsep_procid id,int n,uint16_t type,uint32_t peerid,int fd,void * data,uint16_t datalen)71521032f8aSreyk proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n,
7169ac7219fSreyk uint16_t type, uint32_t peerid, int fd, void *data, uint16_t datalen)
71721032f8aSreyk {
71821032f8aSreyk int m;
71921032f8aSreyk
72021032f8aSreyk proc_range(ps, id, &n, &m);
72121032f8aSreyk for (; n < m; n++) {
72221032f8aSreyk if (imsg_compose_event(&ps->ps_ievs[id][n],
7237be2c8aaSrzalamena type, peerid, ps->ps_instance + 1, fd, data, datalen) == -1)
72421032f8aSreyk return (-1);
72521032f8aSreyk }
72621032f8aSreyk
72721032f8aSreyk return (0);
72821032f8aSreyk }
72921032f8aSreyk
73021032f8aSreyk int
proc_compose(struct privsep * ps,enum privsep_procid id,uint16_t type,void * data,uint16_t datalen)7319ac7219fSreyk proc_compose(struct privsep *ps, enum privsep_procid id,
7329ac7219fSreyk uint16_t type, void *data, uint16_t datalen)
7339ac7219fSreyk {
7349ac7219fSreyk return (proc_compose_imsg(ps, id, -1, type, -1, -1, data, datalen));
7359ac7219fSreyk }
7369ac7219fSreyk
7379ac7219fSreyk int
proc_composev_imsg(struct privsep * ps,enum privsep_procid id,int n,uint16_t type,uint32_t peerid,int fd,const struct iovec * iov,int iovcnt)73821032f8aSreyk proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n,
7399ac7219fSreyk uint16_t type, uint32_t peerid, int fd, const struct iovec *iov, int iovcnt)
74060996bedSreyk {
74121032f8aSreyk int m;
74221032f8aSreyk
74321032f8aSreyk proc_range(ps, id, &n, &m);
74421032f8aSreyk for (; n < m; n++)
74521032f8aSreyk if (imsg_composev_event(&ps->ps_ievs[id][n],
7467be2c8aaSrzalamena type, peerid, ps->ps_instance + 1, fd, iov, iovcnt) == -1)
74721032f8aSreyk return (-1);
74821032f8aSreyk
74921032f8aSreyk return (0);
75060996bedSreyk }
75160996bedSreyk
75260996bedSreyk int
proc_composev(struct privsep * ps,enum privsep_procid id,uint16_t type,const struct iovec * iov,int iovcnt)7539ac7219fSreyk proc_composev(struct privsep *ps, enum privsep_procid id,
7549ac7219fSreyk uint16_t type, const struct iovec *iov, int iovcnt)
7559ac7219fSreyk {
7569ac7219fSreyk return (proc_composev_imsg(ps, id, -1, type, -1, -1, iov, iovcnt));
7579ac7219fSreyk }
7589ac7219fSreyk
75921032f8aSreyk struct imsgbuf *
proc_ibuf(struct privsep * ps,enum privsep_procid id,int n)76021032f8aSreyk proc_ibuf(struct privsep *ps, enum privsep_procid id, int n)
76121032f8aSreyk {
76221032f8aSreyk int m;
76321032f8aSreyk
76421032f8aSreyk proc_range(ps, id, &n, &m);
76521032f8aSreyk return (&ps->ps_ievs[id][n].ibuf);
76621032f8aSreyk }
76721032f8aSreyk
76821032f8aSreyk struct imsgev *
proc_iev(struct privsep * ps,enum privsep_procid id,int n)76921032f8aSreyk proc_iev(struct privsep *ps, enum privsep_procid id, int n)
77021032f8aSreyk {
77121032f8aSreyk int m;
77221032f8aSreyk
77321032f8aSreyk proc_range(ps, id, &n, &m);
77421032f8aSreyk return (&ps->ps_ievs[id][n]);
77521032f8aSreyk }
7767be2c8aaSrzalamena
7777be2c8aaSrzalamena /* This function should only be called with care as it breaks async I/O */
7787be2c8aaSrzalamena int
proc_flush_imsg(struct privsep * ps,enum privsep_procid id,int n)7797be2c8aaSrzalamena proc_flush_imsg(struct privsep *ps, enum privsep_procid id, int n)
7807be2c8aaSrzalamena {
7817be2c8aaSrzalamena struct imsgbuf *ibuf;
7827be2c8aaSrzalamena int m, ret = 0;
7837be2c8aaSrzalamena
7847be2c8aaSrzalamena proc_range(ps, id, &n, &m);
7857be2c8aaSrzalamena for (; n < m; n++) {
7867be2c8aaSrzalamena if ((ibuf = proc_ibuf(ps, id, n)) == NULL)
7877be2c8aaSrzalamena return (-1);
7887be2c8aaSrzalamena do {
7897be2c8aaSrzalamena ret = imsg_flush(ibuf);
7907be2c8aaSrzalamena } while (ret == -1 && errno == EAGAIN);
7917be2c8aaSrzalamena if (ret == -1)
7927be2c8aaSrzalamena break;
7937be2c8aaSrzalamena imsg_event_add(&ps->ps_ievs[id][n]);
7947be2c8aaSrzalamena }
7957be2c8aaSrzalamena
7967be2c8aaSrzalamena return (ret);
7977be2c8aaSrzalamena }
798