Update iked to use the same proc.c that relayd uses.
authorreyk <reyk@openbsd.org>
Tue, 22 Apr 2014 12:00:03 +0000 (12:00 +0000)
committerreyk <reyk@openbsd.org>
Tue, 22 Apr 2014 12:00:03 +0000 (12:00 +0000)
Less differences, less code to audit.

ok mikeb@

sbin/iked/ca.c
sbin/iked/config.c
sbin/iked/control.c
sbin/iked/iked.c
sbin/iked/iked.h
sbin/iked/ikev1.c
sbin/iked/ikev2_msg.c
sbin/iked/ocsp.c
sbin/iked/proc.c

index 3cc03d8..18be7e5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ca.c,v 1.26 2014/02/17 15:07:23 markus Exp $  */
+/*     $OpenBSD: ca.c,v 1.27 2014/04/22 12:00:03 reyk Exp $    */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -47,7 +47,7 @@
 #include "iked.h"
 #include "ikev2.h"
 
-void    ca_reset(struct privsep *, void *);
+void    ca_reset(struct privsep *, struct privsep_proc *, void *);
 int     ca_reload(struct iked *);
 
 int     ca_getreq(struct iked *, struct imsg *);
@@ -119,7 +119,7 @@ caproc(struct privsep *ps, struct privsep_proc *p)
 }
 
 void
-ca_reset(struct privsep *ps, void *arg)
+ca_reset(struct privsep *ps, struct privsep_proc *p, void *arg)
 {
        struct iked     *env = ps->ps_env;
        struct ca_store *store = arg;
@@ -160,7 +160,7 @@ ca_dispatch_parent(int fd, struct privsep_proc *p, struct imsg *imsg)
                memcpy(&mode, imsg->data, sizeof(mode));
                if (mode == RESET_ALL || mode == RESET_CA) {
                        log_debug("%s: config reload", __func__);
-                       ca_reset(&env->sc_ps, store);
+                       ca_reset(&env->sc_ps, p, store);
                }
                break;
        case IMSG_OCSP_FD:
@@ -241,7 +241,8 @@ ca_setcert(struct iked *env, struct iked_sahdr *sh, struct iked_id *id,
        iov[iovcnt].iov_len = len;
        iovcnt++;
 
-       if (proc_composev_imsg(env, procid, IMSG_CERT, -1, iov, iovcnt) == -1)
+       if (proc_composev_imsg(&env->sc_ps, procid, -1,
+           IMSG_CERT, -1, iov, iovcnt) == -1)
                return (-1);
        return (0);
 }
@@ -282,7 +283,7 @@ ca_setreq(struct iked *env, struct iked_sahdr *sh,
        iov[iovcnt].iov_len = len;
        iovcnt++;
 
-       if (proc_composev_imsg(env, procid,
+       if (proc_composev_imsg(&env->sc_ps, procid, -1,
            IMSG_CERTREQ, -1, iov, iovcnt) == -1)
                goto done;
 
@@ -319,7 +320,8 @@ ca_setauth(struct iked *env, struct iked_sa *sa,
                log_debug("%s: auth length %zu", __func__, ibuf_size(authmsg));
        }
 
-       if (proc_composev_imsg(env, id, IMSG_AUTH, -1, iov, iovcnt) == -1)
+       if (proc_composev_imsg(&env->sc_ps, id, -1,
+           IMSG_AUTH, -1, iov, iovcnt) == -1)
                return (-1);
        return (0);
 }
@@ -379,7 +381,8 @@ ca_getcert(struct iked *env, struct imsg *imsg)
        iov[1].iov_base = &type;
        iov[1].iov_len = sizeof(type);
 
-       if (proc_composev_imsg(env, PROC_IKEV2, cmd, -1, iov, iovcnt) == -1)
+       if (proc_composev_imsg(&env->sc_ps, PROC_IKEV2, -1,
+           cmd, -1, iov, iovcnt) == -1)
                return (-1);
        return (0);
 }
@@ -623,8 +626,8 @@ ca_reload(struct iked *env)
                    ibuf_length(env->sc_certreq) == SHA_DIGEST_LENGTH ?
                    "" : "s");
 
-               (void)proc_composev_imsg(env, PROC_IKEV2, IMSG_CERTREQ, -1,
-                   iov, iovcnt);
+               (void)proc_composev_imsg(&env->sc_ps, PROC_IKEV2, -1,
+                   IMSG_CERTREQ, -1, iov, iovcnt);
        }
 
        /*
@@ -672,8 +675,8 @@ ca_reload(struct iked *env)
 
        iov[0].iov_base = &env->sc_certreqtype;
        iov[0].iov_len = sizeof(env->sc_certreqtype);
-       (void)proc_composev_imsg(env, PROC_IKEV2, IMSG_CERTREQ, -1,
-           iov, iovcnt);
+       (void)proc_composev_imsg(&env->sc_ps, PROC_IKEV2, -1,
+           IMSG_CERTREQ, -1, iov, iovcnt);
 
        return (0);
 }
index 333b005..987f5f2 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: config.c,v 1.26 2014/02/17 15:53:46 markus Exp $      */
+/*     $OpenBSD: config.c,v 1.27 2014/04/22 12:00:03 reyk Exp $        */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -391,8 +391,8 @@ config_setcoupled(struct iked *env, u_int couple)
        u_int    type;
 
        type = couple ? IMSG_CTL_COUPLE : IMSG_CTL_DECOUPLE;
