]> git.ipfire.org Git - thirdparty/lldpd.git/commitdiff
Start privilege separation
authorVincent Bernat <vbernat@wanadooportails.com>
Fri, 7 Nov 2008 10:38:27 +0000 (11:38 +0100)
committerVincent Bernat <vbernat@wanadooportails.com>
Fri, 7 Nov 2008 10:38:27 +0000 (11:38 +0100)
configure.ac
debian/control
debian/postinst [new file with mode: 0644]
debian/postrm [new file with mode: 0644]
src/Makefile.am
src/ctl.c
src/lldpd.c
src/lldpd.h
src/priv.c [new file with mode: 0644]

index bd3f9563fc10df75c40caa85d1ce44e6e81e302d..669c4dd5a075422bf74b4795238dc888b2fe2e2a 100644 (file)
@@ -22,6 +22,22 @@ AC_ARG_WITH(snmp,
 )
 AM_CONDITIONAL([USE_SNMP], [test "${with_snmp}" != "no"])
 
+AC_ARG_WITH(privsep-user,
+       AC_HELP_STRING([--with-privsep-user],
+                      [Which user to use for privilege separation]),
+       AC_DEFINE_UNQUOTED([PRIVSEP_USER], "$withval", [User for privilege separation]),
+       AC_DEFINE_UNQUOTED([PRIVSEP_USER], "lldpd", [User for privilege separation]))
+AC_ARG_WITH(privsep-group,
+       AC_HELP_STRING([--with-privsep-group],
+                      [Which group to use for privilege separation]),
+       AC_DEFINE_UNQUOTED([PRIVSEP_GROUP], "$withval", [Group for privilege separation]),
+       AC_DEFINE_UNQUOTED([PRIVSEP_GROUP], "lldpd", [Group for privilege separation]))
+AC_ARG_WITH(privsep-chroot,
+       AC_HELP_STRING([--with-privsep-chroot],
+                      [Which directory to use to chroot lldpd]),
+       AC_DEFINE_UNQUOTED([PRIVSEP_CHROOT], "$withval", [Chroot directory]),
+       AC_DEFINE_UNQUOTED([PRIVSEP_CHROOT], "/var/run/lldpd", [Chroot directory]))
+
 # Checks for header files.
 AC_CHECK_DECLS([TAILQ_FIRST, TAILQ_NEXT, TAILQ_FOREACH, TAILQ_EMPTY],[],[],[[#include <sys/queue.h>]])
 AC_CHECK_DECL([PACKET_ORIGDEV],[],[],[[#include <linux/if_packet.h>]])
index 70292a31055cd715b8b0e0065b84b0aab21ae847..ca0551ffe1b25e6cca7363d44afe663081d51d37 100644 (file)
@@ -7,7 +7,7 @@ Standards-Version: 3.8.0
 
 Package: lldpd
 Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}
+Depends: ${shlibs:Depends}, ${misc:Depends}, adduser
 Description: implementation of IEEE 802.1ab (LLDP)
  This implementation provides LLDP sending and reception, supports
  VLAN and includes an SNMP subagent that can interface to an SNMP
diff --git a/debian/postinst b/debian/postinst
new file mode 100644 (file)
index 0000000..5a59b31
--- /dev/null
@@ -0,0 +1,13 @@
+#!/bin/sh -e
+
+if ! ([ "$1" = "configure" ] || [ "$1" = "reconfigure" ]); then
+  exit 0
+fi
+
+adduser --system --disabled-password --disabled-login --home /var/run/lldpd \
+               --no-create-home --quiet --group lldpd
+[ -d /var/run/lldpd ] || mkdir -p /var/run/lldpd
+
+#DEBHELPER#
+
+exit 0
diff --git a/debian/postrm b/debian/postrm
new file mode 100644 (file)
index 0000000..cd94b7c
--- /dev/null
@@ -0,0 +1,16 @@
+#!/bin/sh -e
+
+#DEBHELPER#
+
+case "$1" in
+    purge)
+        deluser --system lldpd || true
+        delgroup --system lldpd || true
+
+        rm -rf /var/run/lldpd
+    ;;
+    *)
+    ;;
+esac
+
+exit 0
index 5696b8abfc3cf3b14666a47738a2325f99536406..ebae44b172b0565f4fe05b38f1726ee7e604ec4e 100644 (file)
@@ -1,7 +1,7 @@
 sbin_PROGRAMS = lldpd lldpctl
 
 COMMON = log.c ctl.c lldpd.h lldp.h cdp.h compat.h sonmp.h llc.h edp.h
-lldpd_SOURCES = lldpd.c lldp.c cdp.c sonmp.c edp.c iov.c features.c client.c $(COMMON)
+lldpd_SOURCES = lldpd.c lldp.c cdp.c sonmp.c edp.c iov.c features.c client.c priv.c $(COMMON)
 lldpctl_SOURCES = lldpctl.c $(COMMON)
 
 lldpd_LDADD = @LIBOBJS@
index 0c74d8d93610aa50abd1488ed0dfcdd920543d8d..6c2be325fd0a56cfcbd2948a72ad3e0c4528836c 100644 (file)
--- a/src/ctl.c
+++ b/src/ctl.c
@@ -23,7 +23,7 @@
 #include <sys/un.h>
 
 int
-ctl_create(struct lldpd *cfg, char *name)
+ctl_create(char *name)
 {
        int s;
        struct sockaddr_un su;
@@ -41,7 +41,6 @@ ctl_create(struct lldpd *cfg, char *name)
                rc = errno; close(s); errno = rc;
                return -1;
        }
-       TAILQ_INIT(&cfg->g_clients);
        return s;
 }
 
@@ -139,9 +138,8 @@ ctl_close(struct lldpd *cfg, int c)
 }
 
 void
-ctl_cleanup(int c, char *name)
+ctl_cleanup(char *name)
 {
-       close(c);
        if (unlink(name) == -1)
                LLOG_WARN("unable to unlink %s", name);
 }
index 3d0253dd54bc41db16677a35adc13e3ffa7fefa0..02b46095903f096d9bfce1569edb38dfb756ac29 100644 (file)
@@ -24,7 +24,6 @@
 #include <fcntl.h>
 #include <fnmatch.h>
 #include <time.h>
-#include <netdb.h>
 #include <sys/utsname.h>
 #include <sys/types.h>
 #include <sys/socket.h>
@@ -133,7 +132,6 @@ int                  lldpd_iface_switchto(struct lldpd *, short int,
                            struct lldpd_hardware *);
 struct lldpd_hardware  *lldpd_port_add(struct lldpd *, struct ifaddrs *);
 void                    lldpd_loop(struct lldpd *);
-void                    lldpd_hangup(int);
 void                    lldpd_shutdown(int);
 void                    lldpd_exit();
 void                    lldpd_send_all(struct lldpd *);
@@ -478,6 +476,7 @@ lldpd_port_add_vlan(struct lldpd *cfg, struct ifaddrs *ifa)
                        free(vif);
                        return NULL;
                }
