From 37941236c18baccbf89f5842ca63aee471d79256 Mon Sep 17 00:00:00 2001 Message-Id: <37941236c18baccbf89f5842ca63aee471d79256.1714523198.git.mdw@distorted.org.uk> From: Mark Wooding Date: Fri, 7 Oct 2005 14:41:54 +0000 Subject: [PATCH] Greetings and challenges. Organization: Straylight/Edgeware From: mdw --- Makefile.am | 2 +- admin.c | 387 ++++++++++++++++++++++++---------------- chal.c | 143 +++++++++++++++ debian/control | 2 +- doc/tripe-admin.5 | 93 ++++++++-- ethereal/packet-tripe.c | 7 +- keyset.c | 29 +-- peer.c | 55 ++++-- servutil.c | 72 +++++++- tripe-protocol.h | 1 + tripe.h | 115 +++++++++++- tripemon.in | 12 +- 12 files changed, 699 insertions(+), 219 deletions(-) create mode 100644 chal.c diff --git a/Makefile.am b/Makefile.am index 8fbec707..144671da 100644 --- a/Makefile.am +++ b/Makefile.am @@ -44,7 +44,7 @@ EXTRA_SCRIPTS = ${PYTHONSCRIPTS} ${PYGTKSCRIPTS} tripe_SOURCES = \ tripe.c tripe.h tripe-protocol.h \ admin.c peer.c \ - keymgmt.c keyexch.c keyset.c \ + keymgmt.c keyexch.c keyset.c chal.c \ servutil.c util.c util.h \ tun-unet.c tun-bsd.c tun-linux.c tun-slip.c tripe_LDADD = $(CATACOMB_LIBS) diff --git a/admin.c b/admin.c index 987de7ab..da7ccad6 100644 --- a/admin.c +++ b/admin.c @@ -38,11 +38,12 @@ 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" }, + { 'l', T_CHAL, "challenge management" }, + { 'p', T_PACKET, "packet contents" }, + { 'c', T_CRYPTO, "crypto details" }, { 'A', T_ALL, "all of the above" }, { 0, 0, 0 } }; @@ -512,6 +513,25 @@ static long a_parsetime(const char *p) return (t); } +/* --- @a_findpeer@ --- * + * + * Arguments: @admin *a@ = admin connection + * @const char *pn@ = peer name + * + * Returns: The peer, or null if not there. + * + * Use: Finds a peer, reporting an error if it failed. + */ + +static peer *a_findpeer(admin *a, const char *pn) +{ + peer *p; + + if ((p = p_find(pn)) == 0) + a_fail(a, "unknown-peer %s", pn); + return (p); +} + /*----- Backgrounded operations -------------------------------------------*/ #define BGTAG(bg) \ @@ -604,108 +624,192 @@ static void a_bgadd(admin *a, admin_bgop *bg, const char *tag, if (tag) a_write(a, "DETACH", tag, 0); } -/*----- Adding peers ------------------------------------------------------*/ +/*----- Name resolution operations ----------------------------------------*/ -/* --- @a_addfree@ --- * +/* --- @a_resolved@ --- * * - * Arguments: @admin_addop *add@ = operation block + * Arguments: @struct hostent *h@ = pointer to resolved hostname + * @void *v@ = pointer to resolver operation * * Returns: --- * - * Use: Frees an add operation. + * Use: Handles a completed name resolution. */ -static void a_addfree(admin_addop *add) +static void a_resolved(struct hostent *h, void *v) { - T( trace(T_ADMIN, "admin: free add op %s", BGTAG(add)); ) - if (add->peer.name) xfree(add->peer.name); - if (add->paddr) xfree(add->paddr); -} + admin_resop *r = v; + + T( trace(T_ADMIN, "admin: resop %s resolved", BGTAG(r)); ) + TIMER; + if (!h) { + a_bgfail(&r->bg, "resolve-error %s", r->addr); + r->func(r, ARES_FAIL); + } else { + memcpy(&r->sa.sin.sin_addr, h->h_addr, sizeof(struct in_addr)); + r->func(r, ARES_OK); + } + sel_rmtimer(&r->t); + xfree(r->addr); + a_bgrelease(&r->bg); +} -/* --- @a_addcancel@ --- * +/* --- @a_restimer@ --- * * - * Arguments: @admin_bgop *bg@ = background operation + * Arguments: @struct timeval *tv@ = timer + * @void *v@ = pointer to resolver operation * * Returns: --- * - * Use: Cancels an add operation. + * Use: Times out a resolver. */ -static void a_addcancel(admin_bgop *bg) +static void a_restimer(struct timeval *tv, void *v) { - admin_addop *add = (admin_addop *)bg; - - T( trace(T_ADMIN, "admin: cancel add op %s", BGTAG(add)); ) - sel_rmtimer(&add->t); - bres_abort(&add->r); - a_addfree(add); + admin_resop *r = v; + + T( trace(T_ADMIN, "admin: resop %s timeout", BGTAG(r)); ) + a_bgfail(&r->bg, "resolver-timeout %s\n", r->addr); + r->func(r, ARES_FAIL); + bres_abort(&r->r); + xfree(r->addr); + a_bgrelease(&r->bg); } -/* --- @a_doadd@ --- * +/* --- @a_rescancel@ --- * * - * Arguments: @admin_addop *add@ = operation block + * Arguments: @admin_bgop *bg@ = background operation * * Returns: --- * - * Use: Does the peer add thing. + * Use: Cancels an add operation. */ -static void a_doadd(admin_addop *add) +static void a_rescancel(admin_bgop *bg) { - if (p_find(add->peer.name)) - a_bgfail(&add->bg, "peer-exists %s", add->peer.name); - else if (!p_create(&add->peer)) - a_bgfail(&add->bg, "peer-create-fail %s", add->peer.name); - else - a_bgok(&add->bg); + admin_resop *r = (admin_resop *)bg; + + T( trace(T_ADMIN, "admin: cancel resop %s", BGTAG(r)); ) + r->func(r, ARES_FAIL); + sel_rmtimer(&r->t); + xfree(r->addr); + bres_abort(&r->r); } - -/* --- @a_addresolve@ --- * + +/* --- @a_resolve@ --- * * - * Arguments: @struct hostent *h@ = pointer to resolved hostname - * @void *v@ = pointer to add operation + * Arguments: @admin *a@ = administration connection + * @admin_resop *r@ = resolver operation to run + * @const char *tag@ = background operation tag + * @void (*func)(struct admin_resop *, int@ = handler function + * @unsigned ac@ = number of remaining arguments + * @char *av[]@ = pointer to remaining arguments * * Returns: --- * - * Use: Handles a completed name resolution. + * Use: Cranks up a resolver job. */ -static void a_addresolve(struct hostent *h, void *v) +static void a_resolve(admin *a, admin_resop *r, const char *tag, + void (*func)(struct admin_resop *, int), + unsigned ac, char *av[]) { - admin_addop *add = v; + struct timeval tv; + unsigned long pt; + char *p; + int i = 0; - T( trace(T_ADMIN, "admin: add op %s resolved", BGTAG(add)); ) - TIMER; - if (!h) - a_bgfail(&add->bg, "resolve-error %s", add->paddr); - else { - memcpy(&add->peer.sa.sin.sin_addr, h->h_addr, sizeof(struct in_addr)); - a_doadd(add); + /* --- Fill in the easy bits of address --- */ + + r->addr = 0; + r->func = func; + if (mystrieq(av[i], "inet")) i++; + if (ac - i != 2) { + a_fail(a, "bad-addr-syntax [inet] ADDRESS PORT"); + goto fail; + } + r->sa.sin.sin_family = AF_INET; + r->sasz = sizeof(r->sa.sin); + r->addr = xstrdup(av[i]); + pt = strtoul(av[i + 1], &p, 0); + if (*p) { + struct servent *s = getservbyname(av[i + 1], "udp"); + if (!s) { + a_fail(a, "unknown-service %s", av[i + 1]); + goto fail; + } + pt = ntohs(s->s_port); } - sel_rmtimer(&add->t); - a_addfree(add); - a_bgrelease(&add->bg); + if (pt == 0 || pt >= 65536) { + a_fail(a, "invalid-port %lu", pt); + goto fail; + } + r->sa.sin.sin_port = htons(pt); + + /* --- Report backgrounding --- * + * + * Do this for consistency of interface, even if we're going to get the + * answer straight away. + */ + + a_bgadd(a, &r->bg, tag, a_rescancel); + T( trace(T_ADMIN, "admin: %u, resop %s, hostname `%s'", + a->seq, BGTAG(r), r->addr); ) + + /* --- If the name is numeric, do it the easy way --- */ + + if (inet_aton(av[i], &r->sa.sin.sin_addr)) { + T( trace(T_ADMIN, "admin: resop %s done the easy way", BGTAG(r)); ) + func(r, ARES_OK); + xfree(r->addr); + a_bgrelease(&r->bg); + return; + } + + /* --- Store everything for later and crank up the resolver --- */ + + gettimeofday(&tv, 0); + tv.tv_sec += T_RESOLVE; + sel_addtimer(&sel, &r->t, &tv, a_restimer, r); + bres_byname(&r->r, r->addr, a_resolved, r); + return; + +fail: + func(r, ARES_FAIL); + if (r->addr) xfree(r->addr); + xfree(r); } -/* --- @a_addtimer@ --- * +/*----- Adding peers ------------------------------------------------------*/ + +/* --- @a_doadd@ --- * * - * Arguments: @struct timeval *tv@ = timer - * @void *v@ = pointer to add operation + * Arguments: @admin_resop *r@ = resolver operation + * @int rc@ = how it worked * * Returns: --- * - * Use: Times out a resolver. + * Use: Handles a completed resolution. */ -static void a_addtimer(struct timeval *tv, void *v) +static void a_doadd(admin_resop *r, int rc) { - admin_addop *add = v; + admin_addop *add = (admin_addop *)r; + + T( trace(T_ADMIN, "admin: done add op %s", BGTAG(add)); ) + + if (rc == ARES_OK) { + add->peer.sasz = add->r.sasz; + add->peer.sa = add->r.sa; + if (p_find(add->peer.name)) + a_bgfail(&add->r.bg, "peer-exists %s", add->peer.name); + else if (!p_create(&add->peer)) + a_bgfail(&add->r.bg, "peer-create-fail %s", add->peer.name); + else + a_bgok(&add->r.bg); + } - T( trace(T_ADMIN, "admin: add op %s timeout", BGTAG(add)); ) - a_bgfail(&add->bg, "resolver-timeout %s\n", add->paddr); - bres_abort(&add->r); - a_addfree(add); - a_bgrelease(&add->bg); + xfree(add->peer.name); } /* --- @acmd_add@ --- * @@ -721,27 +825,23 @@ static void a_addtimer(struct timeval *tv, void *v) static void acmd_add(admin *a, unsigned ac, char *av[]) { - unsigned long pt; - struct timeval tv; unsigned i, j; - char *p; const char *tag = 0; admin_addop *add = 0; - /* --- Make sure someone's not got there already --- */ - - if (p_find(av[0])) { - a_fail(a, "peer-exists %s", av[0]); - goto fail; - } - /* --- Set stuff up --- */ add = xmalloc(sizeof(*add)); add->peer.name = xstrdup(av[0]); add->peer.t_ka = 0; add->peer.tops = tun_default; - add->paddr = 0; + + /* --- Make sure someone's not got there already --- */ + + if (p_find(av[0])) { + a_fail(a, "peer-exists %s", av[0]); + goto fail; + } /* --- Parse options --- */ @@ -757,7 +857,7 @@ static void acmd_add(admin *a, unsigned ac, char *av[]) for (j = 0;; j++) { if (!tunnels[j]) { a_fail(a, "unknown-tunnel %s", av[i]); - return; + goto fail; } if (mystrieq(av[i], tunnels[j]->name)) { add->peer.tops = tunnels[j]; @@ -769,7 +869,7 @@ static void acmd_add(admin *a, unsigned ac, char *av[]) if (!av[++i]) goto bad_syntax; if ((t = a_parsetime(av[i])) < 0) { a_fail(a, "bad-time-spec %s", av[i]); - return; + goto fail; } add->peer.t_ka = t; } else if (mystrieq(av[i], "--")) { @@ -780,66 +880,18 @@ static void acmd_add(admin *a, unsigned ac, char *av[]) i++; } - /* --- Fill in the easy bits of address --- */ - - if (mystrieq(av[i], "inet")) i++; - if (ac - i != 2) { - a_fail(a, "bad-syntax -- add PEER [OPTIONS] [inet] ADDRESS PORT"); - goto fail; - } - add->peer.sa.sin.sin_family = AF_INET; - add->peer.sasz = sizeof(add->peer.sa.sin); - add->paddr = xstrdup(av[i]); - pt = strtoul(av[i + 1], &p, 0); - if (*p) { - struct servent *s = getservbyname(av[i + 1], "udp"); - if (!s) { - a_fail(a, "unknown-service %s", av[i + 1]); - goto fail; - } - pt = ntohs(s->s_port); - } - if (pt == 0 || pt >= 65536) { - a_fail(a, "invalid-port %lu", pt); - goto fail; - } - add->peer.sa.sin.sin_port = htons(pt); - - /* --- Report backgrounding --- * - * - * Do this for consistency of interface, even if we're going to get the - * answer straight away. - */ + /* --- Crank up the resolver --- */ - a_bgadd(a, &add->bg, tag, a_addcancel); - T( trace(T_ADMIN, "admin: %u, add op %s resolving hostname `%s'", - a->seq, BGTAG(add), add->paddr); ) - - /* --- If the name is numeric, do it the easy way --- */ - - if (inet_aton(av[i], &add->peer.sa.sin.sin_addr)) { - T( trace(T_ADMIN, "admin: add op %s done the easy way", BGTAG(add)); ) - a_doadd(add); - a_addfree(add); - a_bgrelease(&add->bg); - return; - } - - /* --- Store everything for later and crank up the resolver --- */ - - gettimeofday(&tv, 0); - tv.tv_sec += T_RESOLVE; - sel_addtimer(&sel, &add->t, &tv, a_addtimer, add); - bres_byname(&add->r, add->paddr, a_addresolve, add); + a_resolve(a, &add->r, tag, a_doadd, ac - i, av + i); return; + /* --- Clearing up --- */ + bad_syntax: a_fail(a, "bad-syntax -- add PEER [OPTIONS] ADDR ..."); fail: - if (add) { - a_addfree(add); - xfree(add); - } + xfree(add->peer.name); + xfree(add); return; } @@ -942,10 +994,8 @@ static void a_ping(admin *a, unsigned ac, char *av[], } if (!av[i]) goto bad_syntax; - if ((p = p_find(av[i])) == 0) { - a_fail(a, "unknown-peer %s", av[i]); + if ((p = a_findpeer(a, av[i])) == 0) return; - } pg = xmalloc(sizeof(*pg)); gettimeofday(&pg->pingtime, 0); a_bgadd(a, &pg->bg, tag, a_pingcancel); @@ -1115,22 +1165,61 @@ static void acmd_ifname(admin *a, unsigned ac, char *av[]) { peer *p; - if ((p = p_find(av[0])) == 0) - a_fail(a, "unknown-peer %s", av[0]); - else { + if ((p = a_findpeer(a, av[0])) != 0) { a_info(a, "%s", p_ifname(p)); a_ok(a); } } +static void acmd_getchal(admin *a, unsigned ac, char *av[]) +{ + buf b; + + buf_init(&b, buf_i, PKBUFSZ); + c_new(&b); + a_info(a, "%s", b64_encode(BBASE(&b), BLEN(&b))); + a_ok(a); +} + +static void acmd_checkchal(admin *a, unsigned ac, char *av[]) +{ + base64_ctx b64; + buf b; + dstr d = DSTR_INIT; + + base64_init(&b64); + base64_decode(&b64, av[0], strlen(av[0]), &d); + base64_decode(&b64, 0, 0, &d); + buf_init(&b, d.buf, d.len); + if (c_check(&b) || BBAD(&b) || BLEFT(&b)) + a_fail(a, "invalid-challenge"); + else + a_ok(a); + dstr_destroy(&d); +} + +static void acmd_greet(admin *a, unsigned ac, char *av[]) +{ + peer *p; + base64_ctx b64; + dstr d = DSTR_INIT; + + if ((p = a_findpeer(a, av[0])) != 0) { + base64_init(&b64); + base64_decode(&b64, av[1], strlen(av[1]), &d); + base64_decode(&b64, 0, 0, &d); + p_greet(p, d.buf, d.len); + dstr_destroy(&d); + a_ok(a); + } +} + static void acmd_addr(admin *a, unsigned ac, char *av[]) { peer *p; const addr *ad; - if ((p = p_find(av[0])) == 0) - a_fail(a, "unknown-peer %s", av[0]); - else { + if ((p = a_findpeer(a, av[0])) != 0) { ad = p_addr(p); assert(ad->sa.sa_family == AF_INET); a_info(a, "INET %s %u", @@ -1145,15 +1234,12 @@ static void acmd_peerinfo(admin *a, unsigned ac, char *av[]) peer *p; const peerspec *ps; - if ((p = p_find(av[0])) == 0) { - a_fail(a, "unknown-peer %s", av[0]); - return; + if ((p = a_findpeer(a, av[0])) != 0) { + ps = p_spec(p); + a_info(a, "tunnel=%s", ps->tops->name); + a_info(a, "keepalive=%lu", ps->t_ka); + a_ok(a); } - - ps = p_spec(p); - a_info(a, "tunnel=%s", ps->tops->name); - a_info(a, "keepalive=%lu", ps->t_ka); - a_ok(a); } static void acmd_servinfo(admin *a, unsigned ac, char *av[]) @@ -1169,10 +1255,8 @@ static void acmd_stats(admin *a, unsigned ac, char *av[]) peer *p; stats *st; - if ((p = p_find(av[0])) == 0) { - a_fail(a, "unknown-peer %s", av[0]); + if ((p = a_findpeer(a, av[0])) == 0) return; - } st = p_stats(p); a_info(a, "start-time=%s", timestr(st->t_start)); @@ -1196,9 +1280,7 @@ static void acmd_stats(admin *a, unsigned ac, char *av[]) static void acmd_kill(admin *a, unsigned ac, char *av[]) { peer *p; - if ((p = p_find(av[0])) == 0) - a_fail(a, "unknown-peer %s", av[0]); - else { + if ((p = a_findpeer(a, av[0])) != 0) { p_destroy(p); a_ok(a); } @@ -1207,9 +1289,7 @@ static void acmd_kill(admin *a, unsigned ac, char *av[]) static void acmd_forcekx(admin *a, unsigned ac, char *av[]) { peer *p; - if ((p = p_find(av[0])) == 0) - a_fail(a, "unknown-peer %s", av[0]); - else { + if ((p = a_findpeer(a, av[0])) != 0) { kx_start(&p->kx, 1); a_ok(a); } @@ -1255,9 +1335,12 @@ static const acmd acmdtab[] = { { "add", "add PEER [OPTIONS] ADDR ...", 2, 0xffff, acmd_add }, { "addr", "addr PEER", 1, 1, acmd_addr }, + { "checkchal", "checkchal CHAL", 1, 1, acmd_checkchal }, { "daemon", "daemon", 0, 0, acmd_daemon }, { "eping", "eping [OPTIONS] PEER", 1, 0xffff, acmd_eping }, { "forcekx", "forcekx PEER", 1, 1, acmd_forcekx }, + { "getchal", "getchal", 0, 0, acmd_getchal }, + { "greet", "greet PEER CHAL", 2, 2, acmd_greet }, { "help", "help", 0, 0, acmd_help }, { "ifname", "ifname PEER", 1, 1, acmd_ifname }, { "kill", "kill PEER", 1, 1, acmd_kill }, diff --git a/chal.c b/chal.c new file mode 100644 index 00000000..a8cd4a43 --- /dev/null +++ b/chal.c @@ -0,0 +1,143 @@ +/* -*-c-*- + * + * $Id$ + * + * Cryptographic challenges + * + * (c) 2005 Straylight/Edgeware + */ + +/*----- Licensing notice --------------------------------------------------* + * + * This file is part of Trivial IP Encryption (TrIPE). + * + * TrIPE is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * TrIPE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with TrIPE; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +/*----- Header files ------------------------------------------------------*/ + +#include "tripe.h" + +/*----- Static variables --------------------------------------------------*/ + +static gmac *mac; +static uint32 oseq; +static seqwin iseq; + +/*----- Main code ---------------------------------------------------------*/ + +/* --- @c_genkey@ --- * + * + * Arguments: --- + * + * Returns: --- + * + * Use: Generates a new challenge key. + */ + +static void c_genkey(void) +{ + if (mac && GM_CLASS(mac) == algs.m && oseq < 0x07ffffff) return; + if (mac) GM_DESTROY(mac); + assert(algs.mksz < sizeof(buf_t)); + rand_get(RAND_GLOBAL, buf_t, algs.mksz); + mac = GM_KEY(algs.m, buf_t, algs.mksz); + oseq = 0; + seq_reset(&iseq); + IF_TRACING(T_CHAL, { + trace(T_CHAL, "chal: generated new challenge key"); + trace_block(T_CRYPTO, "chal: new key", buf_t, algs.mksz); + }) +} + +/* --- @c_new@ --- * + * + * Arguments: @buf *b@ = where to put the challenge + * + * Returns: Zero if OK, nonzero on error. + * + * Use: Issues a new challenge. + */ + +int c_new(buf *b) +{ + octet *p; + ghash *h; + + c_genkey(); + p = BCUR(b); + if (buf_putu32(b, oseq++)) return (-1); + h = GM_INIT(mac); + GH_HASH(h, p, BCUR(b) - p); + buf_put(b, GH_DONE(h, 0), algs.tagsz); + GH_DESTROY(h); + if (BBAD(b)) return (-1); + IF_TRACING(T_CHAL, { + trace(T_CHAL, "chal: issuing challenge %lu", (unsigned long)(oseq - 1)); + trace_block(T_CRYPTO, "chal: challenge block", p, BCUR(b) - p); + }) + return (0); +} + +/* --- @c_check@ --- * + * + * Arguments: @buf *b@ = where to find the challenge + * + * Returns: Zero if OK, nonzero if it didn't work. + * + * Use: Checks a challenge. On failure, the buffer is broken. + */ + +int c_check(buf *b) +{ + const octet *p; + size_t sz = 4 + algs.tagsz; + uint32 seq; + ghash *h; + int ok; + + if ((p = buf_get(b, sz)) == 0) { + a_warn("CHAL invalid-challenge"); + goto fail; + } + IF_TRACING(T_CHAL, trace_block(T_CRYPTO, "chal: check challenge", p, sz); ) + if (!mac) { + a_warn("CHAL impossible-challenge"); + goto fail; + } + h = GM_INIT(mac); + GH_HASH(h, p, 4); + ok = (memcmp(GH_DONE(h, 0), p + 4, algs.tagsz) == 0); + GH_DESTROY(h); + if (!ok) { + a_warn("CHAL incorrect-tag"); + goto fail; + } + seq = LOAD32(p); + switch (seq_check(&iseq, LOAD32(p))) { + case SEQ_OK: break; + case SEQ_OLD: a_warn("CHAL replay old-sequence"); goto fail; + case SEQ_REPLAY: a_warn("CHAL replay duplicated-sequence"); goto fail; + default: abort(); + } + T( trace(T_CHAL, "chal: checked challenge %lu", (unsigned long)seq); ) + return (0); + +fail: + buf_break(b); + return (-1); +} + +/*----- That's all, folks -------------------------------------------------*/ diff --git a/debian/control b/debian/control index ef4865cb..60caf64f 100644 --- a/debian/control +++ b/debian/control @@ -1,7 +1,7 @@ Source: tripe Section: net Priority: extra -Maintainer: Mark Wooding +Maintainer: Mark Wooding Build-Depends: catacomb-dev (>= 2.1.0), mlib-dev (>= 2.0.3), tethereal, ethereal-dev (>= 0.10.10), debhelper (>= 4.0.2) Standards-Version: 3.1.1 diff --git a/doc/tripe-admin.5 b/doc/tripe-admin.5 index b6bcdfc1..4afa6a9f 100644 --- a/doc/tripe-admin.5 +++ b/doc/tripe-admin.5 @@ -24,10 +24,9 @@ with little difficulty. By default, the server listens for admin connections on the Unix-domain socket .BR /var/lib/tripe/tripesock . -Administration commands use a simple textual protocol. Each client -command or server response consists of a line of ASCII text terminated -by a single linefeed character. No command may be longer than 255 -characters. +Administration commands use a textual protocol. Each client command or +server response consists of a line of ASCII text terminated by a single +linefeed character. No command may be longer than 255 characters. .SS "General structure" Each command or response line consists of a sequence of whitespace-separated words. The number and nature of whitespace @@ -251,6 +250,13 @@ Emits an line reporting the IP address and port number stored for .IR peer . .TP +.BI "CHECKCHAL " challenge +Verifies a challenge as being one earlier issued by +.B GETCHAL +and not previously either passed to +.B CHECKCHAL +or in a greeting message. +.TP .B "DAEMON" Causes the server to disassociate itself from its terminal and become a background task. This only works once. A warning is issued. @@ -268,6 +274,20 @@ Requests the server to begin a new key exchange with .I peer immediately. .TP +.B "GETCHAL" +Requests a challenge. The challenge is returned in an +.B INFO +line, as a base64-encoded string. See +.BR CHECKCHAL . +.TP +.BI "GREET " peer " " challenge +Sends a greeting packet containing the +.I challenge +(base-64 encoded) to the named +.IR peer . +The expectation is that this will cause the peer to recognize us and +begin a key-exchange. +.TP .B "HELP" Causes the server to emit an .B INFO @@ -425,15 +445,6 @@ the backgroud name-resolution required by the .B ADD command. .TP -.B p -Display contents of packets sent and received by the tunnel and/or peer -modules. -.TP -.B c -Display inputs, outputs and intermediate results of cryptographic -operations. This includes plaintext and key material. Use with -caution. -.TP .B s Handling of symmetric keysets: creation and expiry of keysets, and encryption and decryption of messages. @@ -443,6 +454,21 @@ Key exchange: reception, parsing and emission of key exchange messages. .TP .B m Key management: loading keys and checking for file modifications. +.TP +.B l +Display information about challenge issuing and verification. +.TP +.B p +Display contents of packets sent and received by the tunnel and/or peer +modules. +.TP +.B c +Display inputs, outputs and intermediate results of cryptographic +operations. This includes plaintext and key material. Use with +caution. +.TP +.B A +All of the above. .PP Note that the .B p @@ -453,19 +479,17 @@ and outputs provide extra detail for other outputs. Specifying .B p without -.B r +.BR r or .B t isn't useful; neither is specifying .B c without one of .BR s , +.BR l , .B x or .BR m . -.TP -.B A -All of the above. .RE .TP .B "TUNNELS" @@ -507,7 +531,7 @@ messages. .B WARN messages. .TP -.B a +.B A All of the above. .RE .TP @@ -530,6 +554,10 @@ The .B tripe server is already running as a daemon. .TP +.BI "bad-addr-syntax \-\- " message +(For commands accepting socket addresses.) The address couldn't be +understood. +.TP .BI "bad-syntax \-\- " message (For any command.) The command couldn't be understood: e.g., the number of arguments was wrong. @@ -635,6 +663,12 @@ and its network address is .BI "DAEMON" The server has forked off into the sunset and become a daemon. .TP +.BI "GREET " challenge " " address \fR... +A valid greeting was received, with the given challenge (exactly as it +was returned by +.B GETCHAL +earlier). +.TP .BI "KILL " peer The peer .I peer @@ -677,6 +711,29 @@ client. .BI "ADMIN client-read-error \-\- " message There was an error sending data to a client. The connection to the client has been closed. +.SS "CHAL warnings" +These indicate errors in challenges, either in the +.B CHECKCHAL +command or in greeting packets. +.TP +.B "CHAL impossible-challenge" +The server hasn't issued any challenges yet. Quite how anyone else +thought he could make one up is hard to imagine. +.TP +.B "CHAL incorrect-tag" +Challenge received contained the wrong authentication data. It might be +very stale, or a forgery. +.TP +.B "CHAL invalid-challenge" +Challenge received was the wrong length. We might have changed MAC +algorithms since the challenge was issued, or it might just be rubbish. +.TP +.B "CHAL replay duplicated-sequence" +Challenge received was a definite replay of an old challenge. Someone's +up to something! +.TP +.B "CHAL replay old-sequence" +Challenge received was old, but maybe not actually a replay. Try again. .SS "KEYMGMT warnings" These indicate a problem with the keyring files, or the keys stored in them. diff --git a/ethereal/packet-tripe.c b/ethereal/packet-tripe.c index b3f2190a..4fa84028 100644 --- a/ethereal/packet-tripe.c +++ b/ethereal/packet-tripe.c @@ -199,6 +199,10 @@ static void dissect_tripe(tvbuff_t *b, packet_info *p, proto_tree *t) col_set_str(p->cinfo, COL_INFO, "Miscellaneous, encrypted ping reply"); break; + case MISC_GREET: + col_set_str(p->cinfo, COL_INFO, + "Miscellaneous, greeting"); + break; default: col_add_fstr(p->cinfo, COL_INFO, "Miscellaneous, unknown type code %u", @@ -269,6 +273,7 @@ static void dissect_tripe(tvbuff_t *b, packet_info *p, proto_tree *t) case MISC_NOP: case MISC_PING: case MISC_PONG: + case MISC_GREET: proto_tree_add_item(tt, hf_tripe_misc_payload, b, off, -1, FALSE); goto done; @@ -366,7 +371,7 @@ void proto_register_tripe(void) "This is the TrIPE miscellaneous message type subcode." } }, { &hf_tripe_misc_payload, { - "Miscellaneous message type", "tripe.misc.payload", + "Miscellaneous message payload", "tripe.misc.payload", FT_BYTES, BASE_NONE, 0, 0, "This is the miscellaneous message payload." } }, diff --git a/keyset.c b/keyset.c index cdd0c999..9b804ea6 100644 --- a/keyset.c +++ b/keyset.c @@ -265,27 +265,12 @@ static int dodecrypt(keyset *ks, unsigned ty, buf *b, buf *bb, uint32 *seq) static int dosequence(keyset *ks, uint32 seq) { - uint32 seqbit; - uint32 n; - - if (seq < ks->iseq) { - a_warn("SYMM replay old-sequence"); - return (-1); - } - if (seq >= ks->iseq + KS_SEQWINSZ) { - n = seq - (ks->iseq + KS_SEQWINSZ - 1); - if (n < KS_SEQWINSZ) - ks->iwin >>= n; - else - ks->iwin = 0; - ks->iseq += n; - } - seqbit = 1 << (seq - ks->iseq); - if (ks->iwin & seqbit) { - a_warn("SYMM replay duplicated-sequence"); - return (-1); + switch (seq_check(&ks->iseq, seq)) { + case SEQ_OK: break; + case SEQ_OLD: a_warn("SYMM replay old-sequence"); return (-1); + case SEQ_REPLAY: a_warn("SYMM replay duplicated-sequence"); return (-1); + default: abort(); } - ks->iwin |= seqbit; return (0); } @@ -397,8 +382,8 @@ keyset *ks_gen(const void *k, size_t x, size_t y, size_t z, peer *p) ks->ref = 1; ks->t_exp = now + T_EXP; ks->sz_exp = SZ_EXP; - ks->oseq = ks->iseq = 0; - ks->iwin = 0; + ks->oseq = 0; + seq_reset(&ks->iseq); ks->next = 0; ks->p = p; ks->f = KSF_LISTEN; diff --git a/peer.c b/peer.c index 5e4c4f9b..6b746ddb 100644 --- a/peer.c +++ b/peer.c @@ -53,17 +53,6 @@ const tunnel_ops *tunnels[] = { /*----- Main code ---------------------------------------------------------*/ -static void checktimers(void) -{ - sel_timer *t, **tt; - - tt = &sel.timers; - while (*tt) { - assert((*tt)->prev == tt); - tt = &(*tt)->next; - } -} - /* --- @p_pingtype@ --- * * * Arguments: @unsigned msg@ = message type @@ -144,7 +133,7 @@ found: static void p_read(int fd, unsigned mode, void *v) { - peer *p; + peer *p = 0; addr a; size_t sz; ssize_t n; @@ -161,6 +150,28 @@ static void p_read(int fd, unsigned mode, void *v) return; } + /* --- If the packet is a greeting, don't check peers --- */ + + if (n && buf_i[0] == (MSG_MISC | MISC_GREET)) { + IF_TRACING(T_PEER, { + trace(T_PEER, "peer: greeting received from INET %s %u", + inet_ntoa(a.sin.sin_addr), + (unsigned)ntohs(a.sin.sin_port)); + trace_block(T_PACKET, "peer: greeting contents", buf_i, n); + }) + buf_init(&b, buf_i, n); + buf_getbyte(&b); + if (c_check(&b) || BLEFT(&b)) { + a_warn("PEER - invalid-greeting"); + return; + } + a_notify("GREET %s INET %s %u", + b64_encode(buf_i + 1, n - 1), + inet_ntoa(a.sin.sin_addr), + (unsigned)ntohs(a.sin.sin_port)); + return; + } + /* --- Find the appropriate peer --- */ assert(a.sa.sa_family == AF_INET); @@ -357,7 +368,6 @@ void p_pingdone(ping *p, int rc) if (rc != PING_TIMEOUT) sel_rmtimer(&p->t); T( trace(T_PEER, "peer: ping 0x%08lx done (rc = %d)", (unsigned long)p->id, rc); ) -checktimers(); if (rc >= 0) p->func(rc, p->arg); } @@ -435,12 +445,29 @@ int p_pingsend(peer *p, ping *pg, unsigned type, gettimeofday(&tv, 0); tv.tv_sec += timeout; sel_addtimer(&sel, &pg->t, &tv, p_pingtimeout, pg); -checktimers(); T( trace(T_PEER, "peer: send %s 0x%08lx to %s", p_pingtype(type), (unsigned long)pg->id, p->spec.name); ) return (0); } +/* --- @p_greet@ --- * + * + * Arguments: @peer *p@ = peer to send to + * @const void *c@ = pointer to challenge + * @size_t sz@ = size of challenge + * + * Returns: --- + * + * Use: Sends a greeting packet. + */ + +void p_greet(peer *p, const void *c, size_t sz) +{ + buf *b = p_txstart(p, MSG_MISC | MISC_GREET); + buf_put(b, c, sz); + p_txend(p); +} + /* --- @p_tun@ --- * * * Arguments: @peer *p@ = pointer to peer block diff --git a/servutil.c b/servutil.c index 590327db..f4ab92e3 100644 --- a/servutil.c +++ b/servutil.c @@ -1,6 +1,6 @@ /* -*-c-*- * - * $Id: servutil.c,v 1.5 2004/04/08 01:36:17 mdw Exp $ + * $Id$ * * Various handy server-only utilities * @@ -91,4 +91,74 @@ const char *timestr(time_t t) return ((const char *)buf_t); } +/* --- @b64_encode@ --- * + * + * Arguments: @const void *p@ = pointer to some gorp + * @size_t sz@ = size of the gorp + * + * Returns: Pointer to base64-encoded version in @buf_t@. + */ + +const char *b64_encode(const void *p, size_t sz) +{ + base64_ctx b64; + dstr d = DSTR_INIT; + + base64_init(&b64); + b64.indent = ""; + b64.maxline = 0; + base64_encode(&b64, p, sz, &d); + base64_encode(&b64, 0, 0, &d); + while (d.len && d.buf[d.len - 1] == '=') d.len--; + assert(d.len < sizeof(buf_t)); + memcpy(buf_t, d.buf, d.len); + buf_t[d.len] = 0; + dstr_destroy(&d); + return ((const char *)buf_t); +} + +/* --- @seq_reset@ --- * + * + * Arguments: @seqwin *s@ = sequence-checking window + * + * Returns: --- + * + * Use: Resets a sequence number window. + */ + +void seq_reset(seqwin *s) { s->seq = 0; s->win = 0; } + +/* --- @seq_check@ --- * + * + * Arguments: @seqwin *s@ = sequence-checking window + * @uint32 q@ = sequence number to check + * + * Returns: A @SEQ_@ code. + * + * Use: Checks a sequence number against the window, updating things + * as necessary. + */ + +int seq_check(seqwin *s, uint32 q) +{ + uint32 qbit; + uint32 n; + + if (q < s->seq) + return (SEQ_OLD); + if (q >= s->seq + SEQ_WINSZ) { + n = q - (s->seq + SEQ_WINSZ - 1); + if (n < SEQ_WINSZ) + s->win >>= n; + else + s->win = 0; + s->seq += n; + } + qbit = 1 << (q - s->seq); + if (s->win & qbit) + return (SEQ_REPLAY); + s->win |= qbit; + return (0); +} + /*----- That's all, folks -------------------------------------------------*/ diff --git a/tripe-protocol.h b/tripe-protocol.h index ebf7a71d..bfa97487 100644 --- a/tripe-protocol.h +++ b/tripe-protocol.h @@ -77,6 +77,7 @@ #define MISC_PONG 2u /* Transport-level ping response */ #define MISC_EPING 3u /* Encrypted ping */ #define MISC_EPONG 4u /* Encrypted ping response */ +#define MISC_GREET 5u /* A greeting from a NATed peer */ /* --- Symmetric encryption and keysets --- * * diff --git a/tripe.h b/tripe.h index 47719fe9..eec40939 100644 --- a/tripe.h +++ b/tripe.h @@ -66,6 +66,7 @@ #include #include +#include #include #include #include @@ -117,8 +118,9 @@ #define T_KEYSET 32u #define T_KEYEXCH 64u #define T_KEYMGMT 128u +#define T_CHAL 256u -#define T_ALL 255u +#define T_ALL 511u /* --- Units --- */ @@ -160,6 +162,21 @@ typedef union addr { struct sockaddr_in sin; } addr; +/* --- Sequence number checking --- */ + +typedef struct seqwin { + uint32 seq; /* First acceptable input sequence */ + uint32 win; /* Window of acceptable numbers */ +} seqwin; + +#define SEQ_WINSZ 32 /* Bits in sequence number window */ + +#define SEQ_RESET(iseq) { + +#define SEQ_OK 0 /* Sequence number valid */ +#define SEQ_OLD -1 /* Sequence number too old */ +#define SEQ_REPLAY -2 /* Definitely replayed */ + /* --- A symmetric keyset --- * * * A keyset contains a set of symmetric keys for encrypting and decrypting @@ -185,11 +202,9 @@ typedef struct keyset { size_t tagsz; /* Length to truncate MAC tags */ gmac *min, *mout; /* Keyset MACs for integrity */ uint32 oseq; /* Outbound sequence number */ - uint32 iseq, iwin; /* Inbound sequence number */ + seqwin iseq; /* Inbound sequence number */ } keyset; -#define KS_SEQWINSZ 32 /* Bits in sequence number window */ - #define KSF_LISTEN 1u /* Don't encrypt packets yet */ #define KSF_LINK 2u /* Key is in a linked list */ @@ -355,14 +370,29 @@ typedef struct admin_bgop { void (*cancel)(struct admin_bgop *); /* Destructor function */ } admin_bgop; -typedef struct admin_addop { +typedef struct admin_resop { admin_bgop bg; /* Background operation header */ - peerspec peer; /* Peer pending creation */ - char *paddr; /* Hostname to be resolved */ + char *addr; /* Hostname to be resolved */ bres_client r; /* Background resolver task */ sel_timer t; /* Timer for resolver */ + addr sa; /* Socket address */ + size_t sasz; /* Socket address size */ + void (*func)(struct admin_resop *, int); /* Handler */ +} admin_resop; + +enum { ARES_OK, ARES_FAIL }; + +typedef struct admin_addop { + admin_resop r; /* Name resolution header */ + peerspec peer; /* Peer pending creation */ } admin_addop; +typedef struct admin_greetop { + admin_resop r; /* Name resolution header */ + void *c; /* Challenge block */ + size_t sz; /* Length of challenge */ +} admin_greetop; + typedef struct admin_pingop { admin_bgop bg; /* Background operation header */ ping ping; /* Ping pending response */ @@ -691,6 +721,30 @@ extern int ksl_encrypt(keyset **/*ksroot*/, unsigned /*ty*/, extern int ksl_decrypt(keyset **/*ksroot*/, unsigned /*ty*/, buf */*b*/, buf */*bb*/); +/*----- Challenges --------------------------------------------------------*/ + +/* --- @c_new@ --- * + * + * Arguments: @buf *b@ = where to put the challenge + * + * Returns: Zero if OK, nonzero on error. + * + * Use: Issues a new challenge. + */ + +extern int c_new(buf */*b*/); + +/* --- @c_check@ --- * + * + * Arguments: @buf *b@ = where to find the challenge + * + * Returns: Zero if OK, nonzero if it didn't work. + * + * Use: Checks a challenge. On failure, the buffer is broken. + */ + +extern int c_check(buf */*b*/); + /*----- Administration interface ------------------------------------------*/ /* --- @a_warn@ --- * @@ -819,6 +873,19 @@ extern int p_pingsend(peer */*p*/, ping */*pg*/, unsigned /*type*/, extern void p_pingdone(ping */*p*/, int /*rc*/); +/* --- @p_greet@ --- * + * + * Arguments: @peer *p@ = peer to send to + * @const void *c@ = pointer to challenge + * @size_t sz@ = size of challenge + * + * Returns: --- + * + * Use: Sends a greeting packet. + */ + +extern void p_greet(peer */*p*/, const void */*c*/, size_t /*sz*/); + /* --- @p_tun@ --- * * * Arguments: @peer *p@ = pointer to peer block @@ -1031,6 +1098,40 @@ extern const char *timestr(time_t /*t*/); extern int mystrieq(const char */*x*/, const char */*y*/); +/* --- @b64_encode@ --- * + * + * Arguments: @const void *p@ = pointer to some gorp + * @size_t sz@ = size of the gorp + * + * Returns: Pointer to base64-encoded version in @buf_t@. + */ + +extern const char *b64_encode(const void */*p*/, size_t /*sz*/); + +/* --- @seq_reset@ --- * + * + * Arguments: @seqwin *s@ = sequence-checking window + * + * Returns: --- + * + * Use: Resets a sequence number window. + */ + +extern void seq_reset(seqwin */*s*/); + +/* --- @seq_check@ --- * + * + * Arguments: @seqwin *s@ = sequence-checking window + * @uint32 q@ = sequence number to check + * + * Returns: A @SEQ_@ code. + * + * Use: Checks a sequence number against the window, updating things + * as necessary. + */ + +extern int seq_check(seqwin */*s*/, uint32 /*q*/); + /*----- That's all, folks -------------------------------------------------*/ #ifdef __cplusplus diff --git a/tripemon.in b/tripemon.in index a5fe71d0..0db07752 100644 --- a/tripemon.in +++ b/tripemon.in @@ -7,6 +7,7 @@ import socket as S from sys import argv, exit, stdin, stdout, stderr import os as OS from os import environ +import math as M import sets as SET import getopt as O import time as T @@ -815,8 +816,15 @@ def xlate_time(t): """Translate a time in tripe's stats format to something a human might actually want to read.""" if t == 'NEVER': return '(never)' - Y, M, D, h, m, s = map(int, rx_time.match(t).group(1, 2, 3, 4, 5, 6)) - return '%04d:%02d:%02d %02d:%02d:%02d' % (Y, M, D, h, m, s) + YY, MM, DD, hh, mm, ss = map(int, rx_time.match(t).group(1, 2, 3, 4, 5, 6)) + ago = T.time() - T.mktime((YY, MM, DD, hh, mm, ss, 0, 0, -1)) + ago = M.floor(ago); unit = 's' + for n, u in [(60, 'min'), (60, 'hrs'), (24, 'days')]: + if ago < 2*n: break + ago /= n + unit = u + return '%04d:%02d:%02d %02d:%02d:%02d (%.1f %s ago)' % \ + (YY, MM, DD, hh, mm, ss, ago, unit) def xlate_bytes(b): """Translate a number of bytes into something a human might want to read.""" suff = 'B' -- [mdw]