1*6676295fSclaudio /* $OpenBSD: proc.c,v 1.52 2024/11/21 13:38:45 claudio Exp $ */ 20325c666Sreyk 30325c666Sreyk /* 41b81e077Sreyk * Copyright (c) 2010 - 2016 Reyk Floeter <reyk@openbsd.org> 50325c666Sreyk * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org> 60325c666Sreyk * 70325c666Sreyk * Permission to use, copy, modify, and distribute this software for any 80325c666Sreyk * purpose with or without fee is hereby granted, provided that the above 90325c666Sreyk * copyright notice and this permission notice appear in all copies. 100325c666Sreyk * 110325c666Sreyk * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 120325c666Sreyk * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 130325c666Sreyk * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 140325c666Sreyk * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 150325c666Sreyk * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 160325c666Sreyk * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 170325c666Sreyk * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 180325c666Sreyk */ 190325c666Sreyk 200325c666Sreyk #include <sys/types.h> 210325c666Sreyk #include <sys/queue.h> 220325c666Sreyk #include <sys/socket.h> 230325c666Sreyk #include <sys/wait.h> 240325c666Sreyk 2568dc821bSrzalamena #include <fcntl.h> 260325c666Sreyk #include <stdio.h> 270325c666Sreyk #include <stdlib.h> 280325c666Sreyk #include <unistd.h> 290325c666Sreyk #include <string.h> 300325c666Sreyk #include <errno.h> 310325c666Sreyk #include <signal.h> 3238b34180Sbluhm #include <paths.h> 330325c666Sreyk #include <pwd.h> 340325c666Sreyk #include <event.h> 35f04ff968Sreyk #include <imsg.h> 360325c666Sreyk 370325c666Sreyk #include "relayd.h" 380325c666Sreyk 3938b34180Sbluhm void proc_exec(struct privsep *, struct privsep_proc *, unsigned int, int, 407a99b37dSbenno char **); 413d6ff6edSreyk void proc_setup(struct privsep *, struct privsep_proc *, unsigned int); 423d6ff6edSreyk void proc_open(struct privsep *, int, int); 433d6ff6edSreyk void proc_accept(struct privsep *, int, enum privsep_procid, 443d6ff6edSreyk unsigned int); 459326d639Sreyk void proc_close(struct privsep *); 460325c666Sreyk void proc_shutdown(struct privsep_proc *); 470325c666Sreyk void proc_sig_handler(int, short, void *); 480325c666Sreyk void proc_range(struct privsep *, enum privsep_procid, int *, int *); 4922a5535dSreyk int proc_dispatch_null(int, struct privsep_proc *, struct imsg *); 500325c666Sreyk 513d6ff6edSreyk enum privsep_procid 523d6ff6edSreyk proc_getid(struct privsep_proc *procs, unsigned int nproc, 533d6ff6edSreyk const char *proc_name) 540325c666Sreyk { 553d6ff6edSreyk struct privsep_proc *p; 563d6ff6edSreyk unsigned int proc; 573d6ff6edSreyk 583d6ff6edSreyk for (proc = 0; proc < nproc; proc++) { 593d6ff6edSreyk p = &procs[proc]; 603d6ff6edSreyk if (strcmp(p->p_title, proc_name)) 613d6ff6edSreyk continue; 623d6ff6edSreyk 633d6ff6edSreyk return (p->p_id); 643d6ff6edSreyk } 653d6ff6edSreyk 663d6ff6edSreyk return (PROC_MAX); 673d6ff6edSreyk } 683d6ff6edSreyk 693d6ff6edSreyk void 703d6ff6edSreyk proc_exec(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc, 717a99b37dSbenno int argc, char **argv) 723d6ff6edSreyk { 733d6ff6edSreyk unsigned int proc, nargc, i, proc_i; 743d6ff6edSreyk char **nargv; 753d6ff6edSreyk struct privsep_proc *p; 763d6ff6edSreyk char num[32]; 773d6ff6edSreyk int fd; 783d6ff6edSreyk 793d6ff6edSreyk /* Prepare the new process argv. */ 803d6ff6edSreyk nargv = calloc(argc + 5, sizeof(char *)); 813d6ff6edSreyk if (nargv == NULL) 823d6ff6edSreyk fatal("%s: calloc", __func__); 833d6ff6edSreyk 843d6ff6edSreyk /* Copy call argument first. */ 853d6ff6edSreyk nargc = 0; 863d6ff6edSreyk nargv[nargc++] = argv[0]; 873d6ff6edSreyk 883d6ff6edSreyk /* Set process name argument and save the position. */ 893d6ff6edSreyk nargv[nargc++] = "-P"; 903d6ff6edSreyk proc_i = nargc; 913d6ff6edSreyk nargc++; 923d6ff6edSreyk 933d6ff6edSreyk /* Point process instance arg to stack and copy the original args. */ 943d6ff6edSreyk nargv[nargc++] = "-I"; 953d6ff6edSreyk nargv[nargc++] = num; 963d6ff6edSreyk for (i = 1; i < (unsigned int) argc; i++) 973d6ff6edSreyk nargv[nargc++] = argv[i]; 983d6ff6edSreyk 993d6ff6edSreyk nargv[nargc] = NULL; 1003d6ff6edSreyk 1013d6ff6edSreyk for (proc = 0; proc < nproc; proc++) { 1023d6ff6edSreyk p = &procs[proc]; 1033d6ff6edSreyk 1043d6ff6edSreyk /* Update args with process title. */ 105d0cb169fSreyk nargv[proc_i] = (char *)(uintptr_t)p->p_title; 1063d6ff6edSreyk 1073d6ff6edSreyk /* Fire children processes. */ 1083d6ff6edSreyk for (i = 0; i < ps->ps_instances[p->p_id]; i++) { 1093d6ff6edSreyk /* Update the process instance number. */ 1103d6ff6edSreyk snprintf(num, sizeof(num), "%u", i); 1113d6ff6edSreyk 1123d6ff6edSreyk fd = ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0]; 1133d6ff6edSreyk ps->ps_pipes[p->p_id][i].pp_pipes[PROC_PARENT][0] = -1; 1143d6ff6edSreyk 1153d6ff6edSreyk switch (fork()) { 1163d6ff6edSreyk case -1: 1173d6ff6edSreyk fatal("%s: fork", __func__); 1183d6ff6edSreyk break; 1193d6ff6edSreyk case 0: 1203d6ff6edSreyk /* Prepare parent socket. */ 12168dc821bSrzalamena if (fd != PROC_PARENT_SOCK_FILENO) { 12268dc821bSrzalamena if (dup2(fd, PROC_PARENT_SOCK_FILENO) 12368dc821bSrzalamena == -1) 12468dc821bSrzalamena fatal("dup2"); 12568dc821bSrzalamena } else if (fcntl(fd, F_SETFD, 0) == -1) 12668dc821bSrzalamena fatal("fcntl"); 1273d6ff6edSreyk 1283d6ff6edSreyk execvp(argv[0], nargv); 1293d6ff6edSreyk fatal("%s: execvp", __func__); 1303d6ff6edSreyk break; 1313d6ff6edSreyk default: 1323d6ff6edSreyk /* Close child end. */ 1333d6ff6edSreyk close(fd); 1343d6ff6edSreyk break; 1353d6ff6edSreyk } 1363d6ff6edSreyk } 1373d6ff6edSreyk } 1383d6ff6edSreyk free(nargv); 1393d6ff6edSreyk } 1403d6ff6edSreyk 1413d6ff6edSreyk void 1423d6ff6edSreyk proc_connect(struct privsep *ps) 1433d6ff6edSreyk { 1443d6ff6edSreyk struct imsgev *iev; 14551cb1125Sreyk unsigned int src, dst, inst; 1463d6ff6edSreyk 14751cb1125Sreyk /* Don't distribute any sockets if we are not really going to run. */ 14851cb1125Sreyk if (ps->ps_noaction) 14951cb1125Sreyk return; 1503d6ff6edSreyk 15151cb1125Sreyk for (dst = 0; dst < PROC_MAX; dst++) { 15251cb1125Sreyk /* We don't communicate with ourselves. */ 15351cb1125Sreyk if (dst == PROC_PARENT) 1543d6ff6edSreyk continue; 1553d6ff6edSreyk 15651cb1125Sreyk for (inst = 0; inst < ps->ps_instances[dst]; inst++) { 15751cb1125Sreyk 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); 1623d6ff6edSreyk event_set(&iev->ev, iev->ibuf.fd, iev->events, 1633d6ff6edSreyk iev->handler, iev->data); 1643d6ff6edSreyk event_add(&iev->ev, NULL); 1653d6ff6edSreyk } 1663d6ff6edSreyk } 1673d6ff6edSreyk 16851cb1125Sreyk /* Distribute the socketpair()s for everyone. */ 16951cb1125Sreyk for (src = 0; src < PROC_MAX; src++) 17051cb1125Sreyk for (dst = src; dst < PROC_MAX; dst++) { 17151cb1125Sreyk /* Parent already distributed its fds. */ 17251cb1125Sreyk if (src == PROC_PARENT || dst == PROC_PARENT) 1733d6ff6edSreyk continue; 1743d6ff6edSreyk 17551cb1125Sreyk proc_open(ps, src, dst); 1763d6ff6edSreyk } 1773d6ff6edSreyk } 1783d6ff6edSreyk 1793d6ff6edSreyk void 1803d6ff6edSreyk proc_init(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc, 18138b34180Sbluhm int debug, int argc, char **argv, enum privsep_procid proc_id) 1823d6ff6edSreyk { 1833d6ff6edSreyk struct privsep_proc *p = NULL; 18451cb1125Sreyk struct privsep_pipes *pa, *pb; 1853d6ff6edSreyk unsigned int proc; 18651cb1125Sreyk unsigned int dst; 18751cb1125Sreyk int fds[2]; 18851cb1125Sreyk 18951cb1125Sreyk /* Don't initiate anything if we are not really going to run. */ 19051cb1125Sreyk if (ps->ps_noaction) 19151cb1125Sreyk return; 1923d6ff6edSreyk 1933d6ff6edSreyk if (proc_id == PROC_PARENT) { 1943d6ff6edSreyk privsep_process = PROC_PARENT; 1953d6ff6edSreyk proc_setup(ps, procs, nproc); 1963d6ff6edSreyk 1977a99b37dSbenno if (!debug && daemon(1, 0) == -1) 1987a99b37dSbenno fatal("failed to daemonize"); 1997a99b37dSbenno 20051cb1125Sreyk /* 20151cb1125Sreyk * Create the children sockets so we can use them 20251cb1125Sreyk * to distribute the rest of the socketpair()s using 20351cb1125Sreyk * proc_connect() later. 20451cb1125Sreyk */ 20551cb1125Sreyk for (dst = 0; dst < PROC_MAX; dst++) { 20651cb1125Sreyk /* Don't create socket for ourselves. */ 20751cb1125Sreyk if (dst == PROC_PARENT) 20851cb1125Sreyk continue; 20951cb1125Sreyk 21051cb1125Sreyk for (proc = 0; proc < ps->ps_instances[dst]; proc++) { 21151cb1125Sreyk pa = &ps->ps_pipes[PROC_PARENT][0]; 21251cb1125Sreyk pb = &ps->ps_pipes[dst][proc]; 21351cb1125Sreyk if (socketpair(AF_UNIX, 21451cb1125Sreyk SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 21551cb1125Sreyk PF_UNSPEC, fds) == -1) 21651cb1125Sreyk fatal("%s: socketpair", __func__); 21751cb1125Sreyk 21851cb1125Sreyk pa->pp_pipes[dst][proc] = fds[0]; 21951cb1125Sreyk pb->pp_pipes[PROC_PARENT][0] = fds[1]; 22051cb1125Sreyk } 22151cb1125Sreyk } 2223d6ff6edSreyk 2233d6ff6edSreyk /* Engage! */ 2247a99b37dSbenno proc_exec(ps, procs, nproc, argc, argv); 2253d6ff6edSreyk return; 2263d6ff6edSreyk } 2273d6ff6edSreyk 2283d6ff6edSreyk /* Initialize a child */ 2293d6ff6edSreyk for (proc = 0; proc < nproc; proc++) { 2303d6ff6edSreyk if (procs[proc].p_id != proc_id) 2313d6ff6edSreyk continue; 2323d6ff6edSreyk p = &procs[proc]; 2333d6ff6edSreyk break; 2343d6ff6edSreyk } 2353d6ff6edSreyk if (p == NULL || p->p_init == NULL) 2363d6ff6edSreyk fatalx("%s: process %d missing process initialization", 2373d6ff6edSreyk __func__, proc_id); 2383d6ff6edSreyk 2393d6ff6edSreyk p->p_init(ps, p); 2403d6ff6edSreyk 2413d6ff6edSreyk fatalx("failed to initiate child process"); 2423d6ff6edSreyk } 2433d6ff6edSreyk 2443d6ff6edSreyk void 2453d6ff6edSreyk proc_accept(struct privsep *ps, int fd, enum privsep_procid dst, 2463d6ff6edSreyk unsigned int n) 2473d6ff6edSreyk { 2483d6ff6edSreyk struct privsep_pipes *pp = ps->ps_pp; 2493d6ff6edSreyk struct imsgev *iev; 2503d6ff6edSreyk 2513d6ff6edSreyk if (ps->ps_ievs[dst] == NULL) { 2523d6ff6edSreyk #if DEBUG > 1 2533d6ff6edSreyk log_debug("%s: %s src %d %d to dst %d %d not connected", 2543d6ff6edSreyk __func__, ps->ps_title[privsep_process], 2553d6ff6edSreyk privsep_process, ps->ps_instance + 1, 2563d6ff6edSreyk dst, n + 1); 2573d6ff6edSreyk #endif 2583d6ff6edSreyk close(fd); 2593d6ff6edSreyk return; 2603d6ff6edSreyk } 2613d6ff6edSreyk 2623d6ff6edSreyk if (pp->pp_pipes[dst][n] != -1) { 2633d6ff6edSreyk log_warnx("%s: duplicated descriptor", __func__); 2643d6ff6edSreyk close(fd); 2653d6ff6edSreyk return; 2663d6ff6edSreyk } else 2673d6ff6edSreyk pp->pp_pipes[dst][n] = fd; 2683d6ff6edSreyk 2693d6ff6edSreyk 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); 2733d6ff6edSreyk event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data); 2743d6ff6edSreyk event_add(&iev->ev, NULL); 2753d6ff6edSreyk } 2763d6ff6edSreyk 2773d6ff6edSreyk void 2783d6ff6edSreyk proc_setup(struct privsep *ps, struct privsep_proc *procs, unsigned int nproc) 2793d6ff6edSreyk { 2803d6ff6edSreyk unsigned int i, j, src, dst, id; 2813d6ff6edSreyk struct privsep_pipes *pp; 2823d6ff6edSreyk 2833d6ff6edSreyk /* Initialize parent title, ps_instances and procs. */ 2843d6ff6edSreyk ps->ps_title[PROC_PARENT] = "parent"; 2850325c666Sreyk 28695aacd6fSreyk for (src = 0; src < PROC_MAX; src++) 287c28c61ccSreyk /* Default to 1 process instance */ 288c28c61ccSreyk if (ps->ps_instances[src] < 1) 289c28c61ccSreyk ps->ps_instances[src] = 1; 290c28c61ccSreyk 2913d6ff6edSreyk for (src = 0; src < nproc; src++) { 2923d6ff6edSreyk procs[src].p_ps = ps; 2933d6ff6edSreyk if (procs[src].p_cb == NULL) 2943d6ff6edSreyk procs[src].p_cb = proc_dispatch_null; 2953d6ff6edSreyk 2963d6ff6edSreyk id = procs[src].p_id; 2973d6ff6edSreyk ps->ps_title[id] = procs[src].p_title; 2983d6ff6edSreyk if ((ps->ps_ievs[id] = calloc(ps->ps_instances[id], 2993d6ff6edSreyk sizeof(struct imsgev))) == NULL) 30051cb1125Sreyk fatal("%s: calloc", __func__); 3013d6ff6edSreyk 302dd7efffeSclaudio /* With this set up, we are ready to call imsgbuf_init(). */ 3033d6ff6edSreyk for (i = 0; i < ps->ps_instances[id]; i++) { 3043d6ff6edSreyk ps->ps_ievs[id][i].handler = proc_dispatch; 3053d6ff6edSreyk ps->ps_ievs[id][i].events = EV_READ; 3063d6ff6edSreyk ps->ps_ievs[id][i].proc = &procs[src]; 3073d6ff6edSreyk ps->ps_ievs[id][i].data = &ps->ps_ievs[id][i]; 3083d6ff6edSreyk } 3093d6ff6edSreyk } 3103d6ff6edSreyk 3110325c666Sreyk /* 3129326d639Sreyk * Allocate pipes for all process instances (incl. parent) 3139326d639Sreyk * 3149326d639Sreyk * - ps->ps_pipes: N:M mapping 3159326d639Sreyk * N source processes connected to M destination processes: 3169326d639Sreyk * [src][instances][dst][instances], for example 3179326d639Sreyk * [PROC_RELAY][3][PROC_CA][3] 3189326d639Sreyk * 3199326d639Sreyk * - ps->ps_pp: per-process 1:M part of ps->ps_pipes 3209326d639Sreyk * Each process instance has a destination array of socketpair fds: 3219326d639Sreyk * [dst][instances], for example 3229326d639Sreyk * [PROC_PARENT][0] 3239326d639Sreyk */ 3249326d639Sreyk for (src = 0; src < PROC_MAX; src++) { 3259326d639Sreyk /* Allocate destination array for each process */ 326c28c61ccSreyk if ((ps->ps_pipes[src] = calloc(ps->ps_instances[src], 3279326d639Sreyk sizeof(struct privsep_pipes))) == NULL) 3283d6ff6edSreyk fatal("%s: calloc", __func__); 3299326d639Sreyk 330c28c61ccSreyk for (i = 0; i < ps->ps_instances[src]; i++) { 3319326d639Sreyk pp = &ps->ps_pipes[src][i]; 3329326d639Sreyk 3339326d639Sreyk for (dst = 0; dst < PROC_MAX; dst++) { 3349326d639Sreyk /* Allocate maximum fd integers */ 3359326d639Sreyk if ((pp->pp_pipes[dst] = 336c28c61ccSreyk calloc(ps->ps_instances[dst], 3379326d639Sreyk sizeof(int))) == NULL) 3383d6ff6edSreyk fatal("%s: calloc", __func__); 3399326d639Sreyk 3409326d639Sreyk /* Mark fd as unused */ 341c28c61ccSreyk for (j = 0; j < ps->ps_instances[dst]; j++) 3429326d639Sreyk pp->pp_pipes[dst][j] = -1; 3439326d639Sreyk } 3449326d639Sreyk } 3459326d639Sreyk } 3469326d639Sreyk 3473d6ff6edSreyk ps->ps_pp = &ps->ps_pipes[privsep_process][ps->ps_instance]; 3480325c666Sreyk } 3490325c666Sreyk 3500325c666Sreyk void 3510325c666Sreyk proc_kill(struct privsep *ps) 3520325c666Sreyk { 353f910ac11Sreyk char *cause; 3540325c666Sreyk pid_t pid; 355f910ac11Sreyk int len, status; 3560325c666Sreyk 3570325c666Sreyk if (privsep_process != PROC_PARENT) 3580325c666Sreyk return; 3590325c666Sreyk 360f910ac11Sreyk proc_close(ps); 3610325c666Sreyk 3620325c666Sreyk do { 363f910ac11Sreyk pid = waitpid(WAIT_ANY, &status, 0); 364f910ac11Sreyk if (pid <= 0) 365f910ac11Sreyk continue; 366a2195becSreyk 367f910ac11Sreyk if (WIFSIGNALED(status)) { 368f910ac11Sreyk len = asprintf(&cause, "terminated; signal %d", 369f910ac11Sreyk WTERMSIG(status)); 370f910ac11Sreyk } else if (WIFEXITED(status)) { 371f910ac11Sreyk if (WEXITSTATUS(status) != 0) 372f910ac11Sreyk len = asprintf(&cause, "exited abnormally"); 373f910ac11Sreyk else 374f910ac11Sreyk len = 0; 375f910ac11Sreyk } else 376f910ac11Sreyk len = -1; 377f910ac11Sreyk 378f910ac11Sreyk if (len == 0) { 379f910ac11Sreyk /* child exited OK, don't print a warning message */ 380f910ac11Sreyk } else if (len != -1) { 381f910ac11Sreyk log_warnx("lost child: pid %u %s", pid, cause); 382f910ac11Sreyk free(cause); 383f910ac11Sreyk } else 384f910ac11Sreyk log_warnx("lost child: pid %u", pid); 385f910ac11Sreyk } while (pid != -1 || (pid == -1 && errno == EINTR)); 3860325c666Sreyk } 3870325c666Sreyk 3880325c666Sreyk void 3893d6ff6edSreyk proc_open(struct privsep *ps, int src, int dst) 3900325c666Sreyk { 3919326d639Sreyk struct privsep_pipes *pa, *pb; 39251cb1125Sreyk struct privsep_fd pf; 3939326d639Sreyk int fds[2]; 3943d6ff6edSreyk unsigned int i, j; 3959326d639Sreyk 39651cb1125Sreyk /* Exchange pipes between process. */ 3979326d639Sreyk for (i = 0; i < ps->ps_instances[src]; i++) { 3983d6ff6edSreyk for (j = 0; j < ps->ps_instances[dst]; j++) { 3993d6ff6edSreyk /* Don't create sockets for ourself. */ 4003d6ff6edSreyk if (src == dst && i == j) 4013d6ff6edSreyk continue; 4029326d639Sreyk 403b27c3a9cSdv /* No need for CA to CA or RELAY to RELAY sockets. */ 404b27c3a9cSdv if ((src == PROC_CA && dst == PROC_CA) || 405b27c3a9cSdv (src == PROC_RELAY && dst == PROC_RELAY)) 406b27c3a9cSdv continue; 407b27c3a9cSdv 4083d6ff6edSreyk pa = &ps->ps_pipes[src][i]; 4093d6ff6edSreyk pb = &ps->ps_pipes[dst][j]; 410b045ffeeSreyk if (socketpair(AF_UNIX, 4113d6ff6edSreyk SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 4129326d639Sreyk PF_UNSPEC, fds) == -1) 41351cb1125Sreyk fatal("%s: socketpair", __func__); 4149326d639Sreyk 4153d6ff6edSreyk pa->pp_pipes[dst][j] = fds[0]; 4169326d639Sreyk pb->pp_pipes[src][i] = fds[1]; 41751cb1125Sreyk 41851cb1125Sreyk pf.pf_procid = src; 41951cb1125Sreyk pf.pf_instance = i; 42051cb1125Sreyk if (proc_compose_imsg(ps, dst, j, IMSG_CTL_PROCFD, 42151cb1125Sreyk -1, pb->pp_pipes[src][i], &pf, sizeof(pf)) == -1) 42251cb1125Sreyk fatal("%s: proc_compose_imsg", __func__); 42351cb1125Sreyk 42451cb1125Sreyk pf.pf_procid = dst; 42551cb1125Sreyk pf.pf_instance = j; 42651cb1125Sreyk if (proc_compose_imsg(ps, src, i, IMSG_CTL_PROCFD, 42751cb1125Sreyk -1, pa->pp_pipes[dst][j], &pf, sizeof(pf)) == -1) 42851cb1125Sreyk fatal("%s: proc_compose_imsg", __func__); 42951cb1125Sreyk 43051cb1125Sreyk /* 43151cb1125Sreyk * We have to flush to send the descriptors and close 43251cb1125Sreyk * them to avoid the fd ramp on startup. 43351cb1125Sreyk */ 43451cb1125Sreyk if (proc_flush_imsg(ps, src, i) == -1 || 43551cb1125Sreyk proc_flush_imsg(ps, dst, j) == -1) 436dd7efffeSclaudio fatal("%s: proc_flush_imsg", __func__); 4379326d639Sreyk } 4380325c666Sreyk } 4390325c666Sreyk } 4400325c666Sreyk 4410325c666Sreyk void 4429326d639Sreyk proc_close(struct privsep *ps) 443a2195becSreyk { 444d7717d63Sreyk unsigned int dst, n; 4459326d639Sreyk struct privsep_pipes *pp; 446a2195becSreyk 447a2195becSreyk if (ps == NULL) 448a2195becSreyk return; 449a2195becSreyk 4509326d639Sreyk pp = ps->ps_pp; 4519326d639Sreyk 452a2195becSreyk for (dst = 0; dst < PROC_MAX; dst++) { 4539326d639Sreyk if (ps->ps_ievs[dst] == NULL) 454a2195becSreyk continue; 455a2195becSreyk 4569326d639Sreyk for (n = 0; n < ps->ps_instances[dst]; n++) { 4579326d639Sreyk if (pp->pp_pipes[dst][n] == -1) 458a2195becSreyk continue; 4599326d639Sreyk 4609326d639Sreyk /* Cancel the fd, close and invalidate the fd */ 461a2195becSreyk event_del(&(ps->ps_ievs[dst][n].ev)); 462dd7efffeSclaudio imsgbuf_clear(&(ps->ps_ievs[dst][n].ibuf)); 4639326d639Sreyk close(pp->pp_pipes[dst][n]); 4649326d639Sreyk pp->pp_pipes[dst][n] = -1; 465a2195becSreyk } 466a2195becSreyk free(ps->ps_ievs[dst]); 467a2195becSreyk } 468a2195becSreyk } 469a2195becSreyk 470a2195becSreyk void 4710325c666Sreyk proc_shutdown(struct privsep_proc *p) 4720325c666Sreyk { 4730325c666Sreyk struct privsep *ps = p->p_ps; 4740325c666Sreyk 4750325c666Sreyk if (p->p_id == PROC_CONTROL && ps) 4760325c666Sreyk control_cleanup(&ps->ps_csock); 4770325c666Sreyk 4780325c666Sreyk if (p->p_shutdown != NULL) 4790325c666Sreyk (*p->p_shutdown)(); 4800325c666Sreyk 4819326d639Sreyk proc_close(ps); 482a2195becSreyk 4830325c666Sreyk log_info("%s exiting, pid %d", p->p_title, getpid()); 4840325c666Sreyk 4853d6ff6edSreyk exit(0); 4860325c666Sreyk } 4870325c666Sreyk 4880325c666Sreyk void 4890325c666Sreyk proc_sig_handler(int sig, short event, void *arg) 4900325c666Sreyk { 4910325c666Sreyk struct privsep_proc *p = arg; 4920325c666Sreyk 4930325c666Sreyk switch (sig) { 4940325c666Sreyk case SIGINT: 4950325c666Sreyk case SIGTERM: 4960325c666Sreyk proc_shutdown(p); 4970325c666Sreyk break; 4980325c666Sreyk case SIGCHLD: 4990325c666Sreyk case SIGHUP: 5000325c666Sreyk case SIGPIPE: 501fb6ac98aSreyk case SIGUSR1: 5020325c666Sreyk /* ignore */ 5030325c666Sreyk break; 5040325c666Sreyk default: 505efc39811Sbenno fatalx("%s: unexpected signal", __func__); 5060325c666Sreyk /* NOTREACHED */ 5070325c666Sreyk } 5080325c666Sreyk } 5090325c666Sreyk 510f910ac11Sreyk void 5110325c666Sreyk proc_run(struct privsep *ps, struct privsep_proc *p, 512d7717d63Sreyk struct privsep_proc *procs, unsigned int nproc, 51322a5535dSreyk void (*run)(struct privsep *, struct privsep_proc *, void *), void *arg) 5140325c666Sreyk { 5151b81e077Sreyk struct passwd *pw; 5160325c666Sreyk const char *root; 51761da45abSblambert struct control_sock *rcs; 5180325c666Sreyk 5190f12961aSreyk log_procinit(p->p_title); 5200f12961aSreyk 5219326d639Sreyk if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) { 5220325c666Sreyk if (control_init(ps, &ps->ps_csock) == -1) 52351cb1125Sreyk fatalx("%s: control_init", __func__); 52461da45abSblambert TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry) 52561da45abSblambert if (control_init(ps, rcs) == -1) 52651cb1125Sreyk fatalx("%s: control_init", __func__); 5270325c666Sreyk } 5280325c666Sreyk 5291b81e077Sreyk /* Use non-standard user */ 5301b81e077Sreyk if (p->p_pw != NULL) 5311b81e077Sreyk pw = p->p_pw; 5321b81e077Sreyk else 5331b81e077Sreyk pw = ps->ps_pw; 5341b81e077Sreyk 5350325c666Sreyk /* Change root directory */ 5360325c666Sreyk if (p->p_chroot != NULL) 5370325c666Sreyk root = p->p_chroot; 5380325c666Sreyk else 5390325c666Sreyk root = pw->pw_dir; 5400325c666Sreyk 5410325c666Sreyk if (chroot(root) == -1) 542efc39811Sbenno fatal("%s: chroot", __func__); 5430325c666Sreyk if (chdir("/") == -1) 544efc39811Sbenno fatal("%s: chdir(\"/\")", __func__); 5450325c666Sreyk 5460325c666Sreyk privsep_process = p->p_id; 5470325c666Sreyk 5480325c666Sreyk setproctitle("%s", p->p_title); 5490325c666Sreyk 5500325c666Sreyk if (setgroups(1, &pw->pw_gid) || 5510325c666Sreyk setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || 5520325c666Sreyk setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) 553efc39811Sbenno fatal("%s: cannot drop privileges", __func__); 5540325c666Sreyk 5550325c666Sreyk event_init(); 5560325c666Sreyk 5570325c666Sreyk signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p); 5580325c666Sreyk signal_set(&ps->ps_evsigterm, SIGTERM, proc_sig_handler, p); 5590325c666Sreyk signal_set(&ps->ps_evsigchld, SIGCHLD, proc_sig_handler, p); 5600325c666Sreyk signal_set(&ps->ps_evsighup, SIGHUP, proc_sig_handler, p); 5610325c666Sreyk signal_set(&ps->ps_evsigpipe, SIGPIPE, proc_sig_handler, p); 562fb6ac98aSreyk signal_set(&ps->ps_evsigusr1, SIGUSR1, proc_sig_handler, p); 5630325c666Sreyk 5640325c666Sreyk signal_add(&ps->ps_evsigint, NULL); 5650325c666Sreyk signal_add(&ps->ps_evsigterm, NULL); 5660325c666Sreyk signal_add(&ps->ps_evsigchld, NULL); 5670325c666Sreyk signal_add(&ps->ps_evsighup, NULL); 5680325c666Sreyk signal_add(&ps->ps_evsigpipe, NULL); 569fb6ac98aSreyk signal_add(&ps->ps_evsigusr1, NULL); 5700325c666Sreyk 5713d6ff6edSreyk proc_setup(ps, procs, nproc); 5728e01c6e3Sreyk proc_accept(ps, PROC_PARENT_SOCK_FILENO, PROC_PARENT, 0); 5730325c666Sreyk if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) { 5740325c666Sreyk if (control_listen(&ps->ps_csock) == -1) 57551cb1125Sreyk fatalx("%s: control_listen", __func__); 57661da45abSblambert TAILQ_FOREACH(rcs, &ps->ps_rcsocks, cs_entry) 57761da45abSblambert if (control_listen(rcs) == -1) 57851cb1125Sreyk fatalx("%s: control_listen", __func__); 5790325c666Sreyk } 5800325c666Sreyk 5813d6ff6edSreyk DPRINTF("%s: %s %d/%d, pid %d", __func__, p->p_title, 5823d6ff6edSreyk ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid()); 5833d6ff6edSreyk 58422a5535dSreyk if (run != NULL) 58522a5535dSreyk run(ps, p, arg); 5860325c666Sreyk 5870325c666Sreyk event_dispatch(); 5880325c666Sreyk 5890325c666Sreyk proc_shutdown(p); 5900325c666Sreyk } 5910325c666Sreyk 5920325c666Sreyk void 5930325c666Sreyk proc_dispatch(int fd, short event, void *arg) 5940325c666Sreyk { 59548240b8fSbluhm struct imsgev *iev = arg; 5960325c666Sreyk struct privsep_proc *p = iev->proc; 5970325c666Sreyk struct privsep *ps = p->p_ps; 5980325c666Sreyk struct imsgbuf *ibuf; 5990325c666Sreyk struct imsg imsg; 6000325c666Sreyk ssize_t n; 6010325c666Sreyk int verbose; 6020325c666Sreyk const char *title; 6033d6ff6edSreyk struct privsep_fd pf; 6040325c666Sreyk 6050325c666Sreyk title = ps->ps_title[privsep_process]; 6060325c666Sreyk ibuf = &iev->ibuf; 6070325c666Sreyk 6080325c666Sreyk if (event & EV_READ) { 609668e5ba9Sclaudio if ((n = imsgbuf_read(ibuf)) == -1) 610dd7efffeSclaudio fatal("%s: imsgbuf_read", __func__); 6110325c666Sreyk if (n == 0) { 6120325c666Sreyk /* this pipe is dead, so remove the event handler */ 6130325c666Sreyk event_del(&iev->ev); 6140325c666Sreyk event_loopexit(NULL); 6150325c666Sreyk return; 6160325c666Sreyk } 6170325c666Sreyk } 6180325c666Sreyk 6190325c666Sreyk if (event & EV_WRITE) { 620dd7efffeSclaudio if (imsgbuf_write(ibuf) == -1) { 621c1aa9554Sclaudio if (errno == EPIPE) { 622c1aa9554Sclaudio /* this pipe is dead, remove the handler */ 62351cb1125Sreyk event_del(&iev->ev); 62451cb1125Sreyk event_loopexit(NULL); 62551cb1125Sreyk return; 62651cb1125Sreyk } 627dd7efffeSclaudio fatal("%s: imsgbuf_write", __func__); 628c1aa9554Sclaudio } 6290325c666Sreyk } 6300325c666Sreyk 6310325c666Sreyk for (;;) { 6320325c666Sreyk if ((n = imsg_get(ibuf, &imsg)) == -1) 63351cb1125Sreyk fatal("%s: imsg_get", __func__); 6340325c666Sreyk if (n == 0) 6350325c666Sreyk break; 6360325c666Sreyk 637a2195becSreyk #if DEBUG > 1 63866094308Sreyk log_debug("%s: %s %d got imsg %d peerid %d from %s %d", 6390325c666Sreyk __func__, title, ps->ps_instance + 1, 640c28c61ccSreyk imsg.hdr.type, imsg.hdr.peerid, p->p_title, imsg.hdr.pid); 6410325c666Sreyk #endif 6420325c666Sreyk 6430325c666Sreyk /* 6440325c666Sreyk * Check the message with the program callback 6450325c666Sreyk */ 6460325c666Sreyk if ((p->p_cb)(fd, p, &imsg) == 0) { 6470325c666Sreyk /* Message was handled by the callback, continue */ 6480325c666Sreyk imsg_free(&imsg); 6490325c666Sreyk continue; 6500325c666Sreyk } 6510325c666Sreyk 6520325c666Sreyk /* 6530325c666Sreyk * Generic message handling 6540325c666Sreyk */ 6550325c666Sreyk switch (imsg.hdr.type) { 6560325c666Sreyk case IMSG_CTL_VERBOSE: 6570325c666Sreyk IMSG_SIZE_CHECK(&imsg, &verbose); 6580325c666Sreyk memcpy(&verbose, imsg.data, sizeof(verbose)); 659871fc12cSreyk log_setverbose(verbose); 6600325c666Sreyk break; 6613d6ff6edSreyk case IMSG_CTL_PROCFD: 6623d6ff6edSreyk IMSG_SIZE_CHECK(&imsg, &pf); 6633d6ff6edSreyk memcpy(&pf, imsg.data, sizeof(pf)); 664a1416996Sclaudio proc_accept(ps, imsg_get_fd(&imsg), pf.pf_procid, 6653d6ff6edSreyk pf.pf_instance); 6663d6ff6edSreyk break; 6670325c666Sreyk default: 66851cb1125Sreyk fatalx("%s: %s %d got invalid imsg %d peerid %d " 66966094308Sreyk "from %s %d", 6700325c666Sreyk __func__, title, ps->ps_instance + 1, 67166094308Sreyk imsg.hdr.type, imsg.hdr.peerid, 672c28c61ccSreyk p->p_title, imsg.hdr.pid); 6730325c666Sreyk } 6740325c666Sreyk imsg_free(&imsg); 6750325c666Sreyk } 6760325c666Sreyk imsg_event_add(iev); 6770325c666Sreyk } 6780325c666Sreyk 67922a5535dSreyk int 68022a5535dSreyk proc_dispatch_null(int fd, struct privsep_proc *p, struct imsg *imsg) 68122a5535dSreyk { 68222a5535dSreyk return (-1); 68322a5535dSreyk } 68422a5535dSreyk 6850325c666Sreyk /* 6860325c666Sreyk * imsg helper functions 6870325c666Sreyk */ 6880325c666Sreyk 6890325c666Sreyk void 6900325c666Sreyk imsg_event_add(struct imsgev *iev) 6910325c666Sreyk { 6920325c666Sreyk if (iev->handler == NULL) { 693dd7efffeSclaudio imsgbuf_flush(&iev->ibuf); 6940325c666Sreyk return; 6950325c666Sreyk } 6960325c666Sreyk 6970325c666Sreyk iev->events = EV_READ; 69831be28caSclaudio if (imsgbuf_queuelen(&iev->ibuf) > 0) 6990325c666Sreyk iev->events |= EV_WRITE; 7000325c666Sreyk 7010325c666Sreyk event_del(&iev->ev); 7020325c666Sreyk event_set(&iev->ev, iev->ibuf.fd, iev->events, iev->handler, iev->data); 7030325c666Sreyk event_add(&iev->ev, NULL); 7040325c666Sreyk } 7050325c666Sreyk 7060325c666Sreyk int 707d7717d63Sreyk imsg_compose_event(struct imsgev *iev, uint16_t type, uint32_t peerid, 708d7717d63Sreyk pid_t pid, int fd, void *data, uint16_t datalen) 7090325c666Sreyk { 7100325c666Sreyk int ret; 7110325c666Sreyk 7120325c666Sreyk if ((ret = imsg_compose(&iev->ibuf, type, peerid, 7130325c666Sreyk pid, fd, data, datalen)) == -1) 7140325c666Sreyk return (ret); 7150325c666Sreyk imsg_event_add(iev); 7160325c666Sreyk return (ret); 7170325c666Sreyk } 7180325c666Sreyk 7190325c666Sreyk int 720d7717d63Sreyk imsg_composev_event(struct imsgev *iev, uint16_t type, uint32_t peerid, 7210325c666Sreyk pid_t pid, int fd, const struct iovec *iov, int iovcnt) 7220325c666Sreyk { 7230325c666Sreyk int ret; 7240325c666Sreyk 7250325c666Sreyk if ((ret = imsg_composev(&iev->ibuf, type, peerid, 7260325c666Sreyk pid, fd, iov, iovcnt)) == -1) 7270325c666Sreyk return (ret); 7280325c666Sreyk imsg_event_add(iev); 7290325c666Sreyk return (ret); 7300325c666Sreyk } 7310325c666Sreyk 7320325c666Sreyk void 7330325c666Sreyk proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m) 7340325c666Sreyk { 7350325c666Sreyk if (*n == -1) { 7369326d639Sreyk /* Use a range of all target instances */ 7370325c666Sreyk *n = 0; 7380325c666Sreyk *m = ps->ps_instances[id]; 7390325c666Sreyk } else { 7400325c666Sreyk /* Use only a single slot of the specified peer process */ 7410325c666Sreyk *m = *n + 1; 7420325c666Sreyk } 7430325c666Sreyk } 7440325c666Sreyk 7450325c666Sreyk int 7460325c666Sreyk proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n, 747c2c37c5dSreyk uint16_t type, uint32_t peerid, int fd, void *data, uint16_t datalen) 7480325c666Sreyk { 7490325c666Sreyk int m; 7500325c666Sreyk 7510325c666Sreyk proc_range(ps, id, &n, &m); 7529326d639Sreyk for (; n < m; n++) { 7530325c666Sreyk if (imsg_compose_event(&ps->ps_ievs[id][n], 754c28c61ccSreyk type, peerid, ps->ps_instance + 1, fd, data, datalen) == -1) 7550325c666Sreyk return (-1); 7569326d639Sreyk } 757a2195becSreyk 7580325c666Sreyk return (0); 7590325c666Sreyk } 7600325c666Sreyk 7610325c666Sreyk int 762c2c37c5dSreyk proc_compose(struct privsep *ps, enum privsep_procid id, 763c2c37c5dSreyk uint16_t type, void *data, uint16_t datalen) 764c2c37c5dSreyk { 765c2c37c5dSreyk return (proc_compose_imsg(ps, id, -1, type, -1, -1, data, datalen)); 766c2c37c5dSreyk } 767c2c37c5dSreyk 768c2c37c5dSreyk int 7690325c666Sreyk proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n, 770c2c37c5dSreyk uint16_t type, uint32_t peerid, int fd, const struct iovec *iov, int iovcnt) 7710325c666Sreyk { 7720325c666Sreyk int m; 7730325c666Sreyk 7740325c666Sreyk proc_range(ps, id, &n, &m); 7750325c666Sreyk for (; n < m; n++) 7760325c666Sreyk if (imsg_composev_event(&ps->ps_ievs[id][n], 777c28c61ccSreyk type, peerid, ps->ps_instance + 1, fd, iov, iovcnt) == -1) 7780325c666Sreyk return (-1); 7790325c666Sreyk 7800325c666Sreyk return (0); 7810325c666Sreyk } 7820325c666Sreyk 7830325c666Sreyk int 784c2c37c5dSreyk proc_composev(struct privsep *ps, enum privsep_procid id, 785c2c37c5dSreyk uint16_t type, const struct iovec *iov, int iovcnt) 786c2c37c5dSreyk { 787c2c37c5dSreyk return (proc_composev_imsg(ps, id, -1, type, -1, -1, iov, iovcnt)); 788c2c37c5dSreyk } 789c2c37c5dSreyk 790c2c37c5dSreyk int 7910325c666Sreyk proc_forward_imsg(struct privsep *ps, struct imsg *imsg, 7920325c666Sreyk enum privsep_procid id, int n) 7930325c666Sreyk { 7940325c666Sreyk return (proc_compose_imsg(ps, id, n, imsg->hdr.type, 795a1416996Sclaudio imsg->hdr.peerid, -1, imsg->data, IMSG_DATA_SIZE(imsg))); 7960325c666Sreyk } 7970325c666Sreyk 7980325c666Sreyk struct imsgbuf * 7990325c666Sreyk proc_ibuf(struct privsep *ps, enum privsep_procid id, int n) 8000325c666Sreyk { 8010325c666Sreyk int m; 8020325c666Sreyk 8030325c666Sreyk proc_range(ps, id, &n, &m); 8040325c666Sreyk return (&ps->ps_ievs[id][n].ibuf); 8050325c666Sreyk } 806fc292d03Sreyk 807fc292d03Sreyk struct imsgev * 808fc292d03Sreyk proc_iev(struct privsep *ps, enum privsep_procid id, int n) 809fc292d03Sreyk { 810fc292d03Sreyk int m; 811fc292d03Sreyk 812fc292d03Sreyk proc_range(ps, id, &n, &m); 813fc292d03Sreyk return (&ps->ps_ievs[id][n]); 814fc292d03Sreyk } 81551cb1125Sreyk 81651cb1125Sreyk /* This function should only be called with care as it breaks async I/O */ 81751cb1125Sreyk int 81851cb1125Sreyk proc_flush_imsg(struct privsep *ps, enum privsep_procid id, int n) 81951cb1125Sreyk { 82051cb1125Sreyk struct imsgbuf *ibuf; 82151cb1125Sreyk int m, ret = 0; 82251cb1125Sreyk 82351cb1125Sreyk proc_range(ps, id, &n, &m); 82451cb1125Sreyk for (; n < m; n++) { 82551cb1125Sreyk if ((ibuf = proc_ibuf(ps, id, n)) == NULL) 82651cb1125Sreyk return (-1); 827dd7efffeSclaudio if ((ret = imsgbuf_flush(ibuf)) == -1) 82851cb1125Sreyk break; 82951cb1125Sreyk imsg_event_add(&ps->ps_ievs[id][n]); 83051cb1125Sreyk } 83151cb1125Sreyk 83251cb1125Sreyk return (ret); 83351cb1125Sreyk } 834