+
                /* Find the real interface */
                vif->vif_real = NULL;
                TAILQ_FOREACH(hardware, &cfg->g_hardware, h_entries) {
@@ -658,13 +657,14 @@ lldpd_port_add(struct lldpd *cfg, struct ifaddrs *ifa)
                        break;
                }
                if (ethc.port == PORT_AUI) port->p_mau_type = LLDP_DOT3_MAU_AUI;
-       }
+       } else
+               LLOG_INFO("unable to get eth info for %s", hardware->h_ifname);
 
        if (!INTERFACE_OPENED(hardware)) {
 
                if (lldpd_iface_init(cfg, hardware) != 0) {
+                       LLOG_WARN("unable to initialize %s", hardware->h_ifname);
                        lldpd_vlan_cleanup(&hardware->h_lport);
-                       free(hardware->h_lladdr);
                        free(hardware->h_proto_macs);
                        free(hardware);
                        return (NULL);
@@ -1184,19 +1184,19 @@ lldpd_loop(struct lldpd *cfg)
        int f;
        char status;
        struct utsname *un;
-       struct hostent *hp;
+       char *hp;
 
        /* Set system name and description */
        if ((un = (struct utsname*)malloc(sizeof(struct utsname))) == NULL)
                fatal(NULL);
        if (uname(un) != 0)
                fatal("failed to get system information");
-       if ((hp = gethostbyname(un->nodename)) == NULL)
+       if ((hp = priv_gethostbyname()) == NULL)
                fatal("failed to get system name");
        free(cfg->g_lchassis.c_name);
        free(cfg->g_lchassis.c_descr);
        if (asprintf(&cfg->g_lchassis.c_name, "%s",
-               hp->h_name) == -1)
+               hp) == -1)
                fatal("failed to set system name");
        if (asprintf(&cfg->g_lchassis.c_descr, "%s %s %s %s",
                un->sysname, un->release, un->version, un->machine) == -1)
