From: mdw Date: Sat, 3 Feb 2001 20:26:37 +0000 (+0000) Subject: Initial checkin. X-Git-Tag: 1.0.0pre1~42 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/commitdiff_plain/410c8acf139e945dce28bbc0c8b17dcfd0815643 Initial checkin. --- 410c8acf139e945dce28bbc0c8b17dcfd0815643 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