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