@@ -1205,7 +1205,7 @@ lldpd_loop(struct lldpd *cfg)
 
        /* Check forwarding */
        cfg->g_lchassis.c_cap_enabled = 0;
-       if ((f = open("/proc/sys/net/ipv4/ip_forward", 0)) >= 0) {
+       if ((f = priv_open("/proc/sys/net/ipv4/ip_forward")) >= 0) {
                if ((read(f, &status, 1) == 1) && (status == '1'))
                        cfg->g_lchassis.c_cap_enabled = LLDP_CAP_ROUTER;
                close(f);
@@ -1295,15 +1295,6 @@ lldpd_loop(struct lldpd *cfg)
        lldpd_recv_all(cfg);
 }
 
-void
-lldpd_hangup(int sig)
-{
-       /* Re-execute */
-       LLOG_INFO("sighup received, reloading");
-       lldpd_exit();
-       execv(saved_argv[0], saved_argv);       
-}
-
 void
 lldpd_shutdown(int sig)
 {
@@ -1319,7 +1310,8 @@ lldpd_exit()
 {
        struct lldpd_hardware *hardware;
        struct lldpd_vif *vif;
-       ctl_cleanup(gcfg->g_ctl, LLDPD_CTL_SOCKET);
+       close(gcfg->g_ctl);
+       priv_ctl_cleanup();
        TAILQ_FOREACH(hardware, &gcfg->g_hardware, h_entries) {
                if (INTERFACE_OPENED(hardware))
                        lldpd_iface_close(gcfg, hardware);
@@ -1386,6 +1378,11 @@ main(int argc, char *argv[])
        }
 
        log_init(debug);
+       priv_init(
+#ifdef USE_SNMP
+               snmp
+#endif
+);
 
        if (probe == 0) probe = LLDPD_TTL;
 
@@ -1429,34 +1426,22 @@ main(int argc, char *argv[])
 #endif /* USE_SNMP */
 
        /* Create socket */
-       if ((cfg->g_ctl = ctl_create(cfg, LLDPD_CTL_SOCKET)) == -1)
-               fatal("unable to create control socket " LLDPD_CTL_SOCKET);
+       if ((cfg->g_ctl = priv_ctl_create(cfg)) == -1)
+               fatalx("unable to create control socket " LLDPD_CTL_SOCKET);
+       TAILQ_INIT(&cfg->g_clients);
 
-       if (!debug && daemon(0, 0) != 0) {
-               ctl_cleanup(cfg->g_ctl, LLDPD_CTL_SOCKET);
-               fatal("failed to detach daemon");
-       }
        gcfg = cfg;
+       if (!debug) {
+               priv_fork();
+       }
        if (atexit(lldpd_exit) != 0) {
-               ctl_cleanup(cfg->g_ctl, LLDPD_CTL_SOCKET);
+               close(cfg->g_ctl);
+               priv_ctl_cleanup();
                fatal("unable to set exit function");
        }
-       if (!debug) {
-               int pid;
-               char *spid;
-               if ((pid = open(LLDPD_PID_FILE,
-                           O_TRUNC | O_CREAT | O_WRONLY)) == -1)
-                       fatal("unable to open pid file " LLDPD_PID_FILE);
-               if (asprintf(&spid, "%d\n", getpid()) == -1)
-                       fatal("unable to create pid file " LLDPD_PID_FILE);
-               if (write(pid, spid, strlen(spid)) == -1)
-                       fatal("unable to write pid file " LLDPD_PID_FILE);
-               free(spid);
-               close(pid);
-       }
 
        /* Signal handling */
-       signal(SIGHUP, lldpd_hangup);
+       signal(SIGHUP, lldpd_shutdown);
        signal(SIGINT, lldpd_shutdown);
        signal(SIGTERM, lldpd_shutdown);
 
index 38e6d4fea2163e3fdcd198cce13ed1abd93da132..1098624033ae77f4dde37d5b3a24228c683248e8 100644 (file)
@@ -260,9 +260,9 @@ int  edp_send(PROTO_SEND_SIG);
 int     edp_decode(PROTO_DECODE_SIG);
 
 /* ctl.c */
-int     ctl_create(struct lldpd *, char *);
+int     ctl_create(char *);
 int     ctl_connect(char *);
-void    ctl_cleanup(int, char *);
+void    ctl_cleanup(char *);
 int     ctl_accept(struct lldpd *, int);
 int     ctl_close(struct lldpd *, int);
 void    ctl_msg_init(struct hmsg *, enum hmsg_type);
@@ -324,4 +324,12 @@ void        client_handle_get_port_related(struct lldpd *, struct hmsg *,
 void    client_handle_shutdown(struct lldpd *, struct hmsg *,
            struct hmsg *);
 
+/* priv.c */
+void            priv_init();
+void            priv_fork();
+int             priv_ctl_create();
+void            priv_ctl_cleanup();
+char           *priv_gethostbyname();
+int             priv_open(char*);
+
 #endif /* _LLDPD_H */
diff --git a/src/priv.c b/src/priv.c
new file mode 100644 (file)
index 0000000..d97ad16
--- /dev/null
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* This file contains code for privilege separation. When an error arises in
+ * monitor (which is running as root), it just stops instead of trying to
+ * recover. This module also contains proxies to privileged operations. In this
+ * case, error can be non fatal. */
+
+#include "lldpd.h"
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/utsname.h>
+#include <netdb.h>
+
+int remote;                    /* Other side */
+int monitored;                 /* Child */
+
+/* Message to be sent between monitor and child. The convention is that both
+ * ends agree on the content (value) which depends on the message and on the
+ * direction. */
+struct priv_msg {
+       enum {
+               PRIV_FORK,
+               PRIV_CREATE_CTL_SOCKET,
+               PRIV_DELETE_CTL_SOCKET,
+               PRIV_GET_HOSTNAME,
+               PRIV_OPEN,
+       }                msg;
+       union {
+               int      integer;
+               char     iface[IFNAMSIZ];
+               char     buf[1024];
+       }                value;
+};
+
+int
+priv_send(struct priv_msg *msg)
+{
+       if (write(remote, msg, sizeof(struct priv_msg)) !=
+           sizeof(struct priv_msg)) {
+               LLOG_WARN("unable to send message");
+               errno = EPIPE;
+               return -1;
+       }
+       if (read(remote, msg, sizeof(struct priv_msg)) !=
+           sizeof(struct priv_msg)) {
+               LLOG_WARN("unable to get answer");
+               errno = EPIPE;
+               return -1;
+       }
+       return 0;
+}
+
+/* Run as root */
+void
+priv_send_back(struct priv_msg *msg)
+{
+       if (write(remote, msg, sizeof(struct priv_msg)) !=
+           sizeof(struct priv_msg)) {
+               fatal("unable to send message");
+       }
+}
+
+/* Run as root */
+void
+priv_send_fd(int fd)
+{
+       struct msghdr    msg = {0};
+       struct cmsghdr  *cmsg;
+       char             buf[CMSG_SPACE(sizeof(int))];
+
+       msg.msg_control = buf;
+       msg.msg_controllen = sizeof(buf);
+       cmsg = CMSG_FIRSTHDR(&msg);
+       cmsg->cmsg_level = SOL_SOCKET;
+       cmsg->cmsg_type = SCM_RIGHTS;
+       cmsg->cmsg_len = CMSG_LEN(sizeof(int));
+       memcpy(CMSG_DATA(cmsg), &fd, sizeof(int));
+       msg.msg_controllen = cmsg->cmsg_len;
+       if (sendmsg(remote, &msg, 0) == -1) {
+               LLOG_WARN("unable to send file descriptor %d", fd);
+               fatal(NULL);
+       }
+}
+
+int
+priv_get_fd()
+{
+       struct msghdr    msg;
+       struct cmsghdr  *cmsg;
+       char             buf[CMSG_SPACE(sizeof(int))];
+
+       memset(&msg, 0, sizeof(struct msghdr));
+       msg.msg_control = buf;
+       msg.msg_controllen = sizeof(buf);
+
+       if (recvmsg(remote, &msg, 0) == -1) {
+               LLOG_WARN("unable to receive file descriptor");
+               return -1;
+       }
+       if ((cmsg = CMSG_FIRSTHDR(&msg)) == NULL) {
+               LLOG_WARNX("no file descriptor in received message");
+               return -1;
+       }
+       if (CMSG_NXTHDR(&msg, cmsg) != NULL) {
+               LLOG_WARNX("more than one file descriptor received");
+               return -1;
+       }
+       if ((cmsg->cmsg_level != SOL_SOCKET) ||
+           (cmsg->cmsg_type != SCM_RIGHTS)) {
+               LLOG_WARNX("unknown control data received (%d, %d)",
+                   cmsg->cmsg_level, cmsg->cmsg_type);
+               return -1;
+       }
+       return (*(int *)CMSG_DATA(cmsg));
+}
+
+/* Proxies */
+
+/* Proxy for fork */
+void
+priv_fork()
+{
+       struct priv_msg(msg);
+       msg.msg = PRIV_FORK;
+       priv_send(&msg);
+}
+
+/* Proxy for ctl_create, no argument since this is the monitor that decides the
+ * location of the socket */
+int
+priv_ctl_create()
+{
+       struct priv_msg msg;
+       msg.msg = PRIV_CREATE_CTL_SOCKET;
+       if ((priv_send(&msg) == -1) ||
+           (msg.value.integer == -1))
+               return -1;
+       return priv_get_fd();
+}
+
+/* Proxy for ctl_cleanup */
+void
+priv_ctl_cleanup()
+{
+       struct priv_msg msg;
+       msg.msg = PRIV_DELETE_CTL_SOCKET;
+       priv_send(&msg);
+}
+
+/* Proxy for gethostbyname */
+char *
+priv_gethostbyname()
+{
+       static struct priv_msg msg;
+       msg.msg = PRIV_GET_HOSTNAME;
+       if (priv_send(&msg) == -1)
+               fatal("unable to get hostname");
+       return msg.value.buf;
+}
+
+/* Proxy for open */
+int
+priv_open(char *file)
+{
+       struct priv_msg msg;
+       msg.msg = PRIV_OPEN;
+       if (strlen(file) >= sizeof(msg.value.buf)) {
+               errno = ENAMETOOLONG;
+               return -1;
+       }
+       strlcpy(msg.value.buf, file, sizeof(msg.value.buf));
+       if ((priv_send(&msg) == -1) ||
+           (msg.value.integer == -1))
+               return -1;
+       return priv_get_fd();
+}
+
+void
+priv_fork_daemon(struct priv_msg *msg)
+{
+       int pid;
+       char *spid;
+       if (daemon(0, 0) != 0)
+               fatal("failed to detach daemon");
+       if ((pid = open(LLDPD_PID_FILE,
+                   O_TRUNC | O_CREAT | O_WRONLY)) == -1)
+               fatal("unable to open pid file " LLDPD_PID_FILE);
+       if (asprintf(&spid, "%d\n", getpid()) == -1)
+               fatal("unable to create pid file " LLDPD_PID_FILE);
+       if (write(pid, spid, strlen(spid)) == -1)
+               fatal("unable to write pid file " LLDPD_PID_FILE);
+       free(spid);
+       close(pid);
+}
+
+void
+priv_create_ctl_socket(struct priv_msg *msg)
+{
+       if ((msg->value.integer =
+               ctl_create(LLDPD_CTL_SOCKET)) == -1) {
+               LLOG_WARN("unable to create control socket");
+               priv_send_back(msg);
+       } else {
+               priv_send_back(msg);
+               priv_send_fd(msg->value.integer);
+               close(msg->value.integer);
+       }
+}
+
+void
+priv_delete_ctl_socket(struct priv_msg *msg)
+{
+       ctl_cleanup(LLDPD_CTL_SOCKET);
+       priv_send_back(msg);
+}
+
+void
+priv_get_hostname(struct priv_msg *msg)
+{
+       struct utsname un;
+       struct hostent *hp;
+       if (uname(&un) != 0)
+               fatal("failed to get system information");
+       if ((hp = gethostbyname(un.nodename)) == NULL)
+               fatal("failed to get system name");
+       strlcpy(msg->value.buf, hp->h_name, sizeof(msg->value.buf));
+       priv_send_back(msg);
+}
+
+void
+priv_open_readonly(struct priv_msg *msg)
+{
+       char* authorized[] = {
+               "/proc/sys/net/ipv4/ip_forward",
+               NULL
+       };
+       char **f;
+       int fd;
+
+       for (f=authorized; *f != NULL; f++) {
+               if (strncmp(msg->value.buf, *f,
+                       sizeof(msg->value.buf)) == 0)
+                       continue;
+       }
+       msg->value.buf[sizeof(msg->value.buf) - 1] = '\0';
+       if (f == NULL) {
+               LLOG_WARNX("not authorized to open %s", msg->value.buf);
+               msg->value.integer = -1;
+               priv_send_back(msg);
+               return;
+       }
+       if ((fd = open(*f, 0)) == -1) {
+               msg->value.integer = -1;
+               priv_send_back(msg);
+               return;
+       }
+       msg->value.integer = fd;
+       priv_send_back(msg);
+       priv_send_fd(fd);
+       close(fd);
+}
+
+struct dispatch_actions {
+       int                             msg;
+       void(*function)(struct priv_msg *);
+};
+
+struct dispatch_actions actions[] = {
+       {PRIV_FORK, priv_fork_daemon},
+       {PRIV_CREATE_CTL_SOCKET, priv_create_ctl_socket},
+       {PRIV_DELETE_CTL_SOCKET, priv_delete_ctl_socket},
+       {PRIV_GET_HOSTNAME, priv_get_hostname},
+       {PRIV_OPEN, priv_open_readonly},
+       {0, NULL}
+};
+
+/* Main loop, run as root */
+void
+priv_loop()
+{
+       struct priv_msg msg;
+       struct dispatch_actions *a;
+
+       while (read(remote, &msg, sizeof(struct priv_msg)) ==
+           sizeof(struct priv_msg)) {
+               for (a = actions; a->function != NULL; a++) {
+                       if (msg.msg == a->msg) {
+                               a->function(&msg);
+                               break;
+                       }
+               }
+               if (a->function == NULL)
+                       fatal("bogus message received");
+       }
+       /* Should never be there */
+}
+
+void
+priv_exit()
+{
+       int status;
+       int rc;
+       if ((rc = waitpid(monitored, &status, WNOHANG)) == 0) {
+               LLOG_DEBUG("killing child");
+               kill(monitored, SIGTERM);
+       }
+       if ((rc = waitpid(monitored, &status, WNOHANG)) == -1)
+               _exit(0);
+       LLOG_DEBUG("waiting for child %d to terminate", monitored);
+}
+
+void
+priv_shutdown(int sig)
+{
+       LLOG_DEBUG("received signal %d, exiting", sig);
+       priv_exit();
+}
+
+/* Initialization */
+void
+#ifdef USE_SNMP
+priv_init(int snmp)
+#else
+priv_init()
+#endif
+{
+       int pair[2];
+       struct passwd *user;
+       uid_t uid;
+       struct group *group;
+       gid_t gid;
+
+       /* Create socket pair */
+       if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) < 0)
+               fatal("unable to create socket pair for privilege separation");
+
+       /* Get users */
+       if ((user = getpwnam(PRIVSEP_USER)) == NULL)
+               fatal("no " PRIVSEP_USER " user for privilege separation");
+       uid = user->pw_uid;
+       if ((group = getgrnam(PRIVSEP_GROUP)) == NULL)
+               fatal("no " PRIVSEP_GROUP " group for privilege separation");
+       gid = group->gr_gid;
+
+       /* Spawn off monitor */
+       if ((monitored = fork()) < 0)
+               fatal("unable to fork monitor");
+       switch (monitored) {
+       case 0:
+               /* We are in the children, drop privileges */
+               if (chroot(PRIVSEP_CHROOT) == -1)
+                       fatal("unable to chroot");
+               if ((setgid(gid) == -1) || (setuid(uid) == -1))
+                       fatal("unable to drop privileges");
+               remote = pair[0];
+               close(pair[1]);
+               break;
+       default:
+               /* We are in the monitor */
+               remote = pair[1];
+               close(pair[0]);
+               if (atexit(priv_exit) != 0)
+                       fatal("unable to set exit function");
+               signal(SIGHUP, priv_shutdown);
+               signal(SIGTERM, priv_shutdown);
+               signal(SIGINT, priv_shutdown);
+               signal(SIGCHLD, priv_shutdown);
+               priv_loop();
+               exit(0);
+       }
+       
+}