chiark / gitweb /
server: Introduce privilege separation.
authorMark Wooding <mdw@distorted.org.uk>
Mon, 29 Dec 2008 20:07:04 +0000 (20:07 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Mon, 29 Dec 2008 21:52:13 +0000 (21:52 +0000)
During initialization, we fork off a child which retains its root
privileges, and maintain communication with it via a Unix-domain socket
pair.  To open a new tunnel, we send it a request and it responds by
passing back the appropriate file descriptor.

The helper process running as root is implemented in a separate program,
tripe-privhelper.  This is done (a) to reduce memory use, (b) to trigger
close-on-exec behaviour and (c) to provide a clear boundary in the
source code for the parts which still run with superuser privileges.

This entails moving our tunnel-open functions into a separate program,
and doing the necessary build-system hacking.  The changes to existing
code aren't as invasive as they at first appear.

20 files changed:
Makefile.am
configure.ac
debian/tripe.install
priv/Makefile.am [new file with mode: 0644]
priv/comm.c [new file with mode: 0644]
priv/helper.c [new file with mode: 0644]
priv/priv.h [new file with mode: 0644]
priv/tripe-privhelper.8.in [new file with mode: 0644]
server/Makefile.am
server/admin.c
server/peer.c
server/privsep.c [new file with mode: 0644]
server/tests.at
server/tripe.c
server/tripe.h
server/tun-bsd.c
server/tun-linux.c
server/tun-slip.c
server/tun-unet.c
vars.am

index 504ca56..192dcc5 100644 (file)
@@ -34,6 +34,7 @@ SUBDIRS                        =
 SUBDIRS                        += common
 SUBDIRS                        += uslip
 SUBDIRS                        += client
+SUBDIRS                        += priv
 SUBDIRS                        += server
 SUBDIRS                        += proxy
 SUBDIRS                        += pkstream
index 0eeb1ec..5b5eaa2 100644 (file)
@@ -97,6 +97,15 @@ TRIPE_DEFINE_PATH(
   [logfile], [FILE], [logging output [[./tripe.log]]], [tripe.log])
 
 dnl--------------------------------------------------------------------------
+dnl Privilege-separation helper.
+
+mdw_DEFINE_PATHS([
+  AC_DEFINE_UNQUOTED([PRIVSEP_HELPER],
+    ["mdw_PATH([$libexecdir])/mdw_PROG([tripe-privhelper])"],
+    [Pathname of privilege-separation helper.])
+])
+
+dnl--------------------------------------------------------------------------
 dnl Other options.
 
 AC_ARG_WITH([tracing],
@@ -292,6 +301,7 @@ AC_CONFIG_FILES(
   [common/Makefile]
   [uslip/Makefile]
   [client/Makefile]
+  [priv/Makefile]
   [server/Makefile]
   [proxy/Makefile]
   [pkstream/Makefile]
index e68861d..7df8923 100644 (file)
@@ -3,4 +3,6 @@ debian/tmp/usr/sbin/tripe
 debian/tmp/usr/share/man/man1/tripectl.1
 debian/tmp/usr/share/man/man5/tripe-admin.5
 debian/tmp/usr/share/man/man8/tripe.8
+debian/tmp/usr/lib/tripe/tripe-privhelper
+debian/tmp/usr/share/man/man8/tripe-privhelper.8
 debian/tmp/usr/lib/pkgconfig/tripe.pc
diff --git a/priv/Makefile.am b/priv/Makefile.am
new file mode 100644 (file)
index 0000000..7c8ad95
--- /dev/null
@@ -0,0 +1,55 @@
+### -*-makefile-*-
+###
+### Makefile for privilege separation
+###
+### (c) 2008 Straylight/Edgeware
+###
+
+###----- Licensing notice ---------------------------------------------------
+###
+### This file is part of Trivial IP Encryption (TrIPE).
+###
+### TrIPE is free software; you can redistribute it and/or modify
+### it under the terms of the GNU General Public License as published by
+### the Free Software Foundation; either version 2 of the License, or
+### (at your option) any later version.
+###
+### TrIPE is distributed in the hope that it will be useful,
+### but WITHOUT ANY WARRANTY; without even the implied warranty of
+### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+### GNU General Public License for more details.
+###
+### You should have received a copy of the GNU General Public License
+### along with TrIPE; if not, write to the Free Software Foundation,
+### Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+include $(top_srcdir)/vars.am
+
+noinst_LIBRARIES        = libpriv.a
+libexec_PROGRAMS        = tripe-privhelper
+man_MANS                =
+
+###--------------------------------------------------------------------------
+### Library.
+
+libpriv_a_SOURCES       =
+
+## Header.
+libpriv_a_SOURCES      += priv.h
+
+## Communications.
+libpriv_a_SOURCES      += comm.c
+
+###--------------------------------------------------------------------------
+### Helper.
+
+## The progam itself.
+tripe_privhelper_SOURCES = helper.c
+tripe_privhelper_LDADD  = $(libpriv) $(libtripe)
+
+## Manual page.
+man_MANS               += tripe-privhelper.8
+CLEANFILES             += tripe-privhelper.8
+EXTRA_DIST             += tripe-privhelper.8.in
+
+###----- That's all, folks --------------------------------------------------
diff --git a/priv/comm.c b/priv/comm.c
new file mode 100644 (file)
index 0000000..ad2e518
--- /dev/null
@@ -0,0 +1,174 @@
+/* -*-c-*-
+ *
+ * Communication between server and helper
+ *
+ * (c) 2008 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Trivial IP Encryption (TrIPE).
+ *
+ * TrIPE is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * TrIPE is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TrIPE; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "priv.h"
+
+/*----- Global variables --------------------------------------------------*/
+
+int pc_fd = 0;                         /* File descriptor for comms */
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @pc_put@ --- *
+ *
+ * Arguments:  @const void *p@ = pointer to buffer
+ *             @size_t sz@ = size of the buffer
+ *
+ * Returns:    Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use:                Writes a buffer, handling short writes and other bogosity.
+ */
+
+int pc_put(const void *p, size_t sz)
+{
+  ssize_t n;
+  const unsigned char *pp = p;
+
+  while (sz) {
+    n = write(pc_fd, pp, sz);
+    if (n < 0) {
+      if (errno == EINTR)
+       continue;
+      return (-1);
+    }
+    if (n == 0) {
+      errno = EIO;
+      return (-1);
+    }
+    pp += n; sz -= n;
+  }
+  return (0);
+}
+
+/* --- @pc_puterr@, @pc_putuint@, @pc_putsz@, @pc_puttops@ --- *
+ *
+ * Arguments:  @int err@ = error number to write
+ *             @uint u@ = unsigned integer to write
+ *             @size_t sz@ = size to write
+ *             @const tunnel_ops *tops@ = tunnel pointer to write
+ *
+ * Returns:    Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use:                Sends an error/integer/size/tunnel-ops pointer.
+ */
+
+#define PUT(abbr, type)                                                \
+  int pc_put##abbr(type x) { return (pc_put(&x, sizeof(x))); }
+COMM_TYPES(PUT)
+
+/* --- @pc_putstring@ --- *
+ *
+ * Arguments:  @const char *s@ = pointer to string to write
+ *
+ * Returns:    Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use:                Sends a string/error/integer/tunnel-ops pointer.
+ */
+
+int pc_putstring(const char *s)
+{
+  size_t sz = strlen(s);
+
+  if (pc_putsz(sz) || pc_put(s, sz))
+    return (-1);
+  return (0);
+}
+
+/* --- @pc_get@ --- *
+ *
+ * Arguments:  @void *p@ = pointer to buffer
+ *             @size_t sz@ = size of the buffer
+ *
+ * Returns:    Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use:                Receives a buffer, handling short reads and other bogosity.
+ */
+
+int pc_get(void *p, size_t sz)
+{
+  ssize_t n;
+  unsigned char *pp = p;
+
+  while (sz) {
+    n = read(pc_fd, pp, sz);
+    if (n < 0) {
+      if (errno == EINTR)
+       continue;
+      else if (errno == ECONNRESET)
+       errno = -1;
+      return (-1);
+    }
+    if (n == 0) {
+      errno = -1;
+      return (-1);
+    }
+    pp += n; sz -= n;
+  }
+  return (0);
+}
+
+/* --- @pc_geterr@, @pc_getuint@, @pc_getsz@, @pc_getops@ --- *
+ *
+ * Arguments:  @int *err@ = where to put the error number
+ *             @uint *u@ = where to put the unsigned integer
+ *             @size_t *sz@ = where to put the size
+ *             @const tunnel_ops **tops@ = where to put the tunnel pointer
+ *
+ * Returns:    Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use:                Receives an error/integer/size/tunnel-ops pointer.
+ */
+
+#define GET(abbr, type)                                                        \
+  int pc_get##abbr(type *x) { return (pc_get(x, sizeof(*x))); }
+COMM_TYPES(GET)
+
+/* --- @pc_gettring@ --- *
+ *
+ * Arguments:  @dstr *d@ = where to pc_put the string
+ *
+ * Returns:    Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use:                Receives a string.
+ */
+
+int pc_getstring(dstr *d)
+{
+  size_t sz;
+
+  if (pc_getsz(&sz))
+    return (-1);
+  DENSURE(d, sz + 1);
+  if (pc_get(d->buf + d->len, sz))
+    return (-1);
+  d->len += sz;
+  d->buf[d->len] = 0;
+  return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/priv/helper.c b/priv/helper.c
new file mode 100644 (file)
index 0000000..c1401f1
--- /dev/null
@@ -0,0 +1,307 @@
+/* -*-c-*-
+ *
+ * Privilege-separated helper
+ *
+ * (c) 2008 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Trivial IP Encryption (TrIPE).
+ *
+ * TrIPE is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * TrIPE is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TrIPE; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "priv.h"
+
+/*----- Helper-side utilities ---------------------------------------------*/
+
+/* --- @lose@ --- *
+ *
+ * Arguments:  @const char *excuse@ = what went wrong
+ *
+ * Returns:    Doesn't.
+ *
+ * Use:                Reports a fatal error and quits.
+ */
+
+static void lose(const char *excuse)
+{
+  moan("helper process bailing out: %s; error: %s",
+       excuse,
+       errno == -1 ? "Unexpected EOF" : strerror(errno));
+  _exit(127);
+}
+
+/*----- Diagnostic functions ----------------------------------------------*/
+
+/* --- @trace@ --- *
+ *
+ * Arguments:  @unsigned mask@ = trace mask to check
+ *             @const char *fmt@ = message format
+ *             @...@ = values for placeholders
+ *
+ * Returns:    ---
+ *
+ * Use:                Writes a trace message.
+ */
+
+#ifndef NTRACE
+
+static void itrace(unsigned mask, const char *fmt, ...)
+{
+  va_list ap;
+  dstr d = DSTR_INIT;
+
+  va_start(ap, fmt);
+  dstr_vputf(&d, fmt, &ap);
+  if (pc_putuint(PS_TRACE) ||
+      pc_putuint(mask) ||
+      pc_putsz(d.len) ||
+      pc_put(d.buf, d.len))
+    lose("write (trace)");
+  va_end(ap);
+  dstr_destroy(&d);
+}
+
+#endif
+
+/* --- @warn@ --- *
+ *
+ * Arguments:  @const char *fmt@ = message format
+ *             @...@ = values for placeholders
+ *
+ * Returns:    ---
+ *
+ * Use:                Writes a warning message.
+ */
+
+#define A_END ((char *)0)
+
+static void warn(const char *fmt, ...)
+{
+  va_list ap;
+  dstr d = DSTR_INIT, dd = DSTR_INIT;
+
+  va_start(ap, fmt);
+  while (fmt) {
+    if (*fmt == '?') {
+      if (strcmp(fmt, "?ERRNO") == 0) {
+       dstr_putf(&d, " E%d", errno);
+       u_quotify(&d, strerror(errno));
+      } else
+       abort();
+    } else {
+      DRESET(&dd);
+      dstr_vputf(&dd, fmt, &ap);
+      u_quotify(&d, dd.buf);
+    }
+    fmt = va_arg(ap, const char *);
+  }
+  va_end(ap);
+
+  if (pc_putuint(PS_WARN) ||
+      pc_putsz(d.len) ||
+      pc_put(d.buf, d.len))
+    lose("write (warn)");
+
+  dstr_destroy(&d);
+  dstr_destroy(&dd);
+}
+
+/*----- Tunnel drivers ----------------------------------------------------*/
+
+/* --- @topen_DRIVER@ --- *
+ *
+ * Arguments:  @char **ifn@ = where to put the interface name
+ *
+ * Returns:    A file descriptor, or @-1@ on failure.
+ *
+ * Use:                Opens a tunnel device.
+ */
+
+#ifdef TUN_LINUX
+
+#include <sys/ioctl.h>
+#include <linux/if.h>
+#include <linux/if_tun.h>
+
+static int topen_linux(char **ifn)
+{
+  int fd;
+  struct ifreq iff;
+
+  if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
+    warn("TUN", "-", "linux",
+        "open-error", "/dev/net/tun", "?ERRNO",
+        A_END);
+    return (-1);
+  }
+  memset(&iff, 0, sizeof(iff));
+  iff.ifr_name[0] = 0;
+  iff.ifr_flags = IFF_TUN | IFF_NO_PI;
+  if (ioctl(fd, TUNSETIFF, &iff) < 0) {
+    warn("TUN", "-", "linux", "config-error", "?ERRNO", A_END);
+    close(fd);
+    return (-1);
+  }
+  iff.ifr_name[IFNAMSIZ - 1] = 0;
+  *ifn = xstrdup(iff.ifr_name);
+  return (fd);
+}
+
+#endif
+
+#ifdef TUN_BSD
+
+static int topen_bsd(char **ifn)
+{
+  int fd;
+  unsigned n;
+  char buf[16];
+
+  n = 0;
+  for (;;) {
+    sprintf(buf, "/dev/tun%u", n);
+    if ((fd = open(buf, O_RDWR)) >= 0)
+      break;
+    switch (errno) {
+      case EBUSY:
+       T( itrace(T_PRIVSEP, "tunnel device %u busy: skipping", n); )
+       break;
+      case ENOENT:
+       warn("TUN", "-", "bsd", "no-tunnel-devices", A_END);
+       return (-1);
+      default:
+       warn("TUN", "-", "open-error", "%s", buf, "?ERRNO", A_END);
+       break;
+    }
+    n++;
+  }
+  return (fd);
+}
+
+#endif
+
+#ifdef TUN_UNET
+
+#include <sys/ioctl.h>
+#include <linux/if.h>
+#include <unet.h>
+
+static int topen_unet(char **ifn)
+{
+  int fd;
+  int f;
+  struct unet_info uni;
+
+  if ((fd = open("/dev/unet", O_RDWR)) < 0) {
+    warn("TUN", "-", "unet", "open-error", "/dev/unet", "?ERRNO", A_END);
+    goto fail_0;
+  }
+  if ((f = ioctl(fd, UNIOCGIFFLAGS)) < 0 ||
+      ioctl(fd, UNIOCSIFFLAGS, f | IFF_POINTOPOINT)) {
+    warn("TUN", "-", "unet", "config-error", "?ERRNO", A_END);
+    goto fail_1;
+  }
+  if (ioctl(fd, UNIOCGINFO, &uni)) {
+    warn("TUN", "-", "unet", "getinfo-error", "?ERRNO", A_END);
+    goto fail_1;
+  }
+  *ifn = xstrdup(uni.uni_ifname);
+  return (fd);
+
+fail_1:
+  close(fd);
+fail_0:
+  return (-1);
+}
+
+#endif
+
+static const struct tunnel {
+  const char *name;
+  int (*open)(char **);
+} tunnels[] = {
+#ifdef TUN_LINUX
+  { "linux", topen_linux },
+#endif
+#ifdef TUN_BSD
+  { "bsd", topen_bsd },
+#endif
+#ifdef TUN_UNET
+  { "unet", topen_unet },
+#endif
+  { 0, 0 }
+};
+
+/*----- Helper process core -----------------------------------------------*/
+
+int main(int argc, char *argv[])
+{
+  struct sockaddr_un sun;
+  socklen_t slen = sizeof(sun);
+  unsigned rq;
+  dstr d = DSTR_INIT;
+  const struct tunnel *t;
+  char *ifn = 0;
+  int fd;
+  ssize_t n;
+
+  ego(argv[0]);
+  if (argc != 1 ||
+      getpeername(0, (struct sockaddr *)&sun, &slen) ||
+      sun.sun_family != AF_UNIX)
+    die(EXIT_FAILURE, "please do not run this program again.");
+
+  for (;;) {
+    if (pc_getuint(&rq)) {
+      if (errno == -1) break;
+      else lose("read (main)");
+    }
+    switch (rq) {
+      case PS_TUNRQ:
+       DRESET(&d);
+       if (pc_getstring(&d)) lose("read (tunnel)");
+       for (t = tunnels;; t++) {
+         if (!t->name) lose("unknown tunnel");
+         if (strcmp(d.buf, t->name) == 0) break;
+       }
+       T( itrace(T_PRIVSEP,
+                   "privsep: received request for %s tunnel",
+                   t->name); )
+       if ((fd = t->open(&ifn)) < 0)
+         goto err;
+       rq = PS_TUNFD;
+       n = fdpass_send(pc_fd, fd, &rq, sizeof(rq)); close(fd);
+       if (n < 0) { xfree(ifn); goto err; }
+       else if (n < sizeof(rq)) lose("partial write (fd-pass)");
+       if (pc_putstring(ifn)) lose("write (ifname)");
+       xfree(ifn);
+       break;
+      err:
+       if (pc_putuint(PS_TUNERR) || pc_puterr(errno)) lose("write (error)");
+       break;
+      default:
+       lose("bad request");
+       break;
+    }
+  }
+  _exit(0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/priv/priv.h b/priv/priv.h
new file mode 100644 (file)
index 0000000..225075a
--- /dev/null
@@ -0,0 +1,188 @@
+/* -*-c-*-
+ *
+ * Privilege separation definitions
+ *
+ * (c) 2008 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Trivial IP Encryption (TrIPE).
+ *
+ * TrIPE is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * TrIPE is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TrIPE; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef PRIV_H
+#define PRIV_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "config.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <mLib/dstr.h>
+#include <mLib/fdpass.h>
+#include <mLib/quis.h>
+#include <mLib/report.h>
+#include <mLib/trace.h>
+
+#include "util.h"
+
+#undef sun
+
+/*----- Protocol ----------------------------------------------------------*/
+
+/* --- Notes --- *
+ *
+ * The protocol is synchronous.  The socket is not marked as nonblocking;
+ * instead we just trust the helper to respond in good time; this is
+ * reasonable since it's not doing anything complicated.  The helper is
+ * completely trusted.
+ *
+ * The protocol works like this.  Messages begin with a request code which is
+ * a single @unsigned int@.  The server sends a request @PS_TUNRQ@ to the
+ * helper, followed by a @const tunnel_ops *@ referring to the tunnel driver
+ * of interest.  The server responds with a sequence of @PS_TRACE@ and/or
+ * @PS_WARN@ messages, followed by either a @PS_TUNFD@ carrying a file
+ * descriptor, or a @PS_TUNERR@ followed by an integer @errno@ code.
+ *
+ * If all else fails, the helper process will just quit.
+ */
+
+enum {
+  PS_TUNRQ,                            /* Request (@tunnel_ops *@) */
+  PS_TUNFD,                            /* Tunnel descriptor (string) */
+  PS_TUNERR,                           /* Error (@int errno@) */
+#ifndef NTRACE
+  PS_TRACE,                            /* Trace (@unsigned mask@, string) */
+#endif
+  PS_WARN,                             /* Warning (string) */
+};
+
+/*----- Tracing definitions -----------------------------------------------*/
+
+#define T_PRIVSEP 512u
+
+/*----- Global variables --------------------------------------------------*/
+
+extern int pc_fd;                      /* File descriptor for comms */
+
+/*----- Functions provided ------------------------------------------------*/
+
+#define COMM_TYPES(_)                                                  \
+  _(err, int)                                                          \
+  _(uint, unsigned int)                                                        \
+  _(sz, size_t)
+
+/* --- @put@ --- *
+ *
+ * Arguments:  @const void *p@ = pointer to buffer
+ *             @size_t sz@ = size of the buffer
+ *
+ * Returns:    Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use:                Writes a buffer, handling short writes and other bogosity.
+ */
+
+extern int pc_put(const void */*p*/, size_t /*sz*/);
+
+/* --- @puterr@, @putuint@, @putsz@, @puttops@ --- *
+ *
+ * Arguments:  @int err@ = error number to write
+ *             @uint u@ = unsigned integer to write
+ *             @size_t sz@ = size to write
+ *             @const tunnel_ops *tops@ = tunnel pointer to write
+ *
+ * Returns:    Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use:                Sends an error/integer/size/tunnel-ops pointer.
+ */
+
+#define DECL(abbr, type) extern int pc_put##abbr(type /*x*/);
+COMM_TYPES(DECL)
+#undef DECL
+
+/* --- @putstring@ --- *
+ *
+ * Arguments:  @const char *s@ = pointer to string to write
+ *
+ * Returns:    Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use:                Sends a string/error/integer/tunnel-ops pointer.
+ */
+
+extern int pc_putstring(const char */*s*/);
+
+/* --- @get@ --- *
+ *
+ * Arguments:  @void *p@ = pointer to buffer
+ *             @size_t sz@ = size of the buffer
+ *
+ * Returns:    Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use:                Receives a buffer, handling short reads and other bogosity.
+ */
+
+extern int pc_get(void */*p*/, size_t /*sz*/);
+
+/* --- @geterr@, @getuint@, @getsz@, @getops@ --- *
+ *
+ * Arguments:  @int *err@ = where to put the error number
+ *             @uint *u@ = where to put the unsigned integer
+ *             @size_t *sz@ = where to put the size
+ *             @const tunnel_ops **tops@ = where to put the tunnel pointer
+ *
+ * Returns:    Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use:                Receives an error/integer/size/tunnel-ops pointer.
+ */
+
+#define DECL(abbr, type) extern int pc_get##abbr(type */*x*/);
+COMM_TYPES(DECL)
+#undef DECL
+
+/* --- @gettring@ --- *
+ *
+ * Arguments:  @dstr *d@ = where to put the string
+ *
+ * Returns:    Zero on success, @-1@ on error (and @errno@ set).
+ *
+ * Use:                Receives a string.
+ */
+
+extern int pc_getstring(dstr */*d*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/priv/tripe-privhelper.8.in b/priv/tripe-privhelper.8.in
new file mode 100644 (file)
index 0000000..ea4a43f
--- /dev/null
@@ -0,0 +1,89 @@
+.\" -*-nroff-*-
+.\".
+.\" Manual for the server
+.\"
+.\" (c) 2008 Straylight/Edgeware
+.\"
+.
+.\"----- Licensing notice ---------------------------------------------------
+.\"
+.\" This file is part of Trivial IP Encryption (TrIPE).
+.\"
+.\" TrIPE is free software; you can redistribute it and/or modify
+.\" it under the terms of the GNU General Public License as published by
+.\" the Free Software Foundation; either version 2 of the License, or
+.\" (at your option) any later version.
+.\"
+.\" TrIPE is distributed in the hope that it will be useful,
+.\" but WITHOUT ANY WARRANTY; without even the implied warranty of
+.\" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+.\" GNU General Public License for more details.
+.\"
+.\" You should have received a copy of the GNU General Public License
+.\" along with TrIPE; if not, write to the Free Software Foundation,
+.\" Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+.
+.\"--------------------------------------------------------------------------
+.so ../defs.man.in \" @@@PRE@@@
+.
+.\"--------------------------------------------------------------------------
+.TH tripe-privhelper 8 "28 April 2008" "Straylight/Edgeware" "TrIPE: Trivial IP Encryption"
+.
+.\"--------------------------------------------------------------------------
+.SH "NAME"
+.
+tripe-privhelper \- privilege-separation helper program
+.
+.\"--------------------------------------------------------------------------
+.SH "SYNOPSIS"
+.
+This program communicates using a binary protocol over a Unix-domain
+socket on file descriptor 0.  It is not intended to be run
+interactively.
+.
+.\"--------------------------------------------------------------------------
+.SH "DESCRIPTION"
+.
+The
+.BR tripe (8)
+server usually needs superuser privileges in order to open new tunnel
+devices, through which it collects and emits network packets.  In order
+to prevent the whole system needing to be run as root, the server splits
+off a child process and then drops its privileges; the child process
+runs this program.
+.PP
+The
+.B tripe-privhelper
+program reads requests for tunnel devices on file descriptor 0 and
+responds with appropriate file descriptors (using Unix-domain socket
+file descriptor passing: see
+.BR unix (7))
+for correctly configured tunnel devices.
+.
+.\"--------------------------------------------------------------------------
+.SH "BUGS"
+.
+The objective of the privilege separation model is to reduce the attack
+surface for the code running with superuser privileges down to a simple
+binary protocol.  There may still be bugs in the small program which
+runs as root.
+.PP
+The `unprivileged' portion of the server still runs with the ability to
+read and write arbitrary data on tunnel devices.  In particular, if
+compromised, it can inject arbitrary packets into the network.  This is
+unfortunately inherent in the nature of a VPN server.
+.
+.\"--------------------------------------------------------------------------
+.SH "SEE ALSO"
+.
+.BR tripe (8).
+.PP
+.IR "The Trivial IP Encryption Protocol" ,
+.IR "The Wrestlers Protocol" .
+.
+.\"--------------------------------------------------------------------------
+.SH "AUTHOR"
+.
+Mark Wooding, <mdw@distorted.org.uk>
+.
+.\"----- That's all, folks --------------------------------------------------
index e033ab0..f4fb936 100644 (file)
@@ -34,7 +34,7 @@ man_MANS               =
 sbin_PROGRAMS          += tripe
 
 tripe_SOURCES           =
-tripe_LDADD             = $(libtripe) $(catacomb_LIBS)
+tripe_LDADD             = $(libpriv) $(libtripe) $(catacomb_LIBS)
 
 ## Main header file.
 tripe_SOURCES          += tripe.h
@@ -47,6 +47,7 @@ tripe_SOURCES         += keyset.c
 tripe_SOURCES          += keyexch.c
 tripe_SOURCES          += chal.c
 tripe_SOURCES          += peer.c
+tripe_SOURCES          += privsep.c
 tripe_SOURCES          += admin.c
 tripe_SOURCES          += tripe.c
 
index 4e87e48..ccd49b7 100644 (file)
@@ -40,6 +40,7 @@ const trace_opt tr_opts[] = {
   { 'x',       T_KEYEXCH,      "key exchange" },
   { 'm',       T_KEYMGMT,      "key management" },
   { 'l',       T_CHAL,         "challenge management" },
+  { 'v',       T_PRIVSEP,      "privilege separation" },
   { 'p',       T_PACKET,       "packet contents" },
   { 'c',       T_CRYPTO,       "crypto details" },
   { 'A',       T_ALL,          "all of the above" },
@@ -521,6 +522,7 @@ void a_quit(void)
   close(sock.fd);
   unlink(sockname);
   FOREACH_PEER(p, { p_destroy(p); });
+  ps_quit();
   exit(0);
 }
 
index 3cb12ea..4bb2c82 100644 (file)
@@ -742,9 +742,9 @@ peer *p_create(peerspec *spec)
   p->ifname = 0;
   memset(&p->st, 0, sizeof(stats));
   p->st.t_start = time(0);
-  if (!tops->open)
+  if (!(tops->flags & TUNF_PRIVOPEN))
     fd = -1;
-  else if ((fd = tops->open(&p->ifname)) < 0)
+  else if ((fd = ps_tunfd(tops, &p->ifname)) < 0)
     goto tidy_2;
   if ((p->t = tops->create(p, fd, &p->ifname)) == 0)
     goto tidy_3;
diff --git a/server/privsep.c b/server/privsep.c
new file mode 100644 (file)
index 0000000..5bc2b89
--- /dev/null
@@ -0,0 +1,215 @@
+/* -*-c-*-
+ *
+ * Privilege separation communication protocol
+ *
+ * (c) 2008 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Trivial IP Encryption (TrIPE).
+ *
+ * TrIPE is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * TrIPE is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TrIPE; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "tripe.h"
+#include "priv.h"
+
+/*----- Static variables --------------------------------------------------*/
+
+static pid_t kid = -1;
+
+/*----- Fetching a tunnel file descriptor ---------------------------------*/
+
+/* --- @ps_tunfd@ --- *
+ *
+ * Arguments:  @const tunnel_ops *tops@ = pointer to tunnel operations
+ *             @char **ifn@ = where to put the interface name
+ *
+ * Returns:    The file descriptor, or @-1@ on error.
+ *
+ * Use:                Fetches a file descriptor for a tunnel driver.
+ */
+
+int ps_tunfd(const tunnel_ops *tops, char **ifn)
+{
+  unsigned code;
+  ssize_t n;
+  dstr d = DSTR_INIT;
+  int fd;
+
+  if (pc_fd == -1) {
+    a_warn("PRIVSEP", "helper-died", A_END);
+    return (-1);
+  }
+  T( trace(T_PRIVSEP,
+          "privsep: requesting descriptor for %s tunnel",
+          tops->name); )
+  if (pc_putuint(PS_TUNRQ) || pc_putstring(tops->name)) {
+    a_warn("PRIVSEP", "helper-write-error", "?ERRNO", A_END);
+    goto lose;
+  }
+  for (;;) {
+    n = fdpass_recv(pc_fd, &fd, &code, sizeof(code));
+    if (n < 0) goto readlose;
+    if (n < sizeof(code)) {
+      a_warn("PRIVSEP", "helper-short-read", A_END);
+      goto lose;
+    }
+    switch (code) {
+      case PS_TUNFD:
+       if (fd == -1) {
+         a_warn("PRIVSEP", "no-fd-from-helper", A_END);
+         goto lose;
+       }
+       if (pc_getstring(&d)) { close(fd); goto readlose; }
+       *ifn = xstrdup(d.buf);
+       T( trace(T_PRIVSEP,
+                "privsep: received winning descriptor for %s",
+                *ifn); )
+       goto done;
+      case PS_TUNERR:
+       if (pc_geterr(&errno)) goto readlose;
+       T( trace(T_PRIVSEP, "privsep: helper lost: %s", strerror(errno)); )
+       fd = -1;
+       goto done;
+#ifndef NTRACE
+      case PS_TRACE:
+       if (pc_getuint(&code) || pc_getstring(&d)) goto readlose;
+       trace(code, "%s", d.buf);
+       DRESET(&d);
+       break;
+#endif
+      case PS_WARN:
+       if (pc_getstring(&d)) goto readlose;
+       a_warn("*%s", d.buf, A_END);
+       DRESET(&d);
+       break;
+      default:
+       a_warn("PRIVSEP", "unknown-response-code", "%u", code, A_END);
+       goto lose;
+    }
+  }
+done:
+  dstr_destroy(&d);
+  return (fd);
+
+readlose:
+  a_warn("PRIVSEP", "helper-read-error", "?ERRNO", A_END);
+lose:
+  dstr_destroy(&d);
+  close(pc_fd);
+  pc_fd = -1;
+  return (-1);
+}
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @reap@ --- *
+ *
+ * Arguments:  @int sig@ = signal number (always @SIGCHLD@; ignored)
+ *
+ * Returns:    ---
+ *
+ * Use:                Notices and reports child process death.
+ */
+
+static void reap(int sig)
+{
+  pid_t k;
+  int st;
+
+  for (;;) {
+    k = waitpid(-1, &st, WNOHANG);
+    if (k < 0) {
+      switch (errno) {
+       case EINTR:
+         break;
+       default:
+         a_warn("SERVER", "waitpid-error", "?ERRNO", A_END);
+       case ECHILD:
+         return;
+      }
+    }
+    if (!k)
+      return;
+    if (k == kid) {
+      if (WIFEXITED(st))
+       a_warn("PRIVSEP", "child-exited", "%d", WEXITSTATUS(st), A_END);
+      else if (WIFSIGNALED(st))
+       a_warn("PRIVSEP", "child-killed", "%d", WTERMSIG(st), A_END);
+      else
+       a_warn("PRIVSEP", "child-died", "%d", st, A_END);
+      kid = -1;
+    }
+  }
+}
+
+/* --- @ps_split@ --- *
+ *
+ * Arguments:  @int detachp@ = whether to detach the child from its terminal
+ *
+ * Returns:    ---
+ *
+ * Use:                Separates off the privileged tunnel-opening service from the
+ *             rest of the server.
+ */
+
+void ps_split(int detachp)
+{
+  pid_t kid;
+  int fd[2];
+  const char *helper;
+
+  if (socketpair(PF_UNIX, SOCK_STREAM, 0, fd)) {
+    die(EXIT_FAILURE,
+       "failed to create socket pair for privilege separation: %s",
+       strerror(errno));
+  }
+  helper = getenv("TRIPE_PRIVHELPER");
+  if (!helper) helper = PRIVSEP_HELPER;
+  fdflags(fd[0], 0, 0, FD_CLOEXEC, FD_CLOEXEC);
+  fdflags(fd[1], 0, 0, FD_CLOEXEC, FD_CLOEXEC);
+  signal(SIGCHLD, reap);
+  kid = fork();
+  if (kid == 0) {
+    signal(SIGCHLD, SIG_DFL);
+    if (detachp) detachtty();
+    if (dup2(fd[0], 0) < 0) goto lose;
+    close(fd[0]); close(fd[1]);
+    execl(helper, helper, (char *)0);
+  lose:
+    fprintf(stderr, "helper: failed to run helper: %s\n", strerror(errno));
+    _exit(127);
+  }
+  T( trace(T_PRIVSEP, "privsep: forked child successfully"); )
+  close(fd[0]);
+  pc_fd = fd[1];
+}
+
+/* --- @ps_quit@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Detaches from the helper process.
+ */
+
+void ps_quit(void) { if (pc_fd != -1) close(pc_fd); }
+
+/*----- That's all, folks -------------------------------------------------*/
index 1a3bc4c..393a661 100644 (file)
@@ -34,7 +34,8 @@ m4_define([SETUPDIR], [
 
 ## Running standard programs with useful options.
 m4_define([TRIPE],
-  [$abs_top_builddir/server/tripe -F -d. -aadmin -p0 -b127.0.0.1 -talice])
+  [env TRIPE_PRIVHELPER=$abs_top_builddir/priv/tripe-privhelper \
+     $abs_top_builddir/server/tripe -F -d. -aadmin -p0 -b127.0.0.1 -talice])
 m4_define([TRIPECTL], [$abs_top_builddir/client/tripectl -d. -aadmin])
 m4_define([USLIP], [$abs_top_builddir/uslip/tripe-uslip])
 
index ba59dfe..a2731c3 100644 (file)
@@ -288,6 +288,7 @@ int main(int argc, char *argv[])
       af |= AF_FOREGROUND;
     a_create(STDIN_FILENO, STDOUT_FILENO, af);
   }
+  ps_split(f & f_daemon);
   a_init(csock, u, g);
   u_setugid(u, g);
   km_init(kr_priv, kr_pub, tag_priv);
index 404bad0..ab15787 100644 (file)
@@ -52,6 +52,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/stat.h>
+#include <sys/wait.h>
 
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -70,6 +71,7 @@
 #include <mLib/dstr.h>
 #include <mLib/env.h>
 #include <mLib/fdflags.h>
+#include <mLib/fdpass.h>
 #include <mLib/fwatch.h>
 #include <mLib/hash.h>
 #include <mLib/macros.h>
 #include <catacomb/ec-keys.h>
 #include <catacomb/group.h>
 
+#include "priv.h"
 #include "protocol.h"
 #include "slip.h"
 #include "util.h"
 #define T_KEYEXCH 64u
 #define T_KEYMGMT 128u
 #define T_CHAL 256u
+/* T_PRIVSEP  in priv.h */
 
-#define T_ALL 511u
+#define T_ALL 1023u
 
 /* --- Units --- */
 
@@ -287,8 +291,9 @@ struct peer;
 
 typedef struct tunnel_ops {
   const char *name;                    /* Name of this tunnel driver */
+  unsigned flags;                      /* Various interesting flags */
+#define TUNF_PRIVOPEN 1u               /*   Need helper to open file */
   void (*init)(void);                  /* Initializes the system */
-  int (*open)(char **/*ifn*/);         /* Open tunnel and report ifname */
   tunnel *(*create)(struct peer */*p*/, int /*fd*/, char **/*ifn*/);
                                        /* Initializes a new tunnel */
   void (*setifname)(tunnel */*t*/, const char */*ifn*/);
@@ -994,6 +999,68 @@ extern void *am_find(addrmap */*m*/, const addr */*a*/,
 
 extern void am_remove(addrmap */*m*/, void */*i*/);
 
+/*----- Privilege separation ----------------------------------------------*/
+
+/* --- @ps_trace@ --- *
+ *
+ * Arguments:  @unsigned mask@ = trace mask to check
+ *             @const char *fmt@ = message format
+ *             @...@ = values for placeholders
+ *
+ * Returns:    ---
+ *
+ * Use:                Writes a trace message.
+ */
+
+T( extern void ps_trace(unsigned /*mask*/, const char */*fmt*/, ...); )
+
+/* --- @ps_warn@ --- *
+ *
+ * Arguments:  @const char *fmt@ = message format
+ *             @...@ = values for placeholders
+ *
+ * Returns:    ---
+ *
+ * Use:                Writes a warning message.
+ */
+
+extern void ps_warn(const char */*fmt*/, ...);
+
+/* --- @ps_tunfd@ --- *
+ *
+ * Arguments:  @const tunnel_ops *tops@ = pointer to tunnel operations
+ *             @char **ifn@ = where to put the interface name
+ *
+ * Returns:    The file descriptor, or @-1@ on error.
+ *
+ * Use:                Fetches a file descriptor for a tunnel driver.
+ */
+
+extern int ps_tunfd(const tunnel_ops */*tops*/, char **/*ifn*/);
+
+/* --- @ps_split@ --- *
+ *
+ * Arguments:  @int detachp@ = whether to detach the child from its terminal
+ *
+ * Returns:    ---
+ *
+ * Use:                Separates off the privileged tunnel-opening service from the
+ *             rest of the server.
+ */
+
+extern void ps_split(int /*detachp*/);
+
+/* --- @ps_quit@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Detaches from the helper process.
+ */
+
+extern void ps_quit(void);
+
 /*----- Peer management ---------------------------------------------------*/
 
 /* --- @p_txstart@ --- *
index 200b5d9..bfa528b 100644 (file)
@@ -82,43 +82,6 @@ static void t_read(int fd, unsigned mode, void *v)
 
 static void t_init(void) { return; }
 
-/* --- @t_open@ --- *
- *
- * Arguments:  @char **ifn@ = where to put the interface name
- *
- * Returns:    A file descriptor, or @-1@ on failure.
- *
- * Use:                Opens a tunnel device.  This will run with root privileges
- *             even if the rest of the server has dropped them.
- */
-
-static int t_open(char **ifn)
-{
-  int fd;
-  unsigned n;
-  char buf[16];
-
-  n = 0;
-  for (;;) {
-    sprintf(buf, "/dev/tun%u", n);
-    if ((fd = open(buf, O_RDWR)) >= 0)
-      break;
-    switch (errno) {
-      case EBUSY:
-       T( trace(T_TUNNEL, "tunnel device %u busy: skipping", n); )
-       break;
-      case ENOENT:
-       a_warn("TUN", "-", "bsd", "no-tunnel-devices", A_END);
-       return (-1);
-      default:
-       a_warn("TUN", "-", "open-error", "%s", buf, "?ERRNO", A_END);
-       break;
-    }
-    n++;
-  }
-  return (fd);
-}
-
 /* --- @t_create@ --- *
  *
  * Arguments:  @peer *p@ = pointer to peer block
@@ -176,8 +139,8 @@ static void t_destroy(tunnel *t)
 
 const tunnel_ops tun_bsd = {
   "bsd",
+  TUNF_PRIVOPEN,
   t_init,
-  t_open,
   t_create,
   0,
   t_inject,
index 11c7f37..b870580 100644 (file)
@@ -88,40 +88,6 @@ static void t_read(int fd, unsigned mode, void *v)
 
 static void t_init(void) { return; }
 
-/* --- @t_open@ --- *
- *
- * Arguments:  @char **ifn@ = where to put the interface name
- *
- * Returns:    A file descriptor, or @-1@ on failure.
- *
- * Use:                Opens a tunnel device.  This will run with root privileges
- *             even if the rest of the server has dropped them.
- */
-
-static int t_open(char **ifn)
-{
-  int fd;
-  struct ifreq iff;
-
-  if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
-    a_warn("TUN", "-", "linux",
-          "open-error", "/dev/net/tun", "?ERRNO",
-          A_END);
-    return (-1);
-  }
-  memset(&iff, 0, sizeof(iff));
-  iff.ifr_name[0] = 0;
-  iff.ifr_flags = IFF_TUN | IFF_NO_PI;
-  if (ioctl(fd, TUNSETIFF, &iff) < 0) {
-    a_warn("TUN", "-", "linux", "config-error", "?ERRNO", A_END);
-    close(fd);
-    return (-1);
-  }
-  iff.ifr_name[IFNAMSIZ - 1] = 0;
-  *ifn = xstrdup(iff.ifr_name);
-  return (fd);
-}
-
 /* --- @t_create@ --- *
  *
  * Arguments:  @peer *p@ = pointer to peer block
@@ -179,8 +145,8 @@ static void t_destroy(tunnel *t)
 
 const tunnel_ops tun_linux = {
   "linux",
+  TUNF_PRIVOPEN,
   t_init,
-  t_open,
   t_create,
   0,
   t_inject,
index 4afef75..40830f9 100644 (file)
@@ -440,8 +440,8 @@ static void t_destroy(tunnel *t)
 
 const tunnel_ops tun_slip = {
   "slip",
-  t_init,
   0,
+  t_init,
   t_create,
   t_setifname,
   t_inject,
index 88ce83d..d4b4cf3 100644 (file)
@@ -32,7 +32,7 @@
 
 #ifdef TUN_UNET
 #  include <sys/ioctl.h>
-#  include <net/if.h>
+#  include <linux/if.h>
 #  include <unet.h>
 #endif
 
@@ -88,44 +88,6 @@ static void t_read(int fd, unsigned mode, void *v)
 
 static void t_init(void) { return; }
 
-/* --- @t_open@ --- *
- *
- * Arguments:  @char **ifn@ = where to put the interface name
- *
- * Returns:    A file descriptor, or @-1@ on failure.
- *
- * Use:                Opens a tunnel device.  This will run with root privileges
- *             even if the rest of the server has dropped them.
- */
-
-static int t_open(char **ifn)
-{
-  int fd;
-  int f;
-  struct unet_info uni;
-
-  if ((fd = open("/dev/unet", O_RDWR)) < 0) {
-    a_warn("TUN", "-", "unet", "open-error", "/dev/unet", "?ERRNO", A_END);
-    goto fail_0;
-  }
-  if ((f = ioctl(fd, UNIOCGIFFLAGS)) < 0 ||
-      ioctl(fd, UNIOCSIFFLAGS, f | IFF_POINTOPOINT)) {
-    a_warn("TUN", "-", "unet", "config-error", "?ERRNO", A_END);
-    goto fail_1;
-  }
-  if (ioctl(t->f.fd, UNIOCGINFO, &uni)) {
-    a_warn("TUN", "-", "unet", "getinfo-error", "?ERRNO", A_END);
-    goto fail_1;
-  }
-  *ifn = xstrdup(uni.uni_ifname);
-  return (fd);
-
-fail_1:
-  close(fd);
-fail_0:
-  return (-1);
-}
-
 /* --- @t_create@ --- *
  *
  * Arguments:  @peer *p@ = pointer to peer block
@@ -183,8 +145,8 @@ static void t_destroy(tunnel *t)
 
 const tunnel_ops tun_unet = {
   "unet",
+  TUNF_PRIVOPEN,
   t_init,
-  t_open,
   t_create,
   0,
   t_inject,
diff --git a/vars.am b/vars.am
index 5780686..6eb59db 100644 (file)
--- a/vars.am
+++ b/vars.am
@@ -39,15 +39,17 @@ SUFFIXES             =
 
 TRIPE_INCLUDES = \
        -I$(top_builddir)/config.h \
-       -I$(top_srcdir)/common
+       -I$(top_srcdir)/common \
+       -I$(top_srcdir)/priv
 
 CPPFLAGS += $(TRIPE_INCLUDES)
 
 ###--------------------------------------------------------------------------
 ### Miscellanous useful definitions.
 
-## Library of common code.
+## Libraries of common code.
 libtripe = $(top_builddir)/common/libtripe.a
+libpriv = $(top_builddir)/priv/libpriv.a
 
 ## Create a directory if it doesn't exist.
 mkdir_p = $(top_srcdir)/config/install-sh -d