chiark / gitweb /
test-common: Use $(wildcard ) rather than $(shell echo )
[secnet.git] / site.c
diff --git a/site.c b/site.c
index 002b7dd48b84b8fb90347f8cbd9d98e8c94908e7..5d2f519e482b8bbcc0fc299a33f3d40a10569a23 100644 (file)
--- 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 */
@@ -707,6 +711,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 +900,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 +1090,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 +2319,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 +2349,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 */
@@ -2344,7 +2425,7 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
     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 +2439,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);
        }