chiark / gitweb /
privcache: New closure for signature key handling
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 29 Sep 2019 13:21:19 +0000 (14:21 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Mon, 30 Dec 2019 13:15:49 +0000 (13:15 +0000)
This will be used by site.c, when it needs to load a key to make a
signature as demanded by the peer.

This commit introduces 5 spurious \n's in messages.
It also introduces a few ` = ' style errors.
These will be removed later.

No callers yet.

Signed-off-by: Ian Jackson <ijackson@chiark.greenend.org.uk>
Dir.sd.mk
README
modules.c
privcache.c [new file with mode: 0644]
secnet.h

index 4111476a5b1bc9d0d5c2347b783235252509aa8b..e201646270da918f3c734fc43dd06f3ebd1cea3a 100644 (file)
--- a/Dir.sd.mk
+++ b/Dir.sd.mk
@@ -50,7 +50,7 @@ TARGETS:=secnet
 
 OBJECTS:=secnet.o util.o conffile.yy.o conffile.tab.o conffile.o modules.o \
        resolver.o random.o udp.o site.o transform-cbcmac.o transform-eax.o \
-       comm-common.o polypath.o \
+       comm-common.o polypath.o privcache.o \
        netlink.o rsa.o dh.o serpent.o serpentbe.o \
        md5.o sha512.o tun.o slip.o sha1.o ipaddr.o log.o \
        process.o @LIBOBJS@ \
diff --git a/README b/README
index d9df1ed1dad13fdb5fdcae0c922fe197de52369d..c27375ab7bf563702272c62870e4d51222518331 100644 (file)
--- a/README
+++ b/README
@@ -546,6 +546,22 @@ tun: dict argument
 I recommend you don't specify the 'interface' option unless you're
 doing something that requires the interface name to be constant.
 
+** privcache
+
+Cache of dynamically loaded private keys.
+
+Defines:
+  priv-cache (closure => privcache closure)
+
+priv-cache: dict argument
+  privkeys (string): path prefix for private keys.  Each key is
+    looked for at this path prefix followed by the 10-character 
+    hex key id.
+  privcache-size (integer): optional, maximum number of private
+    keys to retain at once. [5]
+  privkey-max (integer): optional, maximum size of private key
+    file in bytes. [4095]
+
 ** rsa
 
 Defines:
index f63a149252997144320672f667962dbfae75294e..2fc2e0f9199cf6a48b303cdf6ae86b6399f1d35b 100644 (file)
--- a/modules.c
+++ b/modules.c
@@ -37,6 +37,7 @@ void init_builtin_modules(dict_t *dict)
     tun_module(dict);
     sha1_module(dict);
     log_module(dict);
+    privcache_module(dict);
 }
 
 const struct sigscheme_info sigschemes[]={
diff --git a/privcache.c b/privcache.c
new file mode 100644 (file)
index 0000000..0ad6a0e
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * This file is part of secnet.
+ * See README for full list of copyright holders.
+ *
+ * secnet is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * secnet is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * version 3 along with secnet; if not, see
+ * https://www.gnu.org/licenses/gpl.html.
+ */
+
+#include "secnet.h"
+#include "util.h"
+
+#define DEFAULT_SIZE 5
+
+struct ent {
+    struct sigkeyid id;
+    struct sigprivkey_if *sigpriv; /* 0 means none such */
+};
+
+struct privcache {
+    closure_t cl;
+    struct privcache_if ops;
+    int used, alloc;
+    struct pathprefix_template path;
+    struct ent *ents;
+    struct buffer_if databuf;
+    struct hash_if *defhash;
+};
+
+static struct sigprivkey_if *uncached_get(struct privcache *st,
+                          const struct sigkeyid *id, struct log_if *log)
+{
+    bool_t ok=False;
+    FILE *f=0;
+
+    sprintf(st->path.write_here, SIGKEYID_PR_FMT, SIGKEYID_PR_VAL(id));
+
+    f = fopen(st->path.buffer,"rb");
+    if (!f) {
+       if (errno == ENOENT) {
+           slilog(log,M_DEBUG,"private key %s not found\n",
+                  st->path.write_here);
+       } else {
+           slilog(log,M_ERR,"failed to open private key file %s\n",
+                  st->path.buffer);
+       }
+       goto out;
+    }
+
+    setbuf(f,0);
+    buffer_init(&st->databuf,0);
+    ssize_t got=fread(st->databuf.base,1,st->databuf.alloclen,f);
+    if (ferror(f)) {
+       slilog(log,M_ERR,"failed to read private-key file %s\n",
+              st->path.buffer);
+       goto out;
+    }
+    if (!feof(f)) {
+       slilog(log,M_ERR,"private key file %s longer than max %d\n",
+              st->path.buffer, (int)st->databuf.alloclen);
+       goto out;
+    }
+    fclose(f); f=0;
+
+    struct sigprivkey_if *sigpriv=0;
+    for (const struct sigscheme_info *scheme=sigschemes;
+        scheme->name;
+        scheme++) {
+       st->databuf.start=st->databuf.base;
+       st->databuf.size=got;
+       ok=scheme->loadpriv(scheme, &st->databuf, &sigpriv, log);
+       if (ok) {
+           if (sigpriv->sethash) {
+               if (!st->defhash) {
+                   slilog(log,M_ERR,
+ "private key %s requires `hash' config key for privcache to load",
+                          st->path.buffer);
+                   sigpriv->dispose(sigpriv->st);
+                   sigpriv=0;
+                   goto out;
+               }
+               sigpriv->sethash(sigpriv->st,st->defhash);
+           }
+           goto out;
+       }
+    }
+
+    slilog(log,M_ERR,"private key file %s not loaded (not recognised?)\n",
+          st->path.buffer);
+
+  out:
+    if (f) fclose(f);
+    return ok ? sigpriv : 0;
+}
+
+static struct sigprivkey_if *privcache_lookup(void *sst,
+                                             const struct sigkeyid *id,
+                                             struct log_if *log) {
+    struct privcache *st = sst;
+    int was;
+    struct ent result;
+
+    for (was=0; was<st->used; was++) {
+       if (sigkeyid_equal(id, &st->ents[was].id)) {
+           result = st->ents[was];
+           goto found;
+       }
+    }
+
+    if (st->used < st->alloc) {
+       was = st->used;
+       st->used++;
+    } else {
+       was = st->used-1;
+       if (st->ents[was].sigpriv) {
+           st->ents[was].sigpriv->dispose(st->ents[was].sigpriv->st);
+       }
+    }
+
+    COPY_OBJ(result.id, *id);
+    result.sigpriv=uncached_get(st,id,log);
+
+ found:
+    memmove(&st->ents[1], &st->ents[0], sizeof(st->ents[0]) * was);
+    st->ents[0] = result;
+    return result.sigpriv;
+}
+
+static list_t *privcache_apply(closure_t *self, struct cloc loc,
+                              dict_t *context, list_t *args)
+{
+    struct privcache *st;
+    item_t *item;
+    dict_t *dict;
+
+    NEW(st);
+    st->cl.description="privcache";
+    st->cl.type=CL_PRIVCACHE;
+    st->cl.apply=NULL;
+    st->cl.interface=&st->ops;
+    st->ops.st=st;
+    st->ops.lookup=privcache_lookup;
+    st->ents=0;
+    st->path.buffer=0;
+    st->used=st->alloc=0;
+    st->defhash=0;
+
+    item=list_elem(args,0);
+    if (!item || item->type!=t_dict)
+       cfgfatal(loc,"privcache","parameter must be a dictionary\n");
+    
+    dict=item->data.dict;
+
+    st->alloc=dict_read_number(dict,"privcache-size",False,"privcache",loc,
+                              DEFAULT_SIZE);
+    NEW_ARY(st->ents,st->alloc);
+    st->used=0;
+
+    int32_t buflen=dict_read_number(dict,"privkey-max",False,"privcache",loc,
+                                   4095);
+    buffer_new(&st->databuf,buflen+1);
+
+    const char *path=dict_read_string(dict,"privkeys",True,"privcache",loc);
+    int l=strlen(path);
+    NEW_ARY(st->path.buffer,l+KEYIDSZ*2+1);
+    strcpy(st->path.buffer,path);
+    st->path.write_here=st->path.buffer+l;
+
+    st->defhash=find_cl_if(dict,"hash",CL_HASH,False,"site",loc);
+
+    return new_closure(&st->cl);
+}
+
+void privcache_module(dict_t *dict)
+{
+    add_closure(dict,"priv-cache",privcache_apply);
+}
index ba3e45ddf3989eb97c99b71cc0b448e5dc3e6c71..84e732d88fd2c952d5cbf5ddd9069b79246f67a1 100644 (file)
--- a/secnet.h
+++ b/secnet.h
@@ -391,6 +391,7 @@ extern init_module slip_module;
 extern init_module tun_module;
 extern init_module sha1_module;
 extern init_module log_module;
+extern init_module privcache_module;
 
 /***** END of module support *****/
 
@@ -446,6 +447,7 @@ extern const struct sigscheme_info sigschemes[]; /* sentinel has name==0 */
 #define CL_HASH        12
 #define CL_BUFFER      13
 #define CL_NETLINK     14
+#define CL_PRIVCACHE   15
 
 struct buffer_if;
 
@@ -520,6 +522,19 @@ struct sigprivkey_if {
     sig_dispose_fn *dispose;
 };
 
+/* PRIVCACHE interface */
+
+typedef struct sigprivkey_if *privcache_lookup_fn(void *st,
+                                          const struct sigkeyid *id,
+                                          struct log_if*);
+  /* Return is valid only until you return from the current event!
+   * You do not need to call ->sethash. */
+
+struct privcache_if {
+    void *st;
+    privcache_lookup_fn *lookup;
+};
+
 /* COMM interface */
 
 struct comm_addr {