-       proc_compose_imsg(env, PROC_IKEV1, type, -1, NULL, 0);
-       proc_compose_imsg(env, PROC_IKEV2, type, -1, NULL, 0);
+       proc_compose_imsg(&env->sc_ps, PROC_IKEV1, -1, type, -1, NULL, 0);
+       proc_compose_imsg(&env->sc_ps, PROC_IKEV2, -1, type, -1, NULL, 0);
 
        return (0);
 }
@@ -410,8 +410,8 @@ config_setmode(struct iked *env, u_int passive)
        u_int    type;
 
        type = passive ? IMSG_CTL_PASSIVE : IMSG_CTL_ACTIVE;
-       proc_compose_imsg(env, PROC_IKEV1, type, -1, NULL, 0);
-       proc_compose_imsg(env, PROC_IKEV2, type, -1, NULL, 0);
+       proc_compose_imsg(&env->sc_ps, PROC_IKEV1, -1, type, -1, NULL, 0);
+       proc_compose_imsg(&env->sc_ps, PROC_IKEV2, -1, type, -1, NULL, 0);
 
        return (0);
 }
@@ -437,7 +437,8 @@ config_getmode(struct iked *env, u_int type)
 int
 config_setreset(struct iked *env, u_int mode, enum privsep_procid id)
 {
-       proc_compose_imsg(env, id, IMSG_CTL_RESET, -1, &mode, sizeof(mode));
+       proc_compose_imsg(&env->sc_ps, id, -1,
+           IMSG_CTL_RESET, -1, &mode, sizeof(mode));
        return (0);
 }
 
@@ -491,8 +492,8 @@ config_setsocket(struct iked *env, struct sockaddr_storage *ss,
 
        if ((s = udp_bind((struct sockaddr *)ss, port)) == -1)
                return (-1);
-       proc_compose_imsg(env, id, IMSG_UDP_SOCKET, s,
-           ss, sizeof(*ss));
+       proc_compose_imsg(&env->sc_ps, id, -1,
+           IMSG_UDP_SOCKET, s, ss, sizeof(*ss));
        return (0);
 }
 
@@ -547,7 +548,8 @@ config_setpfkey(struct iked *env, enum privsep_procid id)
 
        if ((s = pfkey_socket()) == -1)
                return (-1);
-       proc_compose_imsg(env, id, IMSG_PFKEY_SOCKET, s, NULL, 0);
+       proc_compose_imsg(&env->sc_ps, id, -1,
+           IMSG_PFKEY_SOCKET, s, NULL, 0);
        return (0);
 }
 
@@ -567,7 +569,8 @@ config_setuser(struct iked *env, struct iked_user *usr, enum privsep_procid id)
                return (0);
        }
 
-       proc_compose_imsg(env, id, IMSG_CFG_USER, -1, usr, sizeof(*usr));
+       proc_compose_imsg(&env->sc_ps, id, -1,
+           IMSG_CFG_USER, -1, usr, sizeof(*usr));
        return (0);
 }
 
@@ -637,8 +640,8 @@ config_setpolicy(struct iked *env, struct iked_policy *pol,
                return (0);
        }
 
-       if (proc_composev_imsg(env, id, IMSG_CFG_POLICY, -1,
-           iov, iovcnt) == -1)
+       if (proc_composev_imsg(&env->sc_ps, id, -1,
+           IMSG_CFG_POLICY, -1, iov, iovcnt) == -1)
                return (-1);
 
        return (0);
@@ -717,7 +720,8 @@ config_setcompile(struct iked *env, enum privsep_procid id)
        if (env->sc_opts & IKED_OPT_NOACTION)
                return (0);
 
-       proc_compose_imsg(env, id, IMSG_COMPILE, -1, NULL, 0);
+       proc_compose_imsg(&env->sc_ps, id, -1,
+           IMSG_COMPILE, -1, NULL, 0);
        return (0);
 }
 
