From: Ian Jackson Date: Sun, 29 Sep 2019 13:21:19 +0000 (+0100) Subject: privcache: New closure for signature key handling X-Git-Tag: v0.6.0~209 X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=secnet.git;a=commitdiff_plain;h=1b8af2f7f86131a5364f2270865895ea597c591e privcache: New closure for signature key handling 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 --- diff --git a/Dir.sd.mk b/Dir.sd.mk index 4111476..e201646 100644 --- 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 d9df1ed..c27375a 100644 --- 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: diff --git a/modules.c b/modules.c index f63a149..2fc2e0f 100644 --- 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 index 0000000..0ad6a0e --- /dev/null +++ b/privcache.c @@ -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; wasused; 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); +} diff --git a/secnet.h b/secnet.h index ba3e45d..84e732d 100644 --- 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 {