X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?a=blobdiff_plain;f=site.c;h=df083f40948f1643863a49617b31257a64293a59;hb=7e311a0bf071a2fb1fc6857a9e9828b8c8f7da54;hp=002b7dd48b84b8fb90347f8cbd9d98e8c94908e7;hpb=db10dcc854450232187cec829c60dac619344d50;p=secnet.git diff --git a/site.c b/site.c index 002b7dd..df083f4 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: @@ -316,6 +318,7 @@ struct site { int ncomms; struct resolver_if *resolver; struct log_if *log; + struct hash_if *defhash; struct random_if *random; struct privcache_if *privkeys; struct sigprivkey_if *privkey_fixed; @@ -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,st->defhash); + 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; @@ -2240,14 +2316,15 @@ static void site_childpersist_clearkeys(void *sst, uint32_t newphase) } static void setup_sethash(struct site *st, dict_t *dict, - struct hash_if **hash, struct cloc loc, + 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); + if (!st->defhash) + cfgfatal(loc,"site","other settings imply `hash' key is needed"); + sethash(sigkey_st,st->defhash); } #define SETUP_SETHASH(k) do{ \ if ((k)->sethash) \ - setup_sethash(st,dict, &hash,loc, (k)->sethash,(k)->st); \ + setup_sethash(st,dict,loc, (k)->sethash,(k)->st); \ }while(0) static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, @@ -2269,6 +2346,7 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context, st->ops.control=site_control; 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 +2355,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,10 +2429,9 @@ 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->defhash=find_cl_if(dict,"hash",CL_HASH,True,"site",loc); st->privkeys=find_cl_if(dict,"key-cache",CL_PRIVCACHE,False,"site",loc); if (!st->privkeys) { @@ -2358,8 +2445,11 @@ 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); + &st->scratch,st->log,M_ERR, + st->defhash); if (fixed_pubkey) { fixed_pubkey->dispose(fixed_pubkey->st); } @@ -2418,16 +2508,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 */