--- /dev/null
+Makefile.in
+configure
+aclocal.m4
+build
--- /dev/null
+COPYING
+install-sh
+mkinstalldirs
+missing
+config.sub
+config.guess
--- /dev/null
+;;; -*-emacs-lisp-*-
+
+(setq skel-alist
+ (append
+ '((author . "Straylight/Edgeware")
+ (full-title . "Trivial IP Encryption (TrIPE)")
+ (program . "TrIPE"))
+ skel-alist))
+
--- /dev/null
+## -*-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 ---------------------------------------------------
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+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 -------------------------------------------------
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+#! /bin/sh
+
+set -e
+mklinks
+mkaclocal
+autoconf
+automake
+mkdir build
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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 -------------------------------------------------*/
--- /dev/null
+/* -*-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