@@ -739,7 +743,8 @@ config_setocsp(struct iked *env)
 {
        if (env->sc_opts & IKED_OPT_NOACTION)
                return (0);
-       proc_compose_imsg(env, PROC_CERT, IMSG_OCSP_URL, -1, env->sc_ocsp_url,
+       proc_compose_imsg(&env->sc_ps, PROC_CERT, -1,
+           IMSG_OCSP_URL, -1, env->sc_ocsp_url,
            env->sc_ocsp_url ? strlen(env->sc_ocsp_url) : 0);
 
        return (0);
index ae63780..6bf3a86 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: control.c,v 1.13 2013/11/15 12:30:19 mikeb Exp $      */
+/*     $OpenBSD: control.c,v 1.14 2014/04/22 12:00:03 reyk Exp $       */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -288,9 +288,9 @@ control_dispatch_imsg(int fd, short event, void *arg)
                        memcpy(&v, imsg.data, sizeof(v));
                        log_verbose(v);
 
-                       proc_forward_imsg(env, &imsg, PROC_PARENT);
-                       proc_forward_imsg(env, &imsg, PROC_IKEV2);
-                       proc_forward_imsg(env, &imsg, PROC_IKEV1);
+                       proc_forward_imsg(&env->sc_ps, &imsg, PROC_PARENT, -1);
+                       proc_forward_imsg(&env->sc_ps, &imsg, PROC_IKEV2, -1);
+                       proc_forward_imsg(&env->sc_ps, &imsg, PROC_IKEV1, -1);
                        break;
                case IMSG_CTL_RELOAD:
                case IMSG_CTL_RESET:
@@ -298,7 +298,7 @@ control_dispatch_imsg(int fd, short event, void *arg)
                case IMSG_CTL_DECOUPLE:
                case IMSG_CTL_ACTIVE:
                case IMSG_CTL_PASSIVE:
-                       proc_forward_imsg(env, &imsg, PROC_PARENT);
+                       proc_forward_imsg(&env->sc_ps, &imsg, PROC_PARENT, -1);
                        break;
                default:
                        log_debug("%s: error handling imsg %d",
index 3c5e2bd..f139dbd 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: iked.c,v 1.19 2014/02/17 15:07:23 markus Exp $        */
+/*     $OpenBSD: iked.c,v 1.20 2014/04/22 12:00:03 reyk Exp $  */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -146,6 +146,8 @@ main(int argc, char *argv[])
                err(1, "failed to daemonize");
 
        group_init();
+
+       ps->ps_ninstances = 1;
        proc_init(ps, procs, nitems(procs));
 
        setproctitle("parent");
@@ -164,7 +166,7 @@ main(int argc, char *argv[])
        signal_add(&ps->ps_evsighup, NULL);
        signal_add(&ps->ps_evsigpipe, NULL);
 
-       proc_config(ps, procs, nitems(procs));
+       proc_listen(ps, procs, nitems(procs));
 
        if (parent_configure(env) == -1)
                fatalx("configuration failed");
@@ -361,8 +363,10 @@ parent_dispatch_ca(int fd, struct privsep_proc *p, struct imsg *imsg)
        case IMSG_CTL_DECOUPLE:
        case IMSG_CTL_ACTIVE:
        case IMSG_CTL_PASSIVE:
-               proc_compose_imsg(env, PROC_IKEV1, type, -1, NULL, 0);
-               proc_compose_imsg(env, PROC_IKEV2, type, -1, NULL, 0);
+               proc_compose_imsg(&env->sc_ps, PROC_IKEV1, -1,
+                   type, -1, NULL, 0);
+               proc_compose_imsg(&env->sc_ps, PROC_IKEV2, -1,
+                   type, -1, NULL, 0);
                break;
        case IMSG_CTL_RELOAD:
                if (IMSG_DATA_SIZE(imsg) > 0)
index e7093c7..7ac47bf 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: iked.h,v 1.71 2014/04/10 16:08:02 reyk Exp $  */
+/*     $OpenBSD: iked.h,v 1.72 2014/04/22 12:00:03 reyk Exp $  */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -50,6 +50,7 @@ struct imsgev {
        struct imsgbuf           ibuf;
        void                    (*handler)(int, short, void *);
        struct event             ev;
+       struct privsep_proc     *proc;
        void                    *data;
        short                    events;
        const char              *name;
@@ -487,15 +488,26 @@ struct iked_user {
 };
 RB_HEAD(iked_users, iked_user);
 
+struct privsep_pipes {
+       int                             *pp_pipes[PROC_MAX];
+};
+
 struct privsep {
-       int                              ps_pipes[PROC_MAX][PROC_MAX];
-       struct imsgev                    ps_ievs[PROC_MAX];
+       struct privsep_pipes            *ps_pipes[PROC_MAX];
+       struct privsep_pipes            *ps_pp;
+
+       struct imsgev                   *ps_ievs[PROC_MAX];
        const char                      *ps_title[PROC_MAX];
        pid_t                            ps_pid[PROC_MAX];
        struct passwd                   *ps_pw;
+       int                              ps_noaction;
 
        struct control_sock              ps_csock;
 
+       u_int                            ps_instances[PROC_MAX];
+       u_int                            ps_ninstances;
+       u_int                            ps_instance;
+
        /* Event and signal handlers */
        struct event                     ps_evsigint;
        struct event                     ps_evsigterm;
@@ -516,6 +528,8 @@ struct privsep_proc {
        const char              *p_chroot;
        struct privsep          *p_ps;
        struct iked             *p_env;
+       void                    (*p_shutdown)(void);
+       u_int                    p_instance;
 };
 
 struct iked_ocsp_entry {
@@ -825,22 +839,26 @@ void       timer_del(struct iked *, struct iked_timer *);
 /* proc.c */
 void    proc_init(struct privsep *, struct privsep_proc *, u_int);
 void    proc_kill(struct privsep *);
-void    proc_config(struct privsep *, struct privsep_proc *, u_int);
+void    proc_listen(struct privsep *, struct privsep_proc *, size_t);
 void    proc_dispatch(int, short event, void *);
 pid_t   proc_run(struct privsep *, struct privsep_proc *,
            struct privsep_proc *, u_int,
-           void (*)(struct privsep *, void *), void *);
+           void (*)(struct privsep *, struct privsep_proc *, void *), void *);
 void    imsg_event_add(struct imsgev *);
 int     imsg_compose_event(struct imsgev *, u_int16_t, u_int32_t,
            pid_t, int, void *, u_int16_t);
 int     imsg_composev_event(struct imsgev *, u_int16_t, u_int32_t,
            pid_t, int, const struct iovec *, int);
-int     proc_compose_imsg(struct iked *, enum privsep_procid,
+int     proc_compose_imsg(struct privsep *, enum privsep_procid, int,
            u_int16_t, int, void *, u_int16_t);
-int     proc_composev_imsg(struct iked *, enum privsep_procid,
+int     proc_composev_imsg(struct privsep *, enum privsep_procid, int,
            u_int16_t, int, const struct iovec *, int);
-int     proc_forward_imsg(struct iked *, struct imsg *,
-           enum privsep_procid);
+int     proc_forward_imsg(struct privsep *, struct imsg *,
+           enum privsep_procid, int);
+struct imsgbuf *
+        proc_ibuf(struct privsep *, enum privsep_procid, int);
+struct imsgev *
+        proc_iev(struct privsep *, enum privsep_procid, int);
 
 /* util.c */
 void    socket_set_blockmode(int, enum blockmodes);
index da152cc..832dcef 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ikev1.c,v 1.14 2014/02/17 11:00:14 reyk Exp $ */
+/*     $OpenBSD: ikev1.c,v 1.15 2014/04/22 12:00:03 reyk Exp $ */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -158,8 +158,8 @@ ikev1_msg_cb(int fd, short event, void *arg)
                iov[1].iov_base = buf;
                iov[1].iov_len = len;
 
-               proc_composev_imsg(env, PROC_IKEV2, IMSG_IKE_MESSAGE, -1,
-                   iov, 2);
+               proc_composev_imsg(&env->sc_ps, PROC_IKEV2, -1,
+                   IMSG_IKE_MESSAGE, -1, iov, 2);
                goto done;
        }
 
index 1ba719c..96abaac 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ikev2_msg.c,v 1.30 2014/04/10 16:08:02 reyk Exp $     */
+/*     $OpenBSD: ikev2_msg.c,v 1.31 2014/04/22 12:00:03 reyk Exp $     */
 
 /*
  * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
@@ -97,8 +97,8 @@ ikev2_msg_cb(int fd, short event, void *arg)
                iov[1].iov_base = buf;
                iov[1].iov_len = len;
 
-               proc_composev_imsg(env, PROC_IKEV1, IMSG_IKE_MESSAGE, -1,
-                   iov, 2);
+               proc_composev_imsg(&env->sc_ps, PROC_IKEV1, -1,
+                   IMSG_IKE_MESSAGE, -1, iov, 2);
                goto done;
        }
        TAILQ_INIT(&msg.msg_proposals);
index f17c54d..80ce1b6 100644 (file)
@@ -184,11 +184,11 @@ ocsp_connect_finish(struct iked *env, int fd, struct ocsp_connect *oc)
                /* the imsg framework will close the FD after send */
                iov[0].iov_base = oc->oc_path;
                iov[0].iov_len = strlen(oc->oc_path);
-               ret = proc_composev_imsg(env, PROC_CERT, IMSG_OCSP_FD, fd,
-                   iov, iovcnt);
+               ret = proc_composev_imsg(&env->sc_ps, PROC_CERT, -1,
+                   IMSG_OCSP_FD, fd, iov, iovcnt);
        } else {
-               ret = proc_compose_imsg(env, PROC_CERT, IMSG_OCSP_FD, -1,
-                   NULL, 0);
+               ret = proc_compose_imsg(&env->sc_ps, PROC_CERT, -1,
+                   IMSG_OCSP_FD, -1, NULL, 0);
                if (fd >= 0)
                        close(fd);
        }
@@ -242,7 +242,8 @@ ocsp_validate_cert(struct iked *env, struct iked_static_id *id,
        TAILQ_INSERT_TAIL(&env->sc_ocsp, ioe, ioe_entry);
 
        /* request connection to ocsp-responder */
-       proc_compose_imsg(env, PROC_PARENT, IMSG_OCSP_FD, -1, NULL, 0);
+       proc_compose_imsg(&env->sc_ps, PROC_PARENT, -1,
+           IMSG_OCSP_FD, -1, NULL, 0);
        return (0);
 
  err:
@@ -493,6 +494,7 @@ ocsp_parse_response(struct iked_ocsp *ocsp, OCSP_RESPONSE *resp)
 int
 ocsp_validate_finish(struct iked_ocsp *ocsp, int valid)
 {
+       struct iked             *env = ocsp->ocsp_env;
        struct iovec             iov[2];
        int                      iovcnt = 2, ret, cmd;
 
@@ -502,8 +504,8 @@ ocsp_validate_finish(struct iked_ocsp *ocsp, int valid)
        iov[1].iov_len = sizeof(ocsp->ocsp_type);
 
        cmd = valid ? IMSG_CERTVALID : IMSG_CERTINVALID;
-       ret = proc_composev_imsg(ocsp->ocsp_env, PROC_IKEV2, cmd, -1, iov,
-           iovcnt);
+       ret = proc_composev_imsg(&env->sc_ps, PROC_IKEV2, -1,
+           cmd, -1, iov, iovcnt);
 
        ocsp_free(ocsp);
        return (ret);
index 57b619e..d1be2c6 100644 (file)
@@ -1,7 +1,7 @@
-/*     $OpenBSD: proc.c,v 1.13 2014/04/18 21:29:20 tedu Exp $  */
+/*     $OpenBSD: proc.c,v 1.14 2014/04/22 12:00:03 reyk Exp $  */
 
 /*
- * Copyright (c) 2010-2013 Reyk Floeter <reyk@openbsd.org>
+ * Copyright (c) 2010 - 2014 Reyk Floeter <reyk@openbsd.org>
  * Copyright (c) 2008 Pierre-Yves Ritschard <pyr@openbsd.org>
  *
  * Permission to use, copy, modify, and distribute this software for any
 
 #include <sys/param.h>
 #include <sys/queue.h>
-#include <sys/tree.h>
-#include <sys/wait.h>
 #include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/tree.h>
+
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
 
 #include <stdio.h>
 #include <stdlib.h>
 #include <event.h>
 
 #include <openssl/rand.h>
+#include <openssl/ssl.h>
 
 #include "iked.h"
 
-void    proc_setup(struct privsep *);
+void    proc_open(struct privsep *, struct privsep_proc *,
+           struct privsep_proc *, size_t);
+void    proc_close(struct privsep *);
+int     proc_ispeer(struct privsep_proc *, u_int, enum privsep_procid);
 void    proc_shutdown(struct privsep_proc *);
 void    proc_sig_handler(int, short, void *);
+void    proc_range(struct privsep *, enum privsep_procid, int *, int *);
+
+int
+proc_ispeer(struct privsep_proc *procs, u_int nproc, enum privsep_procid type)
+{
+       u_int   i;
+
+       for (i = 0; i < nproc; i++)
+               if (procs[i].p_id == type)
+                       return (1);
+       return (0);
+}
 
 void
-proc_init(struct privsep *ps, struct privsep_proc *p, u_int nproc)
+proc_init(struct privsep *ps, struct privsep_proc *procs, u_int nproc)
 {
-       u_int    i;
+       u_int                    i, j, src, dst;
+       struct privsep_pipes    *pp;
 
        /*
-        * Called from parent
+        * Allocate pipes for all process instances (incl. parent)
+        *
+        * - ps->ps_pipes: N:M mapping
+        * N source processes connected to M destination processes:
+        * [src][instances][dst][instances], for example
+        * [PROC_RELAY][3][PROC_CA][3]
+        *
+        * - ps->ps_pp: per-process 1:M part of ps->ps_pipes
+        * Each process instance has a destination array of socketpair fds:
+        * [dst][instances], for example
+        * [PROC_PARENT][0]
+        */
+       for (src = 0; src < PROC_MAX; src++) {
+               /* Allocate destination array for each process */
+               if ((ps->ps_pipes[src] = calloc(ps->ps_ninstances,
+                   sizeof(struct privsep_pipes))) == NULL)
+                       fatal("proc_init: calloc");
+
+               for (i = 0; i < ps->ps_ninstances; i++) {
+                       pp = &ps->ps_pipes[src][i];
+
+                       for (dst = 0; dst < PROC_MAX; dst++) {
+                               /* Allocate maximum fd integers */
+                               if ((pp->pp_pipes[dst] =
+                                   calloc(ps->ps_ninstances,
+                                   sizeof(int))) == NULL)
+                                       fatal("proc_init: calloc");
+
+                               /* Mark fd as unused */
+                               for (j = 0; j < ps->ps_ninstances; j++)
+                                       pp->pp_pipes[dst][j] = -1;
+                       }
+               }
+       }
+
+       /*
+        * Setup and run the parent and its children
         */
        privsep_process = PROC_PARENT;
+       ps->ps_instances[PROC_PARENT] = 1;
        ps->ps_title[PROC_PARENT] = "parent";
        ps->ps_pid[PROC_PARENT] = getpid();
+       ps->ps_pp = &ps->ps_pipes[privsep_process][0];
 
-       proc_setup(ps);
+       for (i = 0; i < nproc; i++) {
+               /* Default to 1 process instance */
+               if (ps->ps_instances[procs[i].p_id] < 1)
+                       ps->ps_instances[procs[i].p_id] = 1;
+               ps->ps_title[procs[i].p_id] = procs[i].p_title;
+       }
+
+       proc_open(ps, NULL, procs, nproc);
 
        /* Engage! */
-       for (i = 0; i < nproc; i++, p++) {
-               ps->ps_title[p->p_id] = p->p_title;
-               ps->ps_pid[p->p_id] = (*p->p_init)(ps, p);
-       }
+       for (i = 0; i < nproc; i++)
+               ps->ps_pid[procs[i].p_id] = (*procs[i].p_init)(ps, &procs[i]);
 }
 
 void
@@ -73,83 +139,157 @@ proc_kill(struct privsep *ps)
        for (i = 0; i < PROC_MAX; i++) {
                if (ps->ps_pid[i] == 0)
                        continue;
-               kill(ps->ps_pid[i], SIGTERM);
+               killpg(ps->ps_pid[i], SIGTERM);
        }
 
        do {
                pid = waitpid(WAIT_ANY, NULL, 0);
        } while (pid != -1 || (pid == -1 && errno == EINTR));
+
+       proc_close(ps);
 }
 
 void
-proc_setup(struct privsep *ps)
+proc_open(struct privsep *ps, struct privsep_proc *p,
+    struct privsep_proc *procs, size_t nproc)
 {
-       int      i, j, sockpair[2];
-
-       for (i = 0; i < PROC_MAX; i++)
-               for (j = 0; j < PROC_MAX; j++) {
-                       if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC,
-                           sockpair) == -1)
-                               fatal("sockpair");
-                       ps->ps_pipes[i][j] = sockpair[0];
-                       ps->ps_pipes[j][i] = sockpair[1];
-                       socket_set_blockmode(ps->ps_pipes[i][j],
-                           BM_NONBLOCK);
-                       socket_set_blockmode(ps->ps_pipes[j][i],
-                           BM_NONBLOCK);
+       struct privsep_pipes    *pa, *pb;
+       int                      fds[2];
+       u_int                    i, j, src, proc;
+
+       if (p == NULL)
+               src = privsep_process; /* parent */
+       else
+               src = p->p_id;
+
+       /*
+        * Open socket pairs for our peers
+        */     
+       for (proc = 0; proc < nproc; proc++) {
+               procs[proc].p_ps = ps;
+               procs[proc].p_env = ps->ps_env;
+
+               for (i = 0; i < ps->ps_instances[src]; i++) {
+                       for (j = 0; j < ps->ps_instances[procs[proc].p_id];
+                           j++) {
+                               pa = &ps->ps_pipes[src][i];
+                               pb = &ps->ps_pipes[procs[proc].p_id][j];
+
+                               /* Check if fds are already set by peer */
+                               if (pa->pp_pipes[procs[proc].p_id][j] != -1)
+                                       continue;
+
+                               if (socketpair(AF_UNIX, SOCK_STREAM,
+                                   PF_UNSPEC, fds) == -1)
+                                       fatal("socketpair");
+
+                               socket_set_blockmode(fds[0], BM_NONBLOCK);
+                               socket_set_blockmode(fds[1], BM_NONBLOCK);
+
+                               pa->pp_pipes[procs[proc].p_id][j] = fds[0];
+                               pb->pp_pipes[src][i] = fds[1];
+                       }
                }
+       }
 }
 
 void
-proc_config(struct privsep *ps, struct privsep_proc *p, u_int nproc)
+proc_listen(struct privsep *ps, struct privsep_proc *procs, size_t nproc)
 {
-       u_int    src, dst, i, j, k, found;
-
-       src = privsep_process;
+       u_int                    i, dst, src, n, m;
+       struct privsep_pipes    *pp;
 
        /*
-        * close unused pipes
+        * Close unused pipes
         */
-       for (i = 0; i < PROC_MAX; i++) {
-               if (i != privsep_process) {
-                       for (j = 0; j < PROC_MAX; j++) {
-                               close(ps->ps_pipes[i][j]);
-                               ps->ps_pipes[i][j] = -1;
-                       }
-               } else {
-                       for (j = found = 0; j < PROC_MAX; j++, found = 0) {
-                               for (k = 0; k < nproc; k++) {
-                                       if (p[k].p_id == j)
-                                               found++;
-                               }
-                               if (!found) {
-                                       close(ps->ps_pipes[i][j]);
-                                       ps->ps_pipes[i][j] = -1;
+       for (src = 0; src < PROC_MAX; src++) {
+               for (n = 0; n < ps->ps_instances[src]; n++) {
+                       /* Ingore current process */
+                       if (src == (u_int)privsep_process &&
+                           n == ps->ps_instance)
+                               continue;
+
+                       pp = &ps->ps_pipes[src][n];
+
+                       for (dst = 0; dst < PROC_MAX; dst++) {
+                               if (src == dst)
+                                       continue;
+                               for (m = 0; m < ps->ps_instances[dst]; m++) {
+                                       if (pp->pp_pipes[dst][m] == -1)
+                                               continue;
+
+                                       /* Close and invalidate fd */
+                                       close(pp->pp_pipes[dst][m]);
+                                       pp->pp_pipes[dst][m] = -1;
                                }
                        }
                }
        }
 
+       src = privsep_process;
+       ps->ps_pp = pp = &ps->ps_pipes[src][ps->ps_instance];
+
        /*
-        * listen on appropriate pipes
+        * Listen on appropriate pipes
         */
-       for (i = 0; i < nproc; i++, p++) {
-               dst = p->p_id;
-               p->p_ps = ps;
-               p->p_env = ps->ps_env;
-
-               imsg_init(&ps->ps_ievs[dst].ibuf,
-                   ps->ps_pipes[src][dst]);
-               ps->ps_ievs[dst].handler = proc_dispatch;
-               ps->ps_ievs[dst].events = EV_READ;
-               ps->ps_ievs[dst].data = p;
-               ps->ps_ievs[dst].name = p->p_title;
-               event_set(&ps->ps_ievs[dst].ev,
-                   ps->ps_ievs[dst].ibuf.fd,
-                   ps->ps_ievs[dst].events,
-                   ps->ps_ievs[dst].handler,
-                   ps->ps_ievs[dst].data);
-               event_add(&ps->ps_ievs[dst].ev, NULL);
+       for (i = 0; i < nproc; i++) {
+               dst = procs[i].p_id;
+
+               if (src == dst)
+                       fatal("proc_listen: cannot peer with oneself");
+
+               if ((ps->ps_ievs[dst] = calloc(ps->ps_instances[dst],
+                   sizeof(struct imsgev))) == NULL)
+                       fatal("proc_open");
+
+               for (n = 0; n < ps->ps_instances[dst]; n++) {
+                       if (pp->pp_pipes[dst][n] == -1)
+                               continue;
+
+                       imsg_init(&(ps->ps_ievs[dst][n].ibuf),
+                           pp->pp_pipes[dst][n]);
+                       ps->ps_ievs[dst][n].handler = proc_dispatch;
+                       ps->ps_ievs[dst][n].events = EV_READ;
+                       ps->ps_ievs[dst][n].proc = &procs[i];
+                       ps->ps_ievs[dst][n].data = &ps->ps_ievs[dst][n];
+                       procs[i].p_instance = n;
+
+                       event_set(&(ps->ps_ievs[dst][n].ev),
+                           ps->ps_ievs[dst][n].ibuf.fd,
+                           ps->ps_ievs[dst][n].events,
+                           ps->ps_ievs[dst][n].handler,
+                           ps->ps_ievs[dst][n].data);
+                       event_add(&(ps->ps_ievs[dst][n].ev), NULL);
+               }
+       }
+}
+
+void
+proc_close(struct privsep *ps)
+{
+       u_int                    dst, n;
+       struct privsep_pipes    *pp;
+
+       if (ps == NULL)
+               return;
+
+       pp = ps->ps_pp;
+
+       for (dst = 0; dst < PROC_MAX; dst++) {
+               if (ps->ps_ievs[dst] == NULL)
+                       continue;
+
+               for (n = 0; n < ps->ps_instances[dst]; n++) {
+                       if (pp->pp_pipes[dst][n] == -1)
+                               continue;
+
+                       /* Cancel the fd, close and invalidate the fd */
+                       event_del(&(ps->ps_ievs[dst][n].ev));
+                       imsg_clear(&(ps->ps_ievs[dst][n].ibuf));
+                       close(pp->pp_pipes[dst][n]);
+                       pp->pp_pipes[dst][n] = -1;
+               }
+               free(ps->ps_ievs[dst]);
        }
 }
 
@@ -161,7 +301,13 @@ proc_shutdown(struct privsep_proc *p)
        if (p->p_id == PROC_CONTROL && ps)
                control_cleanup(&ps->ps_csock);
 
-       log_info("%s exiting", p->p_title);
+       if (p->p_shutdown != NULL)
+               (*p->p_shutdown)();
+
+       proc_close(ps);
+
+       log_info("%s exiting, pid %d", p->p_title, getpid());
+
        _exit(0);
 }
 
@@ -189,16 +335,25 @@ proc_sig_handler(int sig, short event, void *arg)
 pid_t
 proc_run(struct privsep *ps, struct privsep_proc *p,
     struct privsep_proc *procs, u_int nproc,
-    void (*init)(struct privsep *, void *), void *arg)
+    void (*init)(struct privsep *, struct privsep_proc *, void *), void *arg)
 {
        pid_t            pid;
        struct passwd   *pw;
        const char      *root;
+       u_int            n;
+
+       if (ps->ps_noaction)
+               return (0);
 
+       proc_open(ps, p, procs, nproc);
+
+       /* Fork child handlers */
        switch (pid = fork()) {
        case -1:
                fatal("proc_run: cannot fork");
        case 0:
+               /* Set the process group of the current process */
+               setpgrp(0, getpid());
                break;
        default:
                return (pid);
@@ -206,7 +361,7 @@ proc_run(struct privsep *ps, struct privsep_proc *p,
 
        pw = ps->ps_pw;
 
-       if (p->p_id == PROC_CONTROL) {
+       if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) {
                if (control_init(ps, &ps->ps_csock) == -1)
                        fatalx(p->p_title);
        }
@@ -243,6 +398,19 @@ proc_run(struct privsep *ps, struct privsep_proc *p,
                fatal("proc_run: cannot drop privileges");
 #endif
 
+       /* Fork child handlers */
+       for (n = 1; n < ps->ps_instances[p->p_id]; n++) {
+               if (fork() == 0) {
+                       ps->ps_instance = p->p_instance = n;
+                       break;
+               }
+       }
+
+#ifdef DEBUG
+       log_debug("%s: %s %d/%d, pid %d", __func__, p->p_title,
+           ps->ps_instance + 1, ps->ps_instances[p->p_id], getpid());
+#endif
+
        event_init();
 
        signal_set(&ps->ps_evsigint, SIGINT, proc_sig_handler, p);
@@ -257,16 +425,16 @@ proc_run(struct privsep *ps, struct privsep_proc *p,
        signal_add(&ps->ps_evsighup, NULL);
        signal_add(&ps->ps_evsigpipe, NULL);
 
-       proc_config(ps, procs, nproc);
+       proc_listen(ps, procs, nproc);
 
-       if (p->p_id == PROC_CONTROL) {
+       if (p->p_id == PROC_CONTROL && ps->ps_instance == 0) {
                TAILQ_INIT(&ctl_conns);
                if (control_listen(&ps->ps_csock) == -1)
                        fatalx(p->p_title);
        }
 
        if (init != NULL)
-               init(ps, arg);
+               init(ps, p, arg);
 
        event_dispatch();
 
@@ -278,9 +446,9 @@ proc_run(struct privsep *ps, struct privsep_proc *p,
 void
 proc_dispatch(int fd, short event, void *arg)
 {
-       struct privsep_proc     *p = (struct privsep_proc *)arg;
+       struct imsgev           *iev = arg;
+       struct privsep_proc     *p = iev->proc;
        struct privsep          *ps = p->p_ps;
-       struct imsgev           *iev;
        struct imsgbuf          *ibuf;
        struct imsg              imsg;
        ssize_t                  n;
@@ -288,7 +456,6 @@ proc_dispatch(int fd, short event, void *arg)
        const char              *title;
 
        title = ps->ps_title[privsep_process];
-       iev = &ps->ps_ievs[p->p_id];
        ibuf = &iev->ibuf;
 
        if (event & EV_READ) {
@@ -303,7 +470,7 @@ proc_dispatch(int fd, short event, void *arg)
        }
 
        if (event & EV_WRITE) {
-               if (msgbuf_write(&ibuf->w) <= 0 && errno != EAGAIN)
+               if (msgbuf_write(&ibuf->w) == -1 && errno != EAGAIN)
                        fatal(title);
        }
 
@@ -313,6 +480,12 @@ proc_dispatch(int fd, short event, void *arg)
                if (n == 0)
                        break;
 
+#if DEBUG > 1
+               log_debug("%s: %s %d got imsg %d from %s %d",
+                   __func__, title, ps->ps_instance + 1,
+                   imsg.hdr.type, p->p_title, p->p_instance);
+#endif
+
                /*
                 * Check the message with the program callback
                 */
@@ -328,13 +501,13 @@ proc_dispatch(int fd, short event, void *arg)
                switch (imsg.hdr.type) {
                case IMSG_CTL_VERBOSE:
                        IMSG_SIZE_CHECK(&imsg, &verbose);
-
                        memcpy(&verbose, imsg.data, sizeof(verbose));
                        log_verbose(verbose);
                        break;
                default:
-                       log_warnx("%s: %s got imsg %d", __func__, p->p_title,
-                           imsg.hdr.type);
+                       log_warnx("%s: %s %d got invalid imsg %d from %s %d",
+                           __func__, title, ps->ps_instance + 1,
+                           imsg.hdr.type, p->p_title, p->p_instance);
                        fatalx(title);
                }
                imsg_free(&imsg);
@@ -342,6 +515,10 @@ proc_dispatch(int fd, short event, void *arg)
        imsg_event_add(iev);
 }
 
+/*
+ * imsg helper functions
+ */
+
 void
 imsg_event_add(struct imsgev *iev)
 {
@@ -385,26 +562,72 @@ imsg_composev_event(struct imsgev *iev, u_int16_t type, u_int32_t peerid,
        return (ret);
 }
 
+void
+proc_range(struct privsep *ps, enum privsep_procid id, int *n, int *m)
+{
+       if (*n == -1) {
+               /* Use a range of all target instances */
+               *n = 0;
+               *m = ps->ps_instances[id];
+       } else {
+               /* Use only a single slot of the specified peer process */
+               *m = *n + 1;
+       }
+}
+
 int
-proc_compose_imsg(struct iked *env, enum privsep_procid id,
+proc_compose_imsg(struct privsep *ps, enum privsep_procid id, int n,
     u_int16_t type, int fd, void *data, u_int16_t datalen)
 {
-       return (imsg_compose_event(&env->sc_ps.ps_ievs[id],
-           type, -1, 0, fd, data, datalen));
+       int      m;
+
+       proc_range(ps, id, &n, &m);
+       for (; n < m; n++) {
+               if (imsg_compose_event(&ps->ps_ievs[id][n],
+                   type, -1, 0, fd, data, datalen) == -1)
+                       return (-1);
+       }
+
+       return (0);
 }
 
 int
-proc_composev_imsg(struct iked *env, enum privsep_procid id,
+proc_composev_imsg(struct privsep *ps, enum privsep_procid id, int n,
     u_int16_t type, int fd, const struct iovec *iov, int iovcnt)
 {
-       return (imsg_composev_event(&env->sc_ps.ps_ievs[id],
-           type, -1, 0, fd, iov, iovcnt));
+       int      m;
+
+       proc_range(ps, id, &n, &m);
+       for (; n < m; n++)
+               if (imsg_composev_event(&ps->ps_ievs[id][n],
+                   type, -1, 0, fd, iov, iovcnt) == -1)
+                       return (-1);
+
+       return (0);
 }
 
 int
-proc_forward_imsg(struct iked *env, struct imsg *imsg,
-    enum privsep_procid id)
+proc_forward_imsg(struct privsep *ps, struct imsg *imsg,
+    enum privsep_procid id, int n)
 {
-       return (proc_compose_imsg(env, id, imsg->hdr.type,
+       return (proc_compose_imsg(ps, id, n, imsg->hdr.type,
            imsg->fd, imsg->data, IMSG_DATA_SIZE(imsg)));
 }
+
+struct imsgbuf *
+proc_ibuf(struct privsep *ps, enum privsep_procid id, int n)
+{
+       int      m;
+
+       proc_range(ps, id, &n, &m);
+       return (&ps->ps_ievs[id][n].ibuf);
+}
+
+struct imsgev *
+proc_iev(struct privsep *ps, enum privsep_procid id, int n)
+{
+       int      m;
+
+       proc_range(ps, id, &n, &m);
+       return (&ps->ps_ievs[id][n]);
+}