e22b00db30156e084505a3ec9a748a45c0c910e7
[onak.git] / keydb_dynamic.c
1 /*
2  * keydb_dynamic.c - backend that can load the other backends
3  *
4  * Brett Parker <iDunno@sommitrealweird.co.uk>
5  *
6  * Copyright 2005 Project Purple
7  */
8
9 #include <dlfcn.h>
10 #include <stdio.h>
11 #include <string.h>
12
13 #include "decodekey.h"
14 #include "hash.h"
15 #include "keydb.h"
16 #include "keyid.h"
17 #include "keystructs.h"
18 #include "log.h"
19 #include "mem.h"
20 #include "merge.h"
21 #include "onak-conf.h"
22 #include "parsekey.h"
23 #include "sendsync.h"
24
25 static struct dbfuncs *loaded_backend = NULL;
26 static char *backendsoname;
27 static void *backend_handle;
28
29 static bool close_backend(void)
30 {
31         loaded_backend = NULL;
32         dlclose(backend_handle);
33         backend_handle = NULL;
34
35         return true;
36 }
37
38 static bool load_backend(void)
39 {
40         char *soname = NULL;
41         char *funcsname = NULL;
42
43         if (loaded_backend != NULL) {
44                 close_backend();
45                 loaded_backend = NULL;
46         }
47
48         if (config.use_keyd) {
49                 free(config.db_backend);
50                 config.db_backend = strdup("keyd");
51         }
52
53         if (!config.db_backend) {
54                 logthing(LOGTHING_CRITICAL, "No database backend defined.");
55                 exit(EXIT_FAILURE);
56         }
57
58         if (config.backends_dir == NULL) {
59                 soname = malloc(strlen(config.db_backend)
60                         + strlen("./libkeydb_")
61                         + strlen(".so")
62                         + 1);
63
64                 sprintf(soname, "./libkeydb_%s.so", config.db_backend);
65         } else {
66                 soname = malloc(strlen(config.db_backend)
67                         + strlen("/libkeydb_")
68                         + strlen(".so")
69                         + strlen(config.backends_dir)
70                         + 1);
71
72                 sprintf(soname, "%s/libkeydb_%s.so", config.backends_dir,
73                         config.db_backend);
74         }
75                 
76         logthing(LOGTHING_INFO, "Loading dynamic backend: %s", soname);
77
78         backend_handle = dlopen(soname, RTLD_LAZY);
79         if (backend_handle == NULL) {
80                 logthing(LOGTHING_CRITICAL,
81                                 "Failed to open handle to library '%s': %s",
82                                 soname, dlerror());
83                 free(soname);
84                 soname = NULL;
85                 exit(EXIT_FAILURE);
86         }
87
88         funcsname = malloc(strlen(config.db_backend)
89                         + strlen("keydb_")
90                         + strlen("_funcs")
91                         + 1);
92         sprintf(funcsname, "keydb_%s_funcs", config.db_backend);
93
94         loaded_backend = dlsym(backend_handle, funcsname);
95         free(funcsname);
96
97         if (loaded_backend == NULL) {
98                 logthing(LOGTHING_CRITICAL,
99                                 "Failed to find dbfuncs structure in library "
100                                 "'%s' : %s", soname, dlerror());
101                 free(soname);
102                 soname = NULL;
103                 exit(EXIT_FAILURE);
104         }
105         free(soname);
106         soname = NULL;
107
108         return true;
109 }
110
111 static bool dynamic_starttrans()
112 {
113         struct dynamic_backend *backend;
114
115         if (loaded_backend == NULL) {
116                 load_backend();
117         }
118         
119         if (loaded_backend != NULL) {
120                 if (loaded_backend->starttrans != NULL) {
121                         return loaded_backend->starttrans();
122                 }
123         }
124
125         return false;
126 }
127
128 static void dynamic_endtrans()
129 {
130         struct dynamic_backend *backend;
131
132         if (loaded_backend == NULL) {
133                 load_backend();
134         }
135         
136         if (loaded_backend != NULL) {
137                 if (loaded_backend->endtrans != NULL) {
138                         loaded_backend->endtrans();
139                 }
140         }
141 }
142
143 static int dynamic_fetch_key(uint64_t keyid,
144                 struct openpgp_publickey **publickey, bool intrans)
145 {
146         struct dynamic_backend *backend;
147
148         if (loaded_backend == NULL) {
149                 load_backend();
150         }
151         
152         if (loaded_backend != NULL) {
153                 if (loaded_backend->fetch_key != NULL) {
154                         return loaded_backend->fetch_key(keyid,publickey,intrans);
155                 }
156         }
157
158         return -1;
159 }
160
161 static int dynamic_store_key(struct openpgp_publickey *publickey, bool intrans,
162                 bool update)
163 {
164         struct dynamic_backend *backend;
165
166         if (loaded_backend == NULL) {
167                 load_backend();
168         }
169         
170         if (loaded_backend != NULL) {
171                 if (loaded_backend->store_key != NULL) {
172                         return loaded_backend->store_key(publickey,intrans,update);
173                 }
174         }
175
176         return -1;
177 }
178
179 static int dynamic_delete_key(uint64_t keyid, bool intrans)
180 {
181         struct dynamic_backend *backend;
182
183         if (loaded_backend == NULL) {
184                 load_backend();
185         }
186         
187         if (loaded_backend != NULL) {
188                 if (loaded_backend->delete_key != NULL) {
189                         return loaded_backend->delete_key(keyid, intrans);
190                 }
191         }
192
193         return -1;
194 }
195
196 static int dynamic_fetch_key_text(const char *search,
197                 struct openpgp_publickey **publickey)
198 {
199         struct dynamic_backend *backend;
200
201         if (loaded_backend == NULL) {
202                 load_backend();
203         }
204         
205         if (loaded_backend != NULL) {
206                 if (loaded_backend->fetch_key_text != NULL) {
207                         return loaded_backend->fetch_key_text(search, publickey);
208                 }
209         }
210
211         return -1;
212 }
213
214 static int dynamic_iterate_keys(void (*iterfunc)(void *ctx,
215                 struct openpgp_publickey *key), void *ctx)
216 {
217         struct dynamic_backend *backend;
218
219         if (loaded_backend == NULL) {
220                 load_backend();
221         }
222         
223         if (loaded_backend != NULL) {
224                 if (loaded_backend->iterate_keys != NULL) {
225                         return loaded_backend->iterate_keys(iterfunc, ctx);
226                 }
227         }
228
229         return -1;
230 }
231
232 /**
233  *      keyid2uid - Takes a keyid and returns the primary UID for it.
234  *      @keyid: The keyid to lookup.
235  */
236 static char *dynamic_keyid2uid(uint64_t keyid)
237 {
238         struct openpgp_publickey *publickey = NULL;
239         struct openpgp_signedpacket_list *curuid = NULL;
240         char buf[1024];
241
242         struct dynamic_backend *backend;
243         if (loaded_backend == NULL) {
244                 load_backend();
245         }
246         
247         if (loaded_backend != NULL) {
248                 if (loaded_backend->keyid2uid != NULL) {
249                         return loaded_backend->keyid2uid(keyid);
250                 }
251         }
252         
253         buf[0]=0;
254         if (dynamic_fetch_key(keyid, &publickey, false) && publickey != NULL) {
255                 curuid = publickey->uids;
256                 while (curuid != NULL && buf[0] == 0) {
257                         if (curuid->packet->tag == 13) {
258                                 snprintf(buf, 1023, "%.*s",
259                                                 (int) curuid->packet->length,
260                                                 curuid->packet->data);
261                         }
262                         curuid = curuid -> next;
263                 }
264                 free_publickey(publickey);
265         }
266
267         if (buf[0] == 0) {
268                 return NULL;
269         } else {
270                 return strdup(buf);
271         }
272 }
273
274 /**
275  *      getkeysigs - Gets a linked list of the signatures on a key.
276  *      @keyid: The keyid to get the sigs for.
277  *      @revoked: Is the key revoked?
278  *
279  *      This function gets the list of signatures on a key. Used for key 
280  *      indexing and doing stats bits. If revoked is non-NULL then if the key
281  *      is revoked it's set to true.
282  */
283 static struct ll *dynamic_getkeysigs(uint64_t keyid, bool *revoked)
284 {
285         struct ll *sigs = NULL;
286         struct openpgp_signedpacket_list *uids = NULL;
287         struct openpgp_publickey *publickey = NULL;
288         
289         struct dynamic_backend *backend;
290         if ( loaded_backend == NULL ) {
291                 load_backend();
292         }
293         
294         if (loaded_backend != NULL) {
295                 if (loaded_backend->getkeysigs != NULL) {
296                         return loaded_backend->getkeysigs(keyid,revoked);
297                 }
298         }
299
300         dynamic_fetch_key(keyid, &publickey, false);
301         
302         if (publickey != NULL) {
303                 for (uids = publickey->uids; uids != NULL; uids = uids->next) {
304                         sigs = keysigs(sigs, uids->sigs);
305                 }
306                 if (revoked != NULL) {
307                         *revoked = publickey->revoked;
308                 }
309                 free_publickey(publickey);
310         }
311
312         return sigs;
313 }
314
315 /**
316  *      cached_getkeysigs - Gets the signatures on a key.
317  *      @keyid: The key we want the signatures for.
318  *      
319  *      This function gets the signatures on a key. It's the same as the
320  *      getkeysigs function above except we use the hash module to cache the
321  *      data so if we need it again it's already loaded.
322  */
323 static struct ll *dynamic_cached_getkeysigs(uint64_t keyid)
324 {
325         struct stats_key *key = NULL;
326         struct stats_key *signedkey = NULL;
327         struct ll        *cursig = NULL;
328         bool              revoked = false;
329         
330         struct dynamic_backend *backend;
331
332         if (keyid == 0)  {
333                 return NULL;
334         }
335         
336         if (loaded_backend == NULL) {
337                 load_backend();
338         }
339         
340         if (loaded_backend != NULL) {
341                 if (loaded_backend->cached_getkeysigs != NULL) {
342                         return loaded_backend->cached_getkeysigs(keyid);
343                 }
344         }
345
346         key = createandaddtohash(keyid);
347
348         if (key->gotsigs == false) {
349                 key->sigs = dynamic_getkeysigs(key->keyid, &revoked);
350                 key->revoked = revoked;
351                 for (cursig = key->sigs; cursig != NULL;
352                                 cursig = cursig->next) {
353                         signedkey = (struct stats_key *) cursig->object;
354                         signedkey->signs = lladd(signedkey->signs, key);
355                 }
356                 key->gotsigs = true;
357         }
358
359         return key->sigs;
360 }
361
362 /**
363  *      getfullkeyid - Maps a 32bit key id to a 64bit one.
364  *      @keyid: The 32bit keyid.
365  *
366  *      This function maps a 32bit key id to the full 64bit one. It returns the
367  *      full keyid. If the key isn't found a keyid of 0 is returned.
368  */
369 static uint64_t dynamic_getfullkeyid(uint64_t keyid)
370 {
371         struct openpgp_publickey *publickey = NULL;
372         struct dynamic_backend *backend;
373         
374         if (loaded_backend == NULL) {
375                 load_backend();
376         }
377         
378         if (loaded_backend != NULL) {
379                 if (loaded_backend->getfullkeyid != NULL) {
380                         return loaded_backend->getfullkeyid(keyid);
381                 }
382         }
383
384         if (keyid < 0x100000000LL) {
385                 dynamic_fetch_key(keyid, &publickey, false);
386                 if (publickey != NULL) {
387                         keyid = get_keyid(publickey);
388                         free_publickey(publickey);
389                         publickey = NULL;
390                 } else {
391                         keyid = 0;
392                 }
393         }
394         
395         return keyid;
396 }
397
398 /**
399  *      update_keys - Takes a list of public keys and updates them in the DB.
400  *      @keys: The keys to update in the DB.
401  *      @sendsync: Should we send a sync mail to our peers.
402  *
403  *      Takes a list of keys and adds them to the database, merging them with
404  *      the key in the database if it's already present there. The key list is
405  *      update to contain the minimum set of updates required to get from what
406  *      we had before to what we have now (ie the set of data that was added to
407  *      the DB). Returns the number of entirely new keys added.
408  */
409 static int dynamic_update_keys(struct openpgp_publickey **keys, bool sendsync)
410 {
411         struct openpgp_publickey *curkey = NULL;
412         struct openpgp_publickey *oldkey = NULL;
413         struct openpgp_publickey *prev = NULL;
414         struct dynamic_backend *backend;
415         int newkeys = 0;
416         bool intrans;
417         
418         if (loaded_backend == NULL) {
419                 load_backend();
420         }
421         
422         if (loaded_backend != NULL) {
423                 if (loaded_backend->update_keys != NULL) {
424                         return loaded_backend->update_keys(keys, sendsync);
425                 }
426         }
427
428         for (curkey = *keys; curkey != NULL; curkey = curkey->next) {
429                 intrans = dynamic_starttrans();
430                 logthing(LOGTHING_INFO,
431                         "Fetching key 0x%llX, result: %d",
432                         get_keyid(curkey),
433                         dynamic_fetch_key(get_keyid(curkey), &oldkey, intrans));
434
435                 /*
436                  * If we already have the key stored in the DB then merge it
437                  * with the new one that's been supplied. Otherwise the key
438                  * we've just got is the one that goes in the DB and also the
439                  * one that we send out.
440                  */
441                 if (oldkey != NULL) {
442                         merge_keys(oldkey, curkey);
443                         if (curkey->sigs == NULL &&
444                                         curkey->uids == NULL &&
445                                         curkey->subkeys == NULL) {
446                                 if (prev == NULL) {
447                                         *keys = curkey->next;
448                                 } else {
449                                         prev->next = curkey->next;
450                                         curkey->next = NULL;
451                                         free_publickey(curkey);
452                                         curkey = prev;
453                                 }
454                         } else {
455                                 prev = curkey;
456                                 logthing(LOGTHING_INFO,
457                                         "Merged key; storing updated key.");
458                                 dynamic_store_key(oldkey, intrans, true);
459                         }
460                         free_publickey(oldkey);
461                         oldkey = NULL;
462                 
463                 } else {
464                         logthing(LOGTHING_INFO,
465                                 "Storing completely new key.");
466                         dynamic_store_key(curkey, intrans, false);
467                         newkeys++;
468                 }
469                 dynamic_endtrans();
470                 intrans = false;
471         }
472
473         if (sendsync && keys != NULL) {
474                 sendkeysync(*keys);
475         }
476
477         return newkeys;
478 }
479
480 static void dynamic_initdb(bool readonly)
481 {
482         struct dynamic_backend *backend;
483         
484         if (loaded_backend == NULL) {
485                 load_backend();
486         }
487
488         if (loaded_backend != NULL) {
489                 if (loaded_backend->initdb != NULL) {
490                         loaded_backend->initdb(readonly);
491                 }
492         }
493 }
494
495 static void dynamic_cleanupdb(void)
496 {
497         struct dynamic_backend *backend;
498
499         if (loaded_backend != NULL) {
500                 if (loaded_backend->cleanupdb != NULL) {
501                         loaded_backend->cleanupdb();
502                 }
503         }
504
505         close_backend();
506 }
507
508 struct dbfuncs keydb_dynamic_funcs = {
509         .initdb                 = dynamic_initdb,
510         .cleanupdb              = dynamic_cleanupdb,
511         .starttrans             = dynamic_starttrans,
512         .endtrans               = dynamic_endtrans,
513         .fetch_key              = dynamic_fetch_key,
514         .fetch_key_text         = dynamic_fetch_key_text,
515         .store_key              = dynamic_store_key,
516         .update_keys            = dynamic_update_keys,
517         .delete_key             = dynamic_delete_key,
518         .getkeysigs             = dynamic_getkeysigs,
519         .cached_getkeysigs      = dynamic_cached_getkeysigs,
520         .keyid2uid              = dynamic_keyid2uid,
521         .getfullkeyid           = dynamic_getfullkeyid,
522         .iterate_keys           = dynamic_iterate_keys,
523 };