chiark / gitweb /
site: Be able to use multiple private keys
[secnet.git] / site.c
diff --git a/site.c b/site.c
index 37968896ccb50f1d07370a7ff582d1697a158327..58db825992249ee07d03dff9ea008430e37d0545 100644 (file)
--- a/site.c
+++ b/site.c
@@ -142,6 +142,7 @@ static cstring_t state_name(uint32_t state)
 #define LOG_DUMP          0x00000100
 #define LOG_ERROR         0x00000400
 #define LOG_PEER_ADDRS    0x00000800
+#define LOG_SIGKEYS       0x00001000
 
 static struct flagstr log_event_table[]={
     { "unexpected", LOG_UNEXPECTED },
@@ -155,8 +156,9 @@ static struct flagstr log_event_table[]={
     { "dump-packets", LOG_DUMP },
     { "errors", LOG_ERROR },
     { "peer-addrs", LOG_PEER_ADDRS },
+    { "sigkeys", LOG_SIGKEYS },
     { "default", LOG_SETUP_INIT|LOG_SETUP_TIMEOUT|
-      LOG_ACTIVATE_KEY|LOG_TIMEOUT_KEY|LOG_SEC|LOG_ERROR },
+      LOG_ACTIVATE_KEY|LOG_TIMEOUT_KEY|LOG_SEC|LOG_ERROR|LOG_SIGKEYS },
     { "all", 0xffffffff },
     { NULL, 0 }
 };
@@ -314,7 +316,8 @@ struct site {
     struct resolver_if *resolver;
     struct log_if *log;
     struct random_if *random;
-    struct sigprivkey_if *privkey;
+    struct privcache_if *privkeys;
+    struct sigprivkey_if *privkey_fixed;
     struct sigpubkey_if *pubkey;
     struct transform_if **transforms;
     int ntransforms;
@@ -392,6 +395,7 @@ static uint32_t event_log_priority(struct site *st, uint32_t event)
     case LOG_DUMP:          return M_DEBUG;
     case LOG_ERROR:         return M_ERR;
     case LOG_PEER_ADDRS:    return M_DEBUG;
+    case LOG_SIGKEYS:       return M_INFO;
     default:                return M_ERR;
     }
 }
