chiark / gitweb /
Merge branch 'privsep'
authorMark Wooding <mdw@distorted.org.uk>
Tue, 30 Dec 2008 14:51:07 +0000 (14:51 +0000)
committerMark Wooding <mdw@distorted.org.uk>
Tue, 30 Dec 2008 14:51:07 +0000 (14:51 +0000)
* privsep:
  server: Introduce privilege separation.
  server: Zap spurious space output by a_vformat.
  server: Make a_vformat public.
  server: Set admin socket permissions to match user.
  client: Capture server stderr and send it to the logfile.
  client: Better logging infrastructure.
  client: Clean up variable declarations.
  client: New options for setting user and group identities.
  client: Function for inserting arguments.
  peer, tunnels: New file-descriptor opening interface.

Conflicts:

server/Makefile.am

24 files changed:
Makefile.am
client/tripectl.1.in
client/tripectl.c
configure.ac
debian/tripe.install
init/tripe-init.in
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.8.in
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 8926279034a1a5ec9cb35d7c13bd36fc6db15faa..ce25e3704a9b3158aec6c51c6c44ba647b710549 100644 (file)
@@ -34,6 +34,7 @@ SUBDIRS                        =
 SUBDIRS                        += common
 SUBDIRS                        += uslip
 SUBDIRS                        += client
+SUBDIRS                        += priv
 SUBDIRS                        += server
 SUBDIRS                        += proxy
 SUBDIRS                        += pkstream
index 11fa6c94a5743411ef35c42707263d0939188abe..57061ae1125d543a04ca0ebbef4c479aa0712ba7 100644 (file)
@@ -63,6 +63,10 @@ Options:
        \&
 .RB [ \-p
 .IR path ]
+.RB [ \-U
+.IR user ]
+.RB [ \-G
+.IR group ]
 .RB [ \-S
 .IB arg , arg ,\fR...]
 .
@@ -179,6 +183,31 @@ any passed automatically by
 (e.g.,
 .BR \-a ).
 .TP
+.BI "\-U, \-\-setuid=" user
+Set uid to that of
+.I user
+(either a user name or integer uid) after initialization.  Also set gid
+to
+.IR user 's
+primary group, unless overridden by a
+.B \-G
+option.  If a new
+.BR tripe (8)
+server is going to be spawned then it is also passed corresponding
+.B \-U
+and
+.B \-G
+options.
+.TP
+.BI "\-G, \-\-setgid=" group
+Set gid to that of
+.I group
+(either a group name or integer gid) after initialization.  If a new
+.BR tripe (8)
+server is going to be spawned then it is also passed a corresponding
+.B \-G
+option.
+.TP
 .B "\-l, \-\-syslog"
 Send warnings and trace messages to the
 .BR syslog (8)
index 661e51990c6fa981cc64851abaf63aeb7b25d6b2..2df7935814c6f7211397301cc0cc00a37c16617e 100644 (file)
@@ -74,6 +74,7 @@
 
 /*----- Static variables --------------------------------------------------*/
 
+static sel_state sel;
 static const char *pidfile = 0;
 static const char *logname = 0;
 static FILE *logfp = 0;
@@ -119,6 +120,30 @@ static void checkbg(char **p)
     die(EXIT_FAILURE, "unexpected background tag `%s'", q);
 }
 
+static void dolog(int prio, const char *msg, ...)
+{
+  va_list ap;
+  dstr d = DSTR_INIT;
+  const char *cat;
+
+  va_start(ap, msg);
+  dstr_vputf(&d, msg, &ap);
+  va_end(ap);
+  if (f & f_syslog) syslog(prio, "%s", d.buf);
+  if (logfp) {
+    switch (prio) {
+      case LOG_WARNING: cat = "warning"; break;
+      case LOG_DEBUG: cat = "debug"; break;
+      case LOG_ERR: cat = "error"; break;
+      default: cat = "message"; break;
+    }
+    writelog(cat, d.buf);
+  }
+  if (prio == LOG_WARNING && (f & f_warn))
+    fprintf(stderr, "Warning: %s\n", d.buf);
+  dstr_destroy(&d);
+}
+
 static void checkfg(void)
   { if (bgtag) die(EXIT_FAILURE, "unexpected foreground response"); }
 
