X-Git-Url: https://git.distorted.org.uk/~mdw/secnet/blobdiff_plain/5b5f297f9a9d47ee7e9804d5bdaa552f1953c6b6..b24a72ef9d47c9e51f6efe79b51073d690584aa5:/site.c diff --git a/site.c b/site.c index 6c92193..f87328f 100644 --- a/site.c +++ b/site.c @@ -89,6 +89,8 @@ #define SITE_SENTMSG5 7 #define SITE_WAIT 8 +int32_t site_max_start_pad = 4*4; + static cstring_t state_name(uint32_t state) { switch (state) { @@ -204,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); @@ -231,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; @@ -262,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; @@ -278,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; @@ -294,6 +300,8 @@ struct site { }; static void slog(struct site *st, uint32_t event, cstring_t msg, ...) +FORMAT(printf,3,4); +static void slog(struct site *st, uint32_t event, cstring_t msg, ...) { va_list ap; char buf[240]; @@ -318,7 +326,7 @@ static void slog(struct site *st, uint32_t event, cstring_t msg, ...) } vsnprintf(buf,sizeof(buf),msg,ap); - st->log->log(st->log->st,class,"%s: %s",st->tunname,buf); + slilog(st->log,class,"%s: %s",st->tunname,buf); } va_end(ap); } @@ -329,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); @@ -379,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; @@ -392,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; @@ -480,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); @@ -535,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) { @@ -692,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); @@ -730,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; @@ -766,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; @@ -776,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; @@ -824,7 +852,7 @@ static bool_t generate_msg5(struct site *st) BUF_ALLOC(&st->buffer,"site:MSG5"); /* We are going to add four words to the message */ - buffer_init(&st->buffer,st->new_transform->max_start_pad+(4*4)); + buffer_init(&st->buffer,calculate_max_start_pad()); /* Give the netlink code an opportunity to put its own stuff in the message (configuration information, etc.) */ buf_prepend_uint32(&st->buffer,LABEL_MSG5); @@ -871,7 +899,7 @@ static void create_msg6(struct site *st, struct transform_inst_if *transform, BUF_ALLOC(&st->buffer,"site:MSG6"); /* We are going to add four words to the message */ - buffer_init(&st->buffer,transform->max_start_pad+(4*4)); + buffer_init(&st->buffer,calculate_max_start_pad()); /* Give the netlink code an opportunity to put its own stuff in the message (configuration information, etc.) */ buf_prepend_uint32(&st->buffer,LABEL_MSG6); @@ -983,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; @@ -1013,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 " @@ -1094,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 */ @@ -1103,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"); @@ -1137,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); } @@ -1292,7 +1329,7 @@ static bool_t send_msg7(struct site *st, cstring_t reason) if (current_valid(st) && st->buffer.free && transport_peers_valid(&st->peers)) { BUF_ALLOC(&st->buffer,"site:MSG7"); - buffer_init(&st->buffer,st->current.transform->max_start_pad+(4*3)); + buffer_init(&st->buffer,calculate_max_start_pad()); buf_append_uint32(&st->buffer,LABEL_MSG7); buf_append_string(&st->buffer,reason); if (call_transform_forwards(st, st->current.transform, @@ -1322,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) { @@ -1394,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)) { @@ -1415,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, @@ -1431,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) { @@ -1488,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); @@ -1496,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"); } @@ -1712,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); @@ -1738,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); @@ -1749,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 @@ -1768,17 +1853,6 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, st->sharedsecretlen=st->sharedsecretallocd=0; st->sharedsecret=0; - /* We need to compute some properties of our comms and transports */ -#define COMPUTE_WORST(things,pad) \ - int things##_worst_##pad=0; \ - for (i=0; in##things; i++) { \ - int thispad=st->things[i]->pad; \ - if (thispad > things##_worst_##pad) \ - things##_worst_##pad=thispad; \ - } - COMPUTE_WORST(comms,min_start_pad) - COMPUTE_WORST(transforms,max_start_pad) - for (i=0; intransforms; i++) { struct transform_if *ti=st->transforms[i]; uint32_t capbit = 1UL << ti->capab_transformnum; @@ -1789,9 +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, - transforms_worst_max_start_pad+(4*4)+ - comms_worst_min_start_pad); + 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); @@ -1898,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)"), - st->peers); + "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");