From: Mark Wooding Date: Fri, 25 Jan 2019 12:08:24 +0000 (+0000) Subject: Merge branches 'mdw/knock' and 'mdw/ipv6' into bleeding X-Git-Tag: 1.5.0~41 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/commitdiff_plain/4a3882945f605704ede113a9fe98cd19a92363a7?hp=-c Merge branches 'mdw/knock' and 'mdw/ipv6' into bleeding * mdw/knock: Add notion of `ephemeral' associations and a goodbye protocol. Add new `knock' protocol. server/{keyexch,peer}.c: Maybe key-exchange messages come out of the blue. server/keyexch.c (kx_message): Squish vertically. server/keyexch.c: Abstract out the common message-handling behaviour. server/keymgmt.c: Track and find keys by their 32-bit IDs. server/test.c: Add a program to assist unit tests. server/servutil.c: Add utilities for plain asymmetric encryption. server/servutil.c: Add utilities for simple leaky-bucket rate limiting. server/keyexch.c: Rename kx_init => kx_setup. server/: Augment challenges to allow a payload. server/chal.c: Capture `master->algs.bulk' in a variable. server/chal.c: Rename bulk => bchal. server/: Expose and enhance the bulk-key-derivation protocol. * mdw/ipv6: (64 commits) contrib/greet.in: Accept IPv6 addresses. contrib/tripe-ipif.in: Fixing for IPv6. svc/conntrack.in: Add IPv6 support. svc/conntrack.in: Split out a base class from `InetAddress'. svc/conntrack.in: Contemplate multiple address families. svc/conntrack.in: Allow multiple networks in a peer pattern. svc/conntrack.in (kickpeers): Refactor and reformat the search loop. svc/conntrack.in (kickpeers): Rename `map' variable. svc/conntrack.in: Process peer patterns in order. svc/conntrack.in: Maintain config groups in a dictionary. svc/conntrack.in: Make an `InetAddress' class to do address wrangling. svc/conntrack.in: Factor out network parsing. svc/conntrack.in: Gather address hacking functions into a new section. svc/conntrack.in: Introduce a function for parsing address strings. svc/conntrack.in (strmask): Consistently return a string object. svc/conntrack.in: Fix netmask parsing. svc/conntrack.in: Leave time for network configuration to settle. svc/conntrack.in: Hoist `netupdown' above `kickpeers'. server/, mon/: Introduce transport of TrIPE over IPv6. server/addrmap.c (hash): Visually tighten the arithmetic. ... --- 4a3882945f605704ede113a9fe98cd19a92363a7 diff --combined mon/tripemon.in index 8666575a,1c70d1ff..11ee6dc0 --- a/mon/tripemon.in +++ b/mon/tripemon.in @@@ -323,13 -323,19 +323,19 @@@ class Peer (MonitorObject) def _setaddr(me, addr): """Set the peer's address.""" - if addr[0] == 'INET': - ipaddr, port = addr[1:] + if addr[0] in ['INET', 'INET6']: + af, ipaddr, port = addr try: - name = S.gethostbyaddr(ipaddr)[0] - me.addr = 'INET %s:%s [%s]' % (name, port, ipaddr) - except S.herror: - me.addr = 'INET %s:%s' % (ipaddr, port) + name, _ = S.getnameinfo((ipaddr, int(port)), + S.NI_NUMERICSERV | S.NI_NAMEREQD) + except S.gaierror: + me.addr = '%s %s%s%s:%s' % (af, + af == 'INET6' and '[' or '', + ipaddr, + af == 'INET6' and ']' or '', + port) + else: + me.addr = '%s %s:%s [%s]' % (af, name, port, ipaddr) else: me.addr = ' '.join(addr) @@@ -1042,6 -1048,8 +1048,8 @@@ class AddPeerDialog (MyDialog) * e_name, e_addr, e_port, c_keepalive, l_tunnel: widgets in the dialog """ + AFS = ['ANY', 'INET', 'INET6'] + def __init__(me): """Initialize the dialogue.""" MyDialog.__init__(me, 'Add peer', @@@ -1055,10 -1063,15 +1063,15 @@@ table = GridPacker() me.vbox.pack_start(table, True, True, 0) me.e_name = table.labelled('Name', - ValidatingEntry(r'^[^\s.:]+$', '', 16), + ValidatingEntry(r'^[^\s:]+$', '', 16), width = 3) + me.l_af = table.labelled('Family', combo_box_text(), + newlinep = True, width = 3) + for af in me.AFS: + me.l_af.append_text(af) + me.l_af.set_active(0) me.e_addr = table.labelled('Address', - ValidatingEntry(r'^[a-zA-Z0-9.-]+$', '', 24), + ValidatingEntry(r'^[a-zA-Z0-9.-:]+$', '', 24), newlinep = True) me.e_port = table.labelled('Port', ValidatingEntry(numericvalidate(0, 65535), @@@ -1093,24 -1106,20 +1106,26 @@@ 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 = \ optional_entry('Private key tag', r'^[^.:\s]+$', 16) + me.c_knock, me.e_knock = \ + optional_entry('Knock string', r'^[^:\s]+$', 16) + me.show_all() def ok(me): """Handle an OK press: create the peer.""" try: t = me.l_tunnel.get_active() + afix = me.l_af.get_active() me._addpeer(me.e_name.get_text(), + me.AFS[afix], me.e_addr.get_text(), me.e_port.get_text(), keepalive = (me.c_keepalive.get_active() and @@@ -1118,13 -1127,10 +1133,13 @@@ 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 - me.e_privkey.get_text() or None)) + me.e_privkey.get_text() or None), + knock = (me.c_knock.get_active() and + me.e_knock.get_text() or None)) except ValidationError: GDK.beep() return diff --combined peerdb/peers.in.5.in index ac27b49c,554a8d19..d638b078 --- a/peerdb/peers.in.5.in +++ b/peerdb/peers.in.5.in @@@ -83,14 -83,33 +83,33 @@@ is replaced by the value assigned to th .IR key . .hP \*o An occurrence of - .BI $[ host ] + .BI $ flags [ host ] is replaced by the IP address of the named .IR host . Note that .I host may itself contain .BI $( key ) - substitutions. + substitutions. The + .I flags + consist of zero or more of the following characters: + .RB ` 4 ' + looks up the + .IR host 's + IPv4 address(es); + .RB ` 6 ' + looks up the + .IR host 's + IPv6 address(es); + and + .RB ` * ' + returns all of the found addresses, separated by spaces, rather than + just the first one. If neither address family is requested, then + .RB ` 46 ' + is assumed. IPv6 address lookup of names, rather than address literals, + depends on the external + .BR adnshost (1) + program; if it is not present then only IPv4 lookups will be performed. .PP There is a simple concept of .I inheritance @@@ -175,12 -194,6 +194,12 @@@ Don't initiate immediate key exchange 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). @@@ -213,10 -226,6 +232,10 @@@ Interval for sending keepalive pings. Key tag to use to authenticate the peer. Used by .BR connect (8). .TP +.B knock +Knock string to send when establishing a dynamic connection. Used by +.BR connect (8). +.TP .B mobile Peer's IP address is highly volatile. Used by .BR connect (8). diff --combined py/tripe.py.in index 0126dc58,29911b0e..a9be6687 --- a/py/tripe.py.in +++ b/py/tripe.py.in @@@ -838,8 -838,7 +838,8 @@@ class TripeCommandDispatcher (TripeConn *['ADD'] + _kwopts(kw, ['tunnel', 'keepalive', 'key', 'priv', 'cork', - 'mobile']) + + 'mobile', 'knock', + 'ephemeral']) + [peer] + list(addr))) def addr(me, peer): @@@ -879,8 -878,10 +879,10 @@@ *['PING'] + _kwopts(kw, ['timeout']) + [peer])) - def port(me): - return _oneline(me.command('PORT', filter = _tokenjoin)) + def port(me, af = None): + return _oneline(me.command('PORT', + *((af is not None) and [af] or []), + filter = _tokenjoin)) def quit(me): return _simple(me.command('QUIT')) def reload(me): diff --combined server/Makefile.am index 2a1ff28a,1e6d71bc..f569b455 --- a/server/Makefile.am +++ b/server/Makefile.am @@@ -25,10 -25,10 +25,11 @@@ include $(top_srcdir)/vars.am sbin_PROGRAMS = +noinst_PROGRAMS = man_MANS = - LDADD = $(libtripe) $(libpriv) $(catacomb_LIBS) + LDADD = $(libtripe) $(libpriv) \ + $(catacomb_LIBS) $(ADNS_LIBS) ###-------------------------------------------------------------------------- ### The main server. @@@ -73,26 -73,4 +74,26 @@@ man_MANS += tripe-service.7trip CLEANFILES += tripe-service.7tripe EXTRA_DIST += tripe-service.7.in +###-------------------------------------------------------------------------- +### Unit-test program. + +noinst_PROGRAMS += tripe-test + +tripe_test_SOURCES = test.c + +tripe_test_SOURCES += admin.c +tripe_test_SOURCES += addrmap.c +tripe_test_SOURCES += bulkcrypto.c +tripe_test_SOURCES += chal.c +tripe_test_SOURCES += dh.c +tripe_test_SOURCES += keyexch.c +tripe_test_SOURCES += keymgmt.c +tripe_test_SOURCES += keyset.c +tripe_test_SOURCES += peer.c +tripe_test_SOURCES += privsep.c +tripe_test_SOURCES += servutil.c + +tripe_test_SOURCES += tun-std.c +tripe_test_SOURCES += tun-slip.c + ###----- That's all, folks -------------------------------------------------- diff --combined server/admin.c index 54883afa,8a3e62ca..87bb9053 --- a/server/admin.c +++ b/server/admin.c @@@ -61,6 -61,10 +61,10 @@@ static const trace_opt w_opts[] = /*----- Static variables --------------------------------------------------*/ + #ifdef HAVE_LIBADNS + static adns_state ads; + sel_hook hook; + #endif static admin *admins; static admin *a_dead; static sel_file sock; @@@ -272,14 -276,19 +276,19 @@@ void a_vformat(dstr *d, const char *fmt } else if (*fmt == '?') { if (strcmp(fmt, "?ADDR") == 0) { const addr *a = va_arg(*ap, const addr *); - switch (a->sa.sa_family) { - case AF_INET: - u_quotify(d, "INET"); - u_quotify(d, inet_ntoa(a->sin.sin_addr)); - dstr_putf(d, " %u", (unsigned)ntohs(a->sin.sin_port)); - break; - default: - abort(); + char name[NI_MAXHOST], serv[NI_MAXSERV]; + int ix, err; + if ((err = getnameinfo(&a->sa, addrsz(a), + name, sizeof(name), serv, sizeof(serv), + (NI_NUMERICHOST | NI_NUMERICSERV | + NI_DGRAM)))) { + dstr_putf(d, " E%d", err); + u_quotify(d, gai_strerror(err)); + } else { + ix = afix(a->sa.sa_family); assert(ix >= 0); + u_quotify(d, aftab[ix].name); + u_quotify(d, name); + u_quotify(d, serv); } } else if (strcmp(fmt, "?B64") == 0) { const octet *p = va_arg(*ap, const octet *); @@@ -551,7 -560,7 +560,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); } @@@ -1006,6 -1015,101 +1015,101 @@@ static void a_svcrelease(admin_service /*----- Name resolution operations ----------------------------------------*/ + #ifdef HAVE_LIBADNS + + /* --- @before_select@ --- * + * + * Arguments: @sel_state *s@ = the @sel@ multiplexor (unused) + * @sel_args *a@ = input to @select@, to be updated + * @void *p@ = a context pointer (unused) + * + * Returns: --- + * + * Use: An I/O multiplexor hook, called just before waiting for I/O + * events. + * + * Currently its main purpose is to wire ADNS into the event + * loop. + */ + + static void before_select(sel_state *s, sel_args *a, void *p) + { + struct timeval now; + adns_query q; + adns_answer *n; + admin_resop *r; + int any = 0; + + /* --- Check for name-resolution progress --- * + * + * If there is any, then clobber the timeout: one of the resolver + * callbacks might have renewed its interest in a file descriptor, but too + * late to affect this @select@ call. + * + * I think, strictly, this is an mLib bug, but it's cheap enough to hack + * around here. Fixing it will wait for mLib 3. + */ + + for (;;) { + q = 0; + if (adns_check(ads, &q, &n, &p)) break; + r = p; + any = 1; + if (n->status != adns_s_ok) { + T( trace(T_ADMIN, "admin: resop %s failed: %s", + BGTAG(r), adns_strerror(n->status)); ) + a_bgfail(&r->bg, "resolve-error", "%s", r->addr, A_END); + r->func(r, ARES_FAIL); + } else { + T( trace(T_ADMIN, "admin: resop %s ok", BGTAG(r)); ) + assert(n->type == adns_r_addr); + assert(n->nrrs > 0); + assert(n->rrs.addr[0].len <= sizeof(r->sa)); + memcpy(&r->sa, &n->rrs.addr[0].addr, n->rrs.addr[0].len); + setport(&r->sa, r->port); + r->func(r, ARES_OK); + } + free(n); + sel_rmtimer(&r->t); + xfree(r->addr); + a_bgrelease(&r->bg); + } + + if (any) { a->tvp = &a->tv; a->tv.tv_sec = 0; a->tv.tv_usec = 0; } + + gettimeofday(&now, 0); + adns_beforeselect(ads, &a->maxfd, + &a->fd[SEL_READ], &a->fd[SEL_WRITE], &a->fd[SEL_EXC], + &a->tvp, &a->tv, &now); + } + + /* --- @after_select@ --- * + * + * Arguments: @sel_state *s@ = the @sel@ multiplexor (unused) + * @sel_args *a@ = input to @select@, to be updated + * @void *p@ = a context pointer (unused) + * + * Returns: --- + * + * Use: An I/O multiplexor hook, called just after waiting for I/O + * events. + * + * Currently its main purpose is to wire ADNS into the event + * loop. + */ + + static void after_select(sel_state *s, sel_args *a, void *p) + { + struct timeval now; + + gettimeofday(&now, 0); + adns_afterselect(ads, a->maxfd, + &a->fd[SEL_READ], &a->fd[SEL_WRITE], &a->fd[SEL_EXC], + &now); + } + + #else + /* --- @a_resolved@ --- * * * Arguments: @struct hostent *h@ = pointer to resolved hostname @@@ -1020,13 -1124,17 +1124,17 @@@ static void a_resolved(struct hostent * { admin_resop *r = v; - T( trace(T_ADMIN, "admin: resop %s resolved", BGTAG(r)); ) QUICKRAND; if (!h) { + T( trace(T_ADMIN, "admin: resop %s failed: %s", + BGTAG(r), hstrerror(h_errno)); ) a_bgfail(&r->bg, "resolve-error", "%s", r->addr, A_END); r->func(r, ARES_FAIL); } else { + T( trace(T_ADMIN, "admin: resop %s ok", BGTAG(r)); ) + r->sa.sin.sin_family = AF_INET; memcpy(&r->sa.sin.sin_addr, h->h_addr, sizeof(struct in_addr)); + setport(&r->sa, r->port); r->func(r, ARES_OK); } sel_rmtimer(&r->t); @@@ -1034,6 -1142,8 +1142,8 @@@ a_bgrelease(&r->bg); } + #endif + /* --- @a_restimer@ --- * * * Arguments: @struct timeval *tv@ = timer @@@ -1051,7 -1161,11 +1161,11 @@@ static void a_restimer(struct timeval * T( trace(T_ADMIN, "admin: resop %s timeout", BGTAG(r)); ) a_bgfail(&r->bg, "resolver-timeout", "%s", r->addr, A_END); r->func(r, ARES_FAIL); + #ifdef HAVE_LIBADNS + adns_cancel(r->q); + #else bres_abort(&r->r); + #endif xfree(r->addr); a_bgrelease(&r->bg); } @@@ -1073,7 -1187,11 +1187,11 @@@ static void a_rescancel(admin_bgop *bg r->func(r, ARES_FAIL); sel_rmtimer(&r->t); xfree(r->addr); + #ifdef HAVE_LIBADNS + adns_cancel(r->q); + #else bres_abort(&r->r); + #endif } /* --- @a_resolve@ --- * @@@ -1096,20 -1214,39 +1214,39 @@@ static void a_resolve(admin *a, admin_r { struct timeval tv; unsigned long pt; + int af = AF_UNSPEC; + const char *fam = "ANY"; char *p; - int i = 0; + int i = 0, j; + struct addrinfo *ai, *ailist, aihint = { 0 }; + #ifdef HAVE_LIBADNS + int err; + adns_queryflags qf; + #endif /* --- Fill in the easy bits of address --- */ r->bg.tag = ""; r->addr = 0; r->func = func; - if (mystrieq(av[i], "inet")) i++; + if (mystrieq(av[i], "any")) + { fam = "ANY"; af = AF_UNSPEC; i++; } + else for (j = 0; j < NADDRFAM; j++) { + if (mystrieq(av[i], aftab[j].name)) { + if (udpsock[j].fd < 0) { + a_fail(a, "disabled-address-family", "%s", aftab[j].name, A_END); + goto fail; + } + fam = aftab[j].name; + af = aftab[j].af; + i++; + break; + } + } if (ac - i != 1 && ac - i != 2) { - a_fail(a, "bad-addr-syntax", "[inet] ADDRESS [PORT]", A_END); + a_fail(a, "bad-addr-syntax", "[FAMILY] ADDRESS [PORT]", A_END); goto fail; } - r->sa.sin.sin_family = AF_INET; r->addr = xstrdup(av[i]); if (!av[i + 1]) pt = TRIPE_PORT; @@@ -1128,7 -1265,7 +1265,7 @@@ a_fail(a, "invalid-port", "%lu", pt, A_END); goto fail; } - r->sa.sin.sin_port = htons(pt); + r->port = pt; /* --- Report backgrounding --- * * @@@ -1138,14 -1275,33 +1275,33 @@@ if (a_bgadd(a, &r->bg, tag, a_rescancel)) goto fail; - T( trace(T_ADMIN, "admin: %u, resop %s, hostname `%s'", - a->seq, BGTAG(r), r->addr); ) + T( trace(T_ADMIN, "admin: %u, resop %s, hostname `%s', family `%s'", + a->seq, BGTAG(r), r->addr, fam); ) /* --- 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); + aihint.ai_family = af; + aihint.ai_socktype = SOCK_DGRAM; + aihint.ai_protocol = IPPROTO_UDP; + aihint.ai_flags = AI_NUMERICHOST; + if (!getaddrinfo(av[i], 0, &aihint, &ailist)) { + for (ai = ailist; ai; ai = ai->ai_next) { + if ((j = afix(ai->ai_family)) >= 0 && udpsock[j].fd >= 0) + break; + } + if (!ai) { + T( trace(T_ADMIN, "admin: resop %s failed: " + "no suitable addresses returned", BGTAG(r)); ) + a_bgfail(&r->bg, "resolve-error", "%s" , r->addr, A_END); + func(r, ARES_FAIL); + } else { + T( trace(T_ADMIN, "admin: resop %s done the easy way", BGTAG(r)); ) + assert(ai->ai_addrlen <= sizeof(r->sa)); + memcpy(&r->sa, ai->ai_addr, ai->ai_addrlen); + setport(&r->sa, r->port); + func(r, ARES_OK); + } + freeaddrinfo(ailist); xfree(r->addr); a_bgrelease(&r->bg); return; @@@ -1156,13 -1312,43 +1312,43 @@@ gettimeofday(&tv, 0); tv.tv_sec += T_RESOLVE; sel_addtimer(&sel, &r->t, &tv, a_restimer, r); + #ifdef HAVE_LIBADNS + qf = adns_qf_search; + for (j = 0; j < NADDRFAM; j++) { + if ((af == AF_UNSPEC || af == aftab[i].af) && udpsock[j].fd >= 0) + qf |= aftab[j].qf; + } + if ((err = adns_submit(ads, r->addr, adns_r_addr, qf, r, &r->q)) != 0) { + T( trace(T_ADMIN, "admin: resop %s adns_submit failed: %s", + BGTAG(r), strerror(err)); ) + a_bgfail(&r->bg, "resolve-error", "%s", r->addr, A_END); + goto fail_release; + } + #else + if (af != AF_UNSPEC && af != AF_INET) { + T( trace(T_ADMIN, "admin: resop %s failed: unsupported address family", + BGTAG(r)); ) + a_bgfail(&r->bg, "resolve-error", "%s", r->addr, A_END); + goto fail_release; + } + if (udpsock[AFIX_INET].fd < 0) { + a_bgfail(&r->bg, "disabled-address-family", "INET", A_END); + goto fail_release; + } bres_byname(&r->r, r->addr, a_resolved, r); + #endif return; fail: func(r, ARES_FAIL); if (r->addr) xfree(r->addr); xfree(r); + return; + + fail_release: + func(r, ARES_FAIL); + xfree(r->addr); + a_bgrelease(&r->bg); } /*----- Option parsing ----------------------------------------------------*/ @@@ -1235,7 -1421,6 +1421,7 @@@ static void a_doadd(admin_resop *r, in if (add->peer.tag) xfree(add->peer.tag); if (add->peer.privtag) xfree(add->peer.privtag); + if (add->peer.knock) xfree(add->peer.knock); xfree(add->peer.name); } @@@ -1261,7 -1446,6 +1447,7 @@@ static void acmd_add(admin *a, unsigne add->peer.name = 0; add->peer.tag = 0; add->peer.privtag = 0; + add->peer.knock = 0; add->peer.t_ka = 0; add->peer.tops = tun_default; add->peer.f = 0; @@@ -1285,21 -1469,15 +1471,21 @@@ }) 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); }) + OPTARG("-knock", arg, { + if (add->peer.knock) xfree(add->peer.knock); + add->peer.knock = xstrdup(arg); + add->peer.f |= PSF_EPHEM; + }) }); /* --- Make sure someone's not got there already --- */ @@@ -1326,7 -1504,6 +1512,7 @@@ fail if (add->peer.name) xfree(add->peer.name); if (add->peer.tag) xfree(add->peer.tag); if (add->peer.privtag) xfree(add->peer.privtag); + if (add->peer.knock) xfree(add->peer.knock); xfree(add); return; } @@@ -1675,7 -1852,27 +1861,27 @@@ static void acmd_warn(admin *a, unsigne { alertcmd(a, AF_WARN, AF_WARN, "WARN", av); } static void acmd_port(admin *a, unsigned ac, char *av[]) - { a_info(a, "%u", p_port(), A_END); a_ok(a); } + { + int i; + + if (ac) { + for (i = 0; i < NADDRFAM; i++) + if (mystrieq(av[0], aftab[i].name)) goto found; + a_fail(a, "unknown-address-family", "%s", av[0], A_END); + return; + found: + if (udpsock[i].fd < 0) { + a_fail(a, "disabled-address-family", "%s", aftab[i].name, A_END); + return; + } + } else { + for (i = 0; i < NADDRFAM; i++) + if (udpsock[i].fd >= 0) goto found; + abort(); + } + a_info(a, "%u", p_port(i), A_END); + a_ok(a); + } static void acmd_daemon(admin *a, unsigned ac, char *av[]) { @@@ -1785,7 -1982,7 +1991,7 @@@ static void acmd_getchal(admin *a, unsi buf b; buf_init(&b, buf_i, PKBUFSZ); - c_new(&b); + c_new(0, 0, &b); a_info(a, "?B64", BBASE(&b), (size_t)BLEN(&b), A_END); a_ok(a); } @@@ -1802,7 -1999,7 +2008,7 @@@ static void acmd_checkchal(admin *a, un a_fail(a, "bad-base64", "%s", codec_strerror(err), A_END); else { buf_init(&b, d.buf, d.len); - if (c_check(&b) || BBAD(&b) || BLEFT(&b)) + if (c_check(0, 0, &b) || BBAD(&b) || BLEFT(&b)) a_fail(a, "invalid-challenge", A_END); else a_ok(a); @@@ -1838,7 -2035,6 +2044,6 @@@ static void acmd_addr(admin *a, unsigne if ((p = a_findpeer(a, av[0])) != 0) { ad = p_addr(p); - assert(ad->sa.sa_family == AF_INET); a_info(a, "?ADDR", ad, A_END); a_ok(a); } @@@ -1853,7 -2049,6 +2058,7 @@@ static void acmd_peerinfo(admin *a, uns if ((p = a_findpeer(a, av[0])) != 0) { ps = p_spec(p); a_info(a, "tunnel=%s", ps->tops->name, A_END); + if (ps->knock) a_info(a, "knock=%s", ps->knock, A_END); a_info(a, "key=%s", p_tag(p), "current-key=%s", p->kx.kpub->tag, A_END); if ((ptag = p_privtag(p)) == 0) ptag = "(default)"; @@@ -1862,7 -2057,6 +2067,7 @@@ 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); } @@@ -1918,7 -2112,7 +2123,7 @@@ static void acmd_kill(admin *a, unsigne peer *p; if ((p = a_findpeer(a, av[0])) != 0) { - p_destroy(p); + p_destroy(p, 1); a_ok(a); } } @@@ -1988,7 -2182,7 +2193,7 @@@ static const acmd acmdtab[] = { "notify", "MESSAGE ...", 1, 0xffff, acmd_notify }, { "peerinfo", "PEER", 1, 1, acmd_peerinfo }, { "ping", "[OPTIONS] PEER", 1, 0xffff, acmd_ping }, - { "port", 0, 0, 0, acmd_port }, + { "port", "[FAMILY]", 0, 1, acmd_port }, { "quit", 0, 0, 0, acmd_quit }, { "reload", 0, 0, 0, acmd_reload }, { "servinfo", 0, 0, 0, acmd_servinfo }, @@@ -2297,6 -2491,9 +2502,9 @@@ void a_init(const char *name, uid_t u, struct sigaction sa; size_t sz; mode_t omask; + #ifdef HAVE_LIBADNS + int err; + #endif /* --- Create services table --- */ @@@ -2363,7 -2560,17 +2571,17 @@@ again sel_initfile(&sel, &sock, fd, SEL_READ, a_accept, 0); sel_addfile(&sock); sockname = name; + #ifdef HAVE_LIBADNS + if ((err = adns_init(&ads, + (adns_if_permit_ipv4 | adns_if_permit_ipv6 | + adns_if_noserverwarn | adns_if_nosigpipe | + adns_if_noautosys), + 0)) != 0) + die(EXIT_FAILURE, "failed to initialize ADNS: %s", strerror(errno)); + sel_addhook(&sel, &hook, before_select, after_select, 0); + #else bres_init(&sel); + #endif T( trace_custom(a_trace, 0); trace(T_ADMIN, "admin: enabled custom tracing"); ) flags |= F_INIT; diff --combined server/peer.c index 606b2038,94629213..a8099e41 --- a/server/peer.c +++ b/server/peer.c @@@ -27,11 -27,14 +27,14 @@@ #include "tripe.h" + /*----- Global state ------------------------------------------------------*/ + + sel_file udpsock[NADDRFAM]; + /*----- Static variables --------------------------------------------------*/ static sym_table byname; static addrmap byaddr; - static sel_file sock; static unsigned nmobile; /*----- Tunnel table ------------------------------------------------------*/ @@@ -181,6 -184,7 +184,7 @@@ int p_updateaddr(peer *p, const addr *a { peer *q; peer_byaddr *pa, *qa; + int ix; unsigned f; /* --- Figure out how to proceed --- * @@@ -197,6 -201,7 +201,7 @@@ 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; + p->afix = afix(p->spec.sa.sa.sa_family); assert(p->afix >= 0); a_notify("NEWADDR", "?PEER", p, "?ADDR", a, A_END); return (0); } else { @@@ -205,6 -210,7 +210,7 @@@ 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; + ix = p->afix; p->afix = q->afix; q->afix = ix; a_notify("NEWADDR", "?PEER", p, "?ADDR", a, A_END); a_notify("NEWADDR", "?PEER", q, "?ADDR", &q->spec.sa, A_END); return (0); @@@ -330,6 -336,10 +336,10 @@@ static void p_read(int fd, unsigned mod ssize_t n; int ch; buf b, bb; + #ifndef NTRACE + int ix = -1; + char name[NI_MAXHOST], svc[NI_MAXSERV]; + #endif /* --- Read the data --- */ @@@ -340,19 -350,23 +350,23 @@@ a_warn("PEER", "-", "socket-read-error", "?ERRNO", A_END); return; } + IF_TRACING(T_PEER, { + ix = afix(a.sa.sa_family); + getnameinfo(&a.sa, sz, name, sizeof(name), svc, sizeof(svc), + NI_NUMERICHOST | NI_NUMERICSERV); + }) /* --- 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(T_PEER, "peer: greeting received from %s %s %s", + aftab[ix].name, name, svc); 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)) { + if (c_check(0, 0, &b) || BLEFT(&b)) { a_warn("PEER", "-", "invalid-greeting", A_END); return; } @@@ -373,11 -387,11 +387,11 @@@ IF_TRACING(T_PEER, { if (p) { trace(T_PEER, - "peer: packet received from `%s' from address INET %s %d", - p_name(p), inet_ntoa(a.sin.sin_addr), ntohs(a.sin.sin_port)); + "peer: packet received from `%s' from address %s %s %s", + p_name(p), aftab[ix].name, name, svc); } else { - trace(T_PEER, "peer: packet received from unknown address INET %s %d", - inet_ntoa(a.sin.sin_addr), ntohs(a.sin.sin_port)); + trace(T_PEER, "peer: packet received from unknown address %s %s %s", + aftab[ix].name, name, svc); } trace_block(T_PACKET, "peer: packet contents", buf_i, n); }) @@@ -413,8 -427,9 +427,8 @@@ } break; case MSG_KEYEXCH: - if (!p) goto unexp; - p_rxupdstats(p, n); - 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) { @@@ -454,16 -469,6 +468,16 @@@ 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: @@@ -498,30 -503,6 +512,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 @@@ -543,7 -524,7 +557,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), + if (sendto(udpsock[p->afix].fd, BBASE(&p->b), BLEN(&p->b), 0, &p->spec.sa.sa, sasz) < 0) { a_warn("PEER", "?PEER", p, "socket-write-error", "?ERRNO", A_END); return (0); @@@ -799,45 -780,68 +813,68 @@@ const addr *p_addr(peer *p) { return (& /* --- @p_init@ --- * * - * Arguments: @struct in_addr addr@ = address to bind to - * @unsigned port@ = port number to listen to + * Arguments: @struct addrinfo *ailist@ = addresses to bind to * * Returns: --- * * Use: Initializes the peer system; creates the socket. */ - void p_init(struct in_addr addr, unsigned port) + void p_init(struct addrinfo *ailist) { int fd; - struct sockaddr_in sin; int len = PKBUFSZ; + int yes = 1; + int i; + struct addrinfo *ai; + unsigned port, lastport = 0; + addr a; + socklen_t sz; - /* --- Note on socket buffer sizes --- * - * - * For some bizarre reason, Linux 2.2 (at least) doubles the socket buffer - * sizes I pass to @setsockopt@. I'm not putting special-case code here - * for Linux: BSD (at least TCPv2) does what I tell it rather than second- - * guessing me. - */ - - 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 = addr; - sin.sin_port = htons(port); - if (bind(fd, (struct sockaddr *)&sin, sizeof(sin))) - die(EXIT_FAILURE, "bind failed: %s", strerror(errno)); - if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len)) || - setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &len, sizeof(len))) { - die(EXIT_FAILURE, "failed to set socket buffer sizes: %s", - strerror(errno)); + for (i = 0; i < NADDRFAM; i++) udpsock[i].fd = -1; + + for (ai = ailist; ai; ai = ai->ai_next) { + if ((i = afix(ai->ai_family)) < 0) continue; + if (udpsock[i].fd != -1) continue; + + /* --- Note on socket buffer sizes --- * + * + * For some bizarre reason, Linux 2.2 (at least) doubles the socket + * buffer sizes I pass to @setsockopt@. I'm not putting special-case + * code here for Linux: BSD (at least TCPv2) does what I tell it rather + * than second-guessing me. + */ + + if ((fd = socket(ai->ai_family, SOCK_DGRAM, 0)) < 0) + die(EXIT_FAILURE, "socket creation failed: %s", strerror(errno)); + if (i == AFIX_INET6 && + setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &yes, sizeof(yes))) { + die(EXIT_FAILURE, "failed to set IPv6-only state: %s", + strerror(errno)); + } + assert(ai->ai_addrlen <= sizeof(a)); + memcpy(&a, ai->ai_addr, ai->ai_addrlen); + if ((port = getport(&a)) == 0 && lastport) setport(&a, lastport); + if (bind(fd, &a.sa, addrsz(&a))) + die(EXIT_FAILURE, "bind failed: %s", strerror(errno)); + if (setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &len, sizeof(len)) || + setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &len, sizeof(len))) { + die(EXIT_FAILURE, "failed to set socket buffer sizes: %s", + strerror(errno)); + } + fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC); + sel_initfile(&sel, &udpsock[i], fd, SEL_READ, p_read, 0); + sel_addfile(&udpsock[i]); + T( trace(T_PEER, "peer: created %s socket", aftab[i].name); ) + if (!port) { + sz = sizeof(a); + if (getsockname(fd, &a.sa, &sz)) { + die(EXIT_FAILURE, "failed to read local socket address: %s", + strerror(errno)); + } + lastport = getport(&a); + } } - fdflags(fd, O_NONBLOCK, O_NONBLOCK, FD_CLOEXEC, FD_CLOEXEC); - sel_initfile(&sel, &sock, fd, SEL_READ, p_read, 0); - sel_addfile(&sock); - T( trace(T_PEER, "peer: created socket"); ) sym_create(&byname); am_create(&byaddr); @@@ -845,20 -849,19 +882,19 @@@ /* --- @p_port@ --- * * - * Arguments: --- + * Arguments: @int i@ = address family index to retrieve * * Returns: Port number used for socket. */ - unsigned p_port(void) + unsigned p_port(int i) { addr a; socklen_t sz = sizeof(addr); - if (getsockname(sock.fd, &a.sa, &sz)) + if (getsockname(udpsock[i].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)); + return (getport(&a)); } /* --- @p_keepalive@ --- * @@@ -928,10 -931,10 +964,11 @@@ peer *p_create(peerspec *spec p->spec.name = (/*unconst*/ char *)SYM_NAME(p->byname); 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; + p->afix = afix(p->spec.sa.sa.sa_family); assert(p->afix >= 0); memset(&p->st, 0, sizeof(stats)); p->st.t_start = time(0); if (!(tops->flags & TUNF_PRIVOPEN)) @@@ -943,7 -946,7 +980,7 @@@ 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, @@@ -1054,28 -1057,17 +1091,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); @@@ -1083,7 -1075,6 +1120,7 @@@ 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); for (pg = p->pings; pg; pg = ppg) { diff --combined server/servutil.c index 72b5b80e,f95541a8..703e448e --- a/server/servutil.c +++ b/server/servutil.c @@@ -82,180 -82,6 +82,180 @@@ int seq_check(seqwin *s, uint32 q, cons return (0); } +/*----- Rate limiting -----------------------------------------------------*/ + +/* --- @ratelim_init@ --- * + * + * Arguments: @ratelim *r@ = rate-limiting state to fill in + * @unsigned persec@ = credit to accumulate per second + * @unsigned max@ = maximum credit to retain + * + * Returns: --- + * + * Use: Initialize a rate-limiting state. + */ + +void ratelim_init(ratelim *r, unsigned persec, unsigned max) +{ + r->n = r->max = max; + r->persec = persec; + gettimeofday(&r->when, 0); +} + +/* --- @ratelim_withdraw@ --- * + * + * Arguments: @ratelim *r@ = rate-limiting state + * @unsigned n@ = credit to withdraw + * + * Returns: Zero if successful; @-1@ if there is unsufficient credit + * + * Use: Updates the state with any accumulated credit. Then, if + * there there are more than @n@ credits available, withdraw @n@ + * and return successfully; otherwise, report failure. + */ + +int ratelim_withdraw(ratelim *r, unsigned n) +{ + struct timeval now, delta; + unsigned long d; + + gettimeofday(&now, 0); + TV_SUB(&delta, &now, &r->when); + d = (unsigned long)r->persec*delta.tv_sec + + (unsigned long)r->persec*delta.tv_usec/MILLION; + if (d < r->max - r->n) r->n += d; + else r->n = r->max; + r->when = now; + + if (n > r->n) return (-1); + else { r->n -= n; return (0); } +} + +/*----- Crypto ------------------------------------------------------------*/ + +/* --- @ies_encrypt@ --- * + * + * Arguments: @kdata *kpub@ = recipient's public key + * @unsigned ty@ = message type octet + * @buf *b@ = input message buffer + * @buf *bb@ = output buffer for the ciphertext + * + * Returns: On error, returns a @KSERR_...@ code or breaks the buffer; + * on success, returns zero and the buffer is good. + * + * Use: Encrypts a message for a recipient, given their public key. + * This does not (by itself) provide forward secrecy or sender + * authenticity. The ciphertext is self-delimiting (unlike + * @ks_encrypt@). + */ + +int ies_encrypt(kdata *kpub, unsigned ty, buf *b, buf *bb) +{ + dhgrp *g = kpub->grp; + dhsc *u = g->ops->randsc(g); + dhge *U = g->ops->mul(g, u, 0), *Z = g->ops->mul(g, u, kpub->K); + bulkalgs *algs = kpub->algs.bulk; + octet *len; + bulkctx *bulk; + deriveargs a; + size_t n; + buf bk; + int rc = 0; + + IF_TRACING(T_CRYPTO, { + trace(T_CRYPTO, + "crypto: encrypting IES message (type 0x%02x) for recipient `%s'", + ty, kpub->tag); + trace_block(T_CRYPTO, "crypto: plaintext message", BCUR(b), BLEFT(b)); + }) + + a.hc = kpub->algs.h; a.what = "tripe:ecies-"; a.f = DF_OUT; + buf_init(&bk, buf_u, sizeof(buf_u)); a.k = BBASE(&bk); + g->ops->stge(g, &bk, U, DHFMT_HASH); a.x = a.y = BLEN(&bk); + g->ops->stge(g, &bk, Z, DHFMT_HASH); a.z = BLEN(&bk); + assert(BOK(&bk)); + T( trace_block(T_CRYPTO, "crypto: KEM clue", a.k, a.x); + trace_block(T_CRYPTO, "crypto: shared secret", a.k + a.y, a.z - a.y); ) + + len = BCUR(bb); buf_get(bb, 2); + bulk = algs->ops->genkeys(algs, &a); + bulk->ops = algs->ops; + g->ops->stge(g, bb, U, DHFMT_VAR); if (BBAD(bb)) goto end; + rc = bulk->ops->encrypt(bulk, ty, b, bb, 0); + if (rc || BBAD(bb)) goto end; + n = BCUR(bb) - len - 2; assert(n <= MASK16); STORE16(len, n); + +end: + bulk->ops->freectx(bulk); + g->ops->freesc(g, u); + g->ops->freege(g, U); + g->ops->freege(g, Z); + return (rc); +} + +/* --- @ies_decrypt@ --- * + * + * Arguments: @kdata *kpub@ = private key key + * @unsigned ty@ = message type octet + * @buf *b@ = input ciphertext buffer + * @buf *bb@ = output buffer for the message + * + * Returns: On error, returns a @KSERR_...@ code; on success, returns + * zero and the buffer is good. + * + * Use: Decrypts a message encrypted using @ies_encrypt@, given our + * private key. + */ + +int ies_decrypt(kdata *kpriv, unsigned ty, buf *b, buf *bb) +{ + dhgrp *g = kpriv->grp; + bulkalgs *algs = kpriv->algs.bulk; + bulkctx *bulk = 0; + T( const octet *m; ) + dhge *U = 0, *Z = 0; + deriveargs a; + uint32 seq; + buf bk, bc; + int rc; + + IF_TRACING(T_CRYPTO, { + trace(T_CRYPTO, + "crypto: decrypting IES message (type 0x%02x) to recipient `%s'", + ty, kpriv->tag); + trace_block(T_CRYPTO, "crypto: ciphertext message", BCUR(b), BLEFT(b)); + }) + + if (buf_getbuf16(b, &bc) || + (U = g->ops->ldge(g, &bc, DHFMT_VAR)) == 0 || + g->ops->checkge(g, U)) + { rc = KSERR_MALFORMED; goto end; } + Z = g->ops->mul(g, kpriv->k, U); + + a.hc = kpriv->algs.h; a.what = "tripe:ecies-"; a.f = DF_IN; + buf_init(&bk, buf_u, sizeof(buf_u)); a.k = BBASE(&bk); a.x = 0; + g->ops->stge(g, &bk, U, DHFMT_HASH); a.y = BLEN(&bk); + g->ops->stge(g, &bk, Z, DHFMT_HASH); a.z = BLEN(&bk); + T( trace_block(T_CRYPTO, "crypto: KEM clue", a.k + a.x, a.y - a.x); + trace_block(T_CRYPTO, "crypto: shared secret", a.k + a.y, a.z - a.y); ) + assert(BOK(&bk)); + + bulk = algs->ops->genkeys(algs, &a); + bulk->ops = algs->ops; + T( m = BCUR(bb); ) + rc = bulk->ops->decrypt(bulk, ty, &bc, bb, &seq); + if (rc) goto end; + if (seq) { rc = KSERR_SEQ; goto end; } + assert(BOK(bb)); + T( trace_block(T_CRYPTO, "crypto: decrypted message", m, BCUR(bb) - m); ) + +end: + if (bulk) bulk->ops->freectx(bulk); + g->ops->freege(g, U); + g->ops->freege(g, Z); + return (rc); +} + /*----- Random odds and sods ----------------------------------------------*/ /* --- @timestr@ --- * @@@ -295,6 -121,34 +295,34 @@@ int mystrieq(const char *x, const char } } + /*----- Address handling --------------------------------------------------*/ + + const struct addrfam aftab[] = { + #ifdef HAVE_LIBADNS + # define DEF(af, qf) { AF_##af, #af, adns_qf_##qf }, + #else + # define DEF(af, qf) { AF_##af, #af }, + #endif + ADDRFAM(DEF) + #undef DEF + }; + + /* --- @afix@ --- * + * + * Arguments: @int af@ = an address family code + * + * Returns: The index of the address family's record in @aftab@, or @-1@. + */ + + int afix(int af) + { + int i; + + for (i = 0; i < NADDRFAM; i++) + if (af == aftab[i].af) return (i); + return (-1); + } + /* --- @addrsz@ --- * * * Arguments: @const addr *a@ = a network address @@@ -306,6 -160,35 +334,35 @@@ socklen_t addrsz(const addr *a { switch (a->sa.sa_family) { case AF_INET: return (sizeof(a->sin)); + case AF_INET6: return (sizeof(a->sin6)); + default: abort(); + } + } + + /* --- @getport@, @setport@ --- * + * + * Arguments: @addr *a@ = a network address + * @unsigned port@ = port number to set + * + * Returns: --- + * + * Use: Retrieves or sets the port number in an address structure. + */ + + unsigned getport(addr *a) + { + switch (a->sa.sa_family) { + case AF_INET: return (ntohs(a->sin.sin_port)); break; + case AF_INET6: return (ntohs(a->sin6.sin6_port)); break; + default: abort(); + } + } + + void setport(addr *a, unsigned port) + { + switch (a->sa.sa_family) { + case AF_INET: a->sin.sin_port = htons(port); break; + case AF_INET6: a->sin6.sin6_port = htons(port); break; default: abort(); } } diff --combined server/tripe-admin.5.in index c81dc111,81dd570d..f066ae6d --- a/server/tripe-admin.5.in +++ b/server/tripe-admin.5.in @@@ -251,21 -251,50 +251,50 @@@ the meanings of the subsequent tokens d Address family tokens are not case-sensitive on input; on output, they are always in upper-case. .PP - At present, only one address family is understood. + The following address families are recognized. + .TP + .BI "ANY " address " \fR[" port \fR] + An address and port number for any supported address family. On output, + .B tripe + never uses this form. On input, the + .I address + is examined: if it is a numeric address for some recognized address + family, then it is interpreted as such; otherwise it is looked up using + the DNS (in the background). The background resolver's address-sorting + rules apply, and + .B tripe + simply takes the first address in the returned list which is of a + supported address family. Symbolic port numbers are permitted; if + omitted, the default port 4070 is used. .TP .BI "INET " address " \fR[" port \fR] An Internet socket, naming an IPv4 address and UDP port. On output, the - address is always in numeric dotted-quad form, and the port is given as - a plain number. On input, DNS hostnames and symbolic port names are - permitted; if omitted, the default port 4070 is used. Name resolution - does not block the main server, but will block the requesting client, - unless the command is run in the background. + .I address + is always in numeric dotted-quad form, and the + .I port + is given as a plain decimal number. On input, DNS hostnames and + symbolic port names are permitted; if omitted, the default port 4070 is + used. + .TP + .BI "INET6 " address " \fR[" port \fR] + An Internet socket, naming an IPv6 address and UDP port. On output, the + .I address + is always in numeric hex-and-colons form, and the + .I port + is given as a plain decimal number. On input, DNS hostnames and + symbolic port names may be permitted, depending on how + .B tripe + was compiled; if omitted, the default port 4070 is used. .PP If, on input, no recognized address family token is found, the following tokens are assumed to represent an - .B INET + .B ANY address. Addresses output by the server always have an address family - token. + token, and do not use + .BR ANY . + .PP + Name resolution never blocks the main server, but will block the + requesting client, unless the command is run in the background. .SS "Key-value output" Some commands (e.g., .B STATS @@@ -332,21 -361,6 +361,21 @@@ Run the command in the background, usin 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 @@@ -368,26 -382,6 +397,26 @@@ Use the public ke to authenticate the peer. The default is to use the key tagged .IR peer . .TP +.BI "\-knock \fR[" prefix .\fR] tag +Send the string +.RI [ prefix\fB. ] tag +in +.B token-rq +and +.B knock +messages to the peer during key-exchange. The string as a whole should +name the local machine to the peer, and +.I tag +should name its public key. When such messages are received from a +currently unknown peer, +.BR tripe (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. This option implies +.BR \-ephemeral . +.TP .B "\-mobile" The peer is a mobile device, and is likely to change address rapidly. If a packet arrives from an unknown address, the server's usual response @@@ -396,8 -390,7 +425,8 @@@ peers, however, it will attempt to decr 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 @@@ -507,12 -500,16 +536,16 @@@ tunnel interface. I is the MTU of the path to the peer, then the tunnel MTU should be .IP .I MTU - \- 29 \- + \- + .I header-length + \- 9 \- .I bulk-overhead .PP - allowing 20 bytes of IP header, 8 bytes of UDP header, a packet type - octet, and the bulk-crypto transform overhead (which includes the - sequence number). + allowing + .I header-length + = 20 (IPv4) or 40 (IPv6) bytes of IP header, 8 bytes of UDP header, a + packet type octet, and the bulk-crypto transform overhead (which + includes the sequence number). .RE .SP .BI "BGCANCEL " tag @@@ -608,16 -605,6 +641,16 @@@ The tunnel driver used for this peer The keepalive interval, in seconds, or zero if no keepalives are to be sent. .TP +.B knock +If present, the string sent to the peer to set up the association; see +the +.B \-knock +option to +.BR ADD , +and the +.B KNOCK +notification. +.TP .B key The (short) key tag being used for the peer, as passed to the .B ADD @@@ -656,14 -643,6 +689,14 @@@ o .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 @@@ -717,12 -696,18 +750,18 @@@ given, seconds are assumed .RE .SP .B "PORT" + .RI [ family ] Emits an .B INFO line containing just the number of the UDP port used by the .B tripe - server. If you've allowed your server to allocate a port dynamically, - this is how to find out which one it chose. + server, for the given address + .I family + (or one chosen arbitrarily if omitted -- though + .B tripe + tries to use the same port number consistently so this is not a likely + problem in practice). If you've allowed your server to allocate a port + dynamically, this is how to find out which one it chose. .SP .B "RELOAD" Instructs the server to recheck its keyring files. The server checks @@@ -1048,6 -1033,15 +1087,15 @@@ An unknown watch option was requested An error occurred during the attempt to become a daemon, as reported by .IR message . .SP + .BI "disabled-address-family " afam + (For + .B ADD + and + .BR PORT .) + The address family + .I afam + is supported, but was disabled using command-line arguments. + .SP .BI "invalid-port " number (For .BR ADD .) @@@ -1133,6 -1127,13 +1181,13 @@@ is available, which does not meet the s .I tag is already the tag of an outstanding job. .SP + .BI "unknown-address-family " afam + (For + .BR PORT .) + The address family + .I afam + is unrecognized. + .SP .BI "unknown-command " token The command .I token @@@ -1222,12 -1223,6 +1277,12 @@@ The pee .I peer has been killed. .SP +.BI "KNOCK " peer " " address +The currently unknown +.I peer +is attempting to connect from +.IR address . +.SP .BI "KXDONE " peer Key exchange with .I peer @@@ -1444,11 -1439,6 +1499,11 @@@ A key name .I tag couldn't be found in the keyring. .SP +.BI "KEYMGMT " which "-keyring " file " unknown-key-id 0x" keyid +A key with the given +.I keyid +(in hex) was requested but not found. +.SP .BI "KEYMGMT " which "-keyring " file " line " line " " message The contents of the keyring file are invalid. There may well be a bug in the @@@ -1470,11 -1460,8 +1525,11 @@@ is one of the token .BR challenge , .BR reply , .BR switch-rq , -or .BR switch-ok . +.BR token-rq , +.BR token , +or +.BR knock . .SP .BI "KX " peer " algorithms-mismatch local-private-key " privtag " peer-public-key " pubtag The algorithms specified in the peer's public key @@@ -1589,10 -1576,6 +1644,10 @@@ An error occurred trying to read an inc An error occurred attempting to send a network packet. We lost that one. .SP +.BI "PEER " address\fR... " socket-write-error " ecode " " message +An error occurred attempting to send a network packet. We lost that +one. +.SP .BI "PEER " peer " unexpected-encrypted-ping 0x" id The peer sent an encrypted ping response whose id doesn't match any outstanding ping. Maybe it was delayed for longer than the server was diff --combined server/tripe.c index d50757bc,565c83dd..b8438857 --- a/server/tripe.c +++ b/server/tripe.c @@@ -91,6 -91,8 +91,8 @@@ Options:\n -u, --usage Display pointless usage message.\n\ --tunnels Display IP tunnel drivers and exit.\n\ \n\ + -4, --ipv4 Transport over IPv4 only.\n\ + -6, --ipv6 Transport over IPv6 only.\n\ -D, --daemon Run in the background.\n\ -F, --foreground Quit when stdin reports end-of-file.\n\ -d, --directory=DIR Switch to directory DIR [default " CONFIGDIR "].\n\ @@@ -119,11 -121,11 +121,11 @@@ int main(int argc, char *argv[] int csockmode = 0600; const char *dir = CONFIGDIR; const char *p; - unsigned port = TRIPE_PORT; - struct in_addr baddr = { INADDR_ANY }; + const char *bindhost = 0, *bindsvc = STR(TRIPE_PORT); + struct addrinfo aihint = { 0 }, *ailist; unsigned f = 0; int i; - int selerr = 0; + int err, selerr = 0; unsigned af; struct timeval tv; uid_t u = -1; @@@ -141,6 -143,7 +143,7 @@@ if ((p = getenv("TRIPESOCK")) != 0) csock = p; tun_default = tunnels[0]; + aihint.ai_family = AF_UNSPEC; for (;;) { static const struct option opts[] = { @@@ -149,6 -152,8 +152,8 @@@ { "usage", 0, 0, 'u' }, { "tunnels", 0, 0, '0' }, + { "ipv4", 0, 0, '4' }, + { "ipv6", 0, 0, '6' }, { "daemon", 0, 0, 'D' }, { "foreground", 0, 0, 'F' }, { "uid", OPTF_ARGREQ, 0, 'U' }, @@@ -171,7 -176,7 +176,7 @@@ { 0, 0, 0, 0 } }; - i = mdwopt(argc, argv, "hvuDFU:G:b:n:p:d:k:K:t:a:m:" T("T:"), + i = mdwopt(argc, argv, "hvu46DFU:G:b:n:p:d:k:K:t:a:m:" T("T:"), opts, 0, 0, 0); if (i < 0) break; @@@ -186,6 -191,12 +191,12 @@@ usage(stdout); exit(0); + case '4': + aihint.ai_family = AF_INET; + break; + case '6': + aihint.ai_family = AF_INET6; + break; case 'D': f |= f_daemon; break; @@@ -199,25 -210,12 +210,12 @@@ f |= f_foreground; break; - case 'b': { - struct hostent *h = gethostbyname(optarg); - if (!h) - die(EXIT_FAILURE, "unknown host name `%s'", optarg); - memcpy(&baddr, h->h_addr, sizeof(struct in_addr)); - } 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 >= 65536) - die(EXIT_FAILURE, "bad port number %lu", i); - port = i; - } break; + case 'b': + bindhost = optarg; + break; + case 'p': + bindsvc = optarg; + break; case 'n': { int i; for (i = 0;; i++) { @@@ -273,6 -271,17 +271,17 @@@ if (!(~f & (f_daemon | f_foreground))) die(EXIT_FAILURE, "foreground operation for a daemon is silly"); + aihint.ai_protocol = IPPROTO_UDP; + aihint.ai_socktype = SOCK_DGRAM; + aihint.ai_flags = AI_PASSIVE | AI_ADDRCONFIG; + if ((err = getaddrinfo(bindhost, bindsvc, &aihint, &ailist)) != 0) { + die(EXIT_FAILURE, "couldn't resolve hostname %c%s%c, port `%s': %s", + bindhost ? '`' : '<', + bindhost ? bindhost : "nil", + bindhost ? '\'' : '>', + bindsvc, gai_strerror(err)); + } + if (chdir(dir)) { die(EXIT_FAILURE, "can't set current directory to `%s': %s", dir, strerror(errno)); @@@ -285,7 -294,7 +294,7 @@@ signal(SIGPIPE, SIG_IGN); for (i = 0; tunnels[i]; i++) tunnels[i]->init(); - p_init(baddr, port); + p_init(ailist); freeaddrinfo(ailist); if (!(f & f_daemon)) { af = AF_WARN; #ifndef NTRACE @@@ -299,7 -308,6 +308,7 @@@ a_init(csock, u, g, csockmode); u_setugid(u, g); km_init(kr_priv, kr_pub, tag_priv); + kx_init(); if (f & f_daemon) { if (daemonize()) die(EXIT_FAILURE, "couldn't become a daemon: %s", strerror(errno)); diff --combined server/tripe.h index a2907be1,29403993..10a03f5f --- a/server/tripe.h +++ b/server/tripe.h @@@ -62,10 -62,17 +62,17 @@@ #include #include + #ifdef HAVE_LIBADNS + # define ADNS_FEATURE_MANYAF + # include + #endif + #include #include #include - #include + #ifndef HAVE_LIBADNS + # include + #endif #include #include #include @@@ -181,16 -188,6 +188,16 @@@ enum DHFMT_VAR /* Variable-width-format, mostly a bad idea */ }; +typedef struct deriveargs { + const char *what; /* Operation name (hashed) */ + unsigned f; /* Flags */ +#define DF_IN 1u /* Make incoming key */ +#define DF_OUT 2u /* Make outgoing key */ + const gchash *hc; /* Hash class */ + const octet *k; /* Pointer to contributions */ + size_t x, y, z; /* Markers in contributions */ +} deriveargs; + typedef struct bulkalgs { const struct bulkops *ops; } bulkalgs; @@@ -204,6 -201,8 +211,6 @@@ typedef struct bulkchal size_t tagsz; } bulkchal; -struct rawkey; - typedef struct dhops { const char *name; @@@ -333,17 -332,9 +340,17 @@@ typedef struct bulkops * after which the keys must no longer be used. */ - bulkctx *(*genkeys)(const bulkalgs */*a*/, const struct rawkey */*rk*/); + bulkctx *(*genkeys)(const bulkalgs */*a*/, const deriveargs */*a*/); /* Generate session keys and construct and return an appropriate - * context for using them, by calling @ks_derive@. + * context for using them. The offsets @a->x@, @a->y@ and @a->z@ + * separate the key material into three parts. Between @a->k@ and + * @a->k + a->x@ is `my' contribution to the key material; between + * @a->k + a->x@ and @a->k + a->y@ is `your' contribution; and + * between @a->k + a->y@ and @a->k + a->z@ is a shared value we made + * together. These are used to construct (up to) two collections of + * symmetric keys: one for outgoing messages, the other for incoming + * messages. If @a->x == 0@ (or @a->y == a->x@) then my (or your) + * contribution is omitted. */ bulkchal *(*genchal)(const bulkalgs */*a*/); @@@ -376,16 -367,15 +383,16 @@@ /* Release a bulk encryption context and the resources it holds. */ int (*chaltag)(bulkchal */*bc*/, const void */*m*/, size_t /*msz*/, - void */*t*/); - /* Calculate a tag for the challenge in @m@, @msz@, and write it to - * @t@. Return @-1@ on error, zero on success. + uint32 /*seq*/, void */*t*/); + /* Calculate a tag for the challenge in @m@, @msz@, with the sequence + * number @seq@, and write it to @t@. Return @-1@ on error, zero on + * success. */ int (*chalvrf)(bulkchal */*bc*/, const void */*m*/, size_t /*msz*/, - const void */*t*/); - /* Check the tag @t@ on @m@, @msz@: return zero if the tag is OK, - * nonzero if it's bad. + uint32 /*seq*/, const void */*t*/); + /* Check the tag @t@ on @m@, @msz@ and @seq@: return zero if the tag + * is OK, nonzero if it's bad. */ void (*freechal)(bulkchal */*bc*/); @@@ -402,7 -392,6 +409,7 @@@ struct algswitch struct kdata { unsigned ref; /* Reference counter */ struct knode *kn; /* Pointer to cache entry */ + uint32 id; /* The underlying key's id */ char *tag; /* Full tag name of the key */ dhgrp *grp; /* The group we work in */ dhsc *k; /* The private key (or null) */ @@@ -428,6 -417,27 +435,27 @@@ extern const bulkops bulktab[] /*----- Data structures ---------------------------------------------------*/ + /* --- The address-family table --- */ + + #define ADDRFAM(_) \ + _(INET, want_ipv4) \ + _(INET6, want_ipv6) + + enum { + #define ENUM(af, qf) AFIX_##af, + ADDRFAM(ENUM) + #undef ENUM + NADDRFAM + }; + + extern const struct addrfam { + int af; + const char *name; + #ifdef HAVE_LIBADNS + adns_queryflags qf; + #endif + } aftab[NADDRFAM]; + /* --- Socket addresses --- * * * A magic union of supported socket addresses. @@@ -436,6 -446,7 +464,7 @@@ typedef union addr { struct sockaddr sa; struct sockaddr_in sin; + struct sockaddr_in6 sin6; } addr; /* --- Mapping keyed on addresses --- */ @@@ -610,14 -621,12 +639,14 @@@ typedef struct peerspec char *name; /* Peer's name */ char *privtag; /* Private key tag */ char *tag; /* Public key tag */ + char *knock; /* Knock string, or null */ const tunnel_ops *tops; /* Tunnel operations */ unsigned long t_ka; /* Keep alive interval */ addr sa; /* Socket address to speak to */ 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 { @@@ -635,6 -644,7 +664,7 @@@ typedef struct peer peer_byaddr *byaddr; /* Lookup-by-address block */ struct ping *pings; /* Pings we're waiting for */ peerspec spec; /* Specifications for this peer */ + int afix; /* Index of address family */ tunnel *t; /* Tunnel for local packets */ char *ifname; /* Interface name for tunnel */ keyset *ks; /* List head for keysets */ @@@ -691,9 -701,14 +721,14 @@@ typedef struct admin_bgop typedef struct admin_resop { admin_bgop bg; /* Background operation header */ char *addr; /* Hostname to be resolved */ + #ifdef HAVE_LIBADNS + adns_query q; + #else bres_client r; /* Background resolver task */ + #endif sel_timer t; /* Timer for resolver */ addr sa; /* Socket address */ + unsigned port; /* Port number chosen */ size_t sasz; /* Socket address size */ void (*func)(struct admin_resop *, int); /* Handler */ } admin_resop; @@@ -777,6 -792,7 +812,7 @@@ extern sel_state sel; /* Global I/O e extern octet buf_i[PKBUFSZ], buf_o[PKBUFSZ], buf_t[PKBUFSZ], buf_u[PKBUFSZ]; extern const tunnel_ops *tunnels[]; /* Table of tunnels (0-term) */ extern const tunnel_ops *tun_default; /* Default tunnel to use */ + extern sel_file udpsock[NADDRFAM]; /* The master UDP sockets */ extern kdata *master; /* Default private key */ extern const char *tag_priv; /* Default private key tag */ @@@ -830,19 -846,6 +866,19 @@@ extern int km_reload(void) extern kdata *km_findpub(const char */*tag*/); extern kdata *km_findpriv(const char */*tag*/); +/* --- @km_findpubbyid@, @km_findprivbyid@ --- * + * + * Arguments: @uint32 id@ = key id to load + * + * Returns: Pointer to the kdata object if successful, or null on error. + * + * Use: Fetches a public or private key from the keyring given its + * numeric id. + */ + +extern kdata *km_findpubbyid(uint32 /*id*/); +extern kdata *km_findprivbyid(uint32 /*id*/); + /* --- @km_samealgsp@ --- * * * Arguments: @const kdata *kdx, *kdy@ = two key data objects @@@ -905,18 -908,16 +941,18 @@@ extern void kx_start(keyexch */*kx*/, i /* --- @kx_message@ --- * * * Arguments: @keyexch *kx@ = pointer to key exchange context + * @const addr *a@ = sender's IP address and port * @unsigned msg@ = the message code * @buf *b@ = pointer to buffer containing the packet * - * Returns: --- + * Returns: Nonzero if the sender's address was unknown. * * Use: Reads a packet containing key exchange messages and handles * it. */ -extern void kx_message(keyexch */*kx*/, unsigned /*msg*/, buf */*b*/); +extern int kx_message(keyexch */*kx*/, const addr */*a*/, + unsigned /*msg*/, buf */*b*/); /* --- @kx_free@ --- * * @@@ -943,7 -944,7 +979,7 @@@ extern void kx_free(keyexch */*kx*/) extern void kx_newkeys(keyexch */*kx*/); -/* --- @kx_init@ --- * +/* --- @kx_setup@ --- * * * Arguments: @keyexch *kx@ = pointer to key exchange context * @peer *p@ = pointer to peer context @@@ -957,51 -958,69 +993,51 @@@ * exchange. */ -extern int kx_init(keyexch */*kx*/, peer */*p*/, - keyset **/*ks*/, unsigned /*f*/); - -/*----- Keysets and symmetric cryptography --------------------------------*/ +extern int kx_setup(keyexch */*kx*/, peer */*p*/, + keyset **/*ks*/, unsigned /*f*/); -/* --- @ks_drop@ --- * +/* --- @kx_init@ --- * * - * Arguments: @keyset *ks@ = pointer to a keyset + * Arguments: --- * * Returns: --- * - * Use: Decrements a keyset's reference counter. If the counter hits - * zero, the keyset is freed. + * Use: Initializes the key-exchange logic. */ -extern void ks_drop(keyset */*ks*/); +extern void kx_init(void); -/* --- @ks_derivekey@ --- * +/*----- Keysets and symmetric cryptography --------------------------------*/ + +/* --- @ks_drop@ --- * * - * Arguments: @octet *k@ = pointer to an output buffer of at least - * @MAXHASHSZ@ bytes - * @size_t ksz@ = actual size wanted (for tracing) - * @const struct rawkey *rk@ = a raw key, as passed into - * @genkeys@ - * @int dir@ = direction for the key (@DIR_IN@ or @DIR_OUT@) - * @const char *what@ = label for the key (input to derivation) + * Arguments: @keyset *ks@ = pointer to a keyset * * Returns: --- * - * Use: Derives a session key, for use on incoming or outgoing data. - * This function is part of a private protocol between @ks_gen@ - * and the bulk crypto transform @genkeys@ operation. + * Use: Decrements a keyset's reference counter. If the counter hits + * zero, the keyset is freed. */ -extern void ks_derivekey(octet */*k*/, size_t /*ksz*/, - const struct rawkey */*rk*/, - int /*dir*/, const char */*what*/); +extern void ks_drop(keyset */*ks*/); /* --- @ks_gen@ --- * * - * Arguments: @const void *k@ = pointer to key material - * @size_t x, y, z@ = offsets into key material (see below) + * Arguments: @deriveargs *a@ = key derivation parameters (modified) * @peer *p@ = pointer to peer information * * Returns: A pointer to the new keyset. * - * Use: Derives a new keyset from the given key material. The - * offsets @x@, @y@ and @z@ separate the key material into three - * parts. Between the @k@ and @k + x@ is `my' contribution to - * the key material; between @k + x@ and @k + y@ is `your' - * contribution; and between @k + y@ and @k + z@ is a shared - * value we made together. These are used to construct two - * collections of symmetric keys: one for outgoing messages, the - * other for incoming messages. + * Use: Derives a new keyset from the given key material. This will + * set the @what@, @f@, and @hc@ members in @*a@; other members + * must be filled in by the caller. * * The new key is marked so that it won't be selected for output * by @ksl_encrypt@. You can still encrypt data with it by * calling @ks_encrypt@ directly. */ -extern keyset *ks_gen(const void */*k*/, - size_t /*x*/, size_t /*y*/, size_t /*z*/, - peer */*p*/); +extern keyset *ks_gen(deriveargs */*a*/, peer */*p*/); /* --- @ks_activate@ --- * * @@@ -1140,29 -1159,25 +1176,29 @@@ extern int ksl_decrypt(keyset **/*ksroo /* --- @c_new@ --- * * - * Arguments: @buf *b@ = where to put the challenge + * Arguments: @const void *m@ = pointer to associated message, or null + * @size_t msz@ = length of associated message + * @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*/); +extern int c_new(const void */*m*/, size_t /*msz*/, buf */*b*/); /* --- @c_check@ --- * * - * Arguments: @buf *b@ = where to find the challenge + * Arguments: @const void *m@ = pointer to associated message, or null + * @size_t msz@ = length of associated message + * @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*/); +extern int c_check(const void */*m*/, size_t /*msz*/, buf */*b*/); /*----- Administration interface ------------------------------------------*/ @@@ -1456,20 -1471,6 +1492,20 @@@ extern int p_updateaddr(peer */*p*/, co extern buf *p_txstart(peer */*p*/, unsigned /*msg*/); +/* --- @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. + */ + +extern int p_txaddr(const addr */*a*/, const void */*p*/, size_t /*sz*/); + /* --- @p_txend@ --- * * * Arguments: @peer *p@ = pointer to peer block @@@ -1600,24 -1601,23 +1636,23 @@@ extern const addr *p_addr(peer */*p*/) /* --- @p_init@ --- * * - * Arguments: @struct in_addr addr@ = address to bind to - * @unsigned port@ = port number to listen to + * Arguments: @struct addrinfo *ailist@ = addresses to bind to * * Returns: --- * * Use: Initializes the peer system; creates the socket. */ - extern void p_init(struct in_addr /*addr*/, unsigned /*port*/); + extern void p_init(struct addrinfo */*ailist*/); /* --- @p_port@ --- * * - * Arguments: --- + * Arguments: @int i@ = address family index to retrieve * * Returns: Port number used for socket. */ - unsigned p_port(void); + extern unsigned p_port(int /*i*/); /* --- @p_create@ --- * * @@@ -1694,14 -1694,13 +1729,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@ --- * * @@@ -1778,6 -1777,15 +1813,15 @@@ extern const char *timestr(time_t /*t*/ extern int mystrieq(const char */*x*/, const char */*y*/); + /* --- @afix@ --- * + * + * Arguments: @int af@ = an address family code + * + * Returns: The index of the address family's record in @aftab@, or @-1@. + */ + + extern int afix(int af); + /* --- @addrsz@ --- * * * Arguments: @const addr *a@ = a network address @@@ -1787,6 -1795,19 +1831,19 @@@ extern socklen_t addrsz(const addr */*a*/); + /* --- @getport@, @setport@ --- * + * + * Arguments: @addr *a@ = a network address + * @unsigned port@ = port number to set + * + * Returns: --- + * + * Use: Retrieves or sets the port number in an address structure. + */ + + extern unsigned getport(addr */*a*/); + extern void setport(addr */*a*/, unsigned /*port*/); + /* --- @seq_reset@ --- * * * Arguments: @seqwin *s@ = sequence-checking window @@@ -1812,75 -1833,6 +1869,75 @@@ extern void seq_reset(seqwin */*s*/) extern int seq_check(seqwin */*s*/, uint32 /*q*/, const char */*service*/); +typedef struct ratelim { + unsigned n, max, persec; + struct timeval when; +} ratelim; + +/* --- @ratelim_init@ --- * + * + * Arguments: @ratelim *r@ = rate-limiting state to fill in + * @unsigned persec@ = credit to accumulate per second + * @unsigned max@ = maximum credit to retain + * + * Returns: --- + * + * Use: Initialize a rate-limiting state. + */ + +extern void ratelim_init(ratelim */*r*/, + unsigned /*persec*/, unsigned /*max*/); + +/* --- @ratelim_withdraw@ --- * + * + * Arguments: @ratelim *r@ = rate-limiting state + * @unsigned n@ = credit to withdraw + * + * Returns: Zero if successful; @-1@ if there is unsufficient credit + * + * Use: Updates the state with any accumulated credit. Then, if + * there there are more than @n@ credits available, withdraw @n@ + * and return successfully; otherwise, report failure. + */ + +extern int ratelim_withdraw(ratelim */*r*/, unsigned /*n*/); + +/* --- @ies_encrypt@ --- * + * + * Arguments: @kdata *kpub@ = recipient's public key + * @unsigned ty@ = message type octet + * @buf *b@ = input message buffer + * @buf *bb@ = output buffer for the ciphertext + * + * Returns: On error, returns a @KSERR_...@ code or breaks the buffer; + * on success, returns zero and the buffer is good. + * + * Use: Encrypts a message for a recipient, given their public key. + * This does not (by itself) provide forward secrecy or sender + * authenticity. The ciphertext is self-delimiting (unlike + * @ks_encrypt@). + */ + +extern int ies_encrypt(kdata */*kpub*/, unsigned /*ty*/, + buf */*b*/, buf */*bb*/); + +/* --- @ies_decrypt@ --- * + * + * Arguments: @kdata *kpub@ = private key key + * @unsigned ty@ = message type octet + * @buf *b@ = input ciphertext buffer + * @buf *bb@ = output buffer for the message + * + * Returns: On error, returns a @KSERR_...@ code; on success, returns + * zero and the buffer is good. + * + * Use: Decrypts a message encrypted using @ies_encrypt@, given our + * private key. + */ + +extern int ies_decrypt(kdata */*kpriv*/, unsigned /*ty*/, + buf */*b*/, buf */*bb*/); + /*----- That's all, folks -------------------------------------------------*/ #ifdef __cplusplus