@@ -133,28 +158,13 @@ static void cline(char *p, size_t len, void *b)
   q = str_getword(&p);
   if (!q)
     return;
-  if (strcmp(q, "WARN") == 0) {
-    if (f & f_syslog)
-      syslog(LOG_WARNING, "%s", p);
-    if (logfp)
-      writelog("warning", p);
-    if (f & f_warn)
-      fprintf(stderr, "Warning: %s\n", p);
-  } else if (strcmp(q, "TRACE") == 0) {
-    if (f & f_syslog)
-      syslog(LOG_DEBUG, "%s", p);
-    if (logfp)
-      writelog("debug", p);
-  } else if (!(f & f_command)) {
-    if (f & f_syslog)
-      syslog(LOG_ERR, "unexpected output `%s %s'", q, p);
-    if (logfp) {
-      dstr d = DSTR_INIT;
-      dstr_putf(&d, "unexpected output `%s %s'", q, p);
-      writelog("error", d.buf);
-      dstr_destroy(&d);
-    }
-  } else if (strcmp(q, "FAIL") == 0) {
+  if (strcmp(q, "WARN") == 0)
+    dolog(LOG_WARNING, p);
+  else if (strcmp(q, "TRACE") == 0)
+    dolog(LOG_DEBUG, p);
+  else if (!(f & f_command))
+    dolog(LOG_ERR, "unexpected output `%s %s'", q, p);
+  else if (strcmp(q, "FAIL") == 0) {
     checkfg();
     die(EXIT_FAILURE, "%s", p);
   } else if (strcmp(q, "INFO") == 0) {
@@ -207,6 +217,16 @@ static void uline(char *p, size_t len, void *b)
   }
 }
 
+static void eline(char *p, size_t len, void *b)
+{
+  if (p)
+    dolog(LOG_WARNING, "(stderr): %s", p);
+  else {
+    selbuf_destroy(b);
+    close(fd);
+  }
+}
+
 static void setup(const char *cmd)
 {
   dstr d = DSTR_INIT;
@@ -275,6 +295,19 @@ static void cleanup(void) { if (pidfile) unlink(pidfile); }
 static void sigdie(int sig)
   { cleanup(); signal(sig, SIG_DFL); raise(sig); }
 
+static void putarg(string_v *av, const char *fmt, ...)
+{
+  va_list ap;
+  dstr d = DSTR_INIT;
+
+  va_start(ap, fmt);
+  dstr_vputf(&d, fmt, &ap);
+  dstr_putz(&d);
+  va_end(ap);
+  DA_UNSHIFT(av, xstrdup(d.buf));
+  dstr_destroy(&d);
+}
+
 static void version(FILE *fp)
   { pquis(fp, "$, TrIPE version " VERSION "\n"); }
 
@@ -305,6 +338,8 @@ Options in full:\n\
 \n\
 -D, --daemon           Become a background task after connecting.\n\
 -d, --directory=DIR    Select current directory [default " CONFIGDIR "].\n\
+-U, --setuid=USER      Set uid to USER after initialization.\n\
+-G, --setgid=GROUP     Set gid to GROUP after initialization.\n\
 -a, --admin-socket=FILE        Select socket to connect to\n\
                          [default " SOCKETDIR "/tripesock].\n\
 -P, --pidfile=FILE     Write process-id to FILE.\n\
@@ -327,6 +362,18 @@ int main(int argc, char *argv[])
   string_v spawnopts = DA_INIT;
   char *p;
   FILE *pidfp = 0;
+  int i;
+  size_t sz;
+  uid_t u = -1;
+  gid_t g = -1;
+  int pfd[2], efd[2];
+  pid_t kid;
+  struct sigaction sa;
+  sigset_t newmask, oldmask;
+  struct sockaddr_un sun;
+  selbuf bu, bs, be;
+  dstr d = DSTR_INIT;
+  sig hup;
 
   ego(argv[0]);
 
@@ -343,6 +390,10 @@ int main(int argc, char *argv[])
       { "version",     0,              0,      'v' },
       { "usage",       0,              0,      'u' },
       { "daemon",      0,              0,      'D' },
+      { "uid",         OPTF_ARGREQ,    0,      'U' },
+      { "setuid",      OPTF_ARGREQ,    0,      'U' },
+      { "gid",         OPTF_ARGREQ,    0,      'G' },
+      { "setgid",      OPTF_ARGREQ,    0,      'G' },
       { "directory",   OPTF_ARGREQ,    0,      'd' },
       { "admin-socket",        OPTF_ARGREQ,    0,      'a' },
       { "spawn",       0,              0,      's' },
@@ -355,7 +406,7 @@ int main(int argc, char *argv[])
       { 0,             0,              0,      0 }
     };
 
