X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/blobdiff_plain/66196407325756702eaa7ee25384d9b2374ed540..067aa5f013dd6108e81c1df0c2ed19491802bc69:/server/peer.c diff --git a/server/peer.c b/server/peer.c index 68e32e58..606b2038 100644 --- a/server/peer.c +++ b/server/peer.c @@ -9,19 +9,18 @@ * * 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 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 3 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. + * 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. + * along with TrIPE. If not, see . */ /*----- Header files ------------------------------------------------------*/ @@ -167,6 +166,51 @@ static int p_encrypt(peer *p, int ty, buf *bin, buf *bout) return (err); } +/* --- @p_updateaddr@ --- * + * + * Arguments: @peer *p@ = pointer to peer block + * @const addr *a@ = address to associate with this peer + * + * Returns: Zero if the address was changed; @+1@ if it was already + * right. + * + * Use: Updates our idea of @p@'s address. + */ + +int p_updateaddr(peer *p, const addr *a) +{ + peer *q; + peer_byaddr *pa, *qa; + unsigned f; + + /* --- Figure out how to proceed --- * + * + * If this address already belongs to a different peer, then swap the + * addresses over. This doesn't leave the displaced peer in an especially + * good state, but it ought to get sorted out soon enough. + */ + + pa = am_find(&byaddr, a, sizeof(peer_byaddr), &f); + if (f && pa->p == p) + return (+1); + else if (!f) { + T( trace(T_PEER, "peer: updating address for `%s'", p_name(p)); ) + am_remove(&byaddr, p->byaddr); + p->byaddr = pa; p->spec.sa = *a; pa->p = p; + a_notify("NEWADDR", "?PEER", p, "?ADDR", a, A_END); + return (0); + } else { + q = pa->p; qa = p->byaddr; + T( trace(T_PEER, "peer: swapping addresses for `%s' and `%s'", + p_name(p), p_name(q)); ) + q->byaddr = qa; qa->p = q; q->spec.sa = p->spec.sa; + p->byaddr = pa; pa->p = p; p->spec.sa = *a; + a_notify("NEWADDR", "?PEER", p, "?ADDR", a, A_END); + a_notify("NEWADDR", "?PEER", q, "?ADDR", &q->spec.sa, A_END); + return (0); + } +} + /* --- @p_decrypt@ --- * * * Arguments: @peer **pp@ = pointer to peer to decrypt message from @@ -188,55 +232,74 @@ static int p_encrypt(peer *p, int ty, buf *bin, buf *bout) static int p_decrypt(peer **pp, addr *a, size_t n, int ty, buf *bin, buf *bout) { - peer *p; - peer_byaddr *pa; + peer *p, *q; int err = KSERR_DECRYPT; - unsigned f; - if (*pp) { - p = *pp; + /* --- If we have a match on the source address then try that first --- */ + + q = *pp; + if (q) { T( trace(T_PEER, "peer: decrypting packet from known peer `%s'", - p_name(p)); ) - err = ksl_decrypt(&p->ks, ty, bin, bout); - } else { + p_name(q)); ) + if ((err = ksl_decrypt(&q->ks, ty, bin, bout)) != KSERR_DECRYPT || + !(q->spec.f & PSF_MOBILE) || nmobile == 1) { + p = q; + goto match; + } + T( trace(T_PEER, "peer: failed to decrypt: try other mobile peers..."); ) + } else if (nmobile) + T( trace(T_PEER, "peer: unknown source: trying mobile peers...") ); + else { p = 0; - if (nmobile) { - T( trace(T_PEER, "peer: unknown source: trying mobile peers..."); ) - FOREACH_PEER(q, { - if (!(q->spec.f & PSF_MOBILE)) continue; - if ((err = ksl_decrypt(&q->ks, ty, bin, bout)) == KSERR_DECRYPT) { - T( trace(T_PEER, "peer: peer `%s' failed to decrypt", - p_name(q)); ) - continue; - } else { - p = *pp = q; - IF_TRACING(T_PEER, { - if (!err) - trace(T_PEER, "peer: peer `%s' reports success", p_name(p)); - else { - trace(T_PEER, "peer: peer `%s' reports decryption error %d", - p_name(p), err); - } - }) - break; + goto searched; + } + + /* --- See whether any mobile peer is interested --- */ + + p = 0; + FOREACH_PEER(qq, { + if (qq == q || !(qq->spec.f & PSF_MOBILE)) continue; + if ((err = ksl_decrypt(&qq->ks, ty, bin, bout)) == KSERR_DECRYPT) { + T( trace(T_PEER, "peer: peer `%s' failed to decrypt", + p_name(qq)); ) + continue; + } else { + p = qq; + IF_TRACING(T_PEER, { + if (!err) + trace(T_PEER, "peer: peer `%s' reports success", p_name(qq)); + else { + trace(T_PEER, "peer: peer `%s' reports decryption error %d", + p_name(qq), err); } - }); + }) + break; } - if (!p) { + }); + + /* --- We've searched the mobile peers --- */ + +searched: + if (!p) { + if (!q) a_warn("PEER", "-", "unexpected-source", "?ADDR", a, A_END); - return (-1); - } - if (!err) { - T( trace(T_PEER, "peer: updating address for `%s'", p_name(p)); ) - p_rxupdstats(p, n); - pa = am_find(&byaddr, a, sizeof(peer_byaddr), &f); assert(!f); - am_remove(&byaddr, p->byaddr); - p->byaddr = pa; - pa->p = p; - p->spec.sa = *a; - a_notify("NEWADDR", "?PEER", p, "?ADDR", a, A_END); + else { + a_warn("PEER", "?PEER", p, "decrypt-failed", + "error-code", "%d", err, A_END); + p_rxupdstats(q, n); } + return (-1); + } + + /* --- We found one that accepted, so update the peer's address --- */ + + if (!err) { + *pp = p; + p_updateaddr(p, a); } + +match: + p_rxupdstats(p, n); if (err) { if (p) p->st.n_reject++; a_warn("PEER", "?PEER", p, "decrypt-failed", @@ -263,14 +326,14 @@ static void p_read(int fd, unsigned mode, void *v) { peer *p = 0; addr a; - size_t sz; + socklen_t sz; ssize_t n; int ch; buf b, bb; /* --- Read the data --- */ - TIMER; + QUICKRAND; sz = sizeof(addr); n = recvfrom(fd, buf_i, sizeof(buf_i), 0, &a.sa, &sz); if (n < 0) { @@ -289,7 +352,7 @@ static void p_read(int fd, unsigned mode, void *v) }) buf_init(&b, buf_i, n); buf_getbyte(&b); - if (c_check(&b) || BLEFT(&b)) { + if (c_check(0, 0, &b) || BLEFT(&b)) { a_warn("PEER", "-", "invalid-greeting", A_END); return; } @@ -321,7 +384,6 @@ static void p_read(int fd, unsigned mode, void *v) /* --- Pick the packet apart --- */ - if (p) p_rxupdstats(p, n); buf_init(&b, buf_i, n); if ((ch = buf_getbyte(&b)) < 0) { a_warn("PEER", "?PEER", p, "bad-packet", "no-type", A_END); @@ -351,22 +413,25 @@ static void p_read(int fd, unsigned mode, void *v) } break; case MSG_KEYEXCH: - if (!p) goto unexp; - kx_message(&p->kx, ch & MSG_TYPEMASK, &b); + if (p) p_rxupdstats(p, n); + if (kx_message(p ? &p->kx : 0, &a, ch & MSG_TYPEMASK, &b)) goto unexp; break; case MSG_MISC: switch (ch & MSG_TYPEMASK) { case MISC_NOP: if (!p) goto unexp; + p_rxupdstats(p, n); T( trace(T_PEER, "peer: received NOP packet"); ) break; case MISC_PING: if (!p) goto unexp; + p_rxupdstats(p, n); buf_put(p_txstart(p, MSG_MISC | MISC_PONG), BCUR(&b), BLEFT(&b)); p_txend(p); break; case MISC_PONG: if (!p) goto unexp; + p_rxupdstats(p, n); p_ponged(p, MISC_PONG, &b); break; case MISC_EPING: @@ -389,6 +454,16 @@ static void p_read(int fd, unsigned mode, void *v) p_ponged(p, MISC_EPONG, &bb); } break; + case MISC_BYE: + buf_init(&bb, buf_t, sizeof(buf_t)); + if (p_decrypt(&p, &a, n, ch, &b, &bb)) return; + if (!(p->spec.f&PSF_EPHEM)) return; + if (BOK(&bb)) { + buf_flip(&bb); + if (BSZ(&bb)) return; + p_destroy(p, 0); + } + break; } break; default: @@ -423,6 +498,30 @@ buf *p_txstart(peer *p, unsigned msg) return (&p->b); } +/* --- @p_txaddr@ --- * + * + * Arguments: @const addr *a@ = recipient address + * @const void *p@ = pointer to packet to send + * @size_t sz@ = length of packet + * + * Returns: Zero if successful, nonzero on error. + * + * Use: Sends a packet to an address which (possibly) isn't a current + * peer. + */ + +int p_txaddr(const addr *a, const void *p, size_t sz) +{ + socklen_t sasz = addrsz(a); + + IF_TRACING(T_PEER, trace_block(T_PACKET, "peer: sending packet", p, sz); ) + if (sendto(sock.fd, p, sz, 0, &a->sa, sasz) < 0) { + a_warn("PEER", "?ADDR", a, "socket-write-error", "?ERRNO", A_END); + return (-1); + } + return (0); +} + /* --- @p_txend@ --- * * * Arguments: @peer *p@ = pointer to peer block @@ -436,6 +535,8 @@ static void p_setkatimer(peer *); static int p_dotxend(peer *p) { + socklen_t sasz = addrsz(&p->spec.sa); + if (!BOK(&p->b)) { a_warn("PEER", "?PEER", p, "packet-build-failed", A_END); return (0); @@ -443,7 +544,7 @@ static int p_dotxend(peer *p) 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->spec.sa.sa, p->spec.sasz) < 0) { + 0, &p->spec.sa.sa, sasz) < 0) { a_warn("PEER", "?PEER", p, "socket-write-error", "?ERRNO", A_END); return (0); } else { @@ -612,7 +713,7 @@ void p_tun(peer *p, buf *b) { buf *bb = p_txstart(p, MSG_PACKET); - TIMER; + QUICKRAND; p_encrypt(p, MSG_PACKET, b, bb); if (BOK(bb) && BLEN(bb)) { p->st.n_ipout++; @@ -752,7 +853,7 @@ void p_init(struct in_addr addr, unsigned port) unsigned p_port(void) { addr a; - size_t sz = sizeof(addr); + socklen_t sz = sizeof(addr); if (getsockname(sock.fd, &a.sa, &sz)) die(EXIT_FAILURE, "couldn't read port number: %s", strerror(errno)); @@ -773,6 +874,7 @@ unsigned p_port(void) static void p_keepalive(struct timeval *now, void *pv) { peer *p = pv; + p_txstart(p, MSG_MISC | MISC_NOP); p_dotxend(p); T( trace(T_PEER, "peer: sent keepalive to %s", p->spec.name); ) p_setkatimer(p); @@ -824,8 +926,9 @@ peer *p_create(peerspec *spec) T( trace(T_PEER, "peer: creating new peer `%s'", spec->name); ) p->spec = *spec; p->spec.name = (/*unconst*/ char *)SYM_NAME(p->byname); - if (spec->tag) - p->spec.tag = xstrdup(spec->tag); + if (spec->tag) p->spec.tag = xstrdup(spec->tag); + if (spec->privtag) p->spec.privtag = xstrdup(spec->privtag); + if (spec->knock) p->spec.knock = xstrdup(spec->knock); p->ks = 0; p->pings = 0; p->ifname = 0; @@ -840,7 +943,7 @@ peer *p_create(peerspec *spec) T( trace(T_TUNNEL, "peer: attached interface %s to peer `%s'", p->ifname, p_name(p)); ) p_setkatimer(p); - if (kx_init(&p->kx, p, &p->ks, p->spec.f & PSF_KXMASK)) + if (kx_setup(&p->kx, p, &p->ks, p->spec.f & PSF_KXMASK)) goto tidy_4; a_notify("ADD", "?PEER", p, @@ -855,8 +958,7 @@ peer *p_create(peerspec *spec) return (p); tidy_4: - if (spec->t_ka) - sel_rmtimer(&p->tka); + if (spec->t_ka) sel_rmtimer(&p->tka); xfree(p->ifname); p->t->ops->destroy(p->t); tidy_3: @@ -864,6 +966,7 @@ tidy_3: tidy_2: am_remove(&byaddr, p->byaddr); if (p->spec.tag) xfree(p->spec.tag); + if (p->spec.privtag) xfree(p->spec.privtag); tidy_1: sym_remove(&byname, p->byname); tidy_0: @@ -891,6 +994,16 @@ const char *p_name(peer *p) const char *p_tag(peer *p) { return (p->spec.tag ? p->spec.tag : p->spec.name); } +/* --- @p_privtag@ --- * + * + * Arguments: @peer *p@ = pointer to a peer block + * + * Returns: A pointer to the peer's private key tag. + */ + +const char *p_privtag(peer *p) + { return (p->spec.privtag ? p->spec.privtag : tag_priv); } + /* --- @p_spec@ --- * * * Arguments: @peer *p@ = pointer to a peer block @@ -941,29 +1054,38 @@ peer *p_find(const char *name) /* --- @p_destroy@ --- * * * Arguments: @peer *p@ = pointer to a peer + * @int bye@ = say goodbye to the peer? * * Returns: --- * * Use: Destroys a peer. */ -void p_destroy(peer *p) +void p_destroy(peer *p, int bye) { ping *pg, *ppg; + buf *b, bb; T( trace(T_PEER, "peer: destroying peer `%s'", p->spec.name); ) + + if (bye && (p->spec.f&PSF_EPHEM)) { + b = p_txstart(p, MSG_MISC | MISC_BYE); + buf_init(&bb, buf_t, sizeof(buf_t)); + assert(BOK(&bb)); buf_flip(&bb); + p_encrypt(p, MSG_MISC | MISC_BYE, &bb, b); + p_txend(p); + } + a_notify("KILL", "%s", p->spec.name, A_END); ksl_free(&p->ks); kx_free(&p->kx); - if (p->spec.f & PSF_MOBILE) - nmobile--; - if (p->ifname) - xfree(p->ifname); - if (p->spec.tag) - xfree(p->spec.tag); + if (p->spec.f & PSF_MOBILE) nmobile--; + if (p->ifname) xfree(p->ifname); + if (p->spec.tag) xfree(p->spec.tag); + if (p->spec.privtag) xfree(p->spec.privtag); + if (p->spec.knock) xfree(p->spec.knock); p->t->ops->destroy(p->t); - if (p->spec.t_ka) - sel_rmtimer(&p->tka); + if (p->spec.t_ka) sel_rmtimer(&p->tka); for (pg = p->pings; pg; pg = ppg) { ppg = pg->next; p_pingdone(pg, PING_PEERDIED);