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