-    int i = mdwopt(argc, argv, "+hvuDd:a:sp:S:lwf:nP:", opts, 0, 0, 0);
+    i = mdwopt(argc, argv, "+hvuDU:G:d:a:sp:S:lwf:nP:", opts, 0, 0, 0);
     if (i < 0)
       break;
     switch (i) {
@@ -371,6 +422,12 @@ int main(int argc, char *argv[])
       case 'D':
        f |= f_daemon | f_noinput;
        break;
+      case 'U':
+       u = u_getuser(optarg, &g);
+       break;
+      case 'G':
+       g = u_getgroup(optarg);
+       break;
       case 'd':
        dir = optarg;
        break;
@@ -426,6 +483,8 @@ int main(int argc, char *argv[])
     die(EXIT_FAILURE, "couldn't open `%s' for writing: %s",
        pidfile, strerror(errno));
   }
+  sel_init(&sel);
+  sig_init(&sel);
   signal(SIGINT, sigdie);
   signal(SIGQUIT, sigdie);
   signal(SIGTERM, sigdie);
@@ -434,11 +493,6 @@ int main(int argc, char *argv[])
   /* --- Connect to the server --- */
 
   if (f & f_spawn) {
-    int pfd[2];
-    pid_t kid;
-    struct sigaction sa;
-    sigset_t newmask, oldmask;
-
     sa.sa_handler = reap;
     sigemptyset(&sa.sa_mask);
     sa.sa_flags = SA_NOCLDSTOP;
@@ -447,13 +501,14 @@ int main(int argc, char *argv[])
 #endif
     sigaction(SIGCHLD, &sa, 0);
 
-    DA_UNSHIFT(&spawnopts, (char *)sock);
-    DA_UNSHIFT(&spawnopts, "-a");
-    DA_UNSHIFT(&spawnopts, "-d.");
-    DA_UNSHIFT(&spawnopts, "-F");
-    DA_UNSHIFT(&spawnopts, (char *)spawnpath);
     DA_PUSH(&spawnopts, 0);
-    if (socketpair(PF_UNIX, SOCK_STREAM, 0, pfd))
+    if (g != (gid_t)-1) putarg(&spawnopts, "-G%lu", (unsigned long)g);
+    if (u != (uid_t)-1) putarg(&spawnopts, "-U%lu", (unsigned long)u);
+    putarg(&spawnopts, "-a%s", sock);
+    putarg(&spawnopts, "-d.");
+    putarg(&spawnopts, "-F");
+    putarg(&spawnopts, "%s", spawnpath);
+    if (socketpair(PF_UNIX, SOCK_STREAM, 0, pfd) || pipe(efd))
       die(EXIT_FAILURE, "error from socketpair: %s", strerror(errno));
     sigemptyset(&newmask);
     sigaddset(&newmask, SIGCHLD);
@@ -463,8 +518,9 @@ int main(int argc, char *argv[])
     if (!kid) {
       dup2(pfd[1], STDIN_FILENO);
       dup2(pfd[1], STDOUT_FILENO);
-      close(pfd[0]);
-      close(pfd[1]);
+      dup2(efd[1], STDERR_FILENO);
+      close(pfd[0]); close(pfd[1]);
+      close(efd[0]); close(efd[1]);
       if (logfp) fclose(logfp);
       if (pidfp) fclose(pidfp);
       closelog();
@@ -474,10 +530,10 @@ int main(int argc, char *argv[])
     }
     sigprocmask(SIG_SETMASK, &oldmask, 0);
     fd = pfd[0];
-    close(pfd[1]);
+    close(pfd[1]); close(efd[1]);
+    selbuf_init(&be, &sel, efd[0], eline, &be);
   } else {
-    struct sockaddr_un sun;
-    size_t sz = strlen(sock) + 1;
+    sz = strlen(sock) + 1;
     if (sz > sizeof(sun.sun_path))
       die(EXIT_FAILURE, "socket name `%s' too long", sock);
     memset(&sun, 0, sizeof(sun));
@@ -492,6 +548,7 @@ int main(int argc, char *argv[])
     }
   }
 
