From 067aa5f013dd6108e81c1df0c2ed19491802bc69 Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Tue, 5 Sep 2017 22:26:51 +0100 Subject: [PATCH] Add notion of `ephemeral' associations and a goodbye protocol. When TrIPE kills an ephemeral peer, it sends a `bye' message to the peer. When TrIPE receives `bye' from an ephemeral peer, it kills the peer (without sending `bye' back). Augment the `connect' service to set appropriate flags when adding peers, and the Wireshark dissector to understand the new message. --- common/protocol.h | 1 + mon/tripemon.in | 4 ++++ peerdb/peers.in | 24 ++++++++++++++++-------- peerdb/peers.in.5.in | 6 ++++++ py/tripe.py.in | 3 ++- server/admin.c | 9 ++++++--- server/peer.c | 23 ++++++++++++++++++++++- server/tests.at | 5 +++-- server/tripe-admin.5.in | 29 +++++++++++++++++++++++++++-- server/tripe.h | 4 +++- svc/connect.8.in | 14 ++++++++++++++ svc/connect.in | 15 +++++++++------ wireshark/tripe.lua | 2 ++ 13 files changed, 115 insertions(+), 24 deletions(-) diff --git a/common/protocol.h b/common/protocol.h index 2416afcf..e40cf177 100644 --- a/common/protocol.h +++ b/common/protocol.h @@ -79,6 +79,7 @@ #define MISC_EPING 3u /* Encrypted ping */ #define MISC_EPONG 4u /* Encrypted ping response */ #define MISC_GREET 5u /* A greeting from a NATed peer */ +#define MISC_BYE 6u /* Departure notification */ /* --- Symmetric encryption and keysets --- * * diff --git a/mon/tripemon.in b/mon/tripemon.in index 0d15474c..8666575a 100644 --- a/mon/tripemon.in +++ b/mon/tripemon.in @@ -1093,6 +1093,9 @@ class AddPeerDialog (MyDialog): me.c_mobile = G.CheckButton('Mobile') table.pack(me.c_mobile, newlinep = True, width = 4, xopt = G.FILL) + me.c_ephem = G.CheckButton('Ephemeral') + table.pack(me.c_ephem, newlinep = True, width = 4, xopt = G.FILL) + me.c_peerkey, me.e_peerkey = \ optional_entry('Peer key tag', r'^[^.:\s]+$', 16) me.c_privkey, me.e_privkey = \ @@ -1115,6 +1118,7 @@ class AddPeerDialog (MyDialog): tunnel = t and me.tuns[t] or None, cork = me.c_cork.get_active() or None, mobile = me.c_mobile.get_active() or None, + ephemeral = me.c_ephem.get_active() or None, key = (me.c_peerkey.get_active() and me.e_peerkey.get_text() or None), priv = (me.c_privkey.get_active() and diff --git a/peerdb/peers.in b/peerdb/peers.in index 3fb23803..75c039d9 100644 --- a/peerdb/peers.in +++ b/peerdb/peers.in @@ -57,6 +57,10 @@ host = override-me ;; the remote peer. peer = INET $[$(host)] $(port) +;; ephemeral: whether to send the peer a disconnection notification, or +;; react to one from the peer. +ephemeral = nil + ;;;-------------------------------------------------------------------------- ;;; Temporary association defaults. ;;; @@ -81,21 +85,28 @@ retries = 5 ;;; The parameters here affect peers to whom dynamic connections are made. ;;; The user and connect parameters probably need customizing. -[@KNOCK] +[@EPHEMERAL] @inherit = @ACTIVE, @WATCH -;; keepalive: how often to send NOP packets to keep the connection alive, at -;; least in the minds of intermediate stateful firewalls and NAT routers. -keepalive = 2m +;; ephemeral: whether to send the peer a disconnection notification, or +;; react to one from the peer. +ephemeral = t ;; every: interval for checking that this connection is alive. every = 30s +[@KNOCK] +@inherit = @EPHEMERAL + +;; keepalive: how often to send NOP packets to keep the connection alive, at +;; least in the minds of intermediate stateful firewalls and NAT routers. +keepalive = 2m + ;; knock: peer-name string to send to the peer. knock = $(myhost) [@DYNAMIC] -@inherit = @ACTIVE, @WATCH +@inherit = @EPHEMERAL ;; cork: whether to wait for a key-exchange packet from the peer before ;; sending one of our own. @@ -115,9 +126,6 @@ disconnect = ssh -q $(ssh-user)@$[$(host)] goodbye ;; least in the minds of intermediate stateful firewalls and NAT routers. keepalive = 2m -;; every: interval for checking that this connection is alive. -every = 30s - ;;;-------------------------------------------------------------------------- ;;; Passive-peers defaults. ;;; diff --git a/peerdb/peers.in.5.in b/peerdb/peers.in.5.in index 89eb7776..ac27b49c 100644 --- a/peerdb/peers.in.5.in +++ b/peerdb/peers.in.5.in @@ -175,6 +175,12 @@ Don't initiate immediate key exchange. Used by Shell command for closing down connection to this peer. Used by .BR connect (8). .TP +.B ephemeral +Mark the peer as ephemeral: see +.BR tripe-admin (5) +for what this means. Used by +.BR connect (8). +.TP .B every Interval for checking that the peer is still alive and well. Used by .BR connect (8). diff --git a/py/tripe.py.in b/py/tripe.py.in index 63ae0afc..0126dc58 100644 --- a/py/tripe.py.in +++ b/py/tripe.py.in @@ -838,7 +838,8 @@ class TripeCommandDispatcher (TripeConnection): *['ADD'] + _kwopts(kw, ['tunnel', 'keepalive', 'key', 'priv', 'cork', - 'mobile', 'knock']) + + 'mobile', 'knock', + 'ephemeral']) + [peer] + list(addr))) def addr(me, peer): diff --git a/server/admin.c b/server/admin.c index 886c672c..54883afa 100644 --- a/server/admin.c +++ b/server/admin.c @@ -551,7 +551,7 @@ void a_quit(void) { close(sock.fd); unlink(sockname); - FOREACH_PEER(p, { p_destroy(p); }); + FOREACH_PEER(p, { p_destroy(p, 1); }); ps_quit(); exit(0); } @@ -1285,11 +1285,12 @@ static void acmd_add(admin *a, unsigned ac, char *av[]) }) OPTTIME("-keepalive", t, { add->peer.t_ka = t; }) OPT("-cork", { add->peer.f |= KXF_CORK; }) + OPT("-ephemeral", { add->peer.f |= PSF_EPHEM; }) OPTARG("-key", arg, { if (add->peer.tag) xfree(add->peer.tag); add->peer.tag = xstrdup(arg); }) - OPT("-mobile", { add->peer.f |= PSF_MOBILE; }) + OPT("-mobile", { add->peer.f |= PSF_MOBILE | PSF_EPHEM; }) OPTARG("-priv", arg, { if (add->peer.privtag) xfree(add->peer.privtag); add->peer.privtag = xstrdup(arg); @@ -1297,6 +1298,7 @@ static void acmd_add(admin *a, unsigned ac, char *av[]) OPTARG("-knock", arg, { if (add->peer.knock) xfree(add->peer.knock); add->peer.knock = xstrdup(arg); + add->peer.f |= PSF_EPHEM; }) }); @@ -1860,6 +1862,7 @@ static void acmd_peerinfo(admin *a, unsigned ac, char *av[]) a_info(a, "keepalive=%lu", ps->t_ka, A_END); a_info(a, "corked=%s", BOOL(p->kx.f&KXF_CORK), "mobile=%s", BOOL(ps->f&PSF_MOBILE), + "ephemeral=%s", BOOL(ps->f&PSF_EPHEM), A_END); a_ok(a); } @@ -1915,7 +1918,7 @@ static void acmd_kill(admin *a, unsigned ac, char *av[]) peer *p; if ((p = a_findpeer(a, av[0])) != 0) { - p_destroy(p); + p_destroy(p, 1); a_ok(a); } } diff --git a/server/peer.c b/server/peer.c index f600f99e..606b2038 100644 --- a/server/peer.c +++ b/server/peer.c @@ -454,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: @@ -1044,17 +1054,28 @@ 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); diff --git a/server/tests.at b/server/tests.at index 8bceb147..08bfb135 100644 --- a/server/tests.at +++ b/server/tests.at @@ -752,7 +752,7 @@ WITH_TRIPE(, [ AT_CLEANUP ###-------------------------------------------------------------------------- -### Knock. +### Knock and bye. AT_SETUP([server knock]) AT_KEYWORDS([knock]) @@ -786,7 +786,7 @@ WITH_2TRIPES([alice], [bob], [-nslip], [-talice], [-tbob], [ AT_CHECK([cat knock-addr],, [INET 127.0.0.1 5311[]nl]) AWAIT_KXDONE([alice], [alice], [bob], [bob], [ - AT_CHECK([TRIPECTL -dalice ADD bob INET 127.0.0.1 5311]) + AT_CHECK([TRIPECTL -dalice ADD -ephemeral bob INET 127.0.0.1 5311]) ]) COMMS_EPING([alice], [alice], [bob], [bob]) @@ -798,6 +798,7 @@ WITH_2TRIPES([alice], [bob], [-nslip], [-talice], [-tbob], [ AT_CHECK([TRIPECTL -dalice FORCEKX bob]) AT_CHECK([TRIPECTL -dbob FORCEKX alice]) ]) + AT_CHECK([TRIPECTL -dbob KILL alice]) AT_CHECK([TRIPECTL -dalice LIST],, []) ]) diff --git a/server/tripe-admin.5.in b/server/tripe-admin.5.in index a340f57f..c81dc111 100644 --- a/server/tripe-admin.5.in +++ b/server/tripe-admin.5.in @@ -332,6 +332,21 @@ Run the command in the background, using the given Don't send an immediate challenge to the peer; instead, wait until it sends us something before responding. .TP +.B "\-ephemeral" +The association with the peer is not intended to persist indefinitely. +If a peer marked as ephemeral is killed, or the +.BR tripe (8) +daemon is shut down, send a +.B bye +packet to the peer so that it forgets about us; if a peer marked as +ephemeral sends us a +.B bye +packet then it is killed (but in this case no further +.B bye +packet is sent). Peers not marked as ephemeral exhibit neither of these +behaviours; each peer must have the other marked as ephemeral for the +association to be fully torn down if either end kills the other. +.TP .BI "\-keepalive " time Send a no-op packet if we've not sent a packet to the peer in the last .I time @@ -370,7 +385,8 @@ emits a .B KNOCK notification stating the peer's (claimed) name and address. The server will already have verified that the sender is using the peer's private -key by this point. +key by this point. This option implies +.BR \-ephemeral . .TP .B "\-mobile" The peer is a mobile device, and is likely to change address rapidly. @@ -380,7 +396,8 @@ peers, however, it will attempt to decrypt the packet using their keys, and if one succeeds, the server will update its idea of the peer's address and emit an .B NEWADDR -notification. +notification. This option implies +.BR \-ephemeral . .TP .BI "\-priv " tag Use the private key @@ -639,6 +656,14 @@ or .B nil depending on whether or not (respectively) the peer is expected to change its address unpredictably. +.TP +.B ephemeral +Either +.B t +or +.B nil +depending on whether the association with the peer is expected to be +temporary or persistent (respectively). .RE .SP .BI "PING \fR[" options "\fR] " peer diff --git a/server/tripe.h b/server/tripe.h index 2024627a..a2907be1 100644 --- a/server/tripe.h +++ b/server/tripe.h @@ -617,6 +617,7 @@ typedef struct peerspec { unsigned f; /* Flags for the peer */ #define PSF_KXMASK 255u /* Key-exchange flags to set */ #define PSF_MOBILE 256u /* Address may change rapidly */ +#define PSF_EPHEM 512u /* Association is ephemeral */ } peerspec; typedef struct peer_byname { @@ -1693,13 +1694,14 @@ extern 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. */ -extern void p_destroy(peer */*p*/); +extern void p_destroy(peer */*p*/, int /*bye*/); /* --- @FOREACH_PEER@ --- * * diff --git a/svc/connect.8.in b/svc/connect.8.in index 5a111647..1e4e2d25 100644 --- a/svc/connect.8.in +++ b/svc/connect.8.in @@ -467,6 +467,7 @@ The service will submit the command .RB [ \-priv .IR tag ] .RB [ \-mobile ] +.RB [ \-ephemeral ] .RB [ \-tunnel .IR driver ] .I address @@ -533,6 +534,19 @@ to the .B tunnel key. .hP \*o +The option +.B \-ephemeral +is provided if the peer's database record assigns the +.B ephemeral +key one of the values +.BR t , +.BR true , +.BR y , +.BR yes, +or +.BR on ; +or if it is absent. +.hP \*o The .I address is the value assigned to the diff --git a/svc/connect.in b/svc/connect.in index fe8d73b9..99dbd8af 100644 --- a/svc/connect.in +++ b/svc/connect.in @@ -717,11 +717,12 @@ def disownpeer(peer): T.Coroutine(run_ifupdown, name = 'ifdown %s' % peer.name) \ .switch('ifdown', peer) -def addpeer(peer, addr): +def addpeer(peer, addr, ephemp): """ Process a connect request from a new peer PEER on address ADDR. - Any existing peer with this name is disconnected from the server. + Any existing peer with this name is disconnected from the server. EPHEMP + is the default ephemeral-ness state for the new peer. """ if peer.name in S.list(): S.kill(peer.name) @@ -734,6 +735,8 @@ def addpeer(peer, addr): mobile = peer.get('mobile', filter = boolean, default = False), knock = peer.get('knock', default = None), cork = peer.get('cork', filter = boolean, default = False), + ephemeral = peer.get('ephemeral', filter = boolean, + default = ephemp), *addr) except T.TripeError, exc: raise T.TripeJobError(*exc.args) @@ -778,7 +781,7 @@ def notify(_, code, *rest): S.warn(['connect', 'knock-tag-mismatch', 'peer', pname, 'public-key-tag', ktag]) return - T.spawn(addpeer, p, rest[1:]) + T.spawn(addpeer, p, rest[1:], True) ###-------------------------------------------------------------------------- ### Command implementation. @@ -813,7 +816,7 @@ def cmd_active(name): addr = peer.get('peer') if addr == 'PASSIVE': raise T.TripeJobError('passive-peer', name) - addpeer(peer, M.split(addr, quotep = True)[0]) + addpeer(peer, M.split(addr, quotep = True)[0], True) def cmd_listactive(): """ @@ -875,7 +878,7 @@ def cmd_passive(*args): addr = cr.parent.switch() if addr is None: raise T.TripeJobError('connect-timeout') - addpeer(peer, addr) + addpeer(peer, addr, True) finally: del chalmap[chal] @@ -911,7 +914,7 @@ def setup(): for name in M.split(autos)[0]: try: peer = Peer(name, cdb) - addpeer(peer, M.split(peer.get('peer'), quotep = True)[0]) + addpeer(peer, M.split(peer.get('peer'), quotep = True)[0], False) except T.TripeJobError, err: S.warn('connect', 'auto-add-failed', name, *err.args) diff --git a/wireshark/tripe.lua b/wireshark/tripe.lua index aab8e798..5c6d7ba9 100644 --- a/wireshark/tripe.lua +++ b/wireshark/tripe.lua @@ -408,6 +408,8 @@ local PKTINFO = { dissect = { dissect_misc_ciphertext } }, [5] = { label = "MISC_GREET", info = "greeting", dissect = { dissect_misc_payload } }, + [6] = { label = "MISC_BYE", info = "disconnect notification", + dissect = { dissect_misc_ciphertext } }, } } } -- 2.11.0