X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?a=blobdiff_plain;f=site.c;h=9c320a5b31013077cfddaca9842726e3d79dc39e;hb=03c9b2ca595aaf5b6ff312f84d9a13c26f4e4fba;hp=d0dd909d57aac307373df6c3132997f795f1c270;hpb=4c795bdb6d8f0a75a7a484775151e810aac3de4c;p=secnet.git diff --git a/site.c b/site.c index d0dd909..9c320a5 100644 --- a/site.c +++ b/site.c @@ -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,14 +312,14 @@ 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 */ @@ -505,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 { @@ -531,8 +533,7 @@ struct msg { int32_t pklen; char *pk; int32_t hashlen; - int32_t siglen; - char *sig; + struct alg_msg_data sig; }; static int32_t wait_timeout(struct site *st) { @@ -575,7 +576,7 @@ static _Bool set_new_transform(struct site *st, char *pk) 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; } @@ -612,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); @@ -626,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)) { @@ -642,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) @@ -678,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); @@ -713,28 +720,30 @@ 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); - 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) + if (!st->pubkey->unpick(st->pubkey->st,msg,&m->sig)) { return False; + } + + CHECK_EMPTY(msg); return True; } @@ -773,7 +782,7 @@ static bool_t check_msg(struct site *st, uint32_t type, struct msg *m, } /* 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; @@ -809,42 +818,49 @@ static bool_t generate_msg2(struct site *st) } static bool_t process_msg2(struct site *st, struct buffer_if *msg2, - const struct comm_addr *src) + const struct comm_addr *src, + struct msg *m /* returned */) { - struct msg m; cstring_t err; - if (!unpick_msg(st,LABEL_MSG2,msg2,&m)) return False; - if (!check_msg(st,LABEL_MSG2,&m,&err)) { + if (!unpick_msg(st,LABEL_MSG2,msg2,m)) return False; + if (!check_msg(st,LABEL_MSG2,m,&err)) { slog(st,LOG_SEC,"msg2: %s",err); return False; } - st->setup_session_id=m.source; - st->remote_capabilities=m.remote_capabilities; + st->setup_session_id=m->source; + st->remote_capabilities=m->remote_capabilities; /* 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); + memcpy(st->remoteN,m->nR,NONCELEN); return True; } @@ -854,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' - already checked that this will fit */ - 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; @@ -884,51 +892,69 @@ static bool_t process_msg3_msg4(struct site *st, struct msg *m) } static bool_t process_msg3(struct site *st, struct buffer_if *msg3, - const struct comm_addr *src, uint32_t msgtype) + const struct comm_addr *src, uint32_t msgtype, + struct msg *m /* returned */) { - 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)) { + if (!unpick_msg(st,msgtype,msg3,m)) return False; + if (!check_msg(st,msgtype,m,&err)) { slog(st,LOG_SEC,"msg3: %s",err); return False; } - uint32_t capab_adv_late = m.remote_capabilities - & ~st->remote_capabilities & CAPAB_EARLY; + uint32_t capab_adv_late = m->remote_capabilities + & ~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); + 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)) + 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; + 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 */ - if (!set_new_transform(st,m.pk)) return False; + if (!set_new_transform(st,m->pk)) return False; return True; } @@ -941,25 +967,25 @@ static bool_t generate_msg4(struct site *st) } static bool_t process_msg4(struct site *st, struct buffer_if *msg4, - const struct comm_addr *src) + const struct comm_addr *src, + struct msg *m /* returned */) { - struct msg m; cstring_t err; - if (!unpick_msg(st,LABEL_MSG4,msg4,&m)) return False; - if (!check_msg(st,LABEL_MSG4,&m,&err)) { + if (!unpick_msg(st,LABEL_MSG4,msg4,m)) return False; + if (!check_msg(st,LABEL_MSG4,m,&err)) { slog(st,LOG_SEC,"msg4: %s",err); return False; } - if (!process_msg3_msg4(st,&m)) + if (!process_msg3_msg4(st,m)) return False; /* Terminate their DH public key with a '0' */ - m.pk[m.pklen]=0; + m->pk[m->pklen]=0; /* Generate the shared key and set up the transform */ - if (!set_new_transform(st,m.pk)) return False; + if (!set_new_transform(st,m->pk)) return False; return True; } @@ -1792,17 +1818,17 @@ static bool_t we_have_priority(struct site *st, const struct msg *m) { static bool_t setup_late_msg_ok(struct site *st, const struct buffer_if *buf_in, uint32_t msgtype, - const struct comm_addr *source) { + const struct comm_addr *source, + struct msg *m /* returned */) { /* 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)) + 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)) + 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); @@ -1823,10 +1849,10 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, uint32_t dest=get_uint32(buf->start); uint32_t msgtype=get_uint32(buf->start+8); - struct msg named_msg; + struct msg msg; if (msgtype==LABEL_MSG1) { - if (!named_for_us(st,buf,msgtype,&named_msg)) + if (!named_for_us(st,buf,msgtype,&msg)) return False; /* It's a MSG1 addressed to us. Decide what to do about it. */ dump_packet(st,buf,source,True,True); @@ -1834,7 +1860,7 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, st->state==SITE_WAIT) { /* We should definitely process it */ transport_compute_setupinit_peers(st,0,0,source); - if (process_msg1(st,buf,source,&named_msg)) { + if (process_msg1(st,buf,source,&msg)) { slog(st,LOG_SETUP_INIT,"key setup initiated by peer"); bool_t entered=enter_new_state(st,SITE_SENTMSG2); if (entered && st->addresses && st->local_mobile) @@ -1850,7 +1876,7 @@ 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 (we_have_priority(st,&named_msg)) { + if (we_have_priority(st,&msg)) { BUF_FREE(buf); if (!st->msg1_crossed_logged++) slog(st,LOG_SETUP_INIT,"crossed msg1s; we are higher " @@ -1859,7 +1885,7 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, } else { slog(st,LOG_SETUP_INIT,"crossed msg1s; we are lower " "priority => use incoming msg1"); - if (process_msg1(st,buf,source,&named_msg)) { + if (process_msg1(st,buf,source,&msg)) { BUF_FREE(&st->buffer); /* Free our old message 1 */ transport_setup_msgok(st,source); enter_new_state(st,SITE_SENTMSG2); @@ -1872,7 +1898,7 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, } } else if (st->state==SITE_SENTMSG2 || st->state==SITE_SENTMSG4) { - if (consttime_memeq(named_msg.nR,st->remoteN,NONCELEN)) { + if (consttime_memeq(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 */ @@ -1890,7 +1916,7 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, return True; } if (msgtype==LABEL_PROD) { - if (!named_for_us(st,buf,msgtype,&named_msg)) + if (!named_for_us(st,buf,msgtype,&msg)) return False; dump_packet(st,buf,source,True,True); if (st->state!=SITE_RUN) { @@ -1931,25 +1957,24 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, if (st->state!=SITE_SENTMSG1) { if ((st->state==SITE_SENTMSG3 || st->state==SITE_SENTMSG5) && - setup_late_msg_ok(st,buf,msgtype,source)) + setup_late_msg_ok(st,buf,msgtype,source,&msg)) break; slog(st,LOG_UNEXPECTED,"unexpected MSG2"); - } else if (process_msg2(st,buf,source)) { + } else if (process_msg2(st,buf,source,&msg)) { transport_setup_msgok(st,source); enter_new_state(st,SITE_SENTMSG3); } else { 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)) + setup_late_msg_ok(st,buf,msgtype,source,&msg)) break; slog(st,LOG_UNEXPECTED,"unexpected MSG3"); - } else if (process_msg3(st,buf,source,msgtype)) { + } else if (process_msg3(st,buf,source,msgtype,&msg)) { transport_setup_msgok(st,source); enter_new_state(st,SITE_SENTMSG4); } else { @@ -1960,10 +1985,10 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, /* 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)) + setup_late_msg_ok(st,buf,msgtype,source,&msg)) break; slog(st,LOG_UNEXPECTED,"unexpected MSG4"); - } else if (process_msg4(st,buf,source)) { + } else if (process_msg4(st,buf,source,&msg)) { transport_setup_msgok(st,source); enter_new_state(st,SITE_SENTMSG5); } else { @@ -2112,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{ \ @@ -2146,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) @@ -2228,14 +2259,18 @@ 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;