+  u_setugid(u, g);
   if (f & f_daemon) {
     if (daemonize())
       die(EXIT_FAILURE, "error becoming daemon: %s", strerror(errno));
@@ -507,10 +564,6 @@ int main(int argc, char *argv[])
   if (optind == argc)
     setup("WATCH -A+tw");
   if (!(f & f_noinput) && optind == argc) {
-    sel_state sel;
-    selbuf bu, bs;
-
-    sel_init(&sel);
     selbuf_init(&bu, &sel, STDIN_FILENO, uline, &bu);
     selbuf_init(&bs, &sel, fd, sline, &bs);
     for (;;) {
@@ -522,7 +575,6 @@ int main(int argc, char *argv[])
   /* --- If there's a command, submit it --- */
 
   if (optind < argc) {
-    dstr d = DSTR_INIT;
     setup((f & f_warn) ? "WATCH -A+w" : "WATCH -A");
     while (optind < argc)
       u_quotify(&d, argv[optind++]);
@@ -536,24 +588,15 @@ int main(int argc, char *argv[])
 
   /* --- Pull everything else out of the box --- */
 
-  {
-    sel_state sel;
-    selbuf b;
-    sig hup;
-
-    sel_init(&sel);
-    selbuf_init(&b, &sel, fd, cline, 0);
+  selbuf_init(&bs, &sel, fd, cline, 0);
 
-    if (f & f_syslog)
-      openlog(QUIS, 0, LOG_DAEMON);
-    if (logfp) {
-      sig_init(&sel);
-      sig_add(&hup, SIGHUP, sighup, 0);
-    }
-    for (;;) {
-      if (sel_select(&sel) && errno != EINTR && errno != EAGAIN)
-       die(EXIT_FAILURE, "select failed: %s", strerror(errno));
-    }
+  if (f & f_syslog)
+    openlog(QUIS, 0, LOG_DAEMON);
+  if (logfp)
+    sig_add(&hup, SIGHUP, sighup, 0);
+  for (;;) {
+    if (sel_select(&sel) && errno != EINTR && errno != EAGAIN)
+      die(EXIT_FAILURE, "select failed: %s", strerror(errno));
   }
 
   return (0);
index 674116e37da66c091beb09518223100b608c387f..61ed4a8fd1c1c25119d33bcd6989b15b9cc64d09 100644 (file)
@@ -95,6 +95,15 @@ TRIPE_DEFINE_PATH(
 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.
 
@@ -305,6 +314,7 @@ AC_CONFIG_FILES(
   [uslip/Makefile]
   [pathmtu/Makefile]
   [client/Makefile]
+  [priv/Makefile]
   [server/Makefile]
   [proxy/Makefile]
   [pkstream/Makefile]
index e68861de70a9a796f6f5c198a339838bc40fa792..7df892387a5104c25b9d890efd816c462a5f16d0 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
index 439a740d37c07e4a3b70de6806c624ab7c3bd572..dfa7e0319b818c31e10c20f104e09b39a01e2db6 100755 (executable)
@@ -122,8 +122,8 @@ case "$1" in
       ${keytag+-S-t}$keytag \
       ${addr+-S-b}$addr \
       ${port+-S-p}${port} \
-      ${user+-S-U}${user} \
-      ${group+-S-G}${group} \
+      ${user+-U}${user} \
+      ${group+-G}${group} \
       ${trace+-S-T}${trace} \
       ${tunnel+-S-n}${tunnel} \
       ${miscopts}
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 3c050a109a566ac1b22c06f332836200f0287bb4..e54b79789bdb3eb7c614c6fdf3759817c7db926e 100644 (file)
@@ -28,7 +28,7 @@ include $(top_srcdir)/vars.am
 sbin_PROGRAMS           =
 man_MANS                =
 
-LDADD                   = $(libtripe) $(catacomb_LIBS)
+LDADD                   = $(libtripe) $(libpriv) $(catacomb_LIBS)
 
 ###--------------------------------------------------------------------------
 ### The main server.
@@ -48,6 +48,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 045727f71a9df12378786e7a2be311cbf33d154d..a62bbe2243821a077725f71c7f3d1f88eb8e7b52 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" },
@@ -238,16 +239,34 @@ static void a_flush(int fd, unsigned mode, void *v)
  *
  * Returns:    ---
  *
- * Use:                Main message token formatting driver.
+ * Use:                Main message token formatting driver.  The arguments are
+ *             interleaved formatting tokens and their parameters, finally
+ *             terminated by an entry @A_END@.
+ *
+ *             Tokens recognized:
+ *
+ *               * "*..." ... -- pretokenized @dstr_putf@-like string
+ *
+ *               * "?ADDR" SOCKADDR -- a socket address, to be converted
+ *
+ *               * "?B64" BUFFER SIZE -- binary data to be base64-encoded
+ *
+ *               * "?TOKENS" VECTOR -- null-terminated vector of tokens
+ *
+ *               * "?PEER" PEER -- peer's name
+ *
+ *               * "?ERRNO" ERRNO -- system error code
+ *
+ *               * "[!]..." ... -- @dstr_putf@-like string as single token
  */
 
-static void a_vformat(dstr *d, const char *fmt, va_list ap)
+void a_vformat(dstr *d, const char *fmt, va_list ap)
 {
   dstr dd = DSTR_INIT;
 
   while (fmt) {
     if (*fmt == '*') {
-      dstr_putc(d, ' ');
+      if (d->len) dstr_putc(d, ' ');
       dstr_vputf(d, fmt + 1, &ap);
     } else if (*fmt == '?') {
       if (strcmp(fmt, "?ADDR") == 0) {
@@ -503,6 +522,7 @@ void a_quit(void)
   close(sock.fd);
   unlink(sockname);
   FOREACH_PEER(p, { p_destroy(p); });
+  ps_quit();
   exit(0);
 }
 
@@ -2182,13 +2202,15 @@ void a_daemon(void) { flags |= F_DAEMON; }
 /* --- @a_init@ --- *
  *
  * Arguments:  @const char *name@ = socket name to create
+ *             @uid_t u@ = user to own the socket
+ *             @gid_t g@ = group to own the socket
  *
  * Returns:    ---
  *
  * Use:                Creates the admin listening socket.
  */
 
-void a_init(const char *name)
+void a_init(const char *name, uid_t u, gid_t g)
 {
   int fd;
   int n = 5;
@@ -2244,6 +2266,11 @@ again:
     goto again;
   }
   chmod(sun.sun_path, 0600);
+  if (chown(sun.sun_path, u, g)) {
+    T( trace(T_ADMIN,
+            "admin: failed to give away socket: %s",
+            strerror(errno)); )
+  }
   fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
   if (listen(fd, 5))
     die(EXIT_FAILURE, "couldn't listen on socket: %s", strerror(errno));
index 7f4d09405a44db7713cef5aa80b9e615f9bdb595..4bb2c82c6b0f80b07da5f42ab928d7d61532270d 100644 (file)
@@ -724,6 +724,8 @@ static void p_setkatimer(peer *p)
 peer *p_create(peerspec *spec)
 {
   peer *p = CREATE(peer);
+  const tunnel_ops *tops = spec->tops;
+  int fd;
   unsigned f;
 
   p->byname = sym_find(&byname, spec->name, -1, sizeof(peer_byname), &f);
@@ -740,11 +742,17 @@ peer *p_create(peerspec *spec)
   p->ifname = 0;
   memset(&p->st, 0, sizeof(stats));
   p->st.t_start = time(0);
-  if ((p->t = spec->tops->create(p, &p->ifname)) == 0)
+  if (!(tops->flags & TUNF_PRIVOPEN))
+    fd = -1;
+  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;
+  T( trace(T_TUNNEL, "peer: attached interface %s to peer `%s'",
+          p->ifname, p_name(p)); )
   p_setkatimer(p);
   if (kx_init(&p->kx, p, &p->ks, p->spec.kxf))
-    goto tidy_3;
+    goto tidy_4;
   a_notify("ADD",
           "?PEER", p,
           "%s", p->ifname,
@@ -756,11 +764,13 @@ peer *p_create(peerspec *spec)
   }
   return (p);
 
-tidy_3:
+tidy_4:
   if (spec->t_ka)
     sel_rmtimer(&p->tka);
   xfree(p->ifname);
   p->t->ops->destroy(p->t);
+tidy_3:
+  if (fd >= 0) close(fd);
 tidy_2:
   am_remove(&byaddr, p->byaddr);
 tidy_1:
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 1a3bc4c5adb50296ac13733af483d202d00e30ed..393a6616f9523d84976046336c2b83224d15c8d3 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 edf0ea58639219ae53d6cd2e7eb8e09ce1edd7c6..9c6aea80244737cc7011d3fb3a4dd2fe5788dbd3 100644 (file)
@@ -215,7 +215,8 @@ to
 .IR user 's
 primary group, unless overridden by a
 .B \-G
-option.
+option.  The selected user (and group) will also be the owner of the
+administration socket.
 .TP
 .BI "\-G, \-\-setgid=" group
 Set gid to that of
index ebd8efc7eda172cc9fbee5b919403da709bd5516..a2731c384fde32f779ba7377806ef77c2a6744b9 100644 (file)
@@ -288,9 +288,10 @@ 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);
-  a_init(csock);
   if (f & f_daemon) {
     if (daemonize())
       die(EXIT_FAILURE, "couldn't become a daemon: %s", strerror(errno));
index 71c6023e20d4c67c65fc3be4adf01143e223041b..ab15787f386d3dc9e2ee05035969346b3f1d40e4 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,10 @@ 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 */
-  tunnel *(*create)(struct peer */*p*/, char **/*ifn*/);
+  tunnel *(*create)(struct peer */*p*/, int /*fd*/, char **/*ifn*/);
                                        /* Initializes a new tunnel */
   void (*setifname)(tunnel */*t*/, const char */*ifn*/);
                                        /*  Notifies ifname change */
@@ -824,6 +830,37 @@ extern int c_check(buf */*b*/);
 
 #define A_END ((char *)0)
 
+/* --- @a_vformat@ --- *
+ *
+ * Arguments:  @dstr *d@ = where to leave the formatted message
+ *             @const char *fmt@ = pointer to format string
+ *             @va_list ap@ = arguments in list
+ *
+ * Returns:    ---
+ *
+ * Use:                Main message token formatting driver.  The arguments are
+ *             interleaved formatting tokens and their parameters, finally
+ *             terminated by an entry @A_END@.
+ *
+ *             Tokens recognized:
+ *
+ *               * "*..." ... -- pretokenized @dstr_putf@-like string
+ *
+ *               * "?ADDR" SOCKADDR -- a socket address, to be converted
+ *
+ *               * "?B64" BUFFER SIZE -- binary data to be base64-encoded
+ *
+ *               * "?TOKENS" VECTOR -- null-terminated vector of tokens
+ *
+ *               * "?PEER" PEER -- peer's name
+ *
+ *               * "?ERRNO" ERRNO -- system error code
+ *
+ *               * "[!]..." ... -- @dstr_putf@-like string as single token
+ */
+
+extern void a_vformat(dstr */*d*/, const char */*fmt*/, va_list /*ap*/);
+
 /* --- @a_warn@ --- *
  *
  * Arguments:  @const char *fmt@ = pointer to format string
@@ -898,13 +935,15 @@ extern void a_daemon(void);
 /* --- @a_init@ --- *
  *
  * Arguments:  @const char *sock@ = socket name to create
+ *             @uid_t u@ = user to own the socket
+ *             @gid_t g@ = group to own the socket
  *
  * Returns:    ---
  *
  * Use:                Creates the admin listening socket.
  */
 
-extern void a_init(const char */*sock*/);
+extern void a_init(const char */*sock*/, uid_t /*u*/, gid_t /*g*/);
 
 /*----- Mapping with addresses as keys ------------------------------------*/
 
@@ -960,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 49b5e0e2b2c806457e52639b5a782ff6a73941e7..bfa528b2d5c4e63030aaa364ba2aa2961de7ba14 100644 (file)
@@ -38,7 +38,6 @@ struct tunnel {
   const tunnel_ops *ops;               /* Pointer to operations */
   sel_file f;                          /* Selector for tunnel device */
   struct peer *p;                      /* Pointer to my peer */
-  unsigned n;                          /* Number of my tunnel device */
 };
 
 /* --- @t_read@ --- *
@@ -86,6 +85,7 @@ static void t_init(void) { return; }
 /* --- @t_create@ --- *
  *
  * Arguments:  @peer *p@ = pointer to peer block
+ *             @int fd@ = file descriptor of tunnel device
  *             @char **ifn@ = where to put the interface name
  *
  * Returns:    A tunnel block if it worked, or null on failure.
@@ -93,42 +93,16 @@ static void t_init(void) { return; }
  * Use:                Initializes a new tunnel.
  */
 
-static tunnel *t_create(peer *p, char **ifn)
+static tunnel *t_create(peer *p, int fd, char **ifn)
 {
-  int fd;
-  unsigned n;
   tunnel *t;
-  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 (0);
-      default:
-       a_warn("TUN", "-", "open-error", "%s", buf, "?ERRNO", A_END);
-       break;
-    }
-    n++;
-  }
 
+  fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
   t = CREATE(tunnel);
   t->ops = &tun_bsd;
-  fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
   t->p = p;
-  t->n = n;
   sel_initfile(&sel, &t->f, fd, SEL_READ, t_read, t);
   sel_addfile(&t->f);
-  *ifn = xstrdup(buf + 5);
-  T( trace(T_TUNNEL, "tun-bsd: attached interface %s to peer `%s'",
-          *ifn, p_name(p)); )
   return (t);
 }
 
@@ -165,6 +139,7 @@ static void t_destroy(tunnel *t)
 
 const tunnel_ops tun_bsd = {
   "bsd",
+  TUNF_PRIVOPEN,
   t_init,
   t_create,
   0,
index cffeb56d0e7456ffc6ae2b53b2da91418e1db86d..b870580263edc3576c1becaab815c6223d7c9cfc 100644 (file)
@@ -91,6 +91,7 @@ static void t_init(void) { return; }
 /* --- @t_create@ --- *
  *
  * Arguments:  @peer *p@ = pointer to peer block
+ *             @int fd@ = file descriptor of tunnel device
  *             @char **ifn@ = where to put the interface name
  *
  * Returns:    A tunnel block if it worked, or null on failure.
@@ -98,37 +99,16 @@ static void t_init(void) { return; }
  * Use:                Initializes a new tunnel.
  */
 
-static tunnel *t_create(peer *p, char **ifn)
+static tunnel *t_create(peer *p, int fd, char **ifn)
 {
-  int fd;
-  int f;
-  struct ifreq iff;
   tunnel *t;
 
-  if ((fd = open("/dev/net/tun", O_RDWR)) < 0) {
-    a_warn("TUN", "-", "linux",
-           "open-error", "/dev/net/tun", "?ERRNO",
-           A_END);
-    return (0);
-  }
   fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
-  memset(&iff, 0, sizeof(iff));
-  iff.ifr_name[0] = 0;
-  iff.ifr_flags = IFF_TUN | IFF_NO_PI;
-  if ((f = ioctl(fd, TUNSETIFF, &iff)) < 0) {
-    a_warn("TUN", "-", "linux", "config-error", "?ERRNO", A_END);
-    close(fd);
-    return (0);
-  }
   t = CREATE(tunnel);
   t->ops = &tun_linux;
   t->p = p;
   sel_initfile(&sel, &t->f, fd, SEL_READ, t_read, t);
   sel_addfile(&t->f);
-  iff.ifr_name[IFNAMSIZ - 1] = 0;
-  *ifn = xstrdup(iff.ifr_name);
-  T( trace(T_TUNNEL, "tun-linux: attached interface %s to peer `%s'",
-          *ifn, p_name(p)); )
   return (t);
 }
 
@@ -165,6 +145,7 @@ static void t_destroy(tunnel *t)
 
 const tunnel_ops tun_linux = {
   "linux",
+  TUNF_PRIVOPEN,
   t_init,
   t_create,
   0,
index d405cc49f41075fcaa8f0c0b9030b664b60a08db..40830f9d6417acd06583911fa27f9ec5e77f98e8 100644 (file)
@@ -246,6 +246,7 @@ whine:
 /* --- @t_create@ --- *
  *
  * Arguments:  @peer *p@ = pointer to peer block
+ *             @int fd@ = file descriptor of tunnel device (unused)
  *             @char **ifn@ = where to put the interface name
  *
  * Returns:    A tunnel block if it worked, or null on failure.
@@ -253,7 +254,7 @@ whine:
  * Use:                Initializes a new tunnel.
  */
 
-static tunnel *t_create(peer *p, char **ifn)
+static tunnel *t_create(peer *p, int fd, char **ifn)
 {
   slipif *sl = 0;
   int pin[2] = { -1, -1 }, pout[2] = { -1, -1 };
@@ -439,6 +440,7 @@ static void t_destroy(tunnel *t)
 
 const tunnel_ops tun_slip = {
   "slip",
+  0,
   t_init,
   t_create,
   t_setifname,
index 2d11ca0f8e3480057b1e1167c58a59f6baaf81fe..d4b4cf3478a847e26d6897cd15c6e8ddc02cbcba 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
 
@@ -90,46 +90,25 @@ static void t_init(void) { return; }
 
 /* --- @t_create@ --- *
  *
- * Arguments:  @tunnel *t@ = pointer to tunnel block
- *             @peer *p@ = pointer to peer block
- *             @char *ifn@ = where to put the interface name
+ * Arguments:  @peer *p@ = pointer to peer block
+ *             @int fd@ = file descriptor of tunnel device
+ *             @char **ifn@ = where to put the interface name
  *
  * Returns:    A tunnel block if it worked, or null on failure.
  *
  * Use:                Initializes a new tunnel.
  */
 
-static tunnel *t_create(peer *p, char **ifn)
+static tunnel *t_create(peer *p, int fd, char **ifn)
 {
-  int fd;
   tunnel *t;
-  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);
-    return (0);
-  }
   fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
-  if ((f = ioctl(fd, UNIOCGIFFLAGS)) < 0 ||
-      ioctl(fd, UNIOCSIFFLAGS, f | IFF_POINTOPOINT)) {
-    a_warn("TUN", "-", "unet", "config-error", "?ERRNO", A_END);
-    close(fd);
-    return (0);
-  }
   t = CREATE(tunnel);
   t->ops = &tun_unet;
   t->p = p;
   sel_initfile(&sel, &t->f, fd, SEL_READ, t_read, t);
   sel_addfile(&t->f);
-
-  if (ioctl(t->f.fd, UNIOCGINFO, &uni)) {
-    a_warn("TUN", "-", "unet", "getinfo-error", "?ERRNO", A_END);
-    return (0);
-  }
-  *ifn = xstrdup(uni.uni_ifname);
-  T( trace(T_TUNNEL, "tun-unet: attached interface %s to peer `%s'",
-          *ifn, p_name(p)); )
   return (t);
 }
 
@@ -166,6 +145,7 @@ static void t_destroy(tunnel *t)
 
 const tunnel_ops tun_unet = {
   "unet",
+  TUNF_PRIVOPEN,
   t_init,
   t_create,
   0,
diff --git a/vars.am b/vars.am
index 5780686e7800d37512237a7f490e0cdd3341e255..6eb59dbf71c205414fc81971f97c6cf326dc7cfa 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