X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=secnet.git;a=blobdiff_plain;f=site.c;h=df0ab3a46433f41f3f506a00bae751f61cbd5dae;hp=dcac0baaf3428f20c8a97b83eb4d169954271119;hb=13b8fbf4548f3457b02afd36e9284d39839d6f85;hpb=1fc8a4acb3ef658696038c9c4bd3c155fbc27ac3 diff --git a/site.c b/site.c index dcac0ba..df0ab3a 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 @@ -107,6 +107,8 @@ #define SITE_SENTMSG5 7 #define SITE_WAIT 8 +#define CASES_MSG3_KNOWN LABEL_MSG3: case LABEL_MSG3BIS + int32_t site_max_start_pad = 4*4; static cstring_t state_name(uint32_t state) @@ -310,32 +312,32 @@ struct site { struct resolver_if *resolver; struct log_if *log; struct random_if *random; - struct rsaprivkey_if *privkey; - struct rsapubkey_if *pubkey; + struct sigprivkey_if *privkey; + struct sigpubkey_if *pubkey; struct transform_if **transforms; int ntransforms; struct dh_if *dh; - struct hash_if *hash; uint32_t index; /* Index of this site */ + uint32_t early_capabilities; uint32_t local_capabilities; int32_t setup_retries; /* How many times to send setup packets */ int32_t setup_retry_interval; /* Initial timeout for setup packets */ - int32_t wait_timeout; /* How long to wait if setup unsuccessful */ + int32_t wait_timeout_mean; /* How long to wait if setup unsuccessful */ int32_t mobile_peer_expiry; /* How long to remember 2ary addresses */ int32_t key_lifetime; /* How long a key lasts once set up */ int32_t key_renegotiate_time; /* If we see traffic (or a keepalive) 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; @@ -470,14 +472,15 @@ static bool_t current_valid(struct site *st) } #define DEFINE_CALL_TRANSFORM(fwdrev) \ -static int call_transform_##fwdrev(struct site *st, \ +static transform_apply_return \ +call_transform_##fwdrev(struct site *st, \ struct transform_inst_if *transform, \ struct buffer_if *buf, \ const char **errmsg) \ { \ if (!is_transform_valid(transform)) { \ *errmsg="transform not set up"; \ - return 1; \ + return transform_apply_err; \ } \ return transform->fwdrev(transform->st,buf,errmsg); \ } @@ -504,10 +507,10 @@ static void dispose_transform(struct transform_inst_if **transform_var) static _Bool type_is_msg34(uint32_t type) { - return - type == LABEL_MSG3 || - type == LABEL_MSG3BIS || - type == LABEL_MSG4; + switch (type) { + case CASES_MSG3_KNOWN: case LABEL_MSG4: return True; + default: return False; + } } struct parsedname { @@ -530,12 +533,23 @@ struct msg { int32_t pklen; char *pk; int32_t hashlen; - int32_t siglen; - char *sig; + struct alg_msg_data sig; }; -static void set_new_transform(struct site *st, char *pk) +static int32_t wait_timeout(struct site *st) { + int32_t t = st->wait_timeout_mean; + int8_t factor; + if (t < INT_MAX/2) { + st->random->generate(st->random->st,sizeof(factor),&factor); + t += (t / 256) * factor; + } + return t; +} + +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); @@ -553,15 +567,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->chosen_transform->capab_bit, st->local_capabilities, st->remote_capabilities); + return True; } struct xinfoadd { @@ -596,9 +613,8 @@ static void append_string_xinfo_done(struct buffer_if *buf, out using a transform of config data supplied by netlink */ static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what) { - void *hst; - uint8_t *hash; - string_t dhpub, sig; + string_t dhpub; + unsigned minor; st->retries=st->setup_retries; BUF_ALLOC(&st->buffer,what); @@ -610,7 +626,8 @@ static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what) struct xinfoadd xia; append_string_xinfo_start(&st->buffer,&xia,st->localname); - if ((st->local_capabilities & CAPAB_EARLY) || (type != LABEL_MSG1)) { + if ((st->local_capabilities & st->early_capabilities) || + (type != LABEL_MSG1)) { buf_append_uint32(&st->buffer,st->local_capabilities); } if (type_is_msg34(type)) { @@ -626,21 +643,25 @@ static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what) if (hacky_par_mid_failnow()) return False; - if (type==LABEL_MSG3BIS) - buf_append_uint8(&st->buffer,st->chosen_transform->capab_transformnum); + if (MSGMAJOR(type) == 3) do { + minor = MSGMINOR(type); + if (minor < 1) break; + buf_append_uint8(&st->buffer,st->chosen_transform->capab_bit); + } while (0); dhpub=st->dh->makepublic(st->dh->st,st->dhsecret,st->dh->len); buf_append_string(&st->buffer,dhpub); free(dhpub); - hash=safe_malloc(st->hash->len, "generate_msg"); - hst=st->hash->init(); - st->hash->update(hst,st->buffer.start,st->buffer.size); - st->hash->final(hst,hash); - sig=st->privkey->sign(st->privkey->st,hash,st->hash->len); - buf_append_string(&st->buffer,sig); - free(sig); - free(hash); + + bool_t ok=st->privkey->sign(st->privkey->st, + st->buffer.start, + st->buffer.size, + &st->buffer); + if (!ok) goto fail; return True; + + fail: + return False; } static bool_t unpick_name(struct buffer_if *msg, struct parsedname *nm) @@ -662,6 +683,8 @@ static bool_t unpick_name(struct buffer_if *msg, struct parsedname *nm) static bool_t unpick_msg(struct site *st, uint32_t type, struct buffer_if *msg, struct msg *m) { + unsigned minor; + m->capab_transformnum=-1; m->hashstart=msg->start; CHECK_AVAIL(msg,4); @@ -697,22 +720,31 @@ static bool_t unpick_msg(struct site *st, uint32_t type, CHECK_EMPTY(msg); return True; } - if (type==LABEL_MSG3BIS) { - CHECK_AVAIL(msg,1); - m->capab_transformnum = buf_unprepend_uint8(msg); - } else { - m->capab_transformnum = CAPAB_TRANSFORMNUM_ANCIENT; - } + if (MSGMAJOR(type) == 3) do { + minor = MSGMINOR(type); +#define MAYBE_READ_CAP(minminor, kind, dflt) do { \ + if (minor < (minminor)) \ + m->capab_##kind##num = (dflt); \ + else { \ + CHECK_AVAIL(msg, 1); \ + m->capab_##kind##num = buf_unprepend_uint8(msg); \ + } \ +} while (0) + MAYBE_READ_CAP(1, transform, CAPAB_BIT_ANCIENTTRANSFORM); +#undef MAYBE_READ_CAP + } while (0); CHECK_AVAIL(msg,2); m->pklen=buf_unprepend_uint16(msg); CHECK_AVAIL(msg,m->pklen); m->pk=buf_unprepend(msg,m->pklen); m->hashlen=msg->start-m->hashstart; - CHECK_AVAIL(msg,2); - m->siglen=buf_unprepend_uint16(msg); - CHECK_AVAIL(msg,m->siglen); - m->sig=buf_unprepend(msg,m->siglen); + + if (!st->pubkey->unpick(st->pubkey->st,msg,&m->sig)) { + return False; + } + CHECK_EMPTY(msg); + return True; } @@ -744,13 +776,13 @@ static bool_t check_msg(struct site *st, uint32_t type, struct msg *m, return False; } if (type==LABEL_MSG2) return True; - if (!consttime_memeq(m->nR,st->remoteN,NONCELEN)!=0) { + if (!consttime_memeq(m->nR,st->remoteN,NONCELEN)) { *error="wrong remotely-generated nonce"; return False; } /* MSG3 has complicated rules about capabilities, which are * handled in process_msg3. */ - if (type==LABEL_MSG3 || type==LABEL_MSG3BIS) return True; + if (MSGMAJOR(type) == 3) return True; if (m->remote_capabilities!=st->remote_capabilities) { *error="remote capabilities changed"; return False; @@ -801,25 +833,32 @@ static bool_t process_msg2(struct site *st, struct buffer_if *msg2, /* Select the transform to use */ - uint32_t remote_transforms = st->remote_capabilities & CAPAB_TRANSFORM_MASK; - if (!remote_transforms) + uint32_t remote_crypto_caps = st->remote_capabilities & CAPAB_TRANSFORM_MASK; + if (!remote_crypto_caps) /* old secnets only had this one transform */ - remote_transforms = 1UL << CAPAB_TRANSFORMNUM_ANCIENT; + remote_crypto_caps = 1UL << CAPAB_BIT_ANCIENTTRANSFORM; + +#define CHOOSE_CRYPTO(kind, whats) do { \ + struct kind##_if *iface; \ + uint32_t bit, ours = 0; \ + int i; \ + for (i= 0; i < st->n##kind##s; i++) { \ + iface=st->kind##s[i]; \ + bit = 1UL << iface->capab_bit; \ + if (bit & remote_crypto_caps) goto kind##_found; \ + ours |= bit; \ + } \ + slog(st,LOG_ERROR,"no " whats " in common" \ + " (us %#"PRIx32"; them: %#"PRIx32")", \ + st->local_capabilities & ours, remote_crypto_caps); \ + return False; \ +kind##_found: \ + st->chosen_##kind = iface; \ +} while (0) - struct transform_if *ti; - int i; - for (i=0; intransforms; i++) { - ti=st->transforms[i]; - if ((1UL << ti->capab_transformnum) & remote_transforms) - goto transform_found; - } - slog(st,LOG_ERROR,"no transforms in common" - " (us %#"PRIx32"; them: %#"PRIx32")", - st->local_capabilities & CAPAB_TRANSFORM_MASK, - remote_transforms); - return False; - transform_found: - st->chosen_transform=ti; + CHOOSE_CRYPTO(transform, "transforms"); + +#undef CHOOSE_CRYPTO memcpy(st->remoteN,m.nR,NONCELEN); return True; @@ -831,29 +870,21 @@ static bool_t generate_msg3(struct site *st) and create message number 3. */ st->random->generate(st->random->st,st->dh->len,st->dhsecret); return generate_msg(st, - (st->remote_capabilities & CAPAB_TRANSFORM_MASK - ? LABEL_MSG3BIS : LABEL_MSG3), + (st->remote_capabilities & CAPAB_TRANSFORM_MASK) + ? LABEL_MSG3BIS + : LABEL_MSG3, "site:MSG3"); } static bool_t process_msg3_msg4(struct site *st, struct msg *m) { - uint8_t *hash; - void *hst; - /* Check signature and store g^x mod m */ - hash=safe_malloc(st->hash->len, "process_msg3_msg4"); - 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 */ - m->sig[m->siglen]=0; - if (!st->pubkey->check(st->pubkey->st,hash,st->hash->len,m->sig)) { + if (!st->pubkey->check(st->pubkey->st, + m->hashstart,m->hashlen, + &m->sig)) { slog(st,LOG_SEC,"msg3/msg4 signature failed check!"); - free(hash); return False; } - free(hash); st->remote_adv_mtu=m->remote_mtu; @@ -866,7 +897,10 @@ static bool_t process_msg3(struct site *st, struct buffer_if *msg3, struct msg m; cstring_t err; - assert(msgtype==LABEL_MSG3 || msgtype==LABEL_MSG3BIS); + switch (msgtype) { + case CASES_MSG3_KNOWN: break; + default: assert(0); + } if (!unpick_msg(st,msgtype,msg3,&m)) return False; if (!check_msg(st,msgtype,&m,&err)) { @@ -874,38 +908,53 @@ static bool_t process_msg3(struct site *st, struct buffer_if *msg3, return False; } uint32_t capab_adv_late = m.remote_capabilities - & ~st->remote_capabilities & CAPAB_EARLY; + & ~st->remote_capabilities & st->early_capabilities; if (capab_adv_late) { slog(st,LOG_SEC,"msg3 impermissibly adds early capability flag(s)" " %#"PRIx32" (was %#"PRIx32", now %#"PRIx32")", capab_adv_late, st->remote_capabilities, m.remote_capabilities); return False; } - st->remote_capabilities|=m.remote_capabilities; - struct transform_if *ti; - int i; - for (i=0; intransforms; i++) { - ti=st->transforms[i]; - if (ti->capab_transformnum == m.capab_transformnum) - goto transform_found; - } - slog(st,LOG_SEC,"peer chose unknown-to-us transform %d!", - m.capab_transformnum); - return False; - transform_found: - st->chosen_transform=ti; +#define CHOSE_CRYPTO(kind, what) do { \ + struct kind##_if *iface; \ + int i; \ + for (i=0; in##kind##s; i++) { \ + iface=st->kind##s[i]; \ + if (iface->capab_bit == m.capab_##kind##num) \ + goto kind##_found; \ + } \ + slog(st,LOG_SEC,"peer chose unknown-to-us " what " %d!", \ + m.capab_##kind##num); \ + return False; \ +kind##_found: \ + st->chosen_##kind=iface; \ +} while (0) + + CHOSE_CRYPTO(transform, "transform"); + +#undef CHOSE_CRYPTO if (!process_msg3_msg4(st,&m)) return False; + /* Update our idea of the remote site's capabilities, now that we've + * verified that its message was authentic. + * + * Our previous idea of the remote site's capabilities came from the + * unauthenticated MSG1. We've already checked that this new message + * doesn't change any of the bits we relied upon in the past, but it may + * also have set additional capability bits. We simply throw those away + * now, and use the authentic capabilities from this MSG3. */ + st->remote_capabilities=m.remote_capabilities; + /* Terminate their DH public key with a '0' */ m.pk[m.pklen]=0; /* Invent our DH secret key */ 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; } @@ -936,7 +985,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; } @@ -1017,8 +1066,9 @@ static void create_msg6(struct site *st, struct transform_inst_if *transform, /* Give the netlink code an opportunity to put its own stuff in the message (configuration information, etc.) */ buf_prepend_uint32(&st->buffer,LABEL_MSG6); - int problem = call_transform_forwards(st,transform, - &st->buffer,&transform_err); + transform_apply_return problem = + call_transform_forwards(st,transform, + &st->buffer,&transform_err); assert(!problem); buf_prepend_uint32(&st->buffer,LABEL_MSG6); buf_prepend_uint32(&st->buffer,st->index); @@ -1058,12 +1108,13 @@ static bool_t process_msg6(struct site *st, struct buffer_if *msg6, return True; } -static bool_t decrypt_msg0(struct site *st, struct buffer_if *msg0, +static transform_apply_return +decrypt_msg0(struct site *st, struct buffer_if *msg0, const struct comm_addr *src) { cstring_t transform_err, auxkey_err, newkey_err="n/a"; struct msg0 m; - uint32_t problem; + transform_apply_return problem; if (!unpick_msg0(st,msg0,&m)) return False; @@ -1076,15 +1127,15 @@ static bool_t decrypt_msg0(struct site *st, struct buffer_if *msg0, if (!st->auxiliary_is_new) delete_one_key(st,&st->auxiliary_key, "peer has used new key","auxiliary key",LOG_SEC); - return True; + return 0; } - if (problem==2) - goto skew; + if (transform_apply_return_badseq(problem)) + goto badseq; buffer_copy(msg0, &st->scratch); problem = call_transform_reverse(st,st->auxiliary_key.transform, msg0,&auxkey_err); - if (problem==0) { + if (!problem) { slog(st,LOG_DROP,"processing packet which uses auxiliary key"); if (st->auxiliary_is_new) { /* We previously timed out in state SENTMSG5 but it turns @@ -1101,10 +1152,10 @@ static bool_t decrypt_msg0(struct site *st, struct buffer_if *msg0, st->auxiliary_is_new=0; st->renegotiate_key_time=st->auxiliary_renegotiate_key_time; } - return True; + return 0; } - if (problem==2) - goto skew; + if (transform_apply_return_badseq(problem)) + goto badseq; if (st->state==SITE_SENTMSG5) { buffer_copy(msg0, &st->scratch); @@ -1117,29 +1168,41 @@ static bool_t decrypt_msg0(struct site *st, struct buffer_if *msg0, BUF_FREE(&st->buffer); st->timeout=0; activate_new_key(st); - return True; /* do process the data in this packet */ + return 0; /* do process the data in this packet */ } - if (problem==2) - goto skew; + if (transform_apply_return_badseq(problem)) + goto badseq; } slog(st,LOG_SEC,"transform: %s (aux: %s, new: %s)", transform_err,auxkey_err,newkey_err); initiate_key_setup(st,"incoming message would not decrypt",0); send_nak(src,m.dest,m.source,m.type,msg0,"message would not decrypt"); - return False; + assert(problem); + return problem; - skew: - slog(st,LOG_DROP,"transform: %s (merely skew)",transform_err); - return False; + badseq: + slog(st,LOG_DROP,"transform: %s (bad seq.)",transform_err); + assert(problem); + return problem; } static bool_t process_msg0(struct site *st, struct buffer_if *msg0, const struct comm_addr *src) { uint32_t type; - - if (!decrypt_msg0(st,msg0,src)) + transform_apply_return problem; + + problem = decrypt_msg0(st,msg0,src); + if (problem==transform_apply_seqdupe) { + /* We recently received another copy of this packet, maybe due + * to polypath. That's not a problem; indeed, for the + * purposes of transport address management it is a success. + * But we don't want to process the packet. */ + transport_data_msgok(st,src); + return False; + } + if (problem) return False; CHECK_AVAIL(msg0,4); @@ -1326,7 +1389,7 @@ static void decrement_resolving_count(struct site *st, int by) } else if (st->local_mobile) { /* Not very good. We should queue (another) renegotiation * so that we can update the peer address. */ - st->key_renegotiate_time=st->now+st->wait_timeout; + st->key_renegotiate_time=st->now+wait_timeout(st); } else { slog(st,LOG_SETUP_INIT,"resolution failed: " " continuing to use source address of peer's packets"); @@ -1439,7 +1502,8 @@ static void set_link_quality(struct site *st) static void enter_state_run(struct site *st) { - slog(st,LOG_STATE,"entering state RUN"); + slog(st,LOG_STATE,"entering state RUN%s", + current_valid(st) ? " (keyed)" : " (unkeyed)"); st->state=SITE_RUN; st->timeout=0; @@ -1449,7 +1513,7 @@ 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)) @@ -1512,6 +1576,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,7 +1665,7 @@ static bool_t send_msg7(struct site *st, cstring_t reason) static void enter_state_wait(struct site *st) { slog(st,LOG_STATE,"entering state WAIT"); - st->timeout=st->now+st->wait_timeout; + st->timeout=st->now+wait_timeout(st); st->state=SITE_WAIT; set_link_quality(st); BUF_FREE(&st->buffer); /* will have had an outgoing packet in it */ @@ -1741,6 +1806,35 @@ 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; +} + +static bool_t setup_late_msg_ok(struct site *st, + const struct buffer_if *buf_in, + uint32_t msgtype, + const struct comm_addr *source) { + /* For setup packets which seem from their type like they are + * late. Maybe they came via a different path. All we do is make + * a note of the sending address, iff they look like they are part + * of the current key setup attempt. */ + struct msg m; + if (!named_for_us(st,buf_in,msgtype,&m)) + /* named_for_us calls unpick_msg which gets the nonces */ + return False; + if (!consttime_memeq(m.nR,st->remoteN,NONCELEN) || + !consttime_memeq(m.nL,st->localN, NONCELEN)) + /* spoof ? from stale run ? who knows */ + return False; + transport_setup_msgok(st,source); + return True; +} + /* 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 @@ -1782,13 +1876,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 */ @@ -1801,9 +1896,21 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, BUF_FREE(buf); return True; } + } else if (st->state==SITE_SENTMSG2 || + st->state==SITE_SENTMSG4) { + if (consttime_memeq(named_msg.nR,st->remoteN,NONCELEN)) { + /* We are ahead in the protocol, but that msg1 had the + * peer's nonce so presumably it is from this key + * exchange run, via a slower route */ + transport_setup_msgok(st,source); + } else { + slog(st,LOG_UNEXPECTED,"competing incoming message 1"); + } + BUF_FREE(buf); + return True; } /* The message 1 was received at an unexpected stage of the - key setup. XXX POLICY - what do we do? */ + key setup. Well, they lost the race. */ slog(st,LOG_UNEXPECTED,"unexpected incoming message 1"); BUF_FREE(buf); return True; @@ -1848,6 +1955,10 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, case LABEL_MSG2: /* Setup packet: expected only in state SENTMSG1 */ if (st->state!=SITE_SENTMSG1) { + if ((st->state==SITE_SENTMSG3 || + st->state==SITE_SENTMSG5) && + setup_late_msg_ok(st,buf,msgtype,source)) + break; slog(st,LOG_UNEXPECTED,"unexpected MSG2"); } else if (process_msg2(st,buf,source)) { transport_setup_msgok(st,source); @@ -1856,10 +1967,12 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, slog(st,LOG_SEC,"invalid MSG2"); } break; - case LABEL_MSG3: - case LABEL_MSG3BIS: + case CASES_MSG3_KNOWN: /* Setup packet: expected only in state SENTMSG2 */ if (st->state!=SITE_SENTMSG2) { + if ((st->state==SITE_SENTMSG4) && + setup_late_msg_ok(st,buf,msgtype,source)) + break; slog(st,LOG_UNEXPECTED,"unexpected MSG3"); } else if (process_msg3(st,buf,source,msgtype)) { transport_setup_msgok(st,source); @@ -1871,6 +1984,9 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, case LABEL_MSG4: /* Setup packet: expected only in state SENTMSG3 */ if (st->state!=SITE_SENTMSG3) { + if ((st->state==SITE_SENTMSG5) && + setup_late_msg_ok(st,buf,msgtype,source)) + break; slog(st,LOG_UNEXPECTED,"unexpected MSG4"); } else if (process_msg4(st,buf,source)) { transport_setup_msgok(st,source); @@ -2021,6 +2137,7 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, assert(index_sequence < 0xffffffffUL); st->index = ++index_sequence; st->local_capabilities = 0; + st->early_capabilities = CAPAB_PRIORITY_MOBILE; st->netlink=find_cl_if(dict,"link",CL_NETLINK,True,"site",loc); #define GET_CLOSURE_LIST(dictkey,things,nthings,CL_TYPE) do{ \ @@ -2055,17 +2172,22 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, st->log=find_cl_if(dict,"log",CL_LOG,True,"site",loc); st->random=find_cl_if(dict,"random",CL_RANDOMSRC,True,"site",loc); - st->privkey=find_cl_if(dict,"local-key",CL_RSAPRIVKEY,True,"site",loc); + st->privkey=find_cl_if(dict,"local-key",CL_SIGPRIVKEY,True,"site",loc); st->addresses=dict_read_string_array(dict,"address",False,"site",loc,0); if (st->addresses) st->remoteport=dict_read_number(dict,"port",True,"site",loc,0); else st->remoteport=0; - st->pubkey=find_cl_if(dict,"key",CL_RSAPUBKEY,True,"site",loc); + st->pubkey=find_cl_if(dict,"key",CL_SIGPUBKEY,True,"site",loc); GET_CLOSURE_LIST("transform",transforms,ntransforms,CL_TRANSFORM); st->dh=find_cl_if(dict,"dh",CL_DH,True,"site",loc); - st->hash=find_cl_if(dict,"hash",CL_HASH,True,"site",loc); + + if (st->privkey->sethash || st->pubkey->sethash) { + struct hash_if *hash=find_cl_if(dict,"hash",CL_HASH,True,"site",loc); + if (st->privkey->sethash) st->privkey->sethash(st->privkey->st,hash); + if (st->pubkey->sethash) st->pubkey->sethash(st->pubkey->st,hash); + } #define DEFAULT(D) (st->peer_mobile || st->local_mobile \ ? DEFAULT_MOBILE_##D : DEFAULT_##D) @@ -2074,7 +2196,7 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, st->key_lifetime= CFG_NUMBER("key-lifetime", KEY_LIFETIME); st->setup_retries= CFG_NUMBER("setup-retries", SETUP_RETRIES); st->setup_retry_interval= CFG_NUMBER("setup-timeout", SETUP_RETRY_INTERVAL); - st->wait_timeout= CFG_NUMBER("wait-time", WAIT_TIME); + st->wait_timeout_mean= CFG_NUMBER("wait-time", WAIT_TIME); st->mtu_target= dict_read_number(dict,"mtu-target",False,"site",loc,0); st->mobile_peer_expiry= dict_read_number( @@ -2114,7 +2236,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); @@ -2137,14 +2259,21 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, st->sharedsecretlen=st->sharedsecretallocd=0; st->sharedsecret=0; - for (i=0; intransforms; i++) { - struct transform_if *ti=st->transforms[i]; - uint32_t capbit = 1UL << ti->capab_transformnum; - if (st->local_capabilities & capbit) - slog(st,LOG_ERROR,"transformnum capability bit" - " %d (%#"PRIx32") reused", ti->capab_transformnum, capbit); - st->local_capabilities |= capbit; - } +#define SET_CAPBIT(bit) do { \ + uint32_t capflag = 1UL << (bit); \ + if (st->local_capabilities & capflag) \ + slog(st,LOG_ERROR,"capability bit" \ + " %d (%#"PRIx32") reused", (bit), capflag); \ + st->local_capabilities |= capflag; \ +} while (0) + + for (i=0; intransforms; i++) + SET_CAPBIT(st->transforms[i]->capab_bit); + +#undef SET_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 */ @@ -2249,11 +2378,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;