@@ -517,6 +521,13 @@ 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_msg23(uint32_t type)
+{
+    switch (type) {
+       case LABEL_MSG2: case CASES_MSG3_KNOWN: return True;
+       default: return False;
+    }
+}
 static _Bool type_is_msg34(uint32_t type)
 {
     switch (type) {
@@ -546,8 +557,12 @@ struct msg {
     char *pk;
     int32_t hashlen;
     struct alg_msg_data sig;
+    int n_pubkeys_accepted_nom; /* may be > MAX_SIG_KEYS ! */
+    const struct sigkeyid *pubkeys_accepted[MAX_SIG_KEYS];
 };
 
+static const struct sigkeyid keyid_zero;
+
 static int32_t wait_timeout(struct site *st) {
     int32_t t = st->wait_timeout_mean;
     int8_t factor;
@@ -629,6 +644,7 @@ static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what,
 {
     string_t dhpub;
     unsigned minor;
+    int ki;
 
     st->retries=st->setup_retries;
     BUF_ALLOC(&st->buffer,what);
@@ -647,6 +663,52 @@ static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what,
     if (type_is_msg34(type)) {
        buf_append_uint16(&st->buffer,st->mtu_target);
     }
+    if (type_is_msg23(type)) {
+       /* The code to advertise a public key acceptance list will
+        * come in a moment.  But right now we must add the byte
+        * indicating which key advertised by our peer we used, which
+        * comes after the public key acceptance list.  So for now,
+        * explicitly advertise a list with just 0000000000. */
+       buf_append_uint8(&st->buffer,1);
+       BUF_ADD_OBJ(append,&st->buffer,keyid_zero);
+    }
+    struct sigprivkey_if *privkey=0;
+    if (type_is_msg34(type)) {
+       assert(prompt->n_pubkeys_accepted_nom>0);
+       for (ki=0;
+            ki<prompt->n_pubkeys_accepted_nom && ki<MAX_SIG_KEYS;
+            ki++) {
+           const struct sigkeyid *kid=prompt->pubkeys_accepted[ki];
+           if (st->privkeys) {
+               privkey=st->privkeys->lookup(st->privkeys->st,kid,st->log);
+               if (privkey) goto privkey_found;
+           } else {
+               if (sigkeyid_equal(&keyid_zero,kid)) {
+                   privkey=st->privkey_fixed;
+                   goto privkey_found;
+               }
+           }
+       }
+       uint32_t class = slog_start(st,LOG_ERROR);
+       if (class) {
+           slilog_part(st->log,class,"no suitable private key, peer wanted");
+           for (ki=0;
+                ki<prompt->n_pubkeys_accepted_nom && ki<MAX_SIG_KEYS;
+                ki++) {
+               slilog_part(st->log,class, " " SIGKEYID_PR_FMT,
+                           SIGKEYID_PR_VAL(prompt->pubkeys_accepted[ki]));
+           }
+           if (prompt->n_pubkeys_accepted_nom > MAX_SIG_KEYS)
+               slilog_part(st->log,class," +%d",
+                           prompt->n_pubkeys_accepted_nom - MAX_SIG_KEYS);
+           slilog_part(st->log,class,"\n");
+       }
+       return False;
+
+    privkey_found:
+       buf_append_uint8(&st->buffer,ki);
+    }
+
     append_string_xinfo_done(&st->buffer,&xia);
 
     buf_append_string(&st->buffer,st->remotename);
@@ -667,10 +729,10 @@ static bool_t generate_msg(struct site *st, uint32_t type, cstring_t what,
     buf_append_string(&st->buffer,dhpub);
     free(dhpub);
 
-    bool_t ok=st->privkey->sign(st->privkey->st,
-                               st->buffer.start,
-                               st->buffer.size,
-                               &st->buffer);
+    bool_t ok=privkey->sign(privkey->st,
+                           st->buffer.start,
+                           st->buffer.size,
+                           &st->buffer);
     if (!ok) goto fail;
     return True;
 
@@ -699,6 +761,7 @@ static bool_t unpick_msg(struct site *st, uint32_t type,
 {
     unsigned minor;
 
+    m->n_pubkeys_accepted_nom=-1;
     m->capab_transformnum=-1;
     m->hashstart=msg->start;
     CHECK_AVAIL(msg,4);
@@ -717,6 +780,18 @@ static bool_t unpick_msg(struct site *st, uint32_t type,
        CHECK_AVAIL(&m->remote.extrainfo,2);
        m->remote_mtu=buf_unprepend_uint16(&m->remote.extrainfo);
     }
+    if (type_is_msg23(type) && m->remote.extrainfo.size) {
+       m->n_pubkeys_accepted_nom = buf_unprepend_uint8(&m->remote.extrainfo);
+       if (!m->n_pubkeys_accepted_nom) return False;
+       for (int ki_nom=0; ki_nom<m->n_pubkeys_accepted_nom; ki_nom++) {
+           CHECK_AVAIL(&m->remote.extrainfo,KEYIDSZ);
+           struct sigkeyid *kid = buf_unprepend(&m->remote.extrainfo,KEYIDSZ);
+           if (ki_nom<MAX_SIG_KEYS) m->pubkeys_accepted[ki_nom] = kid;
+       }
+    } else {
+       m->n_pubkeys_accepted_nom = 1;
+       m->pubkeys_accepted[0] = &keyid_zero;
+    }
     if (!unpick_name(msg,&m->local)) return False;
     if (type==LABEL_PROD) {
        CHECK_EMPTY(msg);
@@ -752,8 +827,9 @@ static bool_t unpick_msg(struct site *st, uint32_t type,
     CHECK_AVAIL(msg,m->pklen);
     m->pk=buf_unprepend(msg,m->pklen);
     m->hashlen=msg->start-m->hashstart;
+    struct sigpubkey_if *pubkey=st->pubkey;
 
-    if (!st->pubkey->unpick(st->pubkey->st,msg,&m->sig)) {
+    if (!pubkey->unpick(pubkey->st,msg,&m->sig)) {
        return False;
     }
 
@@ -900,10 +976,12 @@ static bool_t generate_msg3(struct site *st, const struct msg *prompt)
 
 static bool_t process_msg3_msg4(struct site *st, struct msg *m)
 {
+    struct sigpubkey_if *pubkey=st->pubkey;
+
     /* Check signature and store g^x mod m */
-    if (!st->pubkey->check(st->pubkey->st,
-                          m->hashstart,m->hashlen,
-                          &m->sig)) {
+    if (!pubkey->check(pubkey->st,
+                      m->hashstart,m->hashlen,
+                      &m->sig)) {
        slog(st,LOG_SEC,"msg3/msg4 signature failed check!");
        return False;
     }
@@ -2126,6 +2204,17 @@ 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)
 {
@@ -2218,7 +2307,15 @@ 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);
 
-    st->privkey=find_cl_if(dict,"local-key",CL_SIGPRIVKEY,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);
+    }
+
     st->addresses=dict_read_string_array(dict,"address",False,"site",loc,0);
     if (st->addresses)
        st->remoteport=dict_read_number(dict,"port",True,"site",loc,0);
@@ -2229,11 +2326,7 @@ static list_t *site_apply(closure_t *self, struct cloc loc, dict_t *context,
 
     st->dh=find_cl_if(dict,"dh",CL_DH,True,"site",loc);
 
-    if (st->privkey->sethash || st->pubkey->sethash) {
-       struct hash_if *hash=find_cl_if(dict,"hash",CL_HASH,True,"site",loc);
-       if (st->privkey->sethash) st->privkey->sethash(st->privkey->st,hash);
-       if (st->pubkey->sethash) st->pubkey->sethash(st->pubkey->st,hash);
-    }
+    SETUP_SETHASH(st->pubkey);
 
 #define DEFAULT(D) (st->peer_mobile || st->local_mobile        \
                     ? DEFAULT_MOBILE_##D : DEFAULT_##D)