X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=secnet.git;a=blobdiff_plain;f=site.c;h=324a2061682e69188605c04650d6e012d4993624;hp=20f9507cee7782c2f801dba295e9a82421855e88;hb=34d3bf4cdcb2d2938c3b92573f66815e4d9392ac;hpb=779837e133a4740798015a1d7de6fbf6b4b1720f diff --git a/site.c b/site.c index 20f9507..324a206 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); @@ -262,6 +265,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; @@ -331,7 +335,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); @@ -542,6 +547,10 @@ static bool_t unpick_msg(struct site *st, uint32_t type, m->remote_capabilities=buf_unprepend_uint32(&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) { @@ -826,7 +835,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); @@ -873,7 +882,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); @@ -985,7 +994,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; @@ -1015,7 +1024,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 " @@ -1096,7 +1105,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 */ @@ -1105,14 +1114,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"); @@ -1294,7 +1304,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, @@ -1324,6 +1334,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) { @@ -1396,6 +1430,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)) { @@ -1417,7 +1453,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, @@ -1433,7 +1469,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) { @@ -1490,6 +1529,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); @@ -1498,7 +1551,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"); } @@ -1740,6 +1795,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); @@ -1751,7 +1808,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 @@ -1770,17 +1827,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; @@ -1791,9 +1837,7 @@ 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); + st->netlink->reg(st->netlink->st, site_outgoing, st); for (i=0; incomms; i++) st->comms[i]->request_notify(st->comms[i]->st, st, site_incoming); @@ -1900,23 +1944,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");