]> the.earth.li Git - onak.git/blob - keydb_stacked.c
fdd05b1e4411795d1dd7b346f352ff22bd2fc4db
[onak.git] / keydb_stacked.c
1 /*
2  * keydb_stacked.c - backend that stacks other backends together
3  *
4  * Copyright 2016 Jonathan McDowell <noodles@earth.li>
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Software Foundation, Inc., 51
17  * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #include <stdio.h>
21 #include <string.h>
22
23 #include "cleankey.h"
24 #include "decodekey.h"
25 #include "hash.h"
26 #include "keydb.h"
27 #include "keyid.h"
28 #include "keystructs.h"
29 #include "log.h"
30 #include "mem.h"
31 #include "merge.h"
32 #include "onak-conf.h"
33 #include "openpgp.h"
34 #include "parsekey.h"
35 #include "sendsync.h"
36
37 struct onak_stacked_dbctx {
38         struct ll *backends;
39         bool store_on_fallback;
40 };
41
42 /*
43  * The following functions only apply to the first backend.
44  */
45
46 static bool stacked_starttrans(struct onak_dbctx *dbctx)
47 {
48         struct onak_stacked_dbctx *privctx =
49                         (struct onak_stacked_dbctx *) dbctx->priv;
50         struct onak_dbctx *backend =
51                         (struct onak_dbctx *) privctx->backends->object;
52
53         return backend->starttrans(backend);
54 }
55
56 static void stacked_endtrans(struct onak_dbctx *dbctx)
57 {
58         struct onak_stacked_dbctx *privctx =
59                         (struct onak_stacked_dbctx *) dbctx->priv;
60         struct onak_dbctx *backend =
61                         (struct onak_dbctx *) privctx->backends->object;
62
63         backend->starttrans(backend);
64 }
65
66 static int stacked_store_key(struct onak_dbctx *dbctx,
67                 struct openpgp_publickey *publickey, bool intrans,
68                 bool update)
69 {
70         struct onak_stacked_dbctx *privctx =
71                         (struct onak_stacked_dbctx *) dbctx->priv;
72         struct onak_dbctx *backend =
73                         (struct onak_dbctx *) privctx->backends->object;
74
75         return backend->store_key(backend,
76                         publickey, intrans, update);
77 }
78
79 static int stacked_delete_key(struct onak_dbctx *dbctx, uint64_t keyid,
80                 bool intrans)
81 {
82         struct onak_stacked_dbctx *privctx =
83                         (struct onak_stacked_dbctx *) dbctx->priv;
84         struct onak_dbctx *backend =
85                         (struct onak_dbctx *) privctx->backends->object;
86
87         return backend->delete_key(backend,
88                         keyid, intrans);
89 }
90
91 static int stacked_update_keys(struct onak_dbctx *dbctx,
92                 struct openpgp_publickey **keys, bool sendsync)
93 {
94         struct onak_stacked_dbctx *privctx =
95                         (struct onak_stacked_dbctx *) dbctx->priv;
96         struct onak_dbctx *backend =
97                         (struct onak_dbctx *) privctx->backends->object;
98
99         return backend->update_keys(backend, keys, sendsync);
100 }
101
102 static int stacked_iterate_keys(struct onak_dbctx *dbctx,
103                 void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
104                 void *ctx)
105 {
106         struct onak_stacked_dbctx *privctx =
107                         (struct onak_stacked_dbctx *) dbctx->priv;
108         struct onak_dbctx *backend =
109                         (struct onak_dbctx *) privctx->backends->object;
110
111         return backend->iterate_keys(backend, iterfunc, ctx);
112 }
113
114 static void store_on_fallback(struct onak_stacked_dbctx *privctx,
115                 struct openpgp_publickey *publickey, bool intrans)
116 {
117         struct onak_dbctx *backend =
118                         (struct onak_dbctx *) privctx->backends->object;
119         struct openpgp_publickey *curkey;
120
121         cleankeys(publickey);
122         /*
123          * If we walked the stack at all, store the key in the first
124          * backend if configured to do so. It's not an update as we
125          * know it's not there or we wouldn't have fallen back.
126          */
127         for (curkey = publickey; curkey != NULL; curkey = curkey->next) {
128                 backend->store_key(backend, curkey, intrans, false);
129         }
130 }
131
132 /*
133  * The functions below will walk along the backend stack until they
134  * reach the end or get a successful result.
135  */
136
137 static int stacked_fetch_key_id(struct onak_dbctx *dbctx, uint64_t keyid,
138                 struct openpgp_publickey **publickey, bool intrans)
139 {
140         struct onak_stacked_dbctx *privctx =
141                         (struct onak_stacked_dbctx *) dbctx->priv;
142         struct onak_dbctx *backend;
143         struct ll *cur;
144         int res = 0;
145
146         for (cur = privctx->backends; cur != NULL && res == 0;
147                         cur = cur->next) {
148                 backend = (struct onak_dbctx *) cur->object;
149                 res = backend->fetch_key_id(backend, keyid, publickey,
150                                 intrans);
151         }
152
153         if (privctx->store_on_fallback && cur != privctx->backends) {
154                 store_on_fallback(privctx, *publickey, intrans);
155         }
156
157         return res;
158 }
159
160 static int stacked_fetch_key_fp(struct onak_dbctx *dbctx,
161                 struct openpgp_fingerprint *fingerprint,
162                 struct openpgp_publickey **publickey, bool intrans)
163 {
164         struct onak_stacked_dbctx *privctx =
165                         (struct onak_stacked_dbctx *) dbctx->priv;
166         struct onak_dbctx *backend;
167         struct ll *cur;
168         int res = 0;
169
170         for (cur = privctx->backends; cur != NULL && res == 0;
171                         cur = cur->next) {
172                 backend = (struct onak_dbctx *) cur->object;
173                 res = backend->fetch_key_fp(backend, fingerprint, publickey,
174                                 intrans);
175         }
176
177         if (privctx->store_on_fallback && cur != privctx->backends) {
178                 store_on_fallback(privctx, *publickey, intrans);
179         }
180
181         return res;
182 }
183
184 static int stacked_fetch_key_text(struct onak_dbctx *dbctx,
185                 const char *search,
186                 struct openpgp_publickey **publickey)
187 {
188         struct onak_stacked_dbctx *privctx =
189                         (struct onak_stacked_dbctx *) dbctx->priv;
190         struct onak_dbctx *backend;
191         struct ll *cur;
192         int res = 0;
193
194         for (cur = privctx->backends; cur != NULL && res == 0;
195                         cur = cur->next) {
196                 backend = (struct onak_dbctx *) cur->object;
197                 res = backend->fetch_key_text(backend, search, publickey);
198         }
199
200         if (privctx->store_on_fallback && cur != privctx->backends) {
201                 store_on_fallback(privctx, *publickey, false);
202         }
203
204         return res;
205 }
206
207 static int stacked_fetch_key_skshash(struct onak_dbctx *dbctx,
208                 const struct skshash *hash,
209                 struct openpgp_publickey **publickey)
210 {
211         struct onak_stacked_dbctx *privctx =
212                         (struct onak_stacked_dbctx *) dbctx->priv;
213         struct onak_dbctx *backend;
214         struct ll *cur;
215         int res = 0;
216
217         for (cur = privctx->backends; cur != NULL && res == 0;
218                         cur = cur->next) {
219                 backend = (struct onak_dbctx *) cur->object;
220                 res = backend->fetch_key_skshash(backend, hash, publickey);
221         }
222
223         if (privctx->store_on_fallback && cur != privctx->backends) {
224                 store_on_fallback(privctx, *publickey, false);
225         }
226
227         return res;
228 }
229
230 /*
231  * Include the basic keydb routines so we can use them for fall back.
232  * For all of the following we try the top keydb backend and if that doesn't
233  * have answer fall back to the generics, which will do a retrieve from a
234  * backend further down the stack, then a fallback store.
235  */
236 #define NEED_KEYID2UID 1
237 #define NEED_GETKEYSIGS 1
238 #define NEED_GETFULLKEYID 1
239 #define NEED_UPDATEKEYS 1
240 #include "keydb.c"
241
242 static struct ll *stacked_getkeysigs(struct onak_dbctx *dbctx,
243                 uint64_t keyid, bool *revoked)
244 {
245         struct onak_stacked_dbctx *privctx =
246                         (struct onak_stacked_dbctx *) dbctx->priv;
247         struct onak_dbctx *backend =
248                         (struct onak_dbctx *) privctx->backends->object;
249         struct ll *res;
250
251         res = backend->getkeysigs(backend, keyid, revoked);
252         if (res == NULL) {
253                 res = generic_getkeysigs(dbctx, keyid, revoked);
254         }
255
256         return res;
257 }
258
259 static struct ll *stacked_cached_getkeysigs(struct onak_dbctx *dbctx,
260                 uint64_t keyid)
261 {
262         struct onak_stacked_dbctx *privctx =
263                         (struct onak_stacked_dbctx *) dbctx->priv;
264         struct onak_dbctx *backend =
265                         (struct onak_dbctx *) privctx->backends->object;
266         struct ll *res;
267
268         res = backend->cached_getkeysigs(backend, keyid);
269         if (res == NULL) {
270                 res = generic_cached_getkeysigs(dbctx, keyid);
271         }
272
273         return res;
274 }
275
276 static char *stacked_keyid2uid(struct onak_dbctx *dbctx,
277                         uint64_t keyid)
278 {
279         struct onak_stacked_dbctx *privctx =
280                         (struct onak_stacked_dbctx *) dbctx->priv;
281         struct onak_dbctx *backend =
282                         (struct onak_dbctx *) privctx->backends->object;
283         char *res = NULL;
284
285         res = backend->keyid2uid(backend, keyid);
286         if (!res) {
287                 res = generic_keyid2uid(dbctx, keyid);
288         }
289
290         return res;
291 }
292
293 static uint64_t stacked_getfullkeyid(struct onak_dbctx *dbctx,
294                 uint64_t keyid)
295 {
296         struct onak_stacked_dbctx *privctx =
297                         (struct onak_stacked_dbctx *) dbctx->priv;
298         struct onak_dbctx *backend =
299                         (struct onak_dbctx *) privctx->backends->object;
300         uint64_t res = 0;
301
302         res = backend->getfullkeyid(backend, keyid);
303         if (res == 0) {
304                 res = generic_getfullkeyid(dbctx, keyid);
305         }
306
307         return res;
308 }
309
310 static void stacked_cleanupdb(struct onak_dbctx *dbctx)
311 {
312         struct onak_stacked_dbctx *privctx =
313                         (struct onak_stacked_dbctx *) dbctx->priv;
314         struct onak_dbctx *backend;
315         struct ll *cur;
316         int res = 0;
317
318         for (cur = privctx->backends; cur != NULL && res == 0;
319                         cur = cur->next) {
320                 backend = (struct onak_dbctx *) cur->object;
321                 backend->cleanupdb(backend);
322         }
323
324         if (dbctx->priv != NULL) {
325                 free(dbctx->priv);
326                 dbctx->priv = NULL;
327         }
328
329         if (dbctx != NULL) {
330                 free(dbctx);
331         }
332 }
333
334 struct onak_dbctx *keydb_stacked_init(struct onak_db_config *dbcfg,
335                 bool readonly)
336 {
337         struct onak_dbctx *dbctx;
338         struct onak_stacked_dbctx *privctx;
339         struct onak_dbctx *backend;
340         struct onak_db_config *backend_cfg;
341         char *backend_name, *saveptr = NULL;
342
343         if (dbcfg == NULL) {
344                 logthing(LOGTHING_CRITICAL,
345                         "No backend database configuration supplied.");
346                 return NULL;
347         }
348
349         dbctx = malloc(sizeof(struct onak_dbctx));
350
351         if (dbctx == NULL) {
352                 return NULL;
353         }
354
355         dbctx->config = dbcfg;
356         dbctx->priv = privctx = malloc(sizeof(struct onak_stacked_dbctx));
357         if (dbctx->priv == NULL) {
358                 free(dbctx);
359                 return (NULL);
360         }
361
362         /* TODO: Make configurable? */
363         privctx->store_on_fallback = true;
364         privctx->backends = NULL;
365
366         backend_name = strtok_r(dbcfg->location, ":", &saveptr);
367         while (backend_name != NULL) {
368                 backend_cfg = find_db_backend_config(config.backends,
369                                 backend_name);
370                 if (backend_cfg == NULL) {
371                         logthing(LOGTHING_CRITICAL,
372                                 "Couldn't find configuration for %s backend",
373                                 backend_name);
374                         stacked_cleanupdb(dbctx);
375                         return NULL;
376                 }
377                 logthing(LOGTHING_INFO, "Loading stacked backend: %s",
378                                 backend_cfg->name);
379
380                 backend = config.dbinit(backend_cfg, readonly);
381                 privctx->backends = lladdend(privctx->backends, backend);
382
383                 backend_name = strtok_r(NULL, ":", &saveptr);
384         }
385
386         if (privctx->backends != NULL) {
387                 dbctx->cleanupdb = stacked_cleanupdb;
388                 dbctx->starttrans = stacked_starttrans;
389                 dbctx->endtrans = stacked_endtrans;
390                 dbctx->fetch_key_id = stacked_fetch_key_id;
391                 dbctx->fetch_key_fp = stacked_fetch_key_fp;
392                 dbctx->fetch_key_text = stacked_fetch_key_text;
393                 dbctx->fetch_key_skshash = stacked_fetch_key_skshash;
394                 dbctx->store_key = stacked_store_key;
395                 dbctx->update_keys = stacked_update_keys;
396                 dbctx->delete_key = stacked_delete_key;
397                 dbctx->getkeysigs = stacked_getkeysigs;
398                 dbctx->cached_getkeysigs = stacked_cached_getkeysigs;
399                 dbctx->keyid2uid = stacked_keyid2uid;
400                 dbctx->getfullkeyid = stacked_getfullkeyid;
401                 dbctx->iterate_keys = stacked_iterate_keys;
402         }
403
404         return dbctx;
405 }