X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=site.c;h=db1452394bf67b7f173cb8133dd2793c573f24eb;hb=b2de6bd2d9b5e9047a660e926c4adb17da54fc29;hp=9ba1a2857051787978c5dec0b1692974dcf4f136;hpb=c215a4bc817daf7b5631236c3c7b6a509479b034;p=secnet.git diff --git a/site.c b/site.c index 9ba1a28..db14523 100644 --- a/site.c +++ b/site.c @@ -6,7 +6,7 @@ * * secnet 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 d of the License, or + * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * secnet is distributed in the hope that it will be useful, but @@ -296,6 +296,7 @@ struct site { /* configuration information */ string_t localname; string_t remotename; + bool_t keepalive; bool_t local_mobile, peer_mobile; /* Mobile client support */ int32_t transport_peers_max; string_t tunname; /* localname<->remotename by default, used in logs */ @@ -304,6 +305,7 @@ struct site { uint32_t mtu_target; struct netlink_if *netlink; struct comm_if **comms; + struct comm_clientinfo **commclientinfos; int ncomms; struct resolver_if *resolver; struct log_if *log; @@ -326,14 +328,14 @@ struct site { after this time, initiate a new key exchange */ - bool_t setup_priority; /* Do we have precedence if both sites emit - message 1 simultaneously? */ + bool_t our_name_later; /* our name > peer name */ uint32_t log_events; /* runtime information */ uint32_t state; uint64_t now; /* Most recently seen time */ bool_t allow_send_prod; + bool_t msg1_crossed_logged; int resolving_count; int resolving_n_results_all; int resolving_n_results_stored; @@ -532,8 +534,10 @@ struct msg { char *sig; }; -static void set_new_transform(struct site *st, char *pk) +static _Bool set_new_transform(struct site *st, char *pk) { + _Bool ok; + /* Make room for the shared key */ st->sharedsecretlen=st->chosen_transform->keylen?:st->dh->ceil_len; assert(st->sharedsecretlen); @@ -551,15 +555,18 @@ static void set_new_transform(struct site *st, char *pk) /* Set up the transform */ struct transform_if *generator=st->chosen_transform; struct transform_inst_if *generated=generator->create(generator->st); - generated->setkey(generated->st,st->sharedsecret, - st->sharedsecretlen,st->setup_priority); + ok = generated->setkey(generated->st,st->sharedsecret, + st->sharedsecretlen,st->our_name_later); + dispose_transform(&st->new_transform); + if (!ok) return False; st->new_transform=generated; slog(st,LOG_SETUP_INIT,"key exchange negotiated transform" " %d (capabilities ours=%#"PRIx32" theirs=%#"PRIx32")", st->chosen_transform->capab_transformnum, st->local_capabilities, st->remote_capabilities); + return True; } struct xinfoadd { @@ -711,6 +718,13 @@ static bool_t unpick_msg(struct site *st, uint32_t type, CHECK_AVAIL(msg,m->siglen); m->sig=buf_unprepend(msg,m->siglen); CHECK_EMPTY(msg); + + /* In `process_msg3_msg4' below, we assume that we can write a nul + * terminator following the signature. Make sure there's enough space. + */ + if (msg->start >= msg->base + msg->alloclen) + return False; + return True; } @@ -844,7 +858,7 @@ static bool_t process_msg3_msg4(struct site *st, struct msg *m) hst=st->hash->init(); st->hash->update(hst,m->hashstart,m->hashlen); st->hash->final(hst,hash); - /* Terminate signature with a '0' - cheating, but should be ok */ + /* Terminate signature with a '0' - already checked that this will fit */ m->sig[m->siglen]=0; if (!st->pubkey->check(st->pubkey->st,hash,st->hash->len,m->sig)) { slog(st,LOG_SEC,"msg3/msg4 signature failed check!"); @@ -903,7 +917,7 @@ static bool_t process_msg3(struct site *st, struct buffer_if *msg3, st->random->generate(st->random->st,st->dh->len,st->dhsecret); /* Generate the shared key and set up the transform */ - set_new_transform(st,m.pk); + if (!set_new_transform(st,m.pk)) return False; return True; } @@ -934,7 +948,7 @@ static bool_t process_msg4(struct site *st, struct buffer_if *msg4, m.pk[m.pklen]=0; /* Generate the shared key and set up the transform */ - set_new_transform(st,m.pk); + if (!set_new_transform(st,m.pk)) return False; return True; } @@ -1129,7 +1143,8 @@ static bool_t decrypt_msg0(struct site *st, struct buffer_if *msg0, skew: slog(st,LOG_DROP,"transform: %s (merely skew)",transform_err); - return False; + assert(problem); + return problem; } static bool_t process_msg0(struct site *st, struct buffer_if *msg0, @@ -1146,6 +1161,10 @@ static bool_t process_msg0(struct site *st, struct buffer_if *msg0, case LABEL_MSG7: /* We must forget about the current session. */ delete_keys(st,"request from peer",LOG_SEC); + /* probably, the peer is shutting down, and this is going to fail, + * but we need to be trying to bring the link up again */ + if (st->keepalive) + initiate_key_setup(st,"peer requested key teardown",0); return True; case LABEL_MSG9: /* Deliver to netlink layer */ @@ -1164,16 +1183,34 @@ static bool_t process_msg0(struct site *st, struct buffer_if *msg0, } static void dump_packet(struct site *st, struct buffer_if *buf, - const struct comm_addr *addr, bool_t incoming) + const struct comm_addr *addr, bool_t incoming, + bool_t ok) { uint32_t dest=get_uint32(buf->start); uint32_t source=get_uint32(buf->start+4); uint32_t msgtype=get_uint32(buf->start+8); if (st->log_events & LOG_DUMP) - slilog(st->log,M_DEBUG,"%s: %s: %08x<-%08x: %08x:", + slilog(st->log,M_DEBUG,"%s: %s: %08x<-%08x: %08x: %s%s", st->tunname,incoming?"incoming":"outgoing", - dest,source,msgtype); + dest,source,msgtype,comm_addr_to_string(addr), + ok?"":" - fail"); +} + +static bool_t comm_addr_sendmsg(struct site *st, + const struct comm_addr *dest, + struct buffer_if *buf) +{ + int i; + struct comm_clientinfo *commclientinfo = 0; + + for (i=0; i < st->ncomms; i++) { + if (st->comms[i] == dest->comm) { + commclientinfo = st->commclientinfos[i]; + break; + } + } + return dest->comm->sendmsg(dest->comm->st, buf, dest, commclientinfo); } static uint32_t site_status(void *st) @@ -1425,8 +1462,11 @@ static void enter_state_run(struct site *st) FILLZERO(st->remoteN); dispose_transform(&st->new_transform); memset(st->dhsecret,0,st->dh->len); - memset(st->sharedsecret,0,st->sharedsecretlen); + if (st->sharedsecret) memset(st->sharedsecret,0,st->sharedsecretlen); set_link_quality(st); + + if (st->keepalive && !current_valid(st)) + initiate_key_setup(st, "keepalive", 0); } static bool_t ensure_resolving(struct site *st) @@ -1485,6 +1525,7 @@ static bool_t enter_new_state(struct site *st, uint32_t next) case SITE_SENTMSG1: state_assert(st,st->state==SITE_RUN || st->state==SITE_RESOLVE); gen=generate_msg1; + st->msg1_crossed_logged = False; break; case SITE_SENTMSG2: state_assert(st,st->state==SITE_RUN || st->state==SITE_RESOLVE || @@ -1600,8 +1641,8 @@ static void generate_send_prod(struct site *st, slog(st,LOG_SETUP_INIT,"prodding peer for key exchange"); st->allow_send_prod=0; generate_prod(st,&st->scratch); - dump_packet(st,&st->scratch,source,False); - source->comm->sendmsg(source->comm->st, &st->scratch, source); + bool_t ok = comm_addr_sendmsg(st, source, &st->scratch); + dump_packet(st,&st->scratch,source,False,ok); } static inline void site_settimeout(uint64_t timeout, int *timeout_io) @@ -1714,6 +1755,15 @@ static bool_t named_for_us(struct site *st, const struct buffer_if *buf_in, && name_matches(&m->local,st->localname); } +static bool_t we_have_priority(struct site *st, const struct msg *m) { + if ((st->local_capabilities & m->remote_capabilities) + && CAPAB_PRIORITY_MOBILE) { + if (st->local_mobile) return True; + if (st-> peer_mobile) return False; + } + return st->our_name_later; +} + /* This function is called by the communication device to deliver packets from our peers. It should return True if the packet is recognised as being for @@ -1734,7 +1784,7 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, if (!named_for_us(st,buf,msgtype,&named_msg)) return False; /* It's a MSG1 addressed to us. Decide what to do about it. */ - dump_packet(st,buf,source,True); + dump_packet(st,buf,source,True,True); if (st->state==SITE_RUN || st->state==SITE_RESOLVE || st->state==SITE_WAIT) { /* We should definitely process it */ @@ -1755,13 +1805,14 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, /* We've just sent a message 1! They may have crossed on the wire. If we have priority then we ignore the incoming one, otherwise we process it as usual. */ - if (st->setup_priority) { + if (we_have_priority(st,&named_msg)) { BUF_FREE(buf); - slog(st,LOG_DUMP,"crossed msg1s; we are higher " - "priority => ignore incoming msg1"); + if (!st->msg1_crossed_logged++) + slog(st,LOG_SETUP_INIT,"crossed msg1s; we are higher " + "priority => ignore incoming msg1"); return True; } else { - slog(st,LOG_DUMP,"crossed msg1s; we are lower " + slog(st,LOG_SETUP_INIT,"crossed msg1s; we are lower " "priority => use incoming msg1"); if (process_msg1(st,buf,source,&named_msg)) { BUF_FREE(&st->buffer); /* Free our old message 1 */ @@ -1784,7 +1835,7 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, if (msgtype==LABEL_PROD) { if (!named_for_us(st,buf,msgtype,&named_msg)) return False; - dump_packet(st,buf,source,True); + dump_packet(st,buf,source,True,True); if (st->state!=SITE_RUN) { slog(st,LOG_DROP,"ignoring PROD when not in state RUN"); } else if (current_valid(st)) { @@ -1797,7 +1848,7 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, } if (dest==st->index) { /* Explicitly addressed to us */ - if (msgtype!=LABEL_MSG0) dump_packet(st,buf,source,True); + if (msgtype!=LABEL_MSG0) dump_packet(st,buf,source,True,True); switch (msgtype) { case LABEL_NAK: /* If the source is our current peer then initiate a key setup, @@ -1965,6 +2016,8 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, st->localname=dict_read_string(dict, "local-name", True, "site", loc); st->remotename=dict_read_string(dict, "name", True, "site", loc); + st->keepalive=dict_read_bool(dict,"keepalive",False,"site",loc,False); + st->peer_mobile=dict_read_bool(dict,"mobile",False,"site",loc,False); st->local_mobile= dict_read_bool(dict,"local-mobile",False,"site",loc,False); @@ -2014,6 +2067,14 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, GET_CLOSURE_LIST("comm",comms,ncomms,CL_COMM); + NEW_ARY(st->commclientinfos, st->ncomms); + dict_t *comminfo = dict_read_dict(dict,"comm-info",False,"site",loc); + for (i=0; incomms; i++) { + st->commclientinfos[i] = + !comminfo ? 0 : + st->comms[i]->clientinfo(st->comms[i],comminfo,loc); + } + st->resolver=find_cl_if(dict,"resolver",CL_RESOLVER,True,"site",loc); st->log=find_cl_if(dict,"log",CL_LOG,True,"site",loc); st->random=find_cl_if(dict,"random",CL_RANDOMSRC,True,"site",loc); @@ -2077,7 +2138,7 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, /* The information we expect to see in incoming messages of type 1 */ /* fixme: lots of unchecked overflows here, but the results are only corrupted packets rather than undefined behaviour */ - st->setup_priority=(strcmp(st->localname,st->remotename)>0); + st->our_name_later=(strcmp(st->localname,st->remotename)>0); buffer_new(&st->buffer,SETUP_BUFFER_LEN); @@ -2109,6 +2170,9 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, st->local_capabilities |= capbit; } + if (st->local_mobile || st->peer_mobile) + st->local_capabilities |= CAPAB_PRIORITY_MOBILE; + /* We need to register the remote networks with the netlink device */ uint32_t netlink_mtu; /* local virtual interface mtu */ st->netlink->reg(st->netlink->st, site_outgoing, st, &netlink_mtu); @@ -2212,11 +2276,18 @@ static void transport_record_peers(struct site *st, transport_peers *peers, * * Caller must first call transport_peers_expire. */ - if (naddrs==1 && peers->npeers>=1 && - comm_addr_equal(&addrs[0], &peers->peers[0].addr)) { - /* optimisation, also avoids debug for trivial updates */ - peers->peers[0].last = *tv_now; - return; + if (naddrs==1) { + /* avoids debug for uninteresting updates */ + int i; + for (i=0; inpeers; i++) { + if (comm_addr_equal(&addrs[0], &peers->peers[i].addr)) { + memmove(peers->peers+1, peers->peers, + sizeof(peers->peers[0]) * i); + peers->peers[0].addr = addrs[0]; + peers->peers[0].last = *tv_now; + return; + } + } } int old_npeers=peers->npeers; @@ -2347,10 +2418,9 @@ void transport_xmit(struct site *st, transport_peers *peers, int nfailed=0; for (slot=0; slotnpeers; slot++) { transport_peer *peer=&peers->peers[slot]; + bool_t ok = comm_addr_sendmsg(st, &peer->addr, buf); if (candebug) - dump_packet(st, buf, &peer->addr, False); - bool_t ok = - peer->addr.comm->sendmsg(peer->addr.comm->st, buf, &peer->addr); + dump_packet(st, buf, &peer->addr, False, ok); if (!ok) { failed |= 1U << slot; nfailed++; @@ -2372,12 +2442,14 @@ void transport_xmit(struct site *st, transport_peers *peers, transport_peers__copy_by_mask(peers->peers,&wslot,~failed,peers); assert(wslot+nfailed == peers->npeers); COPY_ARRAY(peers->peers+wslot, failedpeers, nfailed); + transport_peers_debug(st,peers,"mobile failure reorder",0,0,0); } } else { if (failed && peers->npeers > 1) { int wslot=0; transport_peers__copy_by_mask(peers->peers,&wslot,~failed,peers); peers->npeers=wslot; + transport_peers_debug(st,peers,"non-mobile failure cleanup",0,0,0); } } }