chiark / gitweb /
rsa: Provide dict context argument in load_ctx
[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 #define DEFAULT_MAXPRIV_BYTES 4095
25
26 struct ent {
27     struct sigkeyid id;
28     struct sigprivkey_if *sigpriv; /* 0 means none such */
29 };
30
31 struct privcache {
32     closure_t cl;
33     struct privcache_if ops;
34     int used, alloc;
35     struct pathprefix_template path;
36     struct ent *ents;
37     struct buffer_if databuf;
38     struct hash_if *defhash;
39 };
40
41 static bool_t uncached_load_file(
42                            const struct sigscheme_info *scheme,
43                            const char *path,
44                            struct buffer_if *databuf,
45                            struct hash_if *defhash,
46                            struct sigprivkey_if **sigpriv_r,
47                            closure_t **closure_r,
48                            struct log_if *log);
49
50 static struct sigprivkey_if *uncached_get(struct privcache *st,
51                            const struct sigkeyid *id, struct log_if *log)
52 {
53     sprintf(st->path.write_here, SIGKEYID_PR_FMT, SIGKEYID_PR_VAL(id));
54
55     const char *path=st->path.buffer;
56     const struct sigscheme_info *scheme;
57     for (scheme=sigschemes;
58          scheme->name;
59          scheme++)
60         if (scheme->algid == id->b[GRPIDSZ])
61             goto found;
62
63     slilog(log,M_ERR,"private key file %s not loaded (unknown algid)",
64            path);
65     return 0;
66
67  found:;
68     struct sigprivkey_if *sigpriv;
69     closure_t *cl;
70     bool_t ok=uncached_load_file(scheme,
71                               path,
72                               &st->databuf,
73                               st->defhash,
74                               &sigpriv,
75                               &cl,
76                               log);
77     return ok ? sigpriv : 0;
78 }
79
80 static bool_t uncached_load_file(
81                            const struct sigscheme_info *scheme,
82                            const char *path,
83                            struct buffer_if *databuf,
84                            struct hash_if *defhash,
85                            struct sigprivkey_if **sigpriv_r,
86                            closure_t **closure_r,
87                            struct log_if *log)
88 {
89     bool_t ok=False;
90     FILE *f=0;
91     struct sigprivkey_if *sigpriv=0;
92
93     f=fopen(path,"rb");
94     if (!f) {
95         if (errno == ENOENT) {
96             slilog(log,M_DEBUG,"private key %s not found",
97                    path);
98         } else {
99             slilog(log,M_ERR,"failed to open private key file %s",
100                    path);
101         }
102         goto error_out;
103     }
104
105     setbuf(f,0);
106     buffer_init(databuf,0);
107     ssize_t got=fread(databuf->base,1,databuf->alloclen,f);
108     if (ferror(f)) {
109         slilog(log,M_ERR,"failed to read private-key file %s",
110                path);
111         goto error_out;
112     }
113     if (!feof(f)) {
114         slilog(log,M_ERR,"private key file %s longer than max %d",
115                path, (int)databuf->alloclen);
116         goto error_out;
117     }
118     fclose(f); f=0;
119
120     databuf->start=databuf->base;
121     databuf->size=got;
122     struct cloc loc = { .file=path, .line=0 };
123     ok=scheme->loadpriv(scheme, databuf, &sigpriv, closure_r, log, loc);
124     if (!ok) goto error_out; /* loadpriv will have logged */
125
126     if (sigpriv->sethash) {
127         if (!defhash) {
128             slilog(log,M_ERR,
129  "private key %s requires `hash' config key to load",
130                    path);
131             goto error_out;
132         }
133         sigpriv->sethash(sigpriv->st,defhash);
134     }
135     *sigpriv_r=sigpriv;
136
137   out:
138     if (f) fclose(f);
139     return ok;
140
141  error_out:
142     if (sigpriv) sigpriv->dispose(sigpriv->st);
143     ok=False;
144     goto out;
145 }
146
147 static struct sigprivkey_if *privcache_lookup(void *sst,
148                                               const struct sigkeyid *id,
149                                               struct log_if *log) {
150     struct privcache *st=sst;
151     int was;
152     struct ent result;
153
154     for (was=0; was<st->used; was++) {
155         if (sigkeyid_equal(id, &st->ents[was].id)) {
156             result = st->ents[was];
157             goto found;
158         }
159     }
160
161     if (st->used < st->alloc) {
162         was=st->used;
163         st->used++;
164     } else {
165         was=st->used-1;
166         if (st->ents[was].sigpriv) {
167             st->ents[was].sigpriv->dispose(st->ents[was].sigpriv->st);
168         }
169     }
170
171     COPY_OBJ(result.id, *id);
172     result.sigpriv=uncached_get(st,id,log);
173
174  found:
175     memmove(&st->ents[1], &st->ents[0], sizeof(st->ents[0]) * was);
176     st->ents[0]=result;
177     return result.sigpriv;
178 }
179
180 static list_t *privcache_apply(closure_t *self, struct cloc loc,
181                                dict_t *context, list_t *args)
182 {
183     struct privcache *st;
184     item_t *item;
185     dict_t *dict;
186
187     NEW(st);
188     st->cl.description="privcache";
189     st->cl.type=CL_PRIVCACHE;
190     st->cl.apply=NULL;
191     st->cl.interface=&st->ops;
192     st->ops.st=st;
193     st->ops.lookup=privcache_lookup;
194     st->ents=0;
195     st->path.buffer=0;
196     st->used=st->alloc=0;
197     st->defhash=0;
198
199     item=list_elem(args,0);
200     if (!item || item->type!=t_dict)
201         cfgfatal(loc,"privcache","parameter must be a dictionary\n");
202     
203     dict=item->data.dict;
204
205     st->alloc=dict_read_number(dict,"privcache-size",False,"privcache",loc,
206                                DEFAULT_SIZE);
207     NEW_ARY(st->ents,st->alloc);
208     st->used=0;
209
210     int32_t buflen=dict_read_number(dict,"privkey-max",False,"privcache",loc,
211                                     DEFAULT_MAXPRIV_BYTES);
212     buffer_new(&st->databuf,buflen+1);
213
214     const char *path=dict_read_string(dict,"privkeys",True,"privcache",loc);
215     pathprefix_template_init(&st->path,path,KEYIDSZ*2);
216
217     st->defhash=find_cl_if(dict,"hash",CL_HASH,False,"site",loc);
218
219     return new_closure(&st->cl);
220 }
221
222 static list_t *loadprivate_apply(closure_t *self, struct cloc loc,
223                                dict_t *context, list_t *args)
224 {
225     CL_GET_STR_ARG(0,algname,"algorithm name");
226     CL_GET_STR_ARG(1,path,"private key path");
227
228     const struct sigscheme_info *sch=sigscheme_lookup(algname);
229     if (!sch) cfgfatal(algname_i->loc,"load-private",
230                        "unknown algorithm `%s'",algname);
231
232     struct buffer_if databuf;
233     buffer_new(&databuf,DEFAULT_MAXPRIV_BYTES);
234     BUF_ALLOC(&databuf,"load-private data buf");
235
236     struct hash_if *defhash=
237             find_cl_if(context,"hash",CL_HASH,False,"load-private",loc);
238
239     struct cfgfile_log log;
240     cfgfile_log_init(&log,loc,"load-private");
241
242     struct sigprivkey_if *sigpriv;
243     closure_t *cl;
244     bool_t ok=
245         uncached_load_file(sch,path,&databuf,defhash,&sigpriv,&cl,&log.log);
246     if (!ok) cfgfatal(loc,"load-private","private key loading failed");
247
248     BUF_FREE(&databuf);
249     buffer_destroy(&databuf);
250     return new_closure(cl);
251 }
252
253 void privcache_module(dict_t *dict)
254 {
255     add_closure(dict,"priv-cache",privcache_apply);
256     add_closure(dict,"load-private",loadprivate_apply);
257 }