Initial checkin.
authormdw <mdw>
Sat, 3 Feb 2001 20:26:37 +0000 (20:26 +0000)
committermdw <mdw>
Sat, 3 Feb 2001 20:26:37 +0000 (20:26 +0000)
19 files changed:
.cvsignore [new file with mode: 0644]
.links [new file with mode: 0644]
.skelrc [new file with mode: 0644]
Makefile.am [new file with mode: 0644]
admin.c [new file with mode: 0644]
buf.c [new file with mode: 0644]
client.c [new file with mode: 0644]
configure.in [new file with mode: 0644]
keyexch.c [new file with mode: 0644]
keymgmt.c [new file with mode: 0644]
keyset.c [new file with mode: 0644]
peer.c [new file with mode: 0644]
servutil.c [new file with mode: 0644]
setup [new file with mode: 0755]
tripe.c [new file with mode: 0644]
tripe.h [new file with mode: 0644]
tun-unet.c [new file with mode: 0644]
util.c [new file with mode: 0644]
util.h [new file with mode: 0644]

diff --git a/.cvsignore b/.cvsignore
new file mode 100644 (file)
index 0000000..4e44f05
--- /dev/null
@@ -0,0 +1,4 @@
+Makefile.in
+configure
+aclocal.m4
+build
diff --git a/.links b/.links
new file mode 100644 (file)
index 0000000..c017eca
--- /dev/null
+++ b/.links
@@ -0,0 +1,6 @@
+COPYING
+install-sh
+mkinstalldirs
+missing
+config.sub
+config.guess
diff --git a/.skelrc b/.skelrc
new file mode 100644 (file)
index 0000000..0811d49
--- /dev/null
+++ b/.skelrc
@@ -0,0 +1,9 @@
+;;; -*-emacs-lisp-*-
+
+(setq skel-alist
+      (append
+       '((author . "Straylight/Edgeware")
+        (full-title . "Trivial IP Encryption (TrIPE)")
+        (program . "TrIPE"))
+       skel-alist))
+
diff --git a/Makefile.am b/Makefile.am
new file mode 100644 (file)
index 0000000..afbee21
--- /dev/null
@@ -0,0 +1,48 @@
+## -*-makefile-*-
+##
+## $Id: Makefile.am,v 1.1 2001/02/03 20:26:37 mdw Exp $
+##
+## Makefile for TrIPE
+##
+## (c) 2001 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.
+
+##----- Revision history ----------------------------------------------------
+##
+## $Log: Makefile.am,v $
+## Revision 1.1  2001/02/03 20:26:37  mdw
+## Initial checkin.
+##
+
+AUTOMAKE_OPTIONS = foreign
+
+tun = @tun@
+
+bin_PROGRAMS = tripe tripectl
+tripe_SOURCES = \
+       tripe.c tripe.h \
+       admin.c peer.c tun-$(tun).c \
+       keymgmt.c keyexch.c keyset.c \
+       buf.c servutil.c util.c util.h
+tripectl_SOURCES = \
+       client.c util.c util.h
+
+##----- That's all, folks ---------------------------------------------------
diff --git a/admin.c b/admin.c
new file mode 100644 (file)
index 0000000..508976f
--- /dev/null
+++ b/admin.c
@@ -0,0 +1,724 @@
+/* -*-c-*-
+ *
+ * $Id: admin.c,v 1.1 2001/02/03 20:26:37 mdw Exp $
+ *
+ * Admin interface for configuration
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: admin.c,v $
+ * Revision 1.1  2001/02/03 20:26:37  mdw
+ * Initial checkin.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "tripe.h"
+
+/*----- Global variables --------------------------------------------------*/
+
+#ifndef NTRACE
+
+const trace_opt tr_opts[] = {
+  { 't',       T_TUNNEL,       "tunnel events" },
+  { 'r',       T_PEER,         "peer events" },
+  { 'a',       T_ADMIN,        "admin interface" },
+  { 'p',       T_PACKET,       "packet contents" },
+  { 'c',       T_CRYPTO,       "crypto details" },
+  { 's',       T_KEYSET,       "symmetric keyset management" },
+  { 'x',       T_KEYEXCH,      "key exchange" },
+  { 'm',       T_KEYMGMT,      "key management" },
+  { 'A',       T_ALL,          "all of the above" },
+  { 0,         0,              0 }
+};
+
+unsigned tr_flags = 0;
+#endif
+
+/*----- Static variables --------------------------------------------------*/
+
+static admin *admins;
+static sel_file sock;
+static const char *sockname;
+static unsigned flags = 0;
+static admin *a_stdin = 0;
+static sig s_term, s_int, s_hup;
+
+#define F_DAEMON 1u
+#define F_INIT 2u
+
+#define T_RESOLVE SEC(30)
+
+/*----- Utility functions -------------------------------------------------*/
+
+/* --- @a_write@ --- *
+ *
+ * Arguments:  @admin *a@ = admin connection to write to
+ *             @const char *fmt@ = pointer to format string
+ *             @...@ = other arguments
+ *
+ * Returns:    ---
+ *
+ * Use:                Sends a message to an admin connection.
+ */
+
+static void a_write(admin *a, const char *fmt, ...)
+{
+  va_list ap;
+  dstr d = DSTR_INIT;
+  va_start(ap, fmt);
+  dstr_vputf(&d, fmt, ap);
+  va_end(ap);
+  write(a->fd, d.buf, d.len);
+  dstr_destroy(&d);
+}
+
+/* --- @a_warn@ --- *
+ *
+ * Arguments:  @const char *fmt@ = pointer to format string
+ *             @...@ = other arguments
+ *
+ * Returns:    ---
+ *
+ * Use:                Informs all admin connections of a warning.
+ */
+
+void a_warn(const char *fmt, ...)
+{
+  va_list ap;
+  admin *a;
+  dstr d = DSTR_INIT;
+
+  if (flags & F_INIT)
+    dstr_puts(&d, "WARN ");
+  va_start(ap, fmt);
+  dstr_vputf(&d, fmt, ap);
+  va_end(ap);
+  if (!(flags & F_INIT))
+    moan("%s", d.buf);
+  else {
+    dstr_putc(&d, '\n');
+    for (a = admins; a; a = a->next)
+      write(a->fd, d.buf, d.len);
+  }
+  dstr_destroy(&d);
+}
+
+/* --- @a_trace@ --- *
+ *
+ * Arguments:  @const char *p@ = pointer to a buffer
+ *             @size_t sz@ = size of the buffer
+ *             @void *v@ = uninteresting pointer
+ *
+ * Returns:    ---
+ *
+ * Use:                Custom trace output handler.
+ */
+
+#ifndef NTRACE
+static void a_trace(const char *p, size_t sz, void *v)
+{
+  dstr d = DSTR_INIT;
+  admin *a;
+
+  dstr_puts(&d, "TRACE ");
+  dstr_putm(&d, p, sz);
+  dstr_putc(&d, '\n');
+  for (a = admins; a; a = a->next)
+    write(a->fd, d.buf, d.len);
+  dstr_destroy(&d);  
+}
+#endif
+
+/* --- @a_quit@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Shuts things down nicely.
+ */
+
+void a_quit(void)
+{
+  close(sock.fd);
+  unlink(sockname);
+  exit(0);
+}
+
+/* --- @a_sigdie@ --- *
+ *
+ * Arguments:  @int sig@ = signal number
+ *             @void *v@ = an uninteresting argument
+ *
+ * Returns:    ---
+ *
+ * Use         Shuts down on receipt of a fatal signal.
+ */
+
+static void a_sigdie(int sig, void *v)
+{
+  char *p;
+  char buf[20];
+
+  switch (sig) {
+    case SIGTERM:      p = "SIGTERM"; break;
+    case SIGINT:       p = "SIGINT"; break;
+    default:
+      sprintf(buf, "signal %i", sig);
+      p = buf;
+      break;
+  }
+  a_warn("shutting down on %s", p);
+  a_quit();
+}
+
+/* --- @a_sighup@ --- *
+ *
+ * Arguments:  @int sig@ = signal number
+ *             @void *v@ = an uninteresting argument
+ *
+ * Returns:    ---
+ *
+ * Use         Logs a message about SIGHUP not being useful.
+ */
+
+static void a_sighup(int sig, void *v)
+{
+  a_warn("received SIGHUP: ignoring");
+}
+
+/*----- Adding peers ------------------------------------------------------*/
+/* --- @a_resolve@ --- *
+ *
+ * Arguments:  @struct hostent *h@ = pointer to resolved hostname
+ *             @void *v@ = pointer to admin block
+ *
+ * Returns:    ---
+ *
+ * Use:                Handles a completed name resolution.
+ */
+
+static void a_resolve(struct hostent *h, void *v)
+{
+  admin *a = v;
+  T( trace(T_ADMIN, "admin: %u resolved", a->seq); )
+  sel_rmtimer(&a->t);
+  if (!h)
+    a_write(a, "ERR couldn't resolve hostname `%s'\n", a->paddr);
+  else if (p_find(a->pname))
+    a_write(a, "ERR peer `%s' already registered\n", a->pname);
+  else {
+    memcpy(&a->peer.sin.sin_addr, h->h_addr, sizeof(struct in_addr));
+    if (!p_create(a->pname, &a->peer.sa, a->sasz))
+      a_write(a, "ERR couldn't create peer\n");
+    else
+      a_write(a, "OK\n");
+  }
+  xfree(a->pname);
+  xfree(a->paddr);
+  a->pname = 0;
+  selbuf_enable(&a->b);
+}
+
+/* --- @a_timer@ --- *
+ *
+ * Arguments:  @struct timeval *tv@ = timer
+ *             @void *v@ = pointer to admin block
+ *
+ * Returns:    ---
+ *
+ * Use:                Times out a resolver.
+ */
+
+static void a_timer(struct timeval *tv, void *v)
+{
+  admin *a = v;
+  T( trace(T_ADMIN, "admin: %u resolver timeout", a->seq); )
+  bres_abort(&a->r);
+  a_write(a, "ERR timeout resolving `%s'\n", a->paddr);
+  xfree(a->pname);
+  xfree(a->paddr);
+  a->pname = 0;
+  selbuf_enable(&a->b);
+}
+
+/* --- @acmd_add@ --- *
+ *
+ * Arguments:  @admin *a@ = connection which requested the addition
+ *             @unsigned ac@ = argument count
+ *             @char *av[]@ = pointer to the argument list
+ *
+ * Returns:    ---
+ *
+ * Use:                Adds a new peer.
+ */
+
+static void acmd_add(admin *a, unsigned ac, char *av[])
+{
+  unsigned long pt;
+  struct timeval tv;
+  char *p;
+
+  /* --- Make sure someone's not got there already --- */
+
+  if (p_find(av[0])) {
+    a_write(a, "ERR peer `%s' already registered\n", av[0]);
+    return;
+  }
+
+  /* --- Fill in the easy bits of address --- */
+
+  BURN(a->peer);
+  a->peer.sin.sin_family = AF_INET;
+  a->sasz = sizeof(a->peer.sin);
+  pt = strtoul(av[2], &p, 0);
+  if (*p) {
+    struct servent *s = getservbyname(av[2], "udp");
+    if (!s) {
+      a_write(a, "ERR service `%s' not known\n", av[2]);
+      return;
+    }
+    pt = ntohs(s->s_port);
+  }
+  if (pt == 0 || pt >= 65536) {
+    a_write(a, "ERR bad port number %lu\n", pt);
+    return;
+  }
+  a->peer.sin.sin_port = htons(pt);
+
+  /* --- If the name is numeric, do it the easy way --- */
+  
+  if (inet_aton(av[1], &a->peer.sin.sin_addr)) {
+    if (!p_create(av[0], &a->peer.sa, a->sasz))
+      a_write(a, "ERR couldn't create peer\n");
+    else
+      a_write(a, "OK\n");
+    return;
+  }
+
+  /* --- Store everything for later and crank up the resolver --- *
+   *
+   * We disable the line buffer until the resolver completes (or times out).
+   * This prevents other commands on the same connection (though the rest of
+   * the system continues regardless), but makes life simpler for the client.
+   */
+
+  a->pname = xstrdup(av[0]);
+  a->paddr = xstrdup(av[1]);
+  selbuf_disable(&a->b);
+  gettimeofday(&tv, 0);
+  tv.tv_sec += T_RESOLVE;
+  sel_addtimer(&sel, &a->t, &tv, a_timer, a);
+  bres_byname(&a->r, a->paddr, a_resolve, a);
+  T( trace(T_ADMIN, "admin: %u resolving hostname `%s'",
+          a->seq, a->paddr); )
+}
+
+/*----- Administration commands -------------------------------------------*/
+
+/* --- Miscellaneous commands --- */
+
+#ifndef NTRACE
+
+static void acmd_trace(admin *a, unsigned ac, char *av[])
+{
+  if (!ac || strcmp(av[0], "?") == 0) {
+    const trace_opt *t;
+    a_write(a, "INFO Trace options:\n");
+    for (t = tr_opts; t->ch; t++) {
+      a_write(a, "INFO %c %c  %s\n",
+             t->ch, (tr_flags & t->f) == t->f ? '*' : ' ', t->help);
+    }
+  } else {
+    unsigned sense = 1;
+    unsigned f = tr_flags;
+    const trace_opt *tt;
+    char *p = av[0];
+
+    while (*p) {
+      switch (*p) {
+       case '+': sense = 1; break;
+       case '-': sense = 0; break;
+       default:
+         for (tt = tr_opts; tt->ch; tt++) {
+           if (tt->ch == *p) {
+             if (sense) f |= tt->f;
+             else f &= ~tt->f;
+             goto tropt_ok;
+           }
+         }
+         a_write(a, "ERR unknown trace option `%c'\n", *p);
+         return;
+        tropt_ok:;
+         break;
+      }
+      p++;
+    }
+    tr_flags = f;
+    trace_level(tr_flags);
+  }
+  a_write(a, "OK\n");
+}
+
+#endif
+
+static void acmd_port(admin *a, unsigned ac, char *av[])
+{
+  a_write(a, "INFO %u\nOK\n", p_port());
+}
+
+static void a_destroy(admin */*a*/);
+
+static void acmd_daemon(admin *a, unsigned ac, char *av[])
+{
+  if (flags & F_DAEMON)
+    a_write(a, "ERR already running as a daemon\n");
+  else {
+    if (a_stdin) {
+      a_write(a_stdin, "WARN becoming a daemon\n");
+      a_destroy(a_stdin);
+    }
+    if (u_daemon())
+      a_write(a, "ERR error becoming a daemon: %s", strerror(errno));
+    else
+      flags |= F_DAEMON;
+  }
+}
+
+static void acmd_list(admin *a, unsigned ac, char *av[])
+{
+  peer *p;
+  for (p = p_first(); p; p = p_next(p))
+    a_write(a, "INFO %s\n", p_name(p));
+  a_write(a, "OK\n");
+}
+
+static void acmd_ifname(admin *a, unsigned ac, char *av[])
+{
+  peer *p;
+
+  if ((p = p_find(av[0])) == 0)
+    a_write(a, "ERR peer `%s' not found\n", av[0]);
+  else
+    a_write(a, "INFO %s\nOK\n", p_ifname(p));
+}
+
+static void acmd_addr(admin *a, unsigned ac, char *av[])
+{
+  peer *p;
+  const addr *ad;
+
+  if ((p = p_find(av[0])) == 0)
+    a_write(a, "ERR peer `%s' not found\n", av[0]);
+  else {
+    ad = p_addr(p);
+    assert(ad->sa.sa_family == AF_INET);
+    a_write(a, "INFO %s %u\nOK\n",
+           inet_ntoa(ad->sin.sin_addr),
+           (unsigned)ntohs(ad->sin.sin_port));
+  }
+}
+
+static void acmd_kill(admin *a, unsigned ac, char *av[])
+{
+  peer *p;
+  if ((p = p_find(av[0])) == 0)
+    a_write(a, "ERR peer `%s' not found\n", av[0]);
+  else {
+    p_destroy(p);
+    a_write(a, "OK\n");
+  }
+}
+
+static void acmd_quit(admin *a, unsigned ac, char *av[])
+{
+  a_warn("closing down on admin request");
+  a_quit();
+}
+
+/* --- The command table and help --- */
+
+typedef struct acmd {
+  const char *name;
+  const char *help;
+  unsigned argmin, argmax;
+  void (*func)(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
+} acmd;
+
+static void acmd_help(admin */*a*/, unsigned /*ac*/, char */*av*/[]);
+
+static const acmd acmdtab[] = {
+  { "help",    "HELP",                 0,      0,      acmd_help },
+#ifndef NTRACE
+  { "trace",   "TRACE [options]",      0,      1,      acmd_trace },
+#endif
+  { "port",    "PORT",                 0,      0,      acmd_port },
+  { "daemon",  "DAEMON",               0,      0,      acmd_daemon },
+  { "list",    "LIST",                 0,      0,      acmd_list },
+  { "ifname",  "IFNAME peer",          1,      1,      acmd_ifname },
+  { "addr",    "ADDR peer",            1,      1,      acmd_addr },
+  { "kill",    "KILL peer",            1,      1,      acmd_kill },
+  { "add",     "ADD peer addr port",   3,      3,      acmd_add },
+  { "quit",    "QUIT",                 0,      0,      acmd_quit },
+  { 0,         0,                      0,      0,      0 }
+};
+
+static void acmd_help(admin *a, unsigned ac, char *av[])
+{
+  const acmd *c;
+  for (c = acmdtab; c->name; c++)
+    a_write(a, "INFO %s\n", c->help);
+  a_write(a, "OK\n");
+}
+
+/*----- Connection handling -----------------------------------------------*/
+
+/* --- @a_destroy@ --- *
+ *
+ * Arguments:  @admin *a@ = pointer to an admin block
+ *
+ * Returns:    ---
+ *
+ * Use:                Destroys an admin block.
+ */
+
+static void a_destroy(admin *a)
+{
+  T( trace(T_ADMIN, "admin: destroying connection %u", a->seq); )
+  selbuf_destroy(&a->b);
+  if (a->b.reader.fd != a->fd)
+    close(a->b.reader.fd);
+  close(a->fd);
+  if (a->pname) {
+    xfree(a->pname);
+    xfree(a->paddr);
+    bres_abort(&a->r);
+    sel_rmtimer(&a->t);
+  }
+  if (a->next)
+    a->next->prev = a->prev;
+  if (a->prev)
+    a->prev->next = a->next;
+  else
+    admins = a->next;
+  if (a_stdin == a)
+    a_stdin = 0;
+  DESTROY(a);
+}
+
+/* --- @a_line@ --- *
+ *
+ * Arguments:  @char *p@ = pointer to the line read
+ *             @void *vp@ = pointer to my admin block
+ *
+ * Returns:    ---
+ *
+ * Use:                Handles a line of input.
+ */
+
+static void a_line(char *p, void *vp)
+{
+  admin *a = vp;
+  const acmd *c;
+  char *av[4];
+  size_t ac;
+
+  if (!p) {
+    a_destroy(a);
+    return;
+  }
+  ac = str_qsplit(p, av, 4, 0, STRF_QUOTE);
+  if (!ac)
+    return;
+  for (p = av[0]; *p; p++) *p = tolower((unsigned char)*p);
+  for (c = acmdtab; c->name; c++) {
+    if (strcmp(av[0], c->name) == 0) {
+      ac--;
+      if (c->argmin > ac || ac > c->argmax)
+       a_write(a, "ERR syntax: %s\n", c->help);
+      else
+       c->func(a, ac, av + 1);
+      return;
+    }
+  }
+  a_write(a, "ERR unknown command `%s'\n", av[0]);
+}
+
+/* --- @a_create@ --- *
+ *
+ * Arguments:  @int fd_in, fd_out@ = file descriptors to use
+ *
+ * Returns:    ---
+ *
+ * Use:                Creates a new admin connection.
+ */
+
+void a_create(int fd_in, int fd_out)
+{
+  admin *a = CREATE(admin);
+  T( static unsigned seq = 0; )
+  a->seq = seq++;
+  T( trace(T_ADMIN, "admin: accepted connection %u", a->seq); )
+  a->pname = 0;
+  if (fd_in == STDIN_FILENO)
+    a_stdin = a;
+  fdflags(fd_in, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
+  if (fd_out != fd_in)
+    fdflags(fd_out, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC);
+  a->fd = fd_out;
+  selbuf_init(&a->b, &sel, fd_in, a_line, a);
+  a->next = admins;
+  a->prev = 0;
+  if (admins)
+    admins->prev = a;
+  admins = a;
+}
+
+/* --- @a_accept@ --- *
+ *
+ * Arguments:  @int fd@ = file descriptor to accept
+ *             @unsigned mode@ = what to do
+ *             @void *v@ = uninteresting pointer
+ *
+ * Returns:    ---
+ *
+ * Use:                Accepts a new admin connection.
+ */
+
+static void a_accept(int fd, unsigned mode, void *v)
+{
+  int nfd;
+  struct sockaddr_un sun;
+  size_t sz = sizeof(sun);
+
+  if ((nfd = accept(fd, (struct sockaddr *)&sun, &sz)) < 0) {
+    a_warn("accept admin connection failed: %s", strerror(errno));
+    return;
+  }
+  a_create(nfd, nfd);
+}
+
+/* --- @a_daemon@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Informs the admin module that it's a daemon.
+ */
+
+void a_daemon(void)
+{
+  flags |= F_DAEMON;
+}
+
+/* --- @a_init@ --- *
+ *
+ * Arguments:  @const char *name@ = socket name to create
+ *
+ * Returns:    ---
+ *
+ * Use:                Creates the admin listening socket.
+ */
+
+void a_init(const char *name)
+{
+  int fd;
+  int n = 5;
+  struct sockaddr_un sun;
+  struct sigaction sa;
+  size_t sz;
+
+  /* --- Set up the socket address --- */
+
+  sz = strlen(name) + 1;
+  if (sz > sizeof(sun.sun_path))
+    die(EXIT_FAILURE, "socket name `%s' too long", name);
+  BURN(sun);
+  sun.sun_family = AF_UNIX;
+  memcpy(sun.sun_path, name, sz);
+  sz += offsetof(struct sockaddr_un, sun_path);
+
+  /* --- Attempt to bind to the socket --- */
+
+  umask(0077);
+again:
+  if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
+    die(EXIT_FAILURE, "couldn't create socket: %s", strerror(errno));
+  if (bind(fd, (struct sockaddr *)&sun, sz) < 0) {
+    struct stat st;
+    int e = errno;
+    if (errno != EADDRINUSE) {
+      die(EXIT_FAILURE, "couldn't bind to address `%s': %s",
+         sun.sun_path, strerror(e));
+    }
+    if (!n)
+      die(EXIT_FAILURE, "too many retries; giving up");
+    n--;
+    if (!connect(fd, (struct sockaddr *)&sun, sz)) {
+      die(EXIT_FAILURE, "server already listening on admin socket `%s'",
+         sun.sun_path);
+    }
+    if (errno != ECONNREFUSED)
+      die(EXIT_FAILURE, "couldn't bind to address: %s", strerror(e));
+    if (stat(sun.sun_path, &st)) {
+      die(EXIT_FAILURE, "couldn't stat `%s': %s",
+         sun.sun_path, strerror(errno));
+    }
+    if (!S_ISSOCK(st.st_mode))
+      die(EXIT_FAILURE, "object `%s' isn't a socket", sun.sun_path);
+    T( trace(T_ADMIN, "stale socket found; removing it"); )
+    unlink(sun.sun_path);
+    close(fd);
+    goto again;
+  }
+  chmod(sun.sun_path, 0600);
+  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));
+
+  /* --- Listen to the socket --- */
+
+  sel_initfile(&sel, &sock, fd, SEL_READ, a_accept, 0);
+  sel_addfile(&sock);
+  sockname = name;
+  bres_init(&sel);
+  T( trace_custom(a_trace, 0);
+     trace(T_ADMIN, "admin: enabled custom tracing"); )
+  flags |= F_INIT;
+
+  /* --- Set up signal handlers --- */
+
+  sig_add(&s_term, SIGTERM, a_sigdie, 0);
+  sig_add(&s_hup, SIGHUP, a_sighup, 0);
+  sigaction(SIGINT, 0, &sa);
+  if (sa.sa_handler != SIG_IGN)
+    sig_add(&s_int, SIGINT, a_sigdie, 0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/buf.c b/buf.c
new file mode 100644 (file)
index 0000000..9f14cf6
--- /dev/null
+++ b/buf.c
@@ -0,0 +1,235 @@
+/* -*-c-*-
+ *
+ * $Id: buf.c,v 1.1 2001/02/03 20:26:37 mdw Exp $
+ *
+ * Buffer handling
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: buf.c,v $
+ * Revision 1.1  2001/02/03 20:26:37  mdw
+ * Initial checkin.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "tripe.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @buf_init@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *             @void *p@ = pointer to a buffer
+ *             @size_t sz@ = size of the buffer
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes the buffer block appropriately.
+ */
+
+void buf_init(buf *b, void *p, size_t sz)
+{
+  b->base = b->p = p;
+  b->limit = b->p + sz;
+  b->f = 0;
+}
+
+/* --- @buf_break@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *
+ * Returns:    Some negative value.
+ *
+ * Use:                Marks a buffer as broken.
+ */
+
+int buf_break(buf *b) { b->f |= BF_BROKEN; return (-1); }
+
+/* --- @buf_ensure@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *             @size_t sz@ = size of data wanted
+ *
+ * Returns:    Zero if it worked, nonzero if there wasn't enough space.
+ *
+ * Use:                Ensures that there are @sz@ bytes still in the buffer.
+ */
+
+int buf_ensure(buf *b, size_t sz) { return (BENSURE(b, sz)); }
+
+/* --- @buf_get@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *             @void *p@ = pointer to a buffer
+ *             @size_t sz@ = size of the buffer
+ *
+ * Returns:    Zero if it worked, nonzero if there wasn't enough data.
+ *
+ * Use:                Fetches data from the buffer into some other place.
+ */
+
+int buf_get(buf *b, void *p, size_t sz)
+{
+  if (BENSURE(b, sz))
+    return (-1);
+  memcpy(p, BCUR(b), sz);
+  BSTEP(b, sz);
+  return (0);
+}
+
+/* --- @buf_put@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *             @const void *p@ = pointer to a buffer
+ *             @size_t sz@ = size of the buffer
+ *
+ * Returns:    Zero if it worked, nonzero if there wasn't enough space.
+ *
+ * Use:                Fetches data from some place and puts it in the buffer
+ */
+
+int buf_put(buf *b, const void *p, size_t sz)
+{
+  if (BENSURE(b, sz))
+    return (-1);
+  memcpy(BCUR(b), p, sz);
+  BSTEP(b, sz);
+  return (0);
+}
+
+/* --- @buf_getbyte@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *
+ * Returns:    A byte, or less than zero if there wasn't a byte there.
+ *
+ * Use:                Gets a single byte from a buffer.
+ */
+
+int buf_getbyte(buf *b)
+{
+  if (BENSURE(b, 1))
+    return (-1);
+  return (*b->p++);
+}
+
+/* --- @buf_putbyte@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *             @int ch@ = byte to write
+ *
+ * Returns:    Zero if OK, nonzero if there wasn't enough space.
+ *
+ * Use:                Puts a single byte in a buffer.
+ */
+
+int buf_putbyte(buf *b, int ch)
+{
+  if (BENSURE(b, 1))
+    return (-1);
+  *b->p++ = ch;
+  return (0);
+}
+
+/* --- @buf_getword@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *             @uint32 *w@ = where to put the word
+ *
+ * Returns:    Zero if OK, or nonzero if there wasn't a word there.
+ *
+ * Use:                Gets a 32-bit word from a buffer.
+ */
+
+int buf_getword(buf *b, uint32 *w)
+{
+  if (BENSURE(b, 4))
+    return (-1);
+  *w = LOAD32(b->p);
+  BSTEP(b, 4);
+  return (0);
+}
+
+/* --- @buf_putword@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *             @uint32 w@ = word to write
+ *
+ * Returns:    Zero if OK, nonzero if there wasn't enough space.
+ *
+ * Use:                Puts a 32-but word in a buffer.
+ */
+
+int buf_putword(buf *b, uint32 w)
+{
+  if (BENSURE(b, 4))
+    return (-1);
+  STORE32(b->p, w);
+  BSTEP(b, 4);
+  return (0);
+}
+
+/* --- @buf_getmp@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *
+ * Returns:    A multiprecision integer, or null if there wasn't one there.
+ *
+ * Use:                Gets a multiprecision integer from a buffer.
+ */
+
+mp *buf_getmp(buf *b, mp *d)
+{
+  uint32 sz;
+  if (buf_getword(b, &sz) || buf_ensure(b, sz))
+    return (0);
+  d = mp_loadb(d, BCUR(b), sz);
+  BSTEP(b, sz);
+  return (d);
+}
+
+/* --- @buf_putmp@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *             @mp *m@ = a multiprecision integer
+ *
+ * Returns:    Zero if it worked, nonzero if there wasn't enough space.
+ *
+ * Use:                Puts a multiprecision integer to a buffer.
+ */
+
+int buf_putmp(buf *b, mp *m)
+{
+  size_t sz = mp_octets(m);
+  if (buf_putword(b, sz) || buf_ensure(b, sz))
+    return (-1);
+  mp_storeb(m, BCUR(b), sz);
+  BSTEP(b, sz);
+  return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/client.c b/client.c
new file mode 100644 (file)
index 0000000..f6517fa
--- /dev/null
+++ b/client.c
@@ -0,0 +1,440 @@
+/* -*-c-*-
+ *
+ * $Id: client.c,v 1.1 2001/02/03 20:26:37 mdw Exp $
+ *
+ * Client for TrIPE
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: client.c,v $
+ * Revision 1.1  2001/02/03 20:26:37  mdw
+ * Initial checkin.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/wait.h>
+#include <syslog.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <mLib/alloc.h>
+#include <mLib/darray.h>
+#include <mLib/dstr.h>
+#include <mLib/lbuf.h>
+#include <mLib/mdwopt.h>
+#include <mLib/quis.h>
+#include <mLib/report.h>
+#include <mLib/sel.h>
+#include <mLib/selbuf.h>
+#include <mLib/str.h>
+
+#include "util.h"
+
+#undef sun
+
+/*----- Data structures ---------------------------------------------------*/
+
+#ifndef STRING_V
+#  define STRING_V
+   DA_DECL(string_v, char *);
+#endif
+
+/*----- Static variables --------------------------------------------------*/
+
+static FILE *logfp = 0;
+static unsigned f = 0;
+static int fd;
+
+#define f_bogus 1u
+#define f_spawn 2u
+#define f_daemon 4u
+#define f_spawnopts 8u
+#define f_syslog 16u
+#define f_command 32u
+#define f_noinput 64u
+#define f_warn 128u
+#define f_uclose 256u
+
+/*----- Main code ---------------------------------------------------------*/
+
+static void reap(int sig)
+{
+  int s;
+  int e = errno;
+  while (waitpid(-1, &s, WNOHANG) > 0)
+    ;
+  errno = e;
+}
+
+static void writelog(const char *cat, const char *msg)
+{
+  char buf[256];
+  time_t t = time(0);
+  struct tm *tm = localtime(&t);
+  strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", tm);
+  fprintf(logfp, "%s %s: %s\n", buf, cat, msg);
+}
+
+static void cline(char *p, void *b)
+{
+  char *q;
+  if (!p) {
+    if (f & f_command)
+      die(EXIT_FAILURE, "server dropped the connection");
+    exit(0);
+  }
+  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, "ERR") == 0)
+    die(EXIT_FAILURE, "%s", p);
+  else if (strcmp(q, "INFO") == 0)
+    puts(p);
+  else if (strcmp(q, "OK") == 0)
+    exit(0);
+  else
+    die(EXIT_FAILURE, "unexpected output `%s %s'", q, p); 
+}
+
+static void sline(char *p, void *b)
+{
+  if (!p) {
+    if (!(f & f_uclose))
+      moan("server closed the connection");
+    exit(0);
+  }
+  puts(p);
+}
+
+static void uline(char *p, void *b)
+{
+  size_t sz;
+  if (!p) {
+    selbuf_destroy(b);
+    shutdown(fd, 1);
+    f |= f_uclose;
+  } else {
+    sz = strlen(p);
+    p[sz] = '\n';
+    write(fd, p, sz + 1);
+  }
+}
+
+static void version(FILE *fp)
+{
+  pquis(fp, "$, TrIPE version " VERSION "\n");
+}
+
+static void usage(FILE *fp)
+{
+  pquis(fp, "\
+Usage:\n\
+       $ [-w] [-options] [command [args]...]\n\
+       $ [-Dl] [-f file] [-options]\n\
+Options:\n\
+       [-s] [-d directory] [-a socket] [-p program] [-S arg,arg,...]\n\
+");
+}
+
+static void help(FILE *fp)
+{
+  version(fp);
+  fputc('\n', fp);
+  usage(fp);
+  fputs("\
+\n\
+Options in full:\n\
+\n\
+-h, --help             Show this help text.\n\
+-v, --version          Show version number.\n\
+-u, --usage            Show brief usage message.\n\
+\n\
+-D, --daemon           Become a background task after connecting.\n\
+-d, --directory=DIR    Select current directory [default /var/lib/tripe]\n\
+-a, --admin-socket=FILE        Select socket to connect to.\n\
+\n\
+-s, --spawn            Start server rather than connecting.\n\
+-p, --spawn-path=PATH  Specify path to executable.\n\
+-S, --spawn-args=ARGS  Specify comma-separated arguments.\n\
+\n\
+-l, --syslog           Log messages to system log.\n\
+-f, --logfile=FILE     Log messages to FILE.\n\
+-w, --warnings         Show warnings when running commands.\n\
+", fp);
+}
+
+int main(int argc, char *argv[])
+{
+  const char *dir = "/var/lib/tripe";
+  const char *sock = "tripesock";
+  const char *spawnpath = "tripe";
+  string_v spawnopts = DA_INIT;
+  char *p;
+
+  ego(argv[0]);
+
+  if ((p = getenv("TRIPEDIR")) != 0)
+    dir = p;
+
+  /* --- Parse the arguments --- */
+
+  for (;;) {
+    static const struct option opts[] = {
+      { "help",                0,              0,      'h' },
+      { "version",     0,              0,      'v' },
+      { "usage",       0,              0,      'u' },
+      { "daemon",      0,              0,      'D' },
+      { "directory",   OPTF_ARGREQ,    0,      'd' },
+      { "admin-socket",        OPTF_ARGREQ,    0,      'a' },
+      { "spawn",       0,              0,      's' },
+      { "spawn-path",  OPTF_ARGREQ,    0,      'p' },
+      { "spawn-args",  OPTF_ARGREQ,    0,      'S' },
+      { "syslog",      0,              0,      'l' },
+      { "logfile",     OPTF_ARGREQ,    0,      'f' },
+      { "warnings",    0,              0,      'w' },
+      { 0,             0,              0,      0 }
+    };
+
+    int i = mdwopt(argc, argv, "hvuDd:a:sp:S:lwf:n", opts, 0, 0, 0);
+    if (i < 0)
+      break;
+    switch (i) {
+      case 'h':
+       help(stdout);
+       exit(0);
+      case 'v':
+       version(stdout);
+       exit(0);
+      case 'u':
+       usage(stdout);
+       exit(0);
+      case 'D':
+       f |= f_daemon | f_noinput;
+       break;
+      case 'd':
+       dir = optarg;
+       break;
+      case 'a':
+       sock = optarg;
+       break;
+      case 's':
+       f |= f_spawn;
+       break;
+      case 'p':
+       f |= f_spawn;
+       spawnpath = optarg;
+       break;
+      case 'S':
+       f |= f_spawn | f_spawnopts;
+       for (p = strtok(optarg, ","); p; p = strtok(0, ","))
+         DA_PUSH(&spawnopts, p);
+       break;
+      case 'l':
+       f |= f_syslog | f_noinput;
+       break;
+      case 'w':
+       f |= f_warn;
+       break;
+      case 'f':
+       if (logfp)
+         fclose(logfp);
+       if ((logfp = fopen(optarg, "a")) == 0) {
+         die(EXIT_FAILURE, "error opening logfile `%s': %s",
+             optarg, strerror(errno));
+       }
+       f |= f_noinput;
+       break;
+      default:
+       f |= f_bogus;
+       break;
+    }
+  }
+  if ((f & f_bogus) || ((f & f_noinput) && optind < argc)) {
+    usage(stderr);
+    exit(EXIT_FAILURE);
+  }
+
+  /* --- Set the world up --- */
+
+  if (chdir(dir)) {
+    die(EXIT_FAILURE, "couldn't set current directory to `%s': %s",
+       dir, strerror(errno));
+  }
+
+  /* --- 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;
+#ifdef SA_RESTART
+    sa.sa_flags |= SA_RESTART;
+#endif
+    sigaction(SIGCHLD, &sa, 0);
+
+    DA_UNSHIFT(&spawnopts, (char *)spawnpath);
+    if (!(f & f_spawnopts)) {
+      DA_PUSH(&spawnopts, "-d.");
+      DA_PUSH(&spawnopts, "-a");
+      DA_PUSH(&spawnopts, (char *)sock);
+    }
+    DA_PUSH(&spawnopts, 0);
+    if (socketpair(PF_UNIX, SOCK_STREAM, 0, pfd))
+      die(EXIT_FAILURE, "error from socketpair: %s", strerror(errno));
+    sigemptyset(&newmask);
+    sigaddset(&newmask, SIGCHLD);
+    sigprocmask(SIG_BLOCK, &newmask, &oldmask);
+    if ((kid = fork()) < 0)
+      die(EXIT_FAILURE, "fork failed: %s", strerror(errno));
+    if (!kid) {
+      dup2(pfd[1], STDIN_FILENO);
+      dup2(pfd[1], STDOUT_FILENO);
+      close(pfd[1]);
+      close(pfd[0]);
+      execvp(DA(&spawnopts)[0], DA(&spawnopts));
+      die(127, "couldn't exec `%s': %s", spawnpath, strerror(errno));
+    }
+    sigprocmask(SIG_SETMASK, &oldmask, 0);
+    fd = pfd[0];
+    close(pfd[1]);
+  } else {
+    struct sockaddr_un sun;
+    size_t sz = strlen(sock) + 1;
+    if (sz > sizeof(sun.sun_path))
+      die(EXIT_FAILURE, "socket name `%s' too long", sock);
+    memset(&sun, 0, sizeof(sun));
+    sun.sun_family = AF_UNIX;
+    memcpy(sun.sun_path, sock, sz);
+    sz += offsetof(struct sockaddr_un, sun_path);
+    if ((fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
+      die(EXIT_FAILURE, "error making socket: %s", strerror(errno));
+    if (connect(fd, (struct sockaddr *)&sun, sz)) {
+      die(EXIT_FAILURE, "error connecting to `%s': %s",
+         sock, strerror(errno));
+    }
+  }
+
+  if (f & f_daemon) {
+    if (u_daemon())
+      die(EXIT_FAILURE, "error becoming daemon: %s", strerror(errno));
+  }
+
+  /* --- If we're meant to be interactive, do that --- */
+
+  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 (;;) {
+      if (sel_select(&sel))
+       die(EXIT_FAILURE, "select failed: %s", strerror(errno));
+    }
+  }
+
+  /* --- If there's a command, submit it --- */
+
+  if (optind < argc) {
+    dstr d = DSTR_INIT;
+    dstr_puts(&d, argv[optind++]);
+    while (optind < argc) {
+      dstr_putc(&d, ' ');
+      dstr_puts(&d, argv[optind++]);
+    }
+    dstr_putc(&d, '\n');
+    write(fd, d.buf, d.len);
+    shutdown(fd, 1);
+    dstr_destroy(&d);
+    f |= f_command;
+  }
+
+  /* --- Pull everything else out of the box --- */
+
+  {
+    lbuf b;
+    lbuf_init(&b, cline, 0);
+    if (f & f_syslog)
+      openlog(QUIS, 0, LOG_DAEMON);
+    for (;;) {
+      size_t sz = lbuf_free(&b, &p);
+      ssize_t n = read(fd, p, sz);
+      if (n < 0)
+       die(EXIT_FAILURE, "read failed: %s", strerror(errno));
+      if (n == 0)
+       break;
+      lbuf_flush(&b, p, n);
+    }
+    lbuf_close(&b);
+  }
+
+  return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/configure.in b/configure.in
new file mode 100644 (file)
index 0000000..8e2254e
--- /dev/null
@@ -0,0 +1,62 @@
+dnl -*-fundamental-*-
+dnl
+dnl $Id: configure.in,v 1.1 2001/02/03 20:26:37 mdw Exp $
+dnl
+dnl Configuration script for TrIPE
+dnl
+dnl (c) 2001 Straylight/Edgeware
+dnl
+
+dnl ----- Licensing notice --------------------------------------------------
+dnl
+dnl This file is part of Trivial IP Encryption (TrIPE).
+dnl
+dnl TrIPE is free software; you can redistribute it and/or modify
+dnl it under the terms of the GNU General Public License as published by
+dnl the Free Software Foundation; either version 2 of the License, or
+dnl (at your option) any later version.
+dnl 
+dnl TrIPE is distributed in the hope that it will be useful,
+dnl but WITHOUT ANY WARRANTY; without even the implied warranty of
+dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+dnl GNU General Public License for more details.
+dnl 
+dnl You should have received a copy of the GNU General Public License
+dnl along with TrIPE; if not, write to the Free Software Foundation,
+dnl Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+dnl ----- Revision history --------------------------------------------------
+dnl
+dnl $Log: configure.in,v $
+dnl Revision 1.1  2001/02/03 20:26:37  mdw
+dnl Initial checkin.
+dnl
+
+AC_INIT(tripe.c)
+AM_INIT_AUTOMAKE(tripe, 1.0.0)
+AC_CANONICAL_HOST
+
+AC_PROG_CC
+mdw_GCC_FLAGS
+
+case $host_os in
+  linux*)
+    tun=unet
+    AC_DEFINE([TUN_TYPE], [TUN_UNET])
+    ;;
+  *bsd*)
+    tun=bsd
+    AC_DEFINE([TUN_TYPE], [TUN_BSD])
+    ;;
+  *)
+    AC_MSG_ERROR([Unsupported OS: no tunnel interface available])
+    ;;
+esac
+AC_SUBST(tun)
+
+mdw_MLIB(2.0.0pre4)
+mdw_CATACOMB(2.0.0pre8)
+
+AC_OUTPUT(Makefile)
+
+dnl ----- That's all, folks -------------------------------------------------
diff --git a/keyexch.c b/keyexch.c
new file mode 100644 (file)
index 0000000..1cf79fc
--- /dev/null
+++ b/keyexch.c
@@ -0,0 +1,548 @@
+/* -*-c-*-
+ *
+ * $Id: keyexch.c,v 1.1 2001/02/03 20:26:37 mdw Exp $
+ *
+ * Key exchange protocol
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: keyexch.c,v $
+ * Revision 1.1  2001/02/03 20:26:37  mdw
+ * Initial checkin.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "tripe.h"
+
+/*----- Tunable parameters ------------------------------------------------*/
+
+#define T_VALID MIN(2)
+#define T_QUIET SEC(5)
+#define T_RETRY SEC(10)
+#define T_NEWCHAL SEC(5)
+
+/*----- Handy macros ------------------------------------------------------*/
+
+#define FREECTX(kx) do {                                               \
+  keyexch *_kkx = (kx);                                                        \
+  if (_kkx->f & KXF_INIT) {                                            \
+    mp_drop(_kkx->my_x); mp_drop(_kkx->my_gx); mp_drop(_kkx->my_gxy);  \
+    mp_drop(_kkx->your_gx); mp_drop(_kkx->your_gxy);                   \
+  }                                                                    \
+} while (0)
+
+#define INITCTX(kx, now) do {                                          \
+  keyexch *_kx = (kx);                                                 \
+  time_t _now = (now);                                                 \
+  FREECTX(_kx);                                                                \
+  kx->my_x = kx->my_gx = kx->my_gxy = 0;                               \
+  kx->your_gx = kx->your_gxy = 0;                                      \
+  kx->t_valid = _now + T_VALID;                                                \
+  kx->t_qchal = kx->t_qresp = 0;                                       \
+  kx->t_newchal = 0;                                                   \
+  kx->f = (kx->f | KXF_INIT) & ~(KXF_MYH | KXF_YOURH |                 \
+                                KXF_REPLY | KXF_DONE);                 \
+} while (0)
+
+#define NEWCHAL(kx) do {                                               \
+  kx->f &= ~(KXF_YOURH | KXF_MYH);                                     \
+  mp_drop(kx->your_gx); kx->your_gx = 0;                               \
+  mp_drop(kx->your_gxy); kx->your_gxy = 0;                             \
+} while (0)
+
+#define ISVALID(kx, now) ((kx)->t_valid > (now))
+#define ISQ_CHAL(kx, now) ((kx)->t_qchal > (now))
+#define ISQ_RESP(kx, now) ((kx)->t_qresp > (now))
+#define ISNEWCHAL(kx, now) ((kx)->t_newchal > (now))
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @hashmp@ --- *
+ *
+ * Arguments:  @rmd160_ctx *r@ = pointer to hash context
+ *             @mp *m@ = pointer to multiprecision integer
+ *
+ * Returns:    ---
+ *
+ * Use:                Adds the hash of a multiprecision integer to the context.
+ *             Corrupts @buf_o@.
+ */
+
+static void hashmp(rmd160_ctx *r, mp *m)
+{
+  buf b;
+  buf_init(&b, buf_o, sizeof(buf_o));
+  buf_putmp(&b, m);
+  assert(BOK(&b));
+  rmd160_hash(r, BBASE(&b), BLEN(&b));
+}
+
+/* --- @timer@ --- *
+ *
+ * Arguments:  @struct timeval *tv@ = the current time
+ *             @void *v@ = pointer to key exchange context
+ *
+ * Returns:    ---
+ *
+ * Use:                Acts when the key exchange timer goes off.
+ */
+
+static void timer(struct timeval *tv, void *v)
+{
+  keyexch *kx = v;
+  kx->f &= ~KXF_TIMER;
+  T( trace(T_KEYEXCH, "keyexch: timer has popped"); )
+  kx_start(kx);
+}
+
+/* --- @settimer@ --- *
+ *
+ * Arguments:  @keyexch *kx@ = pointer to key exchange context
+ *             @time_t t@ = when to set the timer for
+ *
+ * Returns:    ---
+ *
+ * Use:                Sets the timer for the next key exchange attempt.
+ */
+
+static void settimer(keyexch *kx, time_t t)
+{
+  struct timeval tv;
+  if (kx->f & KXF_TIMER)
+    sel_rmtimer(&kx->t);
+  tv.tv_sec = t;
+  tv.tv_usec = 0;
+  sel_addtimer(&sel, &kx->t, &tv, timer, kx);
+  kx->f |= KXF_TIMER;
+}
+
+/* --- @update@ --- *
+ *
+ * Arguments:  @keyexch *kx@ = pointer to key exchange context
+ *
+ * Returns:    ---
+ *
+ * Use:                Updates the information in the key exchange context.  Call
+ *             this after new information has arrived.  Expects that the
+ *             context is actually valid.  Doesn't send any packets.
+ *             Assumes that everything in the context is known to be
+ *             correct.
+ */
+
+static void update(keyexch *kx)
+{
+  rmd160_ctx r;
+  octet h[RMD160_HASHSZ];
+  mp *k_shared;
+  buf b;
+
+  /* --- Give up if there's nothing more to do --- */
+
+  if (kx->f & KXF_DONE)
+    return;
+
+  /* --- If we've just started, generate a new challenge --- */
+
+  if (!kx->my_x) {
+    T( trace(T_KEYEXCH, "keyexch: generating new challenge"); )
+    kx->my_x = mprand_range(MP_NEWSEC, kx->kpub.dp.q, &rand_global, 0);
+    kx->my_gx = mpmont_exp(&mg, MP_NEW, kx->kpub.dp.g, kx->my_x);
+    kx->my_gxy = mpmont_exp(&mg, MP_NEW, kx->kpub.y, kx->my_x);
+    IF_TRACING(T_KEYEXCH, IF_TRACING(T_CRYPTO, {
+      trace(T_CRYPTO, "crypto: secret = %s", mpstr(kx->my_x));
+      trace(T_CRYPTO, "crypto: public value = %s", mpstr(kx->my_gx));
+      trace(T_CRYPTO, "crypto: expected reply = %s", mpstr(kx->my_gxy));
+    }))
+  }
+
+  /* --- If I don't have your challenge, I can't do anything more --- */
+
+  if (!kx->your_gx)
+    return;
+
+  /* --- If I've not computed my hash, I should do that --- */
+
+  if (!(kx->f & KXF_MYH)) {    
+    T( trace(T_KEYEXCH, "keyexch: computing my hash"); )
+    rmd160_init(&r);
+    hashmp(&r, kx->my_gx);
+    hashmp(&r, kx->your_gx);
+    hashmp(&r, kx->my_gxy);
+    rmd160_done(&r, kx->my_h);
+    IF_TRACING(T_KEYEXCH, trace_block(T_CRYPTO, "crypto: my hash",
+                                     kx->my_h, sizeof(kx->my_h)); )
+    kx->f |= KXF_MYH;
+  }
+
+  /* --- If I've received a full challenge, answer it --- *
+   *
+   * If it turns out to be wrong, clear the appropriate bits of data.
+   */
+
+  if ((kx->f & KXF_YOURH) && !kx->your_gxy) {
+    kx->your_gxy = mpmont_exp(&mg, MP_NEW, kx->your_gx, kpriv.x);
+    rmd160_init(&r);
+    hashmp(&r, kx->your_gx);
+    hashmp(&r, kx->my_gx);
+    hashmp(&r, kx->your_gxy);
+    rmd160_done(&r, h);
+    IF_TRACING(T_KEYEXCH, trace_block(T_CRYPTO, "crypto: computed hash",
+                                     h, sizeof(h)); )
+    if (memcmp(h, kx->your_h, sizeof(h)) != 0) {
+      IF_TRACING(T_KEYEXCH, {
+       trace_block(T_CRYPTO, "crypto: expected hash",
+                   kx->your_h, sizeof(kx->your_h));
+       trace(T_KEYEXCH, "keyexch: hashes don't match: botched");
+      })
+      NEWCHAL(kx);
+      return;
+    }
+  }
+
+  /* --- If I have a good reply, compute a shared key --- */
+
+  if ((kx->f & KXF_YOURH) && (kx->f & KXF_REPLY)) {
+    k_shared = mpmont_exp(&mg, MP_NEW, kx->your_gx, kx->my_x);
+    IF_TRACING(T_KEYEXCH, {
+      trace(T_KEYEXCH, "keyexch: computed shared key");
+      trace(T_CRYPTO, "crypto: shared key = %s", mpstr(k_shared));
+    })
+    buf_init(&b, buf_o, sizeof(buf_o));
+    buf_putmp(&b, k_shared); assert(BOK(&b));
+    settimer(kx, ks_gen(kx->ks, BBASE(&b), BLEN(&b)));
+    mp_drop(k_shared);
+    BURN(buf_o);
+    kx->f |= KXF_DONE;
+  }
+}
+
+/* --- @resend_chal@, @resent_resp@ --- *
+ *
+ * Arguments:  @keyexch *kx@ = pointer to key exchange context
+ *             @time_t now@ = the time right now
+ *
+ * Returns:    ---
+ *
+ * Use:                Sends packets to the remote host, according to the various
+ *             timers and information available.
+ */
+
+void resend_chal(keyexch *kx, time_t now)
+{
+  buf *b;
+
+  if (kx->f & KXF_DONE)
+    return;
+  if (ISQ_CHAL(kx, now)) {
+    T( trace(T_KEYEXCH, "keyexch: not sending a new challenge yet"); )
+    return;
+  }
+  if (!kx->your_gx) {
+    T( trace(T_KEYEXCH, "keyexch: sending prechallenge"); )
+    b = p_txstart(kx->p, MSG_PRECHALLENGE);
+  } else {
+    T( trace(T_KEYEXCH, "keyexch: sending challenge"); )
+    b = p_txstart(kx->p, MSG_CHALLENGE);
+    buf_put(b, kx->my_h, sizeof(kx->my_h));
+  }
+  buf_putmp(b, kx->my_gx);
+  p_txend(kx->p);
+  kx->t_qchal = now + T_QUIET;
+  settimer(kx, now + T_RETRY);
+}
+
+void resend_resp(keyexch *kx, time_t now)
+{
+  buf *b;
+
+  if (!kx->your_gxy)
+    return;
+  if (ISQ_RESP(kx, now)) {
+    T( trace(T_KEYEXCH, "keyexch: not sending a new response yet"); )
+    return;
+  }
+  T( trace(T_KEYEXCH, "keyexch: sending response"); )
+  b = p_txstart(kx->p, MSG_RESPONSE);
+  buf_putmp(b, kx->your_gxy);
+  p_txend(kx->p);
+  kx->t_qresp = now + T_QUIET;
+}
+
+/* --- @kx_start@ --- *
+ *
+ * Arguments:  @keyexch *kx@ = pointer to key exchange context
+ *
+ * Returns:    ---
+ *
+ * Use:                Stimulates a key exchange.  If a key exchage is in progress,
+ *             a new challenge is sent (unless the quiet timer forbids
+ *             this); if no exchange is in progress, one is commenced.
+ */
+
+void kx_start(keyexch *kx)
+{
+  time_t now = time(0);
+
+  if (!ISVALID(kx, now))
+    INITCTX(kx, now);
+  update(kx);
+  resend_chal(kx, now);
+}
+
+/* --- @dochallenge@ --- *
+ *
+ * Arguments:  @keyexch *kx@ = pointer to key exchange context
+ *             @time_t now@ = the current time
+ *             @mp *m@ = new challenge received
+ *             @const octet *h@ = challenge hash (if any)
+ *
+ * Returns:    ---
+ *
+ * Use:                Common code for handling challenge messages.  The caller
+ *             should have successfullly unpacked the challenge structure.
+ */
+
+static void dochallenge(keyexch *kx, time_t now, mp *m, const octet *h)
+{
+  unsigned f = 0;
+#define f_newchal 1u
+#define f_newhash 2u
+#define f_new (f_newchal | f_newhash)
+#define f_match 4u
+#define f_good (f_new | f_match)
+#define f_ignore 8u
+#define f_reset 16u
+#define f_change (f_new | f_reset)
+
+  /* --- Restart the process if necessary --- */
+
+  if (!ISVALID(kx, now))
+    INITCTX(kx, now);
+  if (!ISNEWCHAL(kx, now))
+    f |= f_ignore;
+
+  /* --- Sort out what to actually do --- */
+  
+  if (!kx->your_gx) {
+    f |= f_newchal;
+    if (h)
+      f |= f_newhash;
+  } else if (mp_eq(kx->your_gx, m)) {
+    if (!h || memcmp(h, kx->your_h, sizeof(kx->your_h)) == 0)
+      f |= f_match;
+    else if (!(kx->f & KXF_YOURH))
+      f |= f_newhash;
+  }
+
+  /* --- Update the values in the context --- */
+
+  if (f & f_good)
+    f &= ~f_ignore;
+  else if (!(f & f_ignore)) {
+    NEWCHAL(kx);
+    f |= f_reset;
+  }
+  if (f & (f_newchal | f_reset))
+    kx->your_gx = MP_COPY(m);
+  if (f & (f_newhash | f_reset)) {
+    memcpy(kx->your_h, h, sizeof(kx->your_h));
+    kx->f |= KXF_YOURH;  
+  }
+  if (f & f_new)
+    kx->t_qchal = 0;    
+
+  if (!(f & f_good)) {
+    a_warn("%s nonmatching challenge from `%s'",
+          (f & f_ignore) ? "rejecting" : "accepting", p_name(kx->p));
+  } else {
+    T( trace(T_KEYEXCH, "keyexch: good challenge (%s, %s) from `%s'",
+            (f & f_newchal) ? "new gxy" : "match gxy",
+            (f & f_newhash) ? "new hash" : "match hash", p_name(kx->p)); )
+  }
+  if (f & f_change)
+    update(kx);
+
+  if (f & f_new)
+    resend_chal(kx, now);
+#undef f_newchal
+#undef f_newhash
+#undef f_new
+#undef f_match
+#undef f_good
+#undef f_ignore
+#undef f_change
+}
+
+/* --- @kx_prechallenge@, @kx_challenge@ --- *
+ *
+ * Arguments:  @keyexch *kx@ = pointer to key exhange context
+ *             @buf *b@ = pointer to buffer containing the packet
+ *
+ * Returns:    ---
+ *
+ * Use:                Handle prechallenges and challenges.
+ */
+
+void kx_prechallenge(keyexch *kx, buf *b)
+{
+  time_t now = time(0);
+  mp *m;
+
+  if ((m = buf_getmp(b, MP_NEW)) == 0 || BLEFT(b)) {
+    a_warn("malformed prechallenge from `%s'", p_name(kx->p));
+    goto tidy;
+  }
+  dochallenge(kx, now, m, 0);
+tidy:
+  mp_drop(m);
+}
+
+void kx_challenge(keyexch *kx, buf *b)
+{
+  time_t now = time(0);
+  mp *m = 0;
+  const octet *h;
+
+  if (buf_ensure(b, RMD160_HASHSZ) ||
+      (h = BCUR(b), BSTEP(b, RMD160_HASHSZ), 
+       (m = buf_getmp(b, MP_NEW)) == 0 || BLEFT(b))) {
+    a_warn("malformed challenge from `%s'", p_name(kx->p));
+    goto tidy;
+  }
+  dochallenge(kx, now, m, h);
+  resend_resp(kx, now);
+tidy:
+  mp_drop(m);
+}
+
+/* --- @kx_response@ --- *
+ *
+ * Arguments:  @keyexch *kx@ = pointer to key exchange context
+ *             @buf *b@ = a buffer containing the packet to read
+ *
+ * Returns:    ---
+ *
+ * Use:                Reads a response from the buffer and handles it.
+ */
+
+void kx_response(keyexch *kx, buf *b)
+{
+  time_t now = time(0);
+  mp *m;
+
+  if ((m = buf_getmp(b, MP_NEW)) == 0 || BLEFT(b)) {
+    a_warn("malformed response from `%s'", p_name(kx->p));
+    goto tidy;
+  }
+  if (!ISVALID(kx, now))
+    INITCTX(kx, now);
+  if (!(kx->f & KXF_MYH)) {
+    a_warn("premature response from `%s'", p_name(kx->p));
+    goto tidy;
+  }
+  if (!mp_eq(m, kx->my_gxy)) {
+    a_warn("incorrect response from `%s'", p_name(kx->p));
+    goto tidy;
+  }
+  T( trace(T_KEYEXCH, "keyexch: valid response from `%s'", p_name(kx->p)); )
+  kx->f |= KXF_REPLY;
+  update(kx);
+
+tidy:
+  mp_drop(m);
+}
+
+/* --- @kx_free@ --- *
+ *
+ * Arguments:  @keyexch *kx@ = pointer to key exchange context
+ *
+ * Returns:    ---
+ *
+ * Use:                Frees everything in a key exchange context.
+ */
+
+void kx_free(keyexch *kx)
+{
+  if (kx->f & KXF_TIMER)
+    sel_rmtimer(&kx->t);
+  FREECTX(kx);
+  dh_pubfree(&kx->kpub);
+}
+
+/* --- @kx_newkeys@ --- *
+ *
+ * Arguments:  @keyexch *kx@ = pointer to key exchange context
+ *
+ * Returns:    ---
+ *
+ * Use:                Informs the key exchange module that its keys may have
+ *             changed.  If fetching the new keys fails, the peer will be
+ *             destroyed, we log messages and struggle along with the old
+ *             keys.
+ */
+
+void kx_newkeys(keyexch *kx)
+{
+  dh_pub dp;
+
+  if (km_getpubkey(p_name(kx->p), &dp))
+    return;
+  dh_pubfree(&kx->kpub);
+  kx->kpub = dp;
+  if (!(kx->f & KXF_DONE)) {
+    T( trace(T_KEYEXCH, "keyexch: restarting key negotiation with `%s'",
+            p_name(kx->p)); )
+    INITCTX(kx, time(0));
+  }
+}
+
+/* --- @kx_init@ --- *
+ *
+ * Arguments:  @keyexch *kx@ = pointer to key exchange context
+ *             @peer *p@ = pointer to peer context
+ *             @keyset **ks@ = pointer to keyset list
+ *
+ * Returns:    Zero if OK, nonzero if it failed.
+ *
+ * Use:                Initializes a key exchange module.  The module currently
+ *             contains no keys, and will attempt to initiate a key
+ *             exchange.
+ */
+
+int kx_init(keyexch *kx, peer *p, keyset **ks)
+{
+  kx->ks = ks;
+  kx->p = p;
+  kx->f = 0;
+  if (km_getpubkey(p_name(p), &kx->kpub))
+    return (-1);
+  kx->t_valid = 0;
+  kx_start(kx);
+  return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/keymgmt.c b/keymgmt.c
new file mode 100644 (file)
index 0000000..960b9c9
--- /dev/null
+++ b/keymgmt.c
@@ -0,0 +1,275 @@
+/* -*-c-*-
+ *
+ * $Id: keymgmt.c,v 1.1 2001/02/03 20:26:37 mdw Exp $
+ *
+ * Key loading and storing
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: keymgmt.c,v $
+ * Revision 1.1  2001/02/03 20:26:37  mdw
+ * Initial checkin.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "tripe.h"
+
+/*----- Global variables --------------------------------------------------*/
+
+mpmont mg;
+dh_priv kpriv;
+
+/*----- Static variables --------------------------------------------------*/
+
+static key_file *kf_pub;
+static const char *kr_priv, *kr_pub, *tag_priv;
+static fwatch w_priv, w_pub;
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @keymoan@ --- *
+ *
+ * Arguments:  @@const char *file@ = name of the file
+ *              @int line@ = line number in file
+ *              @const char *msg@ = error message
+ *              @void *p@ = argument pointer
+ *
+ * Returns:     ---
+ *
+ * Use:         Reports an error message about loading a key file.
+ */
+
+static void keymoan(const char *file, int line, const char *msg, void *p)
+{
+  a_warn("%s:%i: error: %s", file, line, msg);
+}
+
+/* --- @loadpriv@ --- *
+ *
+ * Arguments:  @dstr *d@ = string to write errors in
+ *             @dh_priv *dh@ = where to store the key
+ *
+ * Returns:    Zero if OK, nonzero on error.
+ *
+ * Use:                Loads the private key from its keyfile.
+ */
+
+static int loadpriv(dstr *d, dh_priv *dh)
+{
+  key_file kf;
+  key_packstruct kps[DH_PRIVFETCHSZ];
+  key_packdef *kp;
+  dh_priv mydh;
+  int rc = 0;
+  int e;
+
+  if (key_open(&kf, kr_priv, KOPEN_READ, keymoan, 0)) {
+    dstr_putf(d, "error reading private keyring `%s': %s",
+             kr_priv, strerror(errno));
+    rc = -1;
+  } else {
+    T( trace(T_KEYMGMT, "keymgmt: loaded private keyring `%s'", kr_priv); )
+    kp = key_fetchinit(dh_privfetch, kps, &mydh);
+    if ((e = key_fetchbyname(kp, &kf, tag_priv)) != 0) {
+      dstr_putf(d, "error loading private key `%s': %s",
+               tag_priv, key_strerror(e));
+      rc = -1;
+    } else {
+      dh->dp.p = MP_COPY(mydh.dp.p);
+      dh->dp.q = MP_COPY(mydh.dp.q);
+      dh->dp.g = MP_COPY(mydh.dp.g);
+      dh->x = MP_COPY(mydh.x);
+      dh->y = MP_COPY(mydh.y);      
+      IF_TRACING(T_KEYMGMT, {
+       trace(T_KEYMGMT, "keymgmt: extracted private key `%s'", tag_priv);
+       IF_TRACING(T_CRYPTO, {
+         trace(T_CRYPTO, "crypto: p = %s", mpstr(kpriv.dp.p));
+         trace(T_CRYPTO, "crypto: q = %s", mpstr(kpriv.dp.q));
+         trace(T_CRYPTO, "crypto: g = %s", mpstr(kpriv.dp.g));
+         trace(T_CRYPTO, "crypto: x = %s", mpstr(kpriv.x));
+         trace(T_CRYPTO, "crypto: g^x = %s", mpstr(kpriv.y));
+       })
+      })
+    }
+    key_fetchdone(kp);
+    key_close(&kf);
+  }
+  return (rc);
+}
+
+/* --- @loadpub@ --- *
+ *
+ * Arguments:  @dstr *d@ = string to write errors to
+ *
+ * Returns:    Zero if OK, nonzero on error.
+ *
+ * Use:                Reloads the public keyring.
+ */
+
+static int loadpub(dstr *d)
+{
+  key_file *kf = CREATE(key_file);
+
+  if (key_open(kf, kr_pub, KOPEN_READ, keymoan, 0)) {
+    dstr_putf(d, "error reading public keyring `%s': %s",
+             kr_pub, strerror(errno));
+    DESTROY(kf);
+    return (-1);
+  }
+  kf_pub = kf;
+  T( trace(T_KEYMGMT, "keymgmt: loaded public keyring `%s'", kr_pub); )
+  return (0);
+}
+
+/* --- @km_interval@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    Zero if OK, nonzero to force reloading of keys.
+ *
+ * Use:                Called on the interval timer to perform various useful jobs.
+ */
+
+int km_interval(void)
+{
+  dstr d = DSTR_INIT;
+  dh_priv dh;
+  key_file *kf;
+  int reload = 0;
+
+  /* --- Check the private key first --- */
+
+  if (fwatch_update(&w_priv, kr_priv)) {
+    T( trace(T_KEYMGMT, "keymgmt: private keyring updated: reloading..."); )
+    DRESET(&d);
+    if (loadpriv(&d, &dh))
+      a_warn("%s -- ignoring changes", d.buf);
+    else {
+      reload = 1;
+      mpmont_destroy(&mg);
+      dh_privfree(&kpriv);
+      kpriv = dh;
+      mpmont_create(&mg, kpriv.dp.p);
+    }
+  }
+
+  /* --- Now check the public keys --- */
+
+  if (fwatch_update(&w_pub, kr_pub)) {
+    T( trace(T_KEYMGMT, "keymgmt: public keyring updated: reloading..."); )
+    kf = kf_pub;
+    DRESET(&d);
+    if (loadpub(&d))
+      a_warn("%s -- ignoring changes", d.buf);
+    else {
+      reload = 1;
+      key_close(kf);
+      DESTROY(kf);
+    }
+  }
+
+  /* --- Done --- */
+
+  return (reload);
+}
+
+/* --- @km_init@ --- *
+ *
+ * Arguments:  @const char *priv@ = private keyring file
+ *             @const char *pub@ = public keyring file
+ *             @const char *tag@ = tag to load
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes, and loads the private key.
+ */
+
+void km_init(const char *priv, const char *pub, const char *tag)
+{
+  dstr d = DSTR_INIT;
+
+  kr_priv = priv;
+  kr_pub = pub;
+  tag_priv = tag;
+  fwatch_init(&w_priv, kr_priv);
+  fwatch_init(&w_pub, kr_pub);
+
+  DRESET(&d);
+  if (loadpriv(&d, &kpriv))
+    die(EXIT_FAILURE, "%s", d.buf);
+  mpmont_create(&mg, kpriv.dp.p);
+  if (loadpub(&d))
+    die(EXIT_FAILURE, "%s", d.buf);
+}
+
+/* --- @km_getpubkey@ --- *
+ *
+ * Arguments:  @const char *tag@ = public key tag to load
+ *             @dh_pub *kpub@ = where to put the public key
+ *
+ * Returns:    Zero if OK, nonzero if it failed.
+ *
+ * Use:                Fetches a public key from the keyring.
+ */
+
+int km_getpubkey(const char *tag, dh_pub *kpub)
+{
+  key_packstruct kps[DH_PUBFETCHSZ];
+  key_packdef *kp;
+  dh_pub dp;
+  int e;
+
+  kp = key_fetchinit(dh_pubfetch, kps, &dp);
+  e = key_fetchbyname(kp, kf_pub, tag);
+  key_fetchdone(kp);
+  if (e) {
+    a_warn("error loading public key `%s': %s", tag, key_strerror(e));
+    return (-1);
+  }
+  IF_TRACING(T_KEYMGMT, {
+    trace(T_KEYMGMT, "keymgmt: extracted public key `%s'", tag);
+    IF_TRACING(T_CRYPTO, {
+      trace(T_CRYPTO, "crypto: p = %s", mpstr(dp.dp.p));
+      trace(T_CRYPTO, "crypto: q = %s", mpstr(dp.dp.q));
+      trace(T_CRYPTO, "crypto: g = %s", mpstr(dp.dp.g));
+      trace(T_CRYPTO, "crypto: g^x = %s", mpstr(dp.y));
+    })
+  })
+  if (!mp_eq(dp.dp.p, kpriv.dp.p) ||
+      !mp_eq(dp.dp.q, kpriv.dp.q) ||
+      !mp_eq(dp.dp.g, kpriv.dp.g)) {
+    a_warn("public key `%s' has different group from private key", tag);
+    return (-1);
+  }
+  kpub->dp.p = MP_COPY(dp.dp.p);
+  kpub->dp.q = MP_COPY(dp.dp.q);
+  kpub->dp.g = MP_COPY(dp.dp.g);
+  kpub->y = MP_COPY(dp.y);
+  return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/keyset.c b/keyset.c
new file mode 100644 (file)
index 0000000..95c2549
--- /dev/null
+++ b/keyset.c
@@ -0,0 +1,308 @@
+/* -*-c-*-
+ *
+ * $Id: keyset.c,v 1.1 2001/02/03 20:26:37 mdw Exp $
+ *
+ * Handling of symmetric keysets
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: keyset.c,v $
+ * Revision 1.1  2001/02/03 20:26:37  mdw
+ * Initial checkin.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "tripe.h"
+
+/*----- Tunable parameters ------------------------------------------------*/
+
+#define KEY_EXPTIME MIN(60)            /* Expiry time for a key */
+#define KEY_REGENTIME MIN(45)          /* Regeneration time for a key */
+#define KEY_EXPSZ MEG(512)             /* Expiry data size for a key */
+#define KEY_REGENSZ MEG(256)           /* Data size threshold for regen */
+
+/*----- Handy macros ------------------------------------------------------*/
+
+#define KEYOK(ks, now) ((ks)->sz_exp > 0 && (ks)->t_exp > now)
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @freeks@ --- *
+ *
+ * Arguments:  @keyset *ks@ = pointer to a keyset
+ *
+ * Returns:    ---
+ *
+ * Use:                Frees a keyset.
+ */
+
+static void freeks(keyset *ks)
+{
+  ks->c->ops->destroy(ks->c);
+  ks->m->ops->destroy(ks->m);
+  DESTROY(ks);
+}
+
+/* --- @ks_free@ --- *
+ *
+ * Arguments:  @keyset **ksroot@ = pointer to keyset list head
+ *
+ * Returns:    ---
+ *
+ * Use:                Frees all of the keys in a keyset.
+ */
+
+void ks_free(keyset **ksroot)
+{
+  keyset *ks, *ksn;
+  for (ks = *ksroot; ks; ks = ksn) {
+    ksn = ks->next;
+    freeks(ks);
+  }
+}
+
+/* --- @ks_prune@ --- *
+ *
+ * Arguments:  @keyset **ksroot@ = pointer to keyset list head
+ *
+ * Returns:    ---
+ *
+ * Use:                Prunes the keyset list by removing keys which mustn't be used
+ *             any more.
+ */
+
+void ks_prune(keyset **ksroot)
+{
+  time_t now = time(0);
+
+  while (*ksroot) {
+    keyset *ks = *ksroot;
+    if (ks->t_exp <= now) {
+      T( trace(T_KEYSET, "keyset: expiring keyset %u (time limit reached)",
+              ks->seq); )
+      *ksroot = ks->next;
+      freeks(ks);
+    } else if (ks->sz_exp == 0) {
+      T( trace(T_KEYSET, "keyset: expiring keyset %u (data limit reached)",
+              ks->seq); )
+      *ksroot = ks->next;
+      freeks(ks);
+    } else
+      ksroot = &ks->next;
+  }
+}
+
+/* --- @ks_gen@ --- *
+ *
+ * Arguments:  @keyset **ksroot@ = pointer to keyset list head
+ *             @const void *k@ = pointer to key material
+ *             @size_t sz@ = size of the key material
+ *
+ * Returns:    The regeneration time for the new key.
+ *
+ * Use:                Derives a keyset from the given key material and adds it to
+ *             the list.
+ */
+
+time_t ks_gen(keyset **ksroot, const void *k, size_t sz)
+{
+  rmd160_ctx r;
+  octet buf[RMD160_HASHSZ];
+  keyset *ks = CREATE(keyset);
+  time_t now = time(0);
+  T( static unsigned seq = 0; )
+
+  T( trace(T_KEYSET, "keyset: adding new keyset %u", seq); )
+
+#define GETHASH(str) do {                                              \
+  rmd160_init(&r);                                                     \
+  rmd160_hash(&r, str, sizeof(str) - 1);                               \
+  rmd160_hash(&r, k, sz);                                              \
+  rmd160_done(&r, buf);                                                        \
+  IF_TRACING(T_KEYSET, {                                               \
+    trace_block(T_CRYPTO, "crypto: key " str, buf, sizeof(buf));       \
+  })                                                                   \
+} while (0)
+
+  GETHASH("tripe-encryption "); ks->c = blowfish_cbc.init(buf, sizeof(buf));
+  GETHASH("tripe-integrity "); ks->m = rmd160_hmac.key(buf, sizeof(buf));
+
+#undef GETHASH
+
+  T( ks->seq = seq++; )
+  ks->t_exp = now + KEY_EXPTIME;
+  ks->sz_exp = KEY_EXPSZ;
+  ks->next = *ksroot;
+  *ksroot = ks;
+  BURN(buf);
+  return (now + KEY_REGENTIME);
+}
+
+/* --- @ks_encrypt@ --- *
+ *
+ * Arguments:  @keyset **ksroot@ = pointer to keyset list head
+ *             @buf *b@ = pointer to input buffer
+ *             @buf *bb@ = pointer to output buffer
+ *
+ * Returns:    Nonzero if a new key is needed.
+ *
+ * Use:                Encrypts a packet.
+ */
+
+int ks_encrypt(keyset **ksroot, buf *b, buf *bb)
+{
+  time_t now = time(0);
+  keyset *ks;
+  ghash *h;
+  gcipher *c;
+  size_t ivsz;
+  const octet *p = BCUR(b);
+  octet *q = BCUR(bb);
+  size_t sz = BLEFT(b);
+  size_t osz, nsz;
+  int rc = 0;
+
+  /* --- Get the latest valid key --- */
+
+  ks = *ksroot;
+  for (;;) {
+    if (!ks) {
+      T( trace(T_KEYSET, "keyset: no active keys -- forcing exchange"); )
+      buf_break(bb);
+      return (-1);
+    }
+    if (KEYOK(ks, now))
+      break;
+    ks = ks->next;
+  }
+
+  /* --- MAC and encrypt the packet --- */
+
+  c = ks->c;
+  ivsz = c->ops->c->blksz;
+  if (buf_ensure(bb, ivsz + sz))
+    return (0);
+  h = ks->m->ops->init(ks->m);
+  h->ops->hash(h, p, sz);
+  h->ops->done(h, q);
+  IF_TRACING(T_KEYSET, {
+    trace(T_KEYSET, "keyset: encrypting using keyset %u", ks->seq);
+    trace_block(T_CRYPTO, "crypto: computed MAC", q, ivsz);
+  })
+  c->ops->setiv(c, q);
+  h->ops->destroy(h);
+
+  if (buf_ensure(bb, sz))
+    return (0);
+  c->ops->encrypt(c, p, q + ivsz, sz);
+  IF_TRACING(T_KEYSET, {
+    trace_block(T_CRYPTO, "crypto: encrypted packet", q + ivsz, sz);
+  })
+  BSTEP(bb, ivsz + sz);
+
+  /* --- Deduct the packet size from the key's data life --- */
+
+  osz = ks->sz_exp;
+  if (osz > sz)
+    nsz = osz - sz;
+  else
+    nsz = 0;
+  if (osz >= KEY_REGENSZ && nsz < KEY_REGENSZ) {
+    T( trace(T_KEYSET, "keyset: keyset %u data regen limit exceeded -- "
+            "forcing exchange", ks->seq); )
+    rc = -1;
+  }
+  ks->sz_exp = nsz;
+  return (rc);
+}
+
+/* --- @ks_decrypt@ --- *
+ *
+ * Arguments:  @keyset **ksroot@ = pointer to keyset list head
+ *             @buf *b@ = pointer to input buffer
+ *             @buf *bb@ = pointer to output buffer
+ *
+ * Returns:    Nonzero if the packet couldn't be decrypted.
+ *
+ * Use:                Decrypts a packet.
+ */
+
+int ks_decrypt(keyset **ksroot, buf *b, buf *bb)
+{
+  time_t now = time(0);
+  const octet *pp = BCUR(b);
+  const octet *p;
+  size_t sz = BLEFT(b);
+  octet *q = BCUR(bb);
+  keyset *ks;
+
+  T( trace(T_KEYSET, "keyset: attempting to decrypt packet"); )
+  if (buf_ensure(bb, sz))
+    return (-1);
+  for (ks = *ksroot; ks; ks = ks->next) {
+    ghash *h;
+    gcipher *c = ks->c;
+    size_t ivsz = c->ops->c->blksz;
+    octet *mac;
+    int eq;
+
+    if (!KEYOK(ks, now))
+      continue;
+    if (sz < ivsz) {
+      T( trace(T_KEYSET, "keyset: block too small for keyset %u", ks->seq); )
+      continue;
+    }
+    p = pp + ivsz;
+    c->ops->setiv(c, pp);
+    c->ops->decrypt(c, p, q, sz - ivsz);
+    h = ks->m->ops->init(ks->m);
+    h->ops->hash(h, q, sz - ivsz);
+    mac = h->ops->done(h, 0);
+    eq = !memcmp(mac, pp, ivsz);
+    IF_TRACING(T_KEYSET, {
+      trace(T_KEYSET, "keyset: decrypting using keyset %u", ks->seq);
+      trace_block(T_CRYPTO, "crypto: computed MAC", mac, ivsz);
+    })
+    h->ops->destroy(h);
+    if (eq) {
+      BSTEP(bb, sz - ivsz);
+      IF_TRACING(T_KEYSET, {
+       trace(T_KEYSET, "keyset: decrypted OK");
+       trace_block(T_CRYPTO, "crypto: decrypted packet", q, sz - ivsz);
+      })
+      return (0);
+    }
+    IF_TRACING(T_KEYSET, {
+      trace(T_KEYSET, "keyset: decryption failed");
+      trace_block(T_CRYPTO, "crypto: expected MAC", pp, ivsz);
+    })
+  }
+  T( trace(T_KEYSET, "keyset: no matching keys"); )
+  return (-1);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/peer.c b/peer.c
new file mode 100644 (file)
index 0000000..83123d1
--- /dev/null
+++ b/peer.c
@@ -0,0 +1,380 @@
+/* -*-c-*-
+ *
+ * $Id: peer.c,v 1.1 2001/02/03 20:26:37 mdw Exp $
+ *
+ * Communication with the peer
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: peer.c,v $
+ * Revision 1.1  2001/02/03 20:26:37  mdw
+ * Initial checkin.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "tripe.h"
+
+/*----- Static variables --------------------------------------------------*/
+
+static peer *peers = 0;
+static sel_file sock;
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @p_read@ --- *
+ *
+ * Arguments:  @int fd@ = file descriptor to read from
+ *             @unsigned mode@ = what happened
+ *             @void *v@ = an uninteresting pointer
+ *
+ * Returns:    ---
+ *
+ * Use:                Reads a packet from somewhere.
+ */
+
+static void p_read(int fd, unsigned mode, void *v)
+{
+  peer *p;
+  addr a;
+  size_t sz;
+  ssize_t n;
+  int ch;
+  buf b, bb;
+
+  /* --- Read the data --- */
+
+  sz = sizeof(addr);
+  n = recvfrom(fd, buf_i, sizeof(buf_i), 0, &a.sa, &sz);
+  if (n < 0) {
+    a_warn("error reading socket: %s", strerror(errno));
+    return;
+  }
+
+  /* --- Find the appropriate peer --- */
+
+  assert(a.sa.sa_family == AF_INET);
+  T( trace(T_PEER, "packet from %s:%u", 
+          inet_ntoa(a.sin.sin_addr), (unsigned)ntohs(a.sin.sin_port)); )
+  for (p = peers; p; p = p->next) {
+    T( trace(T_PEER, "trying %s:%u", 
+            inet_ntoa(p->peer.sin.sin_addr), (unsigned)ntohs(p->peer.sin.sin_port)); )
+    if (p->peer.sin.sin_addr.s_addr == a.sin.sin_addr.s_addr &&
+       p->peer.sin.sin_port == a.sin.sin_port)
+      goto found;
+  }
+  a_warn("packet from unexpected peer: %s:%u",
+       inet_ntoa(a.sin.sin_addr), (unsigned)ntohs(a.sin.sin_port));
+  return;
+
+found:
+  T( trace(T_PEER, "peer: packet received from `%s'", p->name);
+     trace_block(T_PACKET, "peer: packet contents", buf_i, n); )
+
+  /* --- Pick the packet apart --- */
+
+  buf_init(&b, buf_i, n);
+  if ((ch = buf_getbyte(&b)) < 0) {
+    a_warn("bad packet from `%s': no type byte", p->name);
+    return;
+  }
+  switch (ch) {
+    case MSG_PACKET:
+      buf_init(&bb, buf_o, sizeof(buf_o));
+      if (ks_decrypt(&p->ks, &b, &bb)) {
+       a_warn("couldn't decrypt inbound packet");
+       return;
+      }
+      if (BOK(&bb))
+       tun_inject(&p->t, &bb);
+      else
+       a_warn("packet build failed");
+      break;
+    case MSG_PRECHALLENGE:
+      kx_prechallenge(&p->kx, &b);
+      break;
+    case MSG_CHALLENGE:
+      kx_challenge(&p->kx, &b);
+      break;
+    case MSG_RESPONSE:
+      kx_response(&p->kx, &b);
+      break;
+    default:
+      a_warn("bad packet from `%s': unknown packet type", p->name);
+      break;
+  }
+}
+
+/* --- @p_txstart@ --- *
+ *
+ * Arguments:  @peer *p@ = pointer to peer block
+ *             @unsigned msg@ = message type code
+ *
+ * Returns:    A pointer to a buffer to write to.
+ *
+ * Use:                Starts sending to a peer.  Only one send can happen at a
+ *             time.
+ */
+
+buf *p_txstart(peer *p, unsigned msg)
+{
+  buf_init(&p->b, buf_o, sizeof(buf_o));
+  buf_putbyte(&p->b, msg);
+  return (&p->b);
+}
+
+/* --- @p_txend@ --- *
+ *
+ * Arguments:  @peer *p@ = pointer to peer block
+ *
+ * Returns:    ---
+ *
+ * Use:                Sends a packet to the peer.
+ */
+
+void p_txend(peer *p)
+{
+  if (!BOK(&p->b)) {
+    a_warn("packet build failed");
+    return;
+  }
+  IF_TRACING(T_PEER, trace_block(T_PACKET, "peer: sending packet",
+                                BBASE(&p->b), BLEN(&p->b)); )
+  if (sendto(sock.fd, BBASE(&p->b), BLEN(&p->b),
+            0, &p->peer.sa, p->sasz) < 0)
+    a_warn("packet send to `%s' failed: %s", p->name, strerror(errno));
+}
+
+/* --- @p_tun@ --- *
+ *
+ * Arguments:  @peer *p@ = pointer to peer block
+ *             @buf *b@ = buffer containing incoming packet
+ *
+ * Returns:    ---
+ *
+ * Use:                Handles a packet which needs to be sent to a peer.
+ */
+
+void p_tun(peer *p, buf *b)
+{
+  buf *bb = p_txstart(p, MSG_PACKET);
+  if (ks_encrypt(&p->ks, b, bb))
+    kx_start(&p->kx);
+  if (BCUR(bb) > BBASE(bb))
+    p_txend(p);
+}
+
+/* --- @p_interval@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Called periodically to do tidying.
+ */
+
+void p_interval(void)
+{
+  peer *p, *pp;
+  int reload;
+
+  reload = km_interval();
+  for (p = peers; p; p = pp) {
+    pp = p->next;
+    if (reload)
+      kx_newkeys(&p->kx);
+    ks_prune(&p->ks);
+  }
+}
+
+/* --- @p_ifname@ --- *
+ *
+ * Arguments:  @peer *p@ = pointer to a peer block
+ *
+ * Returns:    A pointer to the peer's interface name.
+ */
+
+const char *p_ifname(peer *p) { return (tun_ifname(&p->t)); }
+
+/* --- @p_addr@ --- *
+ *
+ * Arguments:  @peer *p@ = pointer to a peer block
+ *
+ * Returns:    A pointer to the peer's address.
+ */
+
+const addr *p_addr(peer *p) { return (&p->peer); }
+
+/* --- @p_init@ --- *
+ *
+ * Arguments:  @unsigned port@ = port number to listen to
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes the peer system; creates the socket.
+ */
+
+void p_init(unsigned port)
+{
+  int fd;
+  struct sockaddr_in sin;
+
+  if ((fd = socket(PF_INET, SOCK_DGRAM, 0)) < 0)
+    die(EXIT_FAILURE, "socket creation failed: %s", strerror(errno));
+  BURN(sin);
+  sin.sin_family = AF_INET;
+  sin.sin_addr.s_addr = INADDR_ANY;
+  sin.sin_port = htons(port);
+  if (bind(fd, (struct sockaddr *)&sin, sizeof(sin)))
+    die(EXIT_FAILURE, "bind failed: %s", strerror(errno));
+  sel_initfile(&sel, &sock, fd, SEL_READ, p_read, 0);
+  sel_addfile(&sock);
+  T( trace(T_PEER, "peer: created socket"); )
+}
+
+/* --- @p_port@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    Port number used for socket.
+ */
+
+unsigned p_port(void)
+{
+  addr a;
+  size_t sz = sizeof(addr);
+
+  if (getsockname(sock.fd, &a.sa, &sz))
+    die(EXIT_FAILURE, "couldn't read port number: %s", strerror(errno));
+  assert(a.sa.sa_family == AF_INET);
+  return (ntohs(a.sin.sin_port));
+}
+
+/* --- @p_create@ --- *
+ *
+ * Arguments:  @const char *name@ = name for this peer
+ *             @struct sockaddr *sa@ = socket address of peer
+ *             @size_t sz@ = size of socket address
+ *
+ * Returns:    Pointer to the peer block, or null if it failed.
+ *
+ * Use:                Creates a new named peer block.  No peer is actually attached
+ *             by this point.
+ */
+
+peer *p_create(const char *name, struct sockaddr *sa, size_t sz)
+{
+  peer *p = CREATE(peer);
+  T( trace(T_PEER, "peer: creating new peer `%s'", name); )
+  p->name = xstrdup(name);
+  p->ks = 0;
+  p->prev = 0;
+  memcpy(&p->peer.sa, sa, sz);
+  p->sasz = sz;
+  if (kx_init(&p->kx, p, &p->ks))
+    goto tidy_0;
+  if (tun_create(&p->t, p))
+    goto tidy_1;
+  p->next = peers;
+  if (peers)
+    peers->prev = p;
+  peers = p;
+  return (p);
+
+tidy_1:
+  kx_free(&p->kx);
+tidy_0:
+  xfree(p->name);
+  DESTROY(p);
+  return (0);
+}
+
+/* --- @p_name@ --- *
+ *
+ * Arguments:  @peer *p@ = pointer to a peer block
+ *
+ * Returns:    A pointer to the peer's name.
+ */
+
+const char *p_name(peer *p) { return (p->name); }
+
+/* --- @p_find@ --- *
+ *
+ * Arguments:  @const char *name@ = name to look up
+ *
+ * Returns:    Pointer to the peer block, or null if not found.
+ *
+ * Use:                Finds a peer by name.
+ */
+
+peer *p_find(const char *name)
+{
+  peer *p;
+  for (p = peers; p; p = p->next) {
+    if (strcmp(name, p->name) == 0)
+      return (p);
+  }
+  return (0);  
+}
+
+/* --- @p_destroy@ --- *
+ *
+ * Arguments:  @peer *p@ = pointer to a peer
+ *
+ * Returns:    ---
+ *
+ * Use:                Destroys a peer.
+ */
+
+void p_destroy(peer *p)
+{
+  T( trace(T_PEER, "peer: destroying peer `%s'", p->name); )
+  ks_free(&p->ks);
+  kx_free(&p->kx);
+  tun_destroy(&p->t);
+  xfree(p->name);
+  if (p->next)
+    p->next->prev = p->prev;
+  if (p->prev)
+    p->prev->next = p->next;
+  else
+    peers = p->next;
+  DESTROY(p);
+}
+
+/* --- @p_first@, @p_next@ --- *
+ *
+ * Arguments:  @peer *p@ = a peer block
+ *
+ * Returns:    @peer_first@ returns the first peer in some ordering;
+ *             @peer_next@ returns the peer following a given one in the
+ *             same ordering.  Null is returned for the end of the list.
+ */
+
+peer *p_first(void) { return (peers); }
+peer *p_next(peer *p) { return (p->next); }
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/servutil.c b/servutil.c
new file mode 100644 (file)
index 0000000..951df2d
--- /dev/null
@@ -0,0 +1,60 @@
+/* -*-c-*-
+ *
+ * $Id: servutil.c,v 1.1 2001/02/03 20:26:37 mdw Exp $
+ *
+ * Various handy server-only utilities
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: servutil.c,v $
+ * Revision 1.1  2001/02/03 20:26:37  mdw
+ * Initial checkin.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "tripe.h"
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @mpstr@ --- *
+ *
+ * Arguments:  @mp *m@ = a multiprecision integer
+ *
+ * Returns:    A pointer to the integer's textual representation.
+ *
+ * Use:                Converts a multiprecision integer to a string.  Corrupts
+ *             @buf_o@.
+ */
+
+const char *mpstr(mp *m)
+{
+  if (mp_writestring(m, (char *)buf_o, sizeof(buf_o), 10))
+    return ("<failed>");
+  return ((const char *)buf_o);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/setup b/setup
new file mode 100755 (executable)
index 0000000..27dde72
--- /dev/null
+++ b/setup
@@ -0,0 +1,8 @@
+#! /bin/sh
+
+set -e
+mklinks
+mkaclocal
+autoconf
+automake
+mkdir build
diff --git a/tripe.c b/tripe.c
new file mode 100644 (file)
index 0000000..6ff4a6a
--- /dev/null
+++ b/tripe.c
@@ -0,0 +1,310 @@
+/* -*-c-*-
+ *
+ * $Id: tripe.c,v 1.1 2001/02/03 20:26:37 mdw Exp $
+ *
+ * Main program
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: tripe.c,v $
+ * Revision 1.1  2001/02/03 20:26:37  mdw
+ * Initial checkin.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "tripe.h"
+
+/*----- Global variables --------------------------------------------------*/
+
+sel_state sel;
+octet buf_i[PKBUFSZ], buf_o[PKBUFSZ];
+
+/*----- Static variables --------------------------------------------------*/
+
+static sel_timer it;
+#define T_INTERVAL MIN(1)
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @interval@ --- *
+ *
+ * Arguments:  @struct timeval *tv@ = time when called
+ *             @void *v@ = boring pointer
+ *
+ * Returns:    ---
+ *
+ * Use:                Called periodically to do housekeeping tasks.
+ */
+
+void interval(struct timeval *tv, void *v)
+{
+  struct timeval tvv;
+  T( trace(T_PEER, "peer: interval timer"); )
+  p_interval();
+  tvv = *tv;
+  tvv.tv_sec += T_INTERVAL;
+  sel_addtimer(&sel, &it, &tvv, interval, v);
+}
+
+/* --- @main@ --- *
+ *
+ * Arguments:  @int argc@ = number of command line arguments
+ *             @char *argv[]@ = vector of arguments
+ *
+ * Returns:    Zero if OK, nonzero on error.
+ *
+ * Use:                Main program.  Provides a simple VPN.
+ */
+
+static void usage(FILE *fp)
+{
+  pquis(fp, "Usage: $ [-options]\n");
+}
+
+static void version(FILE *fp)
+{
+  pquis(fp, "$, version " VERSION "\n");
+}
+
+static void help(FILE *fp)
+{
+  version(fp);
+  fputc('\n', fp);
+  usage(fp);
+  fputs("\n\
+Options:\n\
+\n\
+-h, --help             Display this help text.\n\
+-v, --version          Display version number.\n\
+-u, --usage            Display pointless usage message.\n\
+\n\
+-D, --daemon           Run in the background.\n\
+-d, --directory=DIR    Switch to directory DIR (default $TRIPEDIR).\n\
+-p, --port=PORT                Select UDP port to listen to.\n\
+-k, --priv-keyring=FILE        Get private key from FILE.\n\
+-K, --pub-keyring=FILE Get public keys from FILE.\n\
+-t, --tag=KEYTAG       Use private key labelled TAG.\n\
+-a, --admin-socket=FILE        Use FILE as the adminstration socket.\n\
+-T, --trace=OPTIONS    Turn on tracing options.\n\
+", fp);
+}
+
+int main(int argc, char *argv[])
+{
+  const char *kr_priv = "keyring", *kr_pub = "keyring.pub";
+  const char *tag_priv = "tripe-dh";
+  const char *csock = "tripesock";
+  const char *dir = "/var/lib/tripe";
+  const char *p;
+  unsigned port = 0;
+  unsigned f = 0;
+  uid_t u = -1;
+  gid_t g = -1;
+
+#define f_bogus 1u
+#define f_daemon 2u
+
+  ego(argv[0]);
+  trace_on(stderr, 0);
+
+  if ((p = getenv("TRIPEDIR")) != 0)
+    dir = p;
+
+  for (;;) {
+    static const struct option opts[] = {
+      { "help",                0,              0,      'h' },
+      { "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' },
+      { "port",                OPTF_ARGREQ,    0,      'p' },
+      { "directory",   OPTF_ARGREQ,    0,      'd' },
+      { "priv-keyring",        OPTF_ARGREQ,    0,      'k' },
+      { "pub-keyring", OPTF_ARGREQ,    0,      'K' },
+      { "tag",         OPTF_ARGREQ,    0,      't' },
+      { "admin-socket", OPTF_ARGREQ,   0,      'a' },
+#ifndef NTRACE
+      { "trace",       OPTF_ARGREQ,    0,      'T' },
+#endif
+
+      { 0,             0,              0,      0 }
+    };
+
+    int i = mdwopt(argc, argv, "hvu DU:G: p:d:k:K:t:a:" T("T:"),
+                  opts, 0, 0, 0);
+    if (i < 0)
+      break;
+    switch (i) {
+      case 'h':
+       help(stdout);
+       exit(0);
+      case 'v':
+       version(stdout);
+       exit(0);
+      case 'u':
+       usage(stdout);
+       exit(0);
+
+      case 'D':
+       f |= f_daemon;
+       break;
+      case 'U': {
+       char *p;
+       unsigned long i = strtoul(optarg, &p, 0);
+       if (!*p)
+         u = i;
+       else {
+         struct passwd *pw;
+         if ((pw = getpwnam(optarg)) == 0)
+           die(EXIT_FAILURE, "user name `%s' not found", optarg);
+         u = pw->pw_uid;
+       }
+      } break;
+      case 'G': {
+       char *p;
+       unsigned long i = strtoul(optarg, &p, 0);
+       if (!*p)
+         g = i;
+       else {
+         struct group *gr;
+         if ((gr = getgrnam(optarg)) == 0)
+           die(EXIT_FAILURE, "group name `%s' not found", optarg);
+         g = gr->gr_gid;
+       }
+      } break;
+         
+      case 'p': {
+       char *p;
+       unsigned long i = strtoul(optarg, &p, 0);
+       if (*p) {
+         struct servent *s = getservbyname(optarg, "udp");
+         if (!s)
+           die(EXIT_FAILURE, "unknown service name `%s'", optarg);
+         i = ntohs(s->s_port);
+       }
+       if (i == 0 || i >= 65536)
+         die(EXIT_FAILURE, "bad port number %lu", i);
+       port = i;
+      } break;
+      case 'd':
+       dir = optarg;
+       break;
+      case 'k':
+       kr_priv = optarg;
+       break;
+      case 'K':
+       kr_pub = optarg;
+       break;
+      case 'a':
+       csock = optarg;
+       break;
+      case 't':
+       tag_priv = optarg;
+       break;
+#ifndef NTRACE
+      case 'T':
+       tr_flags = traceopt(tr_opts, optarg, tr_flags, 0);
+       trace_level(tr_flags);
+       break;
+#endif
+      default:
+       f |= f_bogus;
+       break;
+    }
+  }
+
+  if (optind < argc || (f & f_bogus)) {
+    usage(stderr);
+    exit(EXIT_FAILURE);
+  }
+
+  if (chdir(dir)) {
+    die(EXIT_FAILURE, "can't set current directory to `%s': %s",
+       dir, strerror(errno));
+  }
+
+  sel_init(&sel);
+  sig_init(&sel);
+  rand_noisesrc(RAND_GLOBAL, &noise_source);
+  rand_seed(RAND_GLOBAL, RMD160_HASHSZ);
+  signal(SIGPIPE, SIG_IGN);
+  tun_init();
+  p_init(port);
+  if (!(f & f_daemon))
+    a_create(STDIN_FILENO, STDOUT_FILENO);
+  if (g != -1) {
+    if (setgid(g)) {
+      die(EXIT_FAILURE, "couldn't setgid to %u: %s",
+         (unsigned)g, strerror(errno));
+    }
+  }
+  if (u != -1) {
+    if (setuid(u)) {
+      die(EXIT_FAILURE, "couldn't setuid to %u: %s",
+         (unsigned)u, strerror(errno));
+    }
+  }
+  km_init(kr_priv, kr_pub, tag_priv);
+  a_init(csock);
+  if (f & f_daemon) {
+    if (u_daemon)
+      die(EXIT_FAILURE, "couldn't become a daemon: %s", strerror(errno));
+    a_daemon();
+  }
+
+  {
+    struct timeval tv;
+    tv.tv_sec = time(0) + T_INTERVAL;
+    tv.tv_usec = 0;
+    sel_addtimer(&sel, &it, &tv, interval, 0);
+  }
+
+  {
+    int selerr = 0;
+    for (;;) {
+      if (!sel_select(&sel))
+        selerr = 0;
+      else if (errno != EINTR && errno != EAGAIN) {
+       a_warn("select failed: %s", strerror(errno));
+       abort();
+       selerr++;
+       if (selerr > 8) {
+         a_warn("too many select errors: bailing out");
+         a_quit();
+       }
+      }
+    }
+  }
+
+  return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/tripe.h b/tripe.h
new file mode 100644 (file)
index 0000000..171f23a
--- /dev/null
+++ b/tripe.h
@@ -0,0 +1,931 @@
+/* -*-c-*-
+ *
+ * $Id: tripe.h,v 1.1 2001/02/03 20:26:37 mdw Exp $
+ *
+ * Main header file for TrIPE
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: tripe.h,v $
+ * Revision 1.1  2001/02/03 20:26:37  mdw
+ * Initial checkin.
+ *
+ */
+
+#ifndef TRIPE_H
+#define TRIPE_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <pwd.h>
+#include <grp.h>
+
+#include <mLib/alloc.h>
+#include <mLib/arena.h>
+#include <mLib/bres.h>
+#include <mLib/dstr.h>
+#include <mLib/env.h>
+#include <mLib/fdflags.h>
+#include <mLib/fwatch.h>
+#include <mLib/mdwopt.h>
+#include <mLib/quis.h>
+#include <mLib/report.h>
+#include <mLib/sel.h>
+#include <mLib/selbuf.h>
+#include <mLib/sig.h>
+#include <mLib/str.h>
+#include <mLib/sub.h>
+#include <mLib/trace.h>
+
+#include <catacomb/gcipher.h>
+#include <catacomb/gmac.h>
+#include <catacomb/grand.h>
+#include <catacomb/key.h>
+#include <catacomb/paranoia.h>
+
+#include <catacomb/blowfish.h>
+#include <catacomb/blowfish-cbc.h>
+#include <catacomb/noise.h>
+#include <catacomb/rand.h>
+#include <catacomb/rmd160.h>
+#include <catacomb/rmd160-hmac.h>
+
+#include <catacomb/mp.h>
+#include <catacomb/mpmont.h>
+#include <catacomb/mprand.h>
+#include <catacomb/dh.h>
+
+#include "util.h"
+
+#undef sun
+
+/*----- Magic numbers -----------------------------------------------------*/
+
+/* --- Tunnel types --- */
+
+#define TUN_NOTDEF 0
+#define TUN_UNET 1
+#define TUN_BSD 2
+
+/* --- Trace flags --- */
+
+#define T_TUNNEL 1u
+#define T_PEER 2u
+#define T_PACKET 4u
+#define T_ADMIN 8u
+#define T_CRYPTO 16u
+#define T_KEYSET 32u
+#define T_KEYEXCH 64u
+#define T_KEYMGMT 128u
+
+#define T_ALL 255u
+
+/* --- Units --- */
+
+#define SEC(n) (n##u)
+#define MIN(n) (n##u * 60u)
+#define MEG(n) (n##ul * 1024ul * 1024ul)
+
+/* --- Other things --- */
+
+#define PKBUFSZ 65536
+
+/*----- TrIPE protocol ----------------------------------------------------*/
+
+/* --- TrIPE packet format --- *
+ *
+ * A packet begins with a single-byte packet type.  The remaining data
+ * depends on the packet type.
+ */
+
+#define MSG_PACKET 0u
+/* Followed by a 64-bit MAC and an encrypted packet.  The MAC is used as an
+ * IV for a 64-bit block cipher in CBC-stealing mode.
+ */
+
+#define MSG_PRECHALLENGE 1u
+/* Followed by the challenge only.  Useful for bootstrapping the system.
+ */
+
+#define MSG_CHALLENGE 2u
+/* Followed by a response hash and a large-integer challenge.
+ */
+
+#define MSG_RESPONSE 3u
+/* Followed by a large-integer response.
+ */
+
+/*----- Data structures ---------------------------------------------------*/
+
+/* --- Buffers --- *
+ *
+ * Buffers provide a simple stream-like interface for building and parsing
+ * packets.
+ */
+
+typedef struct buf {
+  octet *base, *p, *limit;             /* Pointers to the buffer */
+  unsigned f;                          /* Various flags */
+} buf;
+
+#define BF_BROKEN 1u                   /* Buffer is broken */
+
+/* --- Socket addresses --- *
+ *
+ * A magic union of supported socket addresses.
+ */
+
+typedef union addr {
+  struct sockaddr sa;
+  struct sockaddr_in sin;
+} addr;
+
+/* --- A symmetric keyset --- *
+ *
+ * A keyset contains a set of symmetric keys for encrypting and decrypting
+ * packets.  Keysets are stored in a list, sorted in reverse order of
+ * creation, so that the most recent keyset (the one most likely to be used)
+ * is first.
+ *
+ * Each keyset has a time limit and a data limit.  The keyset is destroyed
+ * when either it has existed for too long, or it has been used to encrypt
+ * too much data.  New key exchanges are triggered when keys are close to
+ * expiry.
+ */
+
+typedef struct keyset {
+  struct keyset *next;                 /* Next active keyset in the list */
+  time_t t_exp;                                /* Expiry time for this keyset */
+  unsigned long sz_exp;                        /* Data limit for the keyset */
+#ifndef NTRACE
+  unsigned seq;                                /* Sequence number for tracing */
+#endif
+  gcipher *c;                          /* Keyset cipher for encryption */
+  gmac *m;                             /* Keyset MAC for integrity */
+} keyset;
+
+/* --- Key exchange --- *
+ *
+ * TrIPE uses the Wrestlers Protocol for its key exchange.  The Wrestlers
+ * Protocol has a number of desirable features (e.g., perfect forward
+ * secrecy, and zero-knowledge authentication) which make it attractive for
+ * use in TrIPE.  The Wrestlers Protocol was designed by Mark Wooding and
+ * Clive Jones.
+ */
+
+typedef struct keyexch {
+  keyset **ks;                         /* Peer's list of keysets */
+  struct peer *p;                      /* Pointer back to the peer */
+  unsigned f;                          /* Various useful flags */
+  sel_timer t;                         /* Timer for next exchange */
+  dh_pub kpub;                         /* Peer's public key */
+  mp *my_x, *my_gx, *my_gxy;           /* My half of the exchange */
+  octet my_h[RMD160_HASHSZ];           /* My challenge hash */
+  mp *your_gx, *your_gxy;              /* Your half of the exchange */
+  octet your_h[RMD160_HASHSZ];         /* Your challenge hash */
+  time_t t_valid;                      /* When this exchange goes bad */
+  time_t t_qchal, t_qresp;             /* Quiet timers for packet types */
+  time_t t_newchal;                    /* When to accept a new challenge */
+} keyexch;
+
+#define KXF_TIMER 1u                   /* Waiting for a timer to go off */
+#define KXF_INIT 2u                    /* Big numbers are initialized */
+#define KXF_MYH 4u                     /* My hash has been computed */
+#define KXF_YOURH 8u                   /* Your hash has been received */
+#define KXF_REPLY 16u                  /* Received your response OK */
+#define KXF_DONE 32u                   /* Key exchange completed */
+
+/* --- Tunnel structure --- *
+ *
+ * Used to maintain system-specific information about the tunnel interface.
+ */
+
+typedef struct tunnel {
+#if TUN_TYPE == TUN_UNET
+  sel_file f;                          /* Selector for Usernet device */
+  struct peer *p;                      /* Pointer to my peer */
+#else
+#  error "No support for this tunnel type"
+#endif
+} tunnel;
+
+/* --- Peer structure --- *
+ *
+ * The main structure which glues everything else together.
+ */
+
+typedef struct peer {
+  struct peer *next, *prev;            /* Links to next and previous */
+  char *name;                          /* Name of this peer */
+  tunnel t;                            /* Tunnel for local packets */
+  keyset *ks;                          /* List head for keysets */
+  keyexch kx;                          /* Key exchange protocol block */
+  buf b;                               /* Buffer for sending packets */
+  addr peer;                           /* Peer socket address */
+  size_t sasz;                         /* Socket address size */
+} peer;
+
+/* --- Admin structure --- */
+
+typedef struct admin {
+  struct admin *next, *prev;           /* Links to next and previous */
+  selbuf b;                            /* Line buffer for commands */
+  int fd;                              /* File descriptor for output */
+#ifndef NTRACE
+  unsigned seq;                                /* Sequence number for tracing */
+#endif
+  char *pname;                         /* Peer name to create */
+  char *paddr;                         /* Address string to resolve */
+  bres_client r;                       /* Background resolver task */
+  sel_timer t;                         /* Timer for resolver */
+  addr peer;                           /* Address to set */
+  size_t sasz;                         /* Size of the address */
+} admin;
+
+/*----- Global variables --------------------------------------------------*/
+
+extern sel_state sel;                  /* Global I/O event state */
+extern dh_priv kpriv;                  /* Our private key */
+extern mpmont mg;                      /* Montgomery context for DH group */
+extern octet buf_i[PKBUFSZ], buf_o[PKBUFSZ]; /* Big packet buffers */
+
+#ifndef NTRACE
+extern const trace_opt tr_opts[];      /* Trace options array */
+extern unsigned tr_flags;              /* Trace options flags */
+#endif
+
+/*----- Key management ----------------------------------------------------*/
+
+/* --- @km_interval@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    Zero if OK, nonzero to force reloading of keys.
+ *
+ * Use:                Called on the interval timer to perform various useful jobs.
+ */
+
+extern int km_interval(void);
+
+/* --- @km_init@ --- *
+ *
+ * Arguments:  @const char *kr_priv@ = private keyring file
+ *             @const char *kr_pub@ = public keyring file
+ *             @const char *tag@ = tag to load
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes, and loads the private key.
+ */
+
+extern void km_init(const char */*kr_priv*/, const char */*kr_pub*/,
+                   const char */*tag*/);
+
+/* --- @km_getpubkey@ --- *
+ *
+ * Arguments:  @const char *tag@ = public key tag to load
+ *             @dh_pub *kpub@ = where to put the public key
+ *
+ * Returns:    Zero if OK, nonzero if it failed.
+ *
+ * Use:                Fetches a public key from the keyring.
+ */
+
+extern int km_getpubkey(const char */*tag*/, dh_pub */*kpub*/);
+
+/*----- Key exchange ------------------------------------------------------*/
+
+/* --- @kx_start@ --- *
+ *
+ * Arguments:  @keyexch *kx@ = pointer to key exchange context
+ *
+ * Returns:    ---
+ *
+ * Use:                Stimulates a key exchange.  If a key exchage is in progress,
+ *             a new challenge is sent (unless the quiet timer forbids
+ *             this); if no exchange is in progress, one is commenced.
+ */
+
+extern void kx_start(keyexch */*kx*/);
+
+/* --- @kx_prechallenge@ --- *
+ *
+ * Arguments:  @keyexch *kx@ = pointer to key exhange context
+ *             @buf *b@ = pointer to buffer containing the packet
+ *
+ * Returns:    ---
+ *
+ * Use:                Reads a prechallenge packet from the buffer and handles it.
+ */
+
+extern void kx_prechallenge(keyexch */*kx*/, buf */*b*/);
+
+/* --- @kx_challenge@ --- *
+ *
+ * Arguments:  @keyexch *kx@ = pointer to key exchange context
+ *             @buf *b@ = a buffer containing the packet to read
+ *
+ * Returns:    ---
+ *
+ * Use:                Reads a challenge from the buffer and handles it.
+ */
+
+extern void kx_challenge(keyexch */*kx*/, buf */*b*/);
+
+/* --- @kx_response@ --- *
+ *
+ * Arguments:  @keyexch *kx@ = pointer to key exchange context
+ *             @buf *b@ = a buffer containing the packet to read
+ *
+ * Returns:    ---
+ *
+ * Use:                Reads a response from the buffer and handles it.
+ */
+
+extern void kx_response(keyexch */*kx*/, buf */*b*/);
+
+/* --- @kx_free@ --- *
+ *
+ * Arguments:  @keyexch *kx@ = pointer to key exchange context
+ *
+ * Returns:    ---
+ *
+ * Use:                Frees everything in a key exchange context.
+ */
+
+extern void kx_free(keyexch */*kx*/);
+
+/* --- @kx_newkeys@ --- *
+ *
+ * Arguments:  @keyexch *kx@ = pointer to key exchange context
+ *
+ * Returns:    ---
+ *
+ * Use:                Informs the key exchange module that its keys may have
+ *             changed.  If fetching the new keys fails, the peer will be
+ *             destroyed, we log messages and struggle along with the old
+ *             keys.
+ */
+
+extern void kx_newkeys(keyexch */*kx*/);
+
+/* --- @kx_init@ --- *
+ *
+ * Arguments:  @keyexch *kx@ = pointer to key exchange context
+ *             @peer *p@ = pointer to peer context
+ *             @keyset **ks@ = pointer to keyset list
+ *
+ * Returns:    Zero if OK, nonzero if it failed.
+ *
+ * Use:                Initializes a key exchange module.  The module currently
+ *             contains no keys, and will attempt to initiate a key
+ *             exchange.
+ */
+
+extern int kx_init(keyexch */*kx*/, peer */*p*/, keyset **/*ks*/);
+
+/*----- Keysets and symmetric cryptography --------------------------------*/
+
+/* --- @ks_free@ --- *
+ *
+ * Arguments:  @keyset **ksroot@ = pointer to keyset list head
+ *
+ * Returns:    ---
+ *
+ * Use:                Frees all of the keys in a keyset.
+ */
+
+extern void ks_free(keyset **/*ksroot*/);
+
+/* --- @ks_prune@ --- *
+ *
+ * Arguments:  @keyset **ksroot@ = pointer to keyset list head
+ *
+ * Returns:    ---
+ *
+ * Use:                Prunes the keyset list by removing keys which mustn't be used
+ *             any more.
+ */
+
+extern void ks_prune(keyset **/*ksroot*/);
+
+/* --- @ks_gen@ --- *
+ *
+ * Arguments:  @keyset **ksroot@ = pointer to keyset list head
+ *             @const void *k@ = pointer to key material
+ *             @size_t sz@ = size of the key material
+ *
+ * Returns:    The regeneration time for the new key.
+ *
+ * Use:                Derives a keyset from the given key material and adds it to
+ *             the list.
+ */
+
+extern time_t ks_gen(keyset **/*ksroot*/, const void */*k*/, size_t /*sz*/);
+
+/* --- @ks_encrypt@ --- *
+ *
+ * Arguments:  @keyset **ksroot@ = pointer to keyset list head
+ *             @buf *b@ = pointer to input buffer
+ *             @buf *bb@ = pointer to output buffer
+ *
+ * Returns:    Nonzero if a new key is needed.
+ *
+ * Use:                Encrypts a packet.
+ */
+
+extern int ks_encrypt(keyset **/*ksroot*/, buf */*b*/, buf */*bb*/);
+
+/* --- @ks_decrypt@ --- *
+ *
+ * Arguments:  @keyset **ksroot@ = pointer to keyset list head
+ *             @buf *b@ = pointer to input buffer
+ *             @buf *bb@ = pointer to output buffer
+ *
+ * Returns:    Nonzero if the packet couldn't be decrypted.
+ *
+ * Use:                Decrypts a packet.
+ */
+
+extern int ks_decrypt(keyset **/*ksroot*/, buf */*b*/, buf */*bb*/);
+
+/*----- Administration interface ------------------------------------------*/
+
+/* --- @a_warn@ --- *
+ *
+ * Arguments:  @const char *fmt@ = pointer to format string
+ *             @...@ = other arguments
+ *
+ * Returns:    ---
+ *
+ * Use:                Informs all admin connections of a warning.
+ */
+
+extern void a_warn(const char */*fmt*/, ...);
+
+/* --- @a_create@ --- *
+ *
+ * Arguments:  @int fd_in, fd_out@ = file descriptors to use
+ *
+ * Returns:    ---
+ *
+ * Use:                Creates a new admin connection.
+ */
+
+extern void a_create(int /*fd_in*/, int /*fd_out*/);
+
+/* --- @a_quit@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Shuts things down nicely.
+ */
+
+extern void a_quit(void);
+
+/* --- @a_daemon@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Informs the admin module that it's a daemon.
+ */
+
+extern void a_daemon(void);
+
+/* --- @a_init@ --- *
+ *
+ * Arguments:  @const char *sock@ = socket name to create
+ *
+ * Returns:    ---
+ *
+ * Use:                Creates the admin listening socket.
+ */
+
+extern void a_init(const char */*sock*/);
+
+/*----- Peer management ---------------------------------------------------*/
+
+/* --- @p_txstart@ --- *
+ *
+ * Arguments:  @peer *p@ = pointer to peer block
+ *             @unsigned msg@ = message type code
+ *
+ * Returns:    A pointer to a buffer to write to.
+ *
+ * Use:                Starts sending to a peer.  Only one send can happen at a
+ *             time.
+ */
+
+extern buf *p_txstart(peer */*p*/, unsigned /*msg*/);
+
+/* --- @p_txend@ --- *
+ *
+ * Arguments:  @peer *p@ = pointer to peer block
+ *
+ * Returns:    ---
+ *
+ * Use:                Sends a packet to the peer.
+ */
+
+extern void p_txend(peer */*p*/);
+
+/* --- @p_tun@ --- *
+ *
+ * Arguments:  @peer *p@ = pointer to peer block
+ *             @buf *b@ = buffer containing incoming packet
+ *
+ * Returns:    ---
+ *
+ * Use:                Handles a packet which needs to be sent to a peer.
+ */
+
+extern void p_tun(peer */*p*/, buf */*b*/);
+
+/* --- @p_interval@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Called periodically to do tidying.
+ */
+
+extern void p_interval(void);
+
+/* --- @p_ifname@ --- *
+ *
+ * Arguments:  @peer *p@ = pointer to a peer block
+ *
+ * Returns:    A pointer to the peer's interface name.
+ */
+
+extern const char *p_ifname(peer */*p*/);
+
+/* --- @p_addr@ --- *
+ *
+ * Arguments:  @peer *p@ = pointer to a peer block
+ *
+ * Returns:    A pointer to the peer's address.
+ */
+
+extern const addr *p_addr(peer */*p*/);
+
+/* --- @p_init@ --- *
+ *
+ * Arguments:  @unsigned port@ = port number to listen to
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes the peer system; creates the socket.
+ */
+
+extern void p_init(unsigned /*port*/);
+
+/* --- @p_port@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    Port number used for socket.
+ */
+
+unsigned p_port(void);
+
+/* --- @p_create@ --- *
+ *
+ * Arguments:  @const char *name@ = name for this peer
+ *             @struct sockaddr *sa@ = socket address of peer
+ *             @size_t sz@ = size of socket address
+ *
+ * Returns:    Pointer to the peer block, or null if it failed.
+ *
+ * Use:                Creates a new named peer block.  No peer is actually attached
+ *             by this point.
+ */
+
+extern peer *p_create(const char */*name*/,
+                     struct sockaddr */*sa*/, size_t /*sz*/);
+
+/* --- @p_name@ --- *
+ *
+ * Arguments:  @peer *p@ = pointer to a peer block
+ *
+ * Returns:    A pointer to the peer's name.
+ */
+
+extern const char *p_name(peer */*p*/);
+
+/* --- @p_find@ --- *
+ *
+ * Arguments:  @const char *name@ = name to look up
+ *
+ * Returns:    Pointer to the peer block, or null if not found.
+ *
+ * Use:                Finds a peer by name.
+ */
+
+extern peer *p_find(const char */*name*/);
+
+/* --- @p_destroy@ --- *
+ *
+ * Arguments:  @peer *p@ = pointer to a peer
+ *
+ * Returns:    ---
+ *
+ * Use:                Destroys a peer.
+ */
+
+extern void p_destroy(peer */*p*/);
+
+/* --- @p_first@, @p_next@ --- *
+ *
+ * Arguments:  @peer *p@ = a peer block
+ *
+ * Returns:    @peer_first@ returns the first peer in some ordering;
+ *             @peer_next@ returns the peer following a given one in the
+ *             same ordering.  Null is returned for the end of the list.
+ */
+
+extern peer *p_first(void);
+extern peer *p_next(peer */*p*/);
+
+/*----- Tunnel interface --------------------------------------------------*/
+
+/* --- @tun_init@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes the tunneling system.  Maybe this will require
+ *             opening file descriptors or something.
+ */
+
+extern void tun_init(void);
+
+/* --- @tun_create@ --- *
+ *
+ * Arguments:  @tunnel *t@ = pointer to tunnel block
+ *             @peer *p@ = pointer to peer block
+ *
+ * Returns:    Zero if it worked, nonzero on failure.
+ *
+ * Use:                Initializes a new tunnel.
+ */
+
+extern int tun_create(tunnel */*t*/, peer */*p*/);
+
+/* --- @tun_ifname@ --- *
+ *
+ * Arguments:  @tunnel *t@ = pointer to tunnel block
+ *
+ * Returns:    A pointer to the tunnel's interface name.
+ */
+
+extern const char *tun_ifname(tunnel */*t*/);
+
+/* --- @tun_inject@ --- *
+ *
+ * Arguments:  @tunnel *t@ = pointer to tunnel block
+ *             @buf *b@ = buffer to send
+ *
+ * Returns:    ---
+ *
+ * Use:                Injects a packet into the local network stack.
+ */
+
+extern void tun_inject(tunnel */*t*/, buf */*b*/);
+
+/* --- @tun_destroy@ --- *
+ *
+ * Arguments:  @tunnel *t@ = pointer to tunnel block
+ *
+ * Returns:    ---
+ *
+ * Use:                Destroys a tunnel.
+ */
+
+extern void tun_destroy(tunnel */*t*/);
+
+/*----- Buffer handling ---------------------------------------------------*/
+
+/* --- Useful macros --- */
+
+#define BBASE(b) ((b)->base)
+#define BLIM(b) ((b)->limit)
+#define BCUR(b) ((b)->p)
+#define BSZ(b) ((b)->limit - (b)->base)
+#define BLEN(b) ((b)->p - (b)->base)
+#define BLEFT(b) ((b)->limit - (b)->p)
+#define BSTEP(b, sz) ((b)->p += (sz))
+#define BBAD(b) ((b)->f & BF_BROKEN)
+#define BOK(b) (!BBAD(b))
+
+#define BENSURE(b, sz)                                                 \
+  (BBAD(b) ? -1 : (sz) > BLEFT(b) ? (b)->f |= BF_BROKEN, -1 : 0)
+
+/* --- @buf_init@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *             @void *p@ = pointer to a buffer
+ *             @size_t sz@ = size of the buffer
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes the buffer block appropriately.
+ */
+
+extern void buf_init(buf */*b*/, void */*p*/, size_t /*sz*/);
+
+/* --- @buf_break@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *
+ * Returns:    Some negative value.
+ *
+ * Use:                Marks a buffer as broken.
+ */
+
+extern int buf_break(buf */*b*/);
+
+/* --- @buf_ensure@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *             @size_t sz@ = size of data wanted
+ *
+ * Returns:    Zero if it worked, nonzero if there wasn't enough space.
+ *
+ * Use:                Ensures that there are @sz@ bytes still in the buffer.
+ */
+
+extern int buf_ensure(buf */*b*/, size_t /*sz*/);
+
+/* --- @buf_get@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *             @void *p@ = pointer to a buffer
+ *             @size_t sz@ = size of the buffer
+ *
+ * Returns:    Zero if it worked, nonzero if there wasn't enough data.
+ *
+ * Use:                Fetches data from the buffer into some other place.
+ */
+
+extern int buf_get(buf */*b*/, void */*p*/, size_t /*sz*/);
+
+/* --- @buf_put@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *             @const void *p@ = pointer to a buffer
+ *             @size_t sz@ = size of the buffer
+ *
+ * Returns:    Zero if it worked, nonzero if there wasn't enough space.
+ *
+ * Use:                Fetches data from some place and puts it in the buffer
+ */
+
+extern int buf_put(buf */*b*/, const void */*p*/, size_t /*sz*/);
+
+/* --- @buf_getbyte@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *
+ * Returns:    A byte, or less than zero if there wasn't a byte there.
+ *
+ * Use:                Gets a single byte from a buffer.
+ */
+
+extern int buf_getbyte(buf */*b*/);
+
+/* --- @buf_putbyte@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *             @int ch@ = byte to write
+ *
+ * Returns:    Zero if OK, nonzero if there wasn't enough space.
+ *
+ * Use:                Puts a single byte in a buffer.
+ */
+
+extern int buf_putbyte(buf */*b*/, int /*ch*/);
+
+/* --- @buf_getword@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *             @uint32 *w@ = where to put the word
+ *
+ * Returns:    Zero if OK, or nonzero if there wasn't a word there.
+ *
+ * Use:                Gets a 32-bit word from a buffer.
+ */
+
+extern int buf_getword(buf */*b*/, uint32 */*w*/);
+
+/* --- @buf_putword@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *             @uint32 w@ = word to write
+ *
+ * Returns:    Zero if OK, nonzero if there wasn't enough space.
+ *
+ * Use:                Puts a 32-but word in a buffer.
+ */
+
+extern int buf_putword(buf */*b*/, uint32 /*w*/);
+
+/* --- @buf_getmp@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *
+ * Returns:    A multiprecision integer, or null if there wasn't one there.
+ *
+ * Use:                Gets a multiprecision integer from a buffer.
+ */
+
+extern mp *buf_getmp(buf */*b*/, mp */*d*/);
+
+/* --- @buf_putmp@ --- *
+ *
+ * Arguments:  @buf *b@ = pointer to a buffer block
+ *             @mp *m@ = a multiprecision integer
+ *
+ * Returns:    Zero if it worked, nonzero if there wasn't enough space.
+ *
+ * Use:                Puts a multiprecision integer to a buffer.
+ */
+
+extern int buf_putmp(buf */*b*/, mp */*m*/);
+
+/*----- Other handy utilities ---------------------------------------------*/
+
+/* --- @mpstr@ --- *
+ *
+ * Arguments:  @mp *m@ = a multiprecision integer
+ *
+ * Returns:    A pointer to the integer's textual representation.
+ *
+ * Use:                Converts a multiprecision integer to a string.  Corrupts
+ *             @buf_o@.
+ */
+
+extern const char *mpstr(mp */*m*/);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif
diff --git a/tun-unet.c b/tun-unet.c
new file mode 100644 (file)
index 0000000..d78c9a1
--- /dev/null
@@ -0,0 +1,174 @@
+/* -*-c-*-
+ *
+ * $Id: tun-unet.c,v 1.1 2001/02/03 20:26:37 mdw Exp $
+ *
+ * Tunnel interface based on Linux Usernet
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: tun-unet.c,v $
+ * Revision 1.1  2001/02/03 20:26:37  mdw
+ * Initial checkin.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "tripe.h"
+
+#include <sys/ioctl.h>
+#include <unet.h>
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @t_read@ --- *
+ *
+ * Arguments:  @int fd@ = file descriptor to read
+ *             @unsigned mode@ = what's happened
+ *             @void *v@ = pointer to tunnel block
+ *
+ * Returns:    ---
+ *
+ * Use:                Reads data from the tunnel.
+ */
+
+void t_read(int fd, unsigned mode, void *v)
+{
+  tunnel *t = v;
+  ssize_t n;
+  buf b;
+
+  n = read(fd, buf_i, sizeof(buf_i));
+  if (n < 0) {
+    a_warn("tunnel read failed (%s): %s", tun_ifname(t), strerror(errno));
+    return;
+  }
+  IF_TRACING(T_TUNNEL, {
+    trace(T_TUNNEL, "tunnel: packet arrived");
+    trace_block(T_PACKET, "tunnel: packet contents", buf_i, n);
+  })
+  buf_init(&b, buf_i, n);
+  p_tun(t->p, &b);
+}
+
+/* --- @tun_init@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    ---
+ *
+ * Use:                Initializes the tunneling system.  Maybe this will require
+ *             opening file descriptors or something.
+ */
+
+void tun_init(void)
+{
+  return;
+}
+
+/* --- @tun_create@ --- *
+ *
+ * Arguments:  @tunnel *t@ = pointer to tunnel block
+ *             @peer *p@ = pointer to peer block
+ *
+ * Returns:    Zero if it worked, nonzero on failure.
+ *
+ * Use:                Initializes a new tunnel.
+ */
+
+int tun_create(tunnel *t, peer *p)
+{
+  int fd;
+
+  if ((fd = open("/dev/unet", O_RDWR)) < 0) {
+    a_warn("open `/dev/unet' failed: %s", strerror(errno));
+    return (-1);
+  }
+  t->p = p;
+  sel_initfile(&sel, &t->f, fd, SEL_READ, t_read, t);
+  sel_addfile(&t->f);
+  T( trace(T_TUNNEL, "tunnel: attached interface %s to peer `%s'",
+          tun_ifname(t), p_name(p)); )
+  return (0);
+}
+
+/* --- @tun_ifname@ --- *
+ *
+ * Arguments:  @tunnel *t@ = pointer to tunnel block
+ *
+ * Returns:    A pointer to the tunnel's interface name.
+ */
+
+const char *tun_ifname(tunnel *t)
+{
+  static char b[UNET_NAMEMAX];
+  struct unet_info uni;
+  if (ioctl(t->f.fd, UNIOCGINFO, &uni)) {
+    a_warn("ioctl(UNIOCGINFO) failed: %s", strerror(errno));
+    return ("<error>");
+  }
+  if (strlen(uni.uni_ifname) + 1 > sizeof(b)) {
+    a_warn("interface name too long!");
+    return ("<error>");
+  }
+  strcpy(b, uni.uni_ifname);
+  return (b);
+}
+
+/* --- @tun_inject@ --- *
+ *
+ * Arguments:  @tunnel *t@ = pointer to tunnel block
+ *             @buf *b@ = buffer to send
+ *
+ * Returns:    ---
+ *
+ * Use:                Injects a packet into the local network stack.
+ */
+
+void tun_inject(tunnel *t, buf *b)
+{
+  IF_TRACING(T_TUNNEL, {
+    trace(T_TUNNEL, "tunnel: inject decrypted packet");
+    trace_block(T_PACKET, "tunnel: packet contents", BBASE(b), BLEN(b));
+  })
+  write(t->f.fd, BBASE(b), BLEN(b));
+}
+
+/* --- @tun_destroy@ --- *
+ *
+ * Arguments:  @tunnel *t@ = pointer to tunnel block
+ *
+ * Returns:    ---
+ *
+ * Use:                Destroys a tunnel.
+ */
+
+void tun_destroy(tunnel *t)
+{
+  sel_rmfile(&t->f);
+  close(t->f.fd);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/util.c b/util.c
new file mode 100644 (file)
index 0000000..4dd6f35
--- /dev/null
+++ b/util.c
@@ -0,0 +1,87 @@
+/* -*-c-*-
+ *
+ * $Id: util.c,v 1.1 2001/02/03 20:26:37 mdw Exp $
+ *
+ * Utilities for the client and the server
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: util.c,v $
+ * Revision 1.1  2001/02/03 20:26:37  mdw
+ * Initial checkin.
+ *
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "util.h"
+
+#include <sys/ioctl.h>
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @u_daemon@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    Zero if OK, nonzero on failure.
+ *
+ * Use:                Becomes a daemon.
+ */
+
+int u_daemon(void)
+{
+  pid_t kid;
+
+  if ((kid = fork()) < 0)
+    return (-1);
+  if (kid)
+    _exit(0);
+#ifdef TIOCNOTTY
+  {
+    int fd;
+    if ((fd = open("/dev/tty", O_RDONLY)) >= 0) {
+      ioctl(fd, TIOCNOTTY);
+      close(fd);
+    }
+  }
+#endif
+  setsid();
+
+  if (fork() > 0)
+    _exit(0);
+  return (0);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
diff --git a/util.h b/util.h
new file mode 100644 (file)
index 0000000..b5fbd57
--- /dev/null
+++ b/util.h
@@ -0,0 +1,65 @@
+/* -*-c-*-
+ *
+ * $Id: util.h,v 1.1 2001/02/03 20:26:37 mdw Exp $
+ *
+ * Utilities for the client and the server
+ *
+ * (c) 2001 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.
+ */
+
+/*----- Revision history --------------------------------------------------* 
+ *
+ * $Log: util.h,v $
+ * Revision 1.1  2001/02/03 20:26:37  mdw
+ * Initial checkin.
+ *
+ */
+
+#ifndef UTIL_H
+#define UTIL_H
+
+#ifdef __cplusplus
+  extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+/*----- Functions provided ------------------------------------------------*/
+
+/* --- @u_daemon@ --- *
+ *
+ * Arguments:  ---
+ *
+ * Returns:    Zero if OK, nonzero on failure.
+ *
+ * Use:                Becomes a daemon.
+ */
+
+int u_daemon(void);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+  }
+#endif
+
+#endif