chiark / gitweb /
secnet: loadpriv interface: Return a closure_t too
[secnet.git] / privcache.c
1 /*
2  * This file is part of secnet.
3  * See README for full list of copyright holders.
4  *
5  * secnet is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 3 of the License, or
8  * (at your option) any later version.
9  * 
10  * secnet is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  * 
15  * You should have received a copy of the GNU General Public License
16  * version 3 along with secnet; if not, see
17  * https://www.gnu.org/licenses/gpl.html.
18  */
19
20 #include "secnet.h"
21 #include "util.h"
22
23 #define DEFAULT_SIZE 5
24
25 struct ent {
26     struct sigkeyid id;
27     struct sigprivkey_if *sigpriv; /* 0 means none such */
28 };
29
30 struct privcache {
31     closure_t cl;
32     struct privcache_if ops;
33     int used, alloc;
34     struct pathprefix_template path;
35     struct ent *ents;
36     struct buffer_if databuf;
37     struct hash_if *defhash;
38 };
39
40 static bool_t uncached_load_file(
41                            const struct sigscheme_info *scheme,
42                            const char *path,
43                            struct buffer_if *databuf,
44                            struct hash_if *defhash,
45                            struct sigprivkey_if **sigpriv_r,
46                            closure_t **closure_r,
47                            struct log_if *log);
48
49 static struct sigprivkey_if *uncached_get(struct privcache *st,
50                            const struct sigkeyid *id, struct log_if *log)
51 {
52     sprintf(st->path.write_here, SIGKEYID_PR_FMT, SIGKEYID_PR_VAL(id));
53
54     const char *path=st->path.buffer;
55     const struct sigscheme_info *scheme;
56     for (scheme=sigschemes;
57          scheme->name;
58          scheme++)
59         if (scheme->algid == id->b[GRPIDSZ])
60             goto found;
61
62     slilog(log,M_ERR,"private key file %s not loaded (unknown algid)",
63            path);
64     return 0;
65
66  found:;
67     struct sigprivkey_if *sigpriv;
68     closure_t *cl;
69     bool_t ok=uncached_load_file(scheme,
70                               path,
71                               &st->databuf,
72                               st->defhash,
73                               &sigpriv,
74                               &cl,
75                               log);
76     return ok ? sigpriv : 0;
77 }
78
79 static bool_t uncached_load_file(
80                            const struct sigscheme_info *scheme,
81                            const char *path,
82                            struct buffer_if *databuf,
83                            struct hash_if *defhash,
84                            struct sigprivkey_if **sigpriv_r,
85                            closure_t **closure_r,
86                            struct log_if *log)
87 {
88     bool_t ok=False;
89     FILE *f=0;
90     struct sigprivkey_if *sigpriv=0;
91
92     f=fopen(path,"rb");
93     if (!f) {
94         if (errno == ENOENT) {
95             slilog(log,M_DEBUG,"private key %s not found",
96                    path);
97         } else {
98             slilog(log,M_ERR,"failed to open private key file %s",
99                    path);
100         }
101         goto error_out;
102     }
103
104     setbuf(f,0);
105     buffer_init(databuf,0);
106     ssize_t got=fread(databuf->base,1,databuf->alloclen,f);
107     if (ferror(f)) {
108         slilog(log,M_ERR,"failed to read private-key file %s",
109                path);
110         goto error_out;
111     }
112     if (!feof(f)) {
113         slilog(log,M_ERR,"private key file %s longer than max %d",
114                path, (int)databuf->alloclen);
115         goto error_out;
116     }
117     fclose(f); f=0;
118
119     databuf->start=databuf->base;
120     databuf->size=got;
121     struct cloc loc = { .file=path, .line=0 };
122     ok=scheme->loadpriv(scheme, databuf, &sigpriv, closure_r, log, loc);
123     if (!ok) goto error_out; /* loadpriv will have logged */
124
125     if (sigpriv->sethash) {
126         if (!defhash) {
127             slilog(log,M_ERR,
128  "private key %s requires `hash' config key for privcache to load",
129                    path);
130             goto error_out;
131         }
132         sigpriv->sethash(sigpriv->st,defhash);
133     }
134     *sigpriv_r=sigpriv;
135
136   out:
137     if (f) fclose(f);
138     return ok;
139
140  error_out:
141     if (sigpriv) sigpriv->dispose(sigpriv->st);
142     ok=False;
143     goto out;
144 }
145
146 static struct sigprivkey_if *privcache_lookup(void *sst,
147                                               const struct sigkeyid *id,
148                                               struct log_if *log) {
149     struct privcache *st=sst;
150     int was;
151     struct ent result;
152
153     for (was=0; was<st->used; was++) {
154         if (sigkeyid_equal(id, &st->ents[was].id)) {
155             result = st->ents[was];
156             goto found;
157         }
158     }
159
160     if (st->used < st->alloc) {
161         was=st->used;
162         st->used++;
163     } else {
164         was=st->used-1;
165         if (st->ents[was].sigpriv) {
166             st->ents[was].sigpriv->dispose(st->ents[was].sigpriv->st);
167         }
168     }
169
170     COPY_OBJ(result.id, *id);
171     result.sigpriv=uncached_get(st,id,log);
172
173  found:
174     memmove(&st->ents[1], &st->ents[0], sizeof(st->ents[0]) * was);
175     st->ents[0]=result;
176     return result.sigpriv;
177 }
178
179 static list_t *privcache_apply(closure_t *self, struct cloc loc,
180                                dict_t *context, list_t *args)
181 {
182     struct privcache *st;
183     item_t *item;
184     dict_t *dict;
185
186     NEW(st);
187     st->cl.description="privcache";
188     st->cl.type=CL_PRIVCACHE;
189     st->cl.apply=NULL;
190     st->cl.interface=&st->ops;
191     st->ops.st=st;
192     st->ops.lookup=privcache_lookup;
193     st->ents=0;
194     st->path.buffer=0;
195     st->used=st->alloc=0;
196     st->defhash=0;
197
198     item=list_elem(args,0);
199     if (!item || item->type!=t_dict)
200         cfgfatal(loc,"privcache","parameter must be a dictionary\n");
201     
202     dict=item->data.dict;
203
204     st->alloc=dict_read_number(dict,"privcache-size",False,"privcache",loc,
205                                DEFAULT_SIZE);
206     NEW_ARY(st->ents,st->alloc);
207     st->used=0;
208
209     int32_t buflen=dict_read_number(dict,"privkey-max",False,"privcache",loc,
210                                     4095);
211     buffer_new(&st->databuf,buflen+1);
212
213     const char *path=dict_read_string(dict,"privkeys",True,"privcache",loc);
214     pathprefix_template_init(&st->path,path,KEYIDSZ*2);
215
216     st->defhash=find_cl_if(dict,"hash",CL_HASH,False,"site",loc);
217
218     return new_closure(&st->cl);
219 }
220
221 void privcache_module(dict_t *dict)
222 {
223     add_closure(dict,"priv-cache",privcache_apply);
224 }