]> the.earth.li Git - onak.git/blob - keydb_stacked.c
c732720ebdbccdf8ba1e89a5eb01635f7ccdb80e
[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 "decodekey.h"
24 #include "hash.h"
25 #include "keydb.h"
26 #include "keyid.h"
27 #include "keystructs.h"
28 #include "log.h"
29 #include "mem.h"
30 #include "merge.h"
31 #include "onak-conf.h"
32 #include "openpgp.h"
33 #include "parsekey.h"
34 #include "sendsync.h"
35
36 struct onak_stacked_dbctx {
37         struct ll *backends;
38         bool store_on_fallback;
39 };
40
41 /*
42  * The following functions only apply to the first backend.
43  */
44
45 static bool stacked_starttrans(struct onak_dbctx *dbctx)
46 {
47         struct onak_stacked_dbctx *privctx =
48                         (struct onak_stacked_dbctx *) dbctx->priv;
49         struct onak_dbctx *backend =
50                         (struct onak_dbctx *) privctx->backends->object;
51
52         return backend->starttrans(backend);
53 }
54
55 static void stacked_endtrans(struct onak_dbctx *dbctx)
56 {
57         struct onak_stacked_dbctx *privctx =
58                         (struct onak_stacked_dbctx *) dbctx->priv;
59         struct onak_dbctx *backend =
60                         (struct onak_dbctx *) privctx->backends->object;
61
62         backend->starttrans(backend);
63 }
64
65 static int stacked_store_key(struct onak_dbctx *dbctx,
66                 struct openpgp_publickey *publickey, bool intrans,
67                 bool update)
68 {
69         struct onak_stacked_dbctx *privctx =
70                         (struct onak_stacked_dbctx *) dbctx->priv;
71         struct onak_dbctx *backend =
72                         (struct onak_dbctx *) privctx->backends->object;
73
74         return backend->store_key(backend,
75                         publickey, intrans, update);
76 }
77
78 static int stacked_delete_key(struct onak_dbctx *dbctx, uint64_t keyid,
79                 bool intrans)
80 {
81         struct onak_stacked_dbctx *privctx =
82                         (struct onak_stacked_dbctx *) dbctx->priv;
83         struct onak_dbctx *backend =
84                         (struct onak_dbctx *) privctx->backends->object;
85
86         return backend->delete_key(backend,
87                         keyid, intrans);
88 }
89
90 static int stacked_update_keys(struct onak_dbctx *dbctx,
91                 struct openpgp_publickey **keys, bool sendsync)
92 {
93         struct onak_stacked_dbctx *privctx =
94                         (struct onak_stacked_dbctx *) dbctx->priv;
95         struct onak_dbctx *backend =
96                         (struct onak_dbctx *) privctx->backends->object;
97
98         return backend->update_keys(backend, keys, sendsync);
99 }
100
101 static int stacked_iterate_keys(struct onak_dbctx *dbctx,
102                 void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
103                 void *ctx)
104 {
105         struct onak_stacked_dbctx *privctx =
106                         (struct onak_stacked_dbctx *) dbctx->priv;
107         struct onak_dbctx *backend =
108                         (struct onak_dbctx *) privctx->backends->object;
109
110         return backend->iterate_keys(backend, iterfunc, ctx);
111 }
112
113 static void store_on_fallback(struct onak_stacked_dbctx *privctx,
114                 struct openpgp_publickey *publickey, bool intrans)
115 {
116         struct onak_dbctx *backend =
117                         (struct onak_dbctx *) privctx->backends->object;
118         struct openpgp_publickey *curkey;
119
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 static struct ll *stacked_getkeysigs(struct onak_dbctx *dbctx,
229                 uint64_t keyid, bool *revoked)
230 {
231         struct onak_stacked_dbctx *privctx =
232                         (struct onak_stacked_dbctx *) dbctx->priv;
233         struct onak_dbctx *backend;
234         struct ll *cur;
235         struct ll *res = NULL;
236
237         for (cur = privctx->backends; cur != NULL && res == NULL;
238                         cur = cur->next) {
239                 backend = (struct onak_dbctx *) cur->object;
240                 res = backend->getkeysigs(backend, keyid, revoked);
241         }
242
243         return res;
244 }
245
246 static struct ll *stacked_cached_getkeysigs(struct onak_dbctx *dbctx,
247                 uint64_t keyid)
248 {
249         struct onak_stacked_dbctx *privctx =
250                         (struct onak_stacked_dbctx *) dbctx->priv;
251         struct onak_dbctx *backend;
252         struct ll *cur;
253         struct ll *res = NULL;
254
255         for (cur = privctx->backends; cur != NULL && res == NULL;
256                         cur = cur->next) {
257                 backend = (struct onak_dbctx *) cur->object;
258                 res = backend->cached_getkeysigs(backend, keyid);
259         }
260
261         return res;
262 }
263
264 static char *stacked_keyid2uid(struct onak_dbctx *dbctx,
265                         uint64_t keyid)
266 {
267         struct onak_stacked_dbctx *privctx =
268                         (struct onak_stacked_dbctx *) dbctx->priv;
269         struct onak_dbctx *backend;
270         struct ll *cur;
271         char *res = NULL;
272
273         for (cur = privctx->backends; cur != NULL && res == NULL;
274                         cur = cur->next) {
275                 backend = (struct onak_dbctx *) cur->object;
276                 res = backend->keyid2uid(backend, keyid);
277         }
278
279         return res;
280 }
281
282 static uint64_t stacked_getfullkeyid(struct onak_dbctx *dbctx,
283                 uint64_t keyid)
284 {
285         struct onak_stacked_dbctx *privctx =
286                         (struct onak_stacked_dbctx *) dbctx->priv;
287         struct onak_dbctx *backend;
288         struct ll *cur;
289         uint64_t res = 0;
290
291         for (cur = privctx->backends; cur != NULL && res == 0;
292                         cur = cur->next) {
293                 backend = (struct onak_dbctx *) cur->object;
294                 res = backend->getfullkeyid(backend, keyid);
295         }
296
297         return res;
298 }
299
300 static void stacked_cleanupdb(struct onak_dbctx *dbctx)
301 {
302         struct onak_stacked_dbctx *privctx =
303                         (struct onak_stacked_dbctx *) dbctx->priv;
304         struct onak_dbctx *backend;
305         struct ll *cur;
306         int res = 0;
307
308         for (cur = privctx->backends; cur != NULL && res == 0;
309                         cur = cur->next) {
310                 backend = (struct onak_dbctx *) cur->object;
311                 backend->cleanupdb(backend);
312         }
313
314         if (dbctx->priv != NULL) {
315                 free(dbctx->priv);
316                 dbctx->priv = NULL;
317         }
318
319         if (dbctx != NULL) {
320                 free(dbctx);
321         }
322 }
323
324 struct onak_dbctx *keydb_stacked_init(struct onak_db_config *dbcfg,
325                 bool readonly)
326 {
327         struct onak_dbctx *dbctx;
328         struct onak_stacked_dbctx *privctx;
329         struct onak_dbctx *backend;
330         struct onak_db_config *backend_cfg;
331         char *backend_name, *saveptr = NULL;
332
333         if (dbcfg == NULL) {
334                 logthing(LOGTHING_CRITICAL,
335                         "No backend database configuration supplied.");
336                 return NULL;
337         }
338
339         dbctx = malloc(sizeof(struct onak_dbctx));
340
341         if (dbctx == NULL) {
342                 return NULL;
343         }
344
345         dbctx->config = dbcfg;
346         dbctx->priv = privctx = malloc(sizeof(struct onak_stacked_dbctx));
347         if (dbctx->priv == NULL) {
348                 free(dbctx);
349                 return (NULL);
350         }
351
352         /* TODO: Make configurable? */
353         privctx->store_on_fallback = true;
354         privctx->backends = NULL;
355
356         backend_name = strtok_r(dbcfg->location, ":", &saveptr);
357         while (backend_name != NULL) {
358                 backend_cfg = find_db_backend_config(config.backends,
359                                 backend_name);
360                 if (backend_cfg == NULL) {
361                         logthing(LOGTHING_CRITICAL,
362                                 "Couldn't find configuration for %s backend",
363                                 backend_name);
364                         stacked_cleanupdb(dbctx);
365                         return NULL;
366                 }
367                 logthing(LOGTHING_INFO, "Loading stacked backend: %s",
368                                 backend_cfg->name);
369
370                 backend = config.dbinit(backend_cfg, readonly);
371                 privctx->backends = lladdend(privctx->backends, backend);
372
373                 backend_name = strtok_r(NULL, ":", &saveptr);
374         }
375
376         if (privctx->backends != NULL) {
377                 dbctx->cleanupdb = stacked_cleanupdb;
378                 dbctx->starttrans = stacked_starttrans;
379                 dbctx->endtrans = stacked_endtrans;
380                 dbctx->fetch_key_id = stacked_fetch_key_id;
381                 dbctx->fetch_key_fp = stacked_fetch_key_fp;
382                 dbctx->fetch_key_text = stacked_fetch_key_text;
383                 dbctx->fetch_key_skshash = stacked_fetch_key_skshash;
384                 dbctx->store_key = stacked_store_key;
385                 dbctx->update_keys = stacked_update_keys;
386                 dbctx->delete_key = stacked_delete_key;
387                 dbctx->getkeysigs = stacked_getkeysigs;
388                 dbctx->cached_getkeysigs = stacked_cached_getkeysigs;
389                 dbctx->keyid2uid = stacked_keyid2uid;
390                 dbctx->getfullkeyid = stacked_getfullkeyid;
391                 dbctx->iterate_keys = stacked_iterate_keys;
392         }
393
394         return dbctx;
395 }