X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ian/git?p=secnet.git;a=blobdiff_plain;f=site.c;h=191c36463da7cf7bd1d6186a1a1f51ebb0484877;hp=002b7dd48b84b8fb90347f8cbd9d98e8c94908e7;hb=HEAD;hpb=db10dcc854450232187cec829c60dac619344d50 diff --git a/site.c b/site.c index 002b7dd..191c364 100644 --- a/site.c +++ b/site.c @@ -61,6 +61,8 @@ #define DEFAULT_MOBILE_PEER_EXPIRY (2*60) /* [s] */ +#define PEERKEYS_SUFFIX_MAXLEN (sizeof("~incoming")-1) + /* Each site can be in one of several possible states. */ /* States: @@ -255,6 +257,7 @@ typedef struct { } transport_peers; /* Basic operations on transport peer address sets */ +static void transport_peers_init(struct site *st, transport_peers *peers); static void transport_peers_clear(struct site *st, transport_peers *peers); static int transport_peers_valid(transport_peers *peers); static void transport_peers_copy(struct site *st, transport_peers *dst, @@ -348,6 +351,7 @@ struct site { int resolving_n_results_stored; struct comm_addr resolving_results[MAX_PEER_ADDRS]; const char *peerkeys_path; + struct pathprefix_template peerkeys_tmpl; struct peer_keyset *peerkeys_current, *peerkeys_kex; /* The currently established session */ @@ -659,10 +663,7 @@ 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 & st->early_capabilities) || - (type != LABEL_MSG1)) { - buf_append_uint32(&st->buffer,st->local_capabilities); - } + buf_append_uint32(&st->buffer,st->local_capabilities); if (type_is_msg34(type)) { buf_append_uint16(&st->buffer,st->mtu_target); } @@ -707,6 +708,8 @@ static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what, return False; privkey_found: + slog(st,LOG_SIGKEYS,"using private key #%d " SIGKEYID_PR_FMT, + ki, SIGKEYID_PR_VAL(prompt->pubkeys_accepted[ki])); buf_append_uint8(&st->buffer,ki); } @@ -894,9 +897,77 @@ static bool_t check_msg(struct site *st, uint32_t type, struct msg *m, return False; } +static void peerkeys_maybe_incorporate(struct site *st, const char *file, + const char *whatmore, + int logcl_enoent) +{ + struct peer_keyset *atsuffix= + keyset_load(file,&st->scratch,st->log,logcl_enoent); + if (!atsuffix) return; + + if (st->peerkeys_current && + serial_cmp(atsuffix->serial,st->peerkeys_current->serial) <= 0) { + slog(st,LOG_SIGKEYS,"keys from %s%s are older, discarding", + file,whatmore); + keyset_dispose(&atsuffix); + int r=unlink(file); + if (r) slog(st,LOG_ERROR,"failed to remove old key update %s: %s\n", + st->peerkeys_tmpl.buffer,strerror(errno)); + return; + } else { + slog(st,LOG_SIGKEYS,"keys from %s%s are newer, installing", + file,whatmore); + keyset_dispose(&st->peerkeys_current); + st->peerkeys_current=atsuffix; + int r=rename(file,st->peerkeys_path); + if (r) slog(st,LOG_ERROR,"failed to install key update %s as %s: %s\n", + st->peerkeys_tmpl.buffer,st->peerkeys_path, + strerror(errno)); + } +} + +static void peerkeys_check_for_update(struct site *st) +{ + if (!st->peerkeys_path) return; + + pathprefix_template_setsuffix(&st->peerkeys_tmpl,"~proc"); + peerkeys_maybe_incorporate(st,st->peerkeys_tmpl.buffer, + " (found old update)", + M_DEBUG); + + pathprefix_template_setsuffix(&st->peerkeys_tmpl,"~update"); + const char *inputp=st->peerkeys_tmpl.buffer; + if (access(inputp,R_OK)) { + if (errno!=ENOENT) + slog(st,LOG_ERROR,"cannot access peer key update file %s\n", + inputp); + return; + } + + buffer_init(&st->scratch,0); + BUF_ADD_BYTES(append,&st->scratch, + st->peerkeys_tmpl.buffer, + strlen(st->peerkeys_tmpl.buffer)+1); + inputp=st->scratch.start; + + pathprefix_template_setsuffix(&st->peerkeys_tmpl,"~proc"); + const char *oursp=st->peerkeys_tmpl.buffer; + + int r=rename(inputp,oursp); + if (r) { + slog(st,LOG_ERROR,"failed to claim key update file %s as %s: %s", + inputp,oursp,strerror(errno)); + return; + } + + peerkeys_maybe_incorporate(st,oursp," (update)",M_ERR); +} + + static bool_t kex_init(struct site *st) { keyset_dispose(&st->peerkeys_kex); + peerkeys_check_for_update(st); if (!st->peerkeys_current) { slog(st,LOG_SETUP_INIT,"no peer public keys, abandoning key setup"); return False; @@ -1016,9 +1087,14 @@ static bool_t process_msg3_msg4(struct site *st, struct msg *m) if (!pubkey->check(pubkey->st, m->hashstart,m->hashlen, &m->sig)) { - slog(st,LOG_SEC,"msg3/msg4 signature failed check!"); + slog(st,LOG_SEC,"msg3/msg4 signature failed check!" + " (key #%d " SIGKEYID_PR_FMT ")", + ki, SIGKEYID_PR_VAL(&st->peerkeys_kex->keys[ki].id)); return False; } + slog(st,LOG_SIGKEYS,"verified peer signature with key #%d " + SIGKEYID_PR_FMT, ki, + SIGKEYID_PR_VAL(&st->peerkeys_kex->keys[ki].id)); st->remote_adv_mtu=m->remote_mtu; @@ -1636,8 +1712,9 @@ static void set_link_quality(struct site *st) static void enter_state_run(struct site *st) { - slog(st,LOG_STATE,"entering state RUN%s", - current_valid(st) ? " (keyed)" : " (unkeyed)"); + if (st->state!=SITE_STOP) + slog(st,LOG_STATE,"entering state RUN%s", + current_valid(st) ? " (keyed)" : " (unkeyed)"); st->state=SITE_RUN; st->timeout=0; @@ -2207,14 +2284,13 @@ static bool_t site_incoming(void *sst, struct buffer_if *buf, return False; } -static void site_control(void *vst, bool_t run) +static void site_startup(void *vst) { struct site *st=vst; - if (run) enter_state_run(st); - else enter_state_stop(st); + enter_state_run(st); } -static void site_phase_hook(void *sst, uint32_t newphase) +static void site_phase_shutdown_hook(void *sst, uint32_t newphase) { struct site *st=sst; @@ -2222,6 +2298,13 @@ static void site_phase_hook(void *sst, uint32_t newphase) send_msg7(st,"shutting down"); } +static void site_phase_run_hook(void *sst, uint32_t newphase) +{ + struct site *st=sst; + slog(st,LOG_STATE,"entering phase RUN in state %s", + state_name(st->state)); +} + static void site_childpersist_clearkeys(void *sst, uint32_t newphase) { struct site *st=sst; @@ -2239,17 +2322,6 @@ static void site_childpersist_clearkeys(void *sst, uint32_t newphase) crypto operations, but that's a task for another day. */ } -static void setup_sethash(struct site *st, dict_t *dict, - struct hash_if **hash, struct cloc loc, - sig_sethash_fn *sethash, void *sigkey_st) { - if (!*hash) *hash=find_cl_if(dict,"hash",CL_HASH,True,"site",loc); - sethash(sigkey_st,*hash); -} -#define SETUP_SETHASH(k) do{ \ - if ((k)->sethash) \ - setup_sethash(st,dict, &hash,loc, (k)->sethash,(k)->st); \ -}while(0) - static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, list_t *args) { @@ -2266,9 +2338,10 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, st->cl.apply=NULL; st->cl.interface=&st->ops; st->ops.st=st; - st->ops.control=site_control; + st->ops.startup=site_startup; st->ops.status=site_status; st->peerkeys_path=0; + st->peerkeys_tmpl.buffer=0; st->peerkeys_current=st->peerkeys_kex=0; /* First parameter must be a dict */ @@ -2277,9 +2350,19 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, cfgfatal(loc,"site","parameter must be a dictionary\n"); dict=item->data.dict; + st->log=find_cl_if(dict,"log",CL_LOG,True,"site",loc); + st->log_events=string_list_to_word(dict_lookup(dict,"log-events"), + log_event_table,"site"); + st->localname=dict_read_string(dict, "local-name", True, "site", loc); st->remotename=dict_read_string(dict, "name", True, "site", loc); + st->tunname=safe_malloc(strlen(st->localname)+strlen(st->remotename)+5, + "site_apply"); + sprintf(st->tunname,"%s<->%s",st->localname,st->remotename); + + /* Now slog is working */ + st->keepalive=dict_read_bool(dict,"keepalive",False,"site",loc,False); st->peer_mobile=dict_read_bool(dict,"mobile",False,"site",loc,False); @@ -2341,16 +2424,12 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, } st->resolver=find_cl_if(dict,"resolver",CL_RESOLVER,True,"site",loc); - st->log=find_cl_if(dict,"log",CL_LOG,True,"site",loc); st->random=find_cl_if(dict,"random",CL_RANDOMSRC,True,"site",loc); - struct hash_if *hash=0; - st->privkeys=find_cl_if(dict,"key-cache",CL_PRIVCACHE,False,"site",loc); if (!st->privkeys) { st->privkey_fixed= find_cl_if(dict,"local-key",CL_SIGPRIVKEY,True,"site",loc); - SETUP_SETHASH(st->privkey_fixed); } struct sigpubkey_if *fixed_pubkey @@ -2358,6 +2437,8 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, st->peerkeys_path=dict_read_string(dict,"peer-keys",fixed_pubkey==0, "site",loc); if (st->peerkeys_path) { + pathprefix_template_init(&st->peerkeys_tmpl,st->peerkeys_path, + PEERKEYS_SUFFIX_MAXLEN + 1 /* nul */); st->peerkeys_current=keyset_load(st->peerkeys_path, &st->scratch,st->log,M_ERR); if (fixed_pubkey) { @@ -2365,7 +2446,6 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, } } else { assert(fixed_pubkey); - SETUP_SETHASH(fixed_pubkey); NEW(st->peerkeys_current); st->peerkeys_current->refcount=1; st->peerkeys_current->nkeys=1; @@ -2418,16 +2498,9 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, "renegotiate-time must be less than key-lifetime\n"); } - st->log_events=string_list_to_word(dict_lookup(dict,"log-events"), - log_event_table,"site"); - st->resolving_count=0; 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); - /* 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 */ @@ -2447,8 +2520,8 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, st->chosen_transform=0; st->current.key_timeout=0; st->auxiliary_key.key_timeout=0; - transport_peers_clear(st,&st->peers); - transport_peers_clear(st,&st->setup_peers); + transport_peers_init(st,&st->peers); + transport_peers_init(st,&st->setup_peers); /* XXX mlock these */ st->dhsecret=safe_malloc(st->dh->len,"site:dhsecret"); st->sharedsecretlen=st->sharedsecretallocd=0; @@ -2486,7 +2559,8 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, enter_state_stop(st); - add_hook(PHASE_SHUTDOWN,site_phase_hook,st); + add_hook(PHASE_SHUTDOWN,site_phase_shutdown_hook,st); + add_hook(PHASE_RUN, site_phase_run_hook, st); add_hook(PHASE_CHILDPERSIST,site_childpersist_clearkeys,st); return new_closure(&st->cl); @@ -2534,6 +2608,8 @@ static void transport_peers_debug(struct site *st, transport_peers *dst, static void transport_peers_expire(struct site *st, transport_peers *peers) { /* peers must be sorted first */ + if (st->local_mobile) return; + int previous_peers=peers->npeers; struct timeval oldest; oldest.tv_sec = tv_now->tv_sec - st->mobile_peer_expiry; @@ -2662,9 +2738,14 @@ static void transport_data_msgok(struct site *st, const struct comm_addr *a) { static int transport_peers_valid(transport_peers *peers) { return peers->npeers; } +static void transport_peers_init(struct site *st, transport_peers *peers) { + peers->npeers= 0; +} static void transport_peers_clear(struct site *st, transport_peers *peers) { + bool_t need_debug=!!peers->npeers; peers->npeers= 0; - transport_peers_debug(st,peers,"clear",0,0,0); + if (need_debug) + transport_peers_debug(st,peers,"clear",0,0,0); } static void transport_peers_copy(struct site *st, transport_peers *dst, const transport_peers *src) {