X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=site.c;h=f87328fda51fbb4cbf6482b30ac342e7fedd34a0;hb=dbe11c2091cfdb8f99f032a9ed81321b09c4094e;hp=909e8e9fa5a9903967fc700d93b4c2cabf70a620;hpb=3abd18e85781e00e2b7fc641f29c99e130238abf;p=secnet.git diff --git a/site.c b/site.c index 909e8e9..f87328f 100644 --- a/site.c +++ b/site.c @@ -206,7 +206,8 @@ static void transport_peers_copy(struct site *st, transport_peers *dst, static void transport_setup_msgok(struct site *st, const struct comm_addr *a); static void transport_data_msgok(struct site *st, const struct comm_addr *a); static bool_t transport_compute_setupinit_peers(struct site *st, - const struct comm_addr *configured_addr /* 0 if none or not found */); + const struct comm_addr *configured_addr /* 0 if none or not found */, + const struct comm_addr *prod_hint_addr /* 0 if none */); static void transport_record_peer(struct site *st, transport_peers *peers, const struct comm_addr *addr, const char *m); @@ -233,6 +234,7 @@ struct site { string_t tunname; /* localname<->remotename by default, used in logs */ string_t address; /* DNS name for bootstrapping, optional */ int remoteport; /* Port for bootstrapping, optional */ + uint32_t mtu_target; struct netlink_if *netlink; struct comm_if **comms; int ncomms; @@ -264,6 +266,7 @@ struct site { /* runtime information */ uint32_t state; uint64_t now; /* Most recently seen time */ + bool_t allow_send_prod; /* The currently established session */ struct data_key current; @@ -280,6 +283,7 @@ struct site { timeout before we can listen for another setup packet); perhaps we should keep a list of 'bad' sources for setup packets. */ uint32_t remote_capabilities; + uint16_t remote_adv_mtu; struct transform_if *chosen_transform; uint32_t setup_session_id; transport_peers setup_peers; @@ -333,7 +337,8 @@ static void delete_one_key(struct site *st, struct data_key *key, const char *reason /* may be 0 meaning don't log*/, const char *which /* ignored if !reasonn */, uint32_t loglevel /* ignored if !reasonn */); -static bool_t initiate_key_setup(struct site *st, cstring_t reason); +static bool_t initiate_key_setup(struct site *st, cstring_t reason, + const struct comm_addr *prod_hint); static void enter_state_run(struct site *st); static bool_t enter_state_resolve(struct site *st); static bool_t enter_new_state(struct site *st,uint32_t next); @@ -383,6 +388,14 @@ static void dispose_transform(struct transform_inst_if **transform_var) type=buf_unprepend_uint32((b)); \ if (type!=(t)) return False; } while(0) +static _Bool type_is_msg34(uint32_t type) +{ + return + type == LABEL_MSG3 || + type == LABEL_MSG3BIS || + type == LABEL_MSG4; +} + struct parsedname { int32_t len; uint8_t *name; @@ -396,6 +409,7 @@ struct msg { struct parsedname remote; struct parsedname local; uint32_t remote_capabilities; + uint16_t remote_mtu; int capab_transformnum; uint8_t *nR; uint8_t *nL; @@ -484,6 +498,9 @@ static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what) if ((st->local_capabilities & CAPAB_EARLY) || (type != LABEL_MSG1)) { buf_append_uint32(&st->buffer,st->local_capabilities); } + if (type_is_msg34(type)) { + buf_append_uint16(&st->buffer,st->mtu_target); + } append_string_xinfo_done(&st->buffer,&xia); buf_append_string(&st->buffer,st->remotename); @@ -539,11 +556,20 @@ static bool_t unpick_msg(struct site *st, uint32_t type, CHECK_TYPE(msg,type); if (!unpick_name(msg,&m->remote)) return False; m->remote_capabilities=0; + m->remote_mtu=0; if (m->remote.extrainfo.size) { CHECK_AVAIL(&m->remote.extrainfo,4); m->remote_capabilities=buf_unprepend_uint32(&m->remote.extrainfo); } + if (type_is_msg34(type) && m->remote.extrainfo.size) { + CHECK_AVAIL(&m->remote.extrainfo,2); + m->remote_mtu=buf_unprepend_uint16(&m->remote.extrainfo); + } if (!unpick_name(msg,&m->local)) return False; + if (type==LABEL_PROD) { + CHECK_EMPTY(msg); + return True; + } CHECK_AVAIL(msg,NONCELEN); m->nR=buf_unprepend(msg,NONCELEN); if (type==LABEL_MSG1) { @@ -696,12 +722,34 @@ static bool_t generate_msg3(struct site *st) "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)) { + slog(st,LOG_SEC,"msg3/msg4 signature failed check!"); + free(hash); + return False; + } + free(hash); + + st->remote_adv_mtu=m->remote_mtu; + + return True; +} + static bool_t process_msg3(struct site *st, struct buffer_if *msg3, const struct comm_addr *src, uint32_t msgtype) { struct msg m; - uint8_t *hash; - void *hst; cstring_t err; assert(msgtype==LABEL_MSG3 || msgtype==LABEL_MSG3BIS); @@ -734,19 +782,8 @@ static bool_t process_msg3(struct site *st, struct buffer_if *msg3, transform_found: st->chosen_transform=ti; - /* Check signature and store g^x mod m */ - hash=safe_malloc(st->hash->len, "process_msg3"); - 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)) { - slog(st,LOG_SEC,"msg3 signature failed check!"); - free(hash); + if (!process_msg3_msg4(st,&m)) return False; - } - free(hash); /* Terminate their DH public key with a '0' */ m.pk[m.pklen]=0; @@ -770,8 +807,6 @@ static bool_t process_msg4(struct site *st, struct buffer_if *msg4, const struct comm_addr *src) { struct msg m; - uint8_t *hash; - void *hst; cstring_t err; if (!unpick_msg(st,LABEL_MSG4,msg4,&m)) return False; @@ -780,19 +815,8 @@ static bool_t process_msg4(struct site *st, struct buffer_if *msg4, return False; } - /* Check signature and store g^x mod m */ - hash=safe_malloc(st->hash->len, "process_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)) { - slog(st,LOG_SEC,"msg4 signature failed check!"); - free(hash); + if (!process_msg3_msg4(st,&m)) return False; - } - free(hash); /* Terminate their DH public key with a '0' */ m.pk[m.pklen]=0; @@ -987,7 +1011,7 @@ static bool_t decrypt_msg0(struct site *st, struct buffer_if *msg0, 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"); + 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; @@ -1017,7 +1041,7 @@ static bool_t process_msg0(struct site *st, struct buffer_if *msg0, transport_data_msgok(st,src); /* See whether we should start negotiating a new key */ if (st->now > st->renegotiate_key_time) - initiate_key_setup(st,"incoming packet in renegotiation window"); + initiate_key_setup(st,"incoming packet in renegotiation window",0); return True; default: slog(st,LOG_SEC,"incoming encrypted message of type %08x " @@ -1098,7 +1122,7 @@ static void site_resolve_callback(void *sst, struct in_addr *address) slog(st,LOG_ERROR,"resolution of %s failed",st->address); ca_use=0; } - if (transport_compute_setupinit_peers(st,ca_use)) { + if (transport_compute_setupinit_peers(st,ca_use,0)) { enter_new_state(st,SITE_SENTMSG1); } else { /* Can't figure out who to try to to talk to */ @@ -1107,14 +1131,15 @@ static void site_resolve_callback(void *sst, struct in_addr *address) } } -static bool_t initiate_key_setup(struct site *st, cstring_t reason) +static bool_t initiate_key_setup(struct site *st, cstring_t reason, + const struct comm_addr *prod_hint) { if (st->state!=SITE_RUN) return False; slog(st,LOG_SETUP_INIT,"initiating key exchange (%s)",reason); if (st->address) { slog(st,LOG_SETUP_INIT,"resolving peer address"); return enter_state_resolve(st); - } else if (transport_compute_setupinit_peers(st,0)) { + } else if (transport_compute_setupinit_peers(st,0,prod_hint)) { return enter_new_state(st,SITE_SENTMSG1); } slog(st,LOG_SETUP_INIT,"key exchange failed: no address for peer"); @@ -1141,7 +1166,15 @@ static void activate_new_key(struct site *st) transport_peers_copy(st,&st->peers,&st->setup_peers); st->current.remote_session_id=st->setup_session_id; - slog(st,LOG_ACTIVATE_KEY,"new key activated"); + /* Compute the inter-site MTU. This is min( our_mtu, their_mtu ). + * But their mtu be unspecified, in which case we just use ours. */ + uint32_t intersite_mtu= + MIN(st->mtu_target, st->remote_adv_mtu ?: ~(uint32_t)0); + st->netlink->set_mtu(st->netlink->st,intersite_mtu); + + slog(st,LOG_ACTIVATE_KEY,"new key activated" + " (mtu ours=%"PRId32" theirs=%"PRId32" intersite=%"PRId32")", + st->mtu_target, st->remote_adv_mtu, intersite_mtu); enter_state_run(st); } @@ -1326,6 +1359,30 @@ static void enter_state_wait(struct site *st) /* XXX Erase keys etc. */ } +static void generate_prod(struct site *st, struct buffer_if *buf) +{ + buffer_init(buf,0); + buf_append_uint32(buf,0); + buf_append_uint32(buf,0); + buf_append_uint32(buf,LABEL_PROD); + buf_append_string(buf,st->localname); + buf_append_string(buf,st->remotename); +} + +static void generate_send_prod(struct site *st, + const struct comm_addr *source) +{ + if (!st->allow_send_prod) return; /* too soon */ + if (!(st->state==SITE_RUN || st->state==SITE_RESOLVE || + st->state==SITE_WAIT)) return; /* we'd ignore peer's MSG1 */ + + 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); +} + static inline void site_settimeout(uint64_t timeout, int *timeout_io) { if (timeout) { @@ -1398,6 +1455,8 @@ static void site_outgoing(void *sst, struct buffer_if *buf) return; } + st->allow_send_prod=1; + /* In all other states we consider delivering the packet if we have a valid key and a valid address to send it to. */ if (current_valid(st) && transport_peers_valid(&st->peers)) { @@ -1419,7 +1478,7 @@ static void site_outgoing(void *sst, struct buffer_if *buf) slog(st,LOG_DROP,"discarding outgoing packet of size %d",buf->size); BUF_FREE(buf); - initiate_key_setup(st,"outgoing packet"); + initiate_key_setup(st,"outgoing packet",0); } static bool_t named_for_us(struct site *st, const struct buffer_if *buf_in, @@ -1435,7 +1494,10 @@ static bool_t named_for_us(struct site *st, const struct buffer_if *buf_in, } /* This function is called by the communication device to deliver - packets from our peers. */ + packets from our peers. + It should return True if the packet is recognised as being for + this current site instance (and should therefore not be processed + by other sites), even if the packet was otherwise ignored. */ static bool_t site_incoming(void *sst, struct buffer_if *buf, const struct comm_addr *source) { @@ -1492,6 +1554,20 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, BUF_FREE(buf); return True; } + if (msgtype==LABEL_PROD) { + if (!named_for_us(st,buf,msgtype,&named_msg)) + return False; + dump_packet(st,buf,source,True); + if (st->state!=SITE_RUN) { + slog(st,LOG_DROP,"ignoring PROD when not in state RUN"); + } else if (current_valid(st)) { + slog(st,LOG_DROP,"ignoring PROD when we think we have a key"); + } else { + initiate_key_setup(st,"peer sent PROD packet",source); + } + BUF_FREE(buf); + return True; + } if (dest==st->index) { /* Explicitly addressed to us */ if (msgtype!=LABEL_MSG0) dump_packet(st,buf,source,True); @@ -1500,7 +1576,9 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, /* If the source is our current peer then initiate a key setup, because our peer's forgotten the key */ if (get_uint32(buf->start+4)==st->current.remote_session_id) { - initiate_key_setup(st,"received a NAK"); + bool_t initiated; + initiated = initiate_key_setup(st,"received a NAK",0); + if (!initiated) generate_send_prod(st,source); } else { slog(st,LOG_SEC,"bad incoming NAK"); } @@ -1716,6 +1794,7 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, 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->mtu_target= dict_read_number(dict,"mtu-target",False,"site",loc,0); st->mobile_peer_expiry= dict_read_number( dict,"mobile-peer-expiry",False,"site",loc,DEFAULT_MOBILE_PEER_EXPIRY); @@ -1742,6 +1821,8 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, st->log_events=string_list_to_word(dict_lookup(dict,"log-events"), log_event_table,"site"); + st->allow_send_prod=0; + st->tunname=safe_malloc(strlen(st->localname)+strlen(st->remotename)+5, "site_apply"); sprintf(st->tunname,"%s<->%s",st->localname,st->remotename); @@ -1753,7 +1834,7 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, buffer_new(&st->buffer,SETUP_BUFFER_LEN); - buffer_new(&st->scratch,0); + buffer_new(&st->scratch,SETUP_BUFFER_LEN); BUF_ALLOC(&st->scratch,"site:scratch"); /* We are interested in poll(), but only for timeouts. We don't have @@ -1782,7 +1863,10 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, } /* We need to register the remote networks with the netlink device */ - st->netlink->reg(st->netlink->st, site_outgoing, st); + uint32_t netlink_mtu; /* local virtual interface mtu */ + st->netlink->reg(st->netlink->st, site_outgoing, st, &netlink_mtu); + if (!st->mtu_target) + st->mtu_target=netlink_mtu; for (i=0; incomms; i++) st->comms[i]->request_notify(st->comms[i]->st, st, site_incoming); @@ -1889,23 +1973,30 @@ static void transport_record_peer(struct site *st, transport_peers *peers, } static bool_t transport_compute_setupinit_peers(struct site *st, - const struct comm_addr *configured_addr /* 0 if none or not found */) { + const struct comm_addr *configured_addr /* 0 if none or not found */, + const struct comm_addr *prod_hint_addr /* 0 if none */) { - if (!configured_addr && !transport_peers_valid(&st->peers)) + if (!configured_addr && !prod_hint_addr && + !transport_peers_valid(&st->peers)) return False; slog(st,LOG_SETUP_INIT, - (!configured_addr ? "using only %d old peer address(es)" - : "using configured address, and/or perhaps %d old peer address(es)"), + "using:%s%s %d old peer address(es)", + configured_addr ? " configured address;" : "", + prod_hint_addr ? " PROD hint address;" : "", st->peers.npeers); /* Non-mobile peers havve st->peers.npeers==0 or ==1, since they * have transport_peers_max==1. The effect is that this code * always uses the configured address if supplied, or otherwise - * the existing data peer if one exists; this is as desired. */ + * the address of the incoming PROD, or the existing data peer if + * one exists; this is as desired. */ transport_peers_copy(st,&st->setup_peers,&st->peers); + if (prod_hint_addr) + transport_record_peer(st,&st->setup_peers,prod_hint_addr,"prod"); + if (configured_addr) transport_record_peer(st,&st->setup_peers,configured_addr,"setupinit");