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