]> git.sommitrealweird.co.uk Git - onak.git/blob - keydb_fs.c
02adf6bae1972b069c393581f92fcea3fde1ee1d
[onak.git] / keydb_fs.c
1 /*
2  * keydb.h - Routines to store and fetch keys.
3  *
4  * Daniel Silverstone <dsilvers@digital-scurf.org>
5  *
6  * Copyright 2004 Daniel Silverstone and Project Purple
7  */
8
9 #include <sys/types.h>
10 #include <sys/uio.h>
11 #include <sys/stat.h>
12 #include <errno.h>
13 #include <fcntl.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 #include <unistd.h>
18 #include <limits.h>
19 #include <dirent.h>
20
21 #include "charfuncs.h"
22 #include "decodekey.h"
23 #include "keydb.h"
24 #include "keyid.h"
25 #include "keystructs.h"
26 #include "ll.h"
27 #include "mem.h"
28 #include "onak-conf.h"
29 #include "parsekey.h"
30 #include "log.h"
31 #include "wordlist.h"
32
33 /* Hack: We should really dynamically allocate our path buffers */
34 #ifndef PATH_MAX
35 #define PATH_MAX 1024
36 #endif
37
38 static int keydb_lockfile_fd = -1;
39 static bool keydb_lockfile_readonly;
40
41 /*****************************************************************************/
42
43 /* Helper functions */
44
45 #define FNV_offset_basis 2166136261ul
46 #define FNV_mixing_prime 16777619ul
47
48 static uint32_t calchash(uint8_t * ptr)
49 {
50         register uint32_t h = FNV_offset_basis;
51         register uint32_t p = FNV_mixing_prime;
52         register uint32_t n = strlen((char *) ptr);
53         register uint8_t *c = ptr;
54         while (n--) {
55                 h *= p;
56                 h ^= *c++;
57         }
58         return h ? h : 1;       /* prevent a hash of zero happening */
59 }
60
61
62 static void keypath(char *buffer, size_t length, uint64_t _keyid)
63 {
64         uint64_t keyid = _keyid << 32;
65         snprintf(buffer, length, "%s/key/%02X/%02X/%08X/%016" PRIX64,
66                  config.db_dir, (uint8_t) ((keyid >> 56) & 0xFF),
67                  (uint8_t) ((keyid >> 48) & 0xFF),
68                  (uint32_t) (keyid >> 32), _keyid);
69 }
70
71 static void keydir(char *buffer, size_t length, uint64_t _keyid)
72 {
73         uint64_t keyid = _keyid << 32;
74         snprintf(buffer, length, "%s/key/%02X/%02X/%08X", config.db_dir,
75                  (uint8_t) ((keyid >> 56) & 0xFF),
76                  (uint8_t) ((keyid >> 48) & 0xFF),
77                  (uint32_t) (keyid >> 32));
78 }
79
80 static void prove_path_to(uint64_t keyid, char *what)
81 {
82         static char buffer[PATH_MAX];
83         snprintf(buffer, sizeof(buffer), "%s/%s", config.db_dir, what);
84         mkdir(buffer, 0777);
85
86         snprintf(buffer, sizeof(buffer), "%s/%s/%02X", config.db_dir, what,
87                  (uint8_t) ((keyid >> 24) & 0xFF));
88         mkdir(buffer, 0777);
89
90         snprintf(buffer, sizeof(buffer), "%s/%s/%02X/%02X", config.db_dir,
91                  what,
92                  (uint8_t) ((keyid >> 24) & 0xFF),
93                  (uint8_t) ((keyid >> 16) & 0xFF));
94         mkdir(buffer, 0777);
95
96         snprintf(buffer, sizeof(buffer), "%s/%s/%02X/%02X/%08X", config.db_dir,
97                  what,
98                  (uint8_t) ((keyid >> 24) & 0xFF),
99                  (uint8_t) ((keyid >> 16) & 0xFF), (uint32_t) (keyid));
100         mkdir(buffer, 0777);
101 }
102
103 static void wordpath(char *buffer, size_t length, char *word, uint32_t hash,
104                 uint64_t keyid)
105 {
106         snprintf(buffer, length, "%s/words/%02X/%02X/%08X/%s/%016" PRIX64,
107                  config.db_dir, (uint8_t) ((hash >> 24) & 0xFF),
108                  (uint8_t) ((hash >> 16) & 0xFF), hash, word, keyid);
109 }
110
111 static void worddir(char *buffer, size_t length, char *word, uint32_t hash)
112 {
113         snprintf(buffer, length, "%s/words/%02X/%02X/%08X/%s", config.db_dir,
114                  (uint8_t) ((hash >> 24) & 0xFF),
115                  (uint8_t) ((hash >> 16) & 0xFF), hash, word);
116 }
117
118 static void subkeypath(char *buffer, size_t length, uint64_t subkey,
119                 uint64_t keyid)
120 {
121         snprintf(buffer, length, "%s/subkeys/%02X/%02X/%08X/%016" PRIX64,
122                  config.db_dir,
123                  (uint8_t) ((subkey >> 24) & 0xFF),
124                  (uint8_t) ((subkey >> 16) & 0xFF),
125                  (uint32_t) (subkey & 0xFFFFFFFF),
126                  keyid);
127 }
128
129 static void skshashpath(char *buffer, size_t length,
130                 const struct skshash *hash)
131 {
132         snprintf(buffer, length, "%s/skshash/%02X/%02X/%02X%02X%02X%02X/"
133                 "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
134                  config.db_dir,
135                  hash->hash[0], hash->hash[1],
136                  hash->hash[0], hash->hash[1], hash->hash[2], hash->hash[3],
137                  hash->hash[4], hash->hash[5], hash->hash[6], hash->hash[7],
138                  hash->hash[8], hash->hash[9], hash->hash[10], hash->hash[11],
139                  hash->hash[12], hash->hash[13], hash->hash[14],
140                  hash->hash[15]);
141 }
142 static void subkeydir(char *buffer, size_t length, uint64_t subkey)
143 {
144         snprintf(buffer, length, "%s/subkeys/%02X/%02X/%08X",
145                  config.db_dir,
146                  (uint8_t) ((subkey >> 24) & 0xFF),
147                  (uint8_t) ((subkey >> 16) & 0xFF),
148                  (uint32_t) (subkey & 0xFFFFFFFF));
149 }
150
151 /*****************************************************************************/
152
153 /**
154  *      initdb - Initialize the key database.
155  */
156 static void fs_initdb(bool readonly)
157 {
158         char buffer[PATH_MAX];
159
160         keydb_lockfile_readonly = readonly;
161
162         snprintf(buffer, sizeof(buffer), "%s/.lock", config.db_dir);
163
164         if (access(config.db_dir, R_OK | W_OK | X_OK) == -1) {
165                 if (errno != ENOENT) {
166                         logthing(LOGTHING_CRITICAL,
167                                  "Unable to access keydb_fs root of '%s'. (%s)",
168                                  config.db_dir, strerror(errno));
169                         exit(1);        /* Lacking rwx on the key dir */
170                 }
171                 mkdir(config.db_dir, 0777);
172                 keydb_lockfile_fd = open(buffer, O_RDWR | O_CREAT, 0600);
173         }
174         chdir(config.db_dir);
175         if (keydb_lockfile_fd == -1)
176                 keydb_lockfile_fd = open(buffer,
177                                          (keydb_lockfile_readonly) ?
178                                          O_RDONLY : O_RDWR);
179         if (keydb_lockfile_fd == -1)
180                 keydb_lockfile_fd = open(buffer, O_RDWR | O_CREAT, 0600);
181         if (keydb_lockfile_fd == -1) {
182                 logthing(LOGTHING_CRITICAL,
183                          "Unable to open lockfile '%s'. (%s)",
184                          buffer, strerror(errno));
185                 exit(1);        /* Lacking rwx on the key dir */
186         }
187 }
188
189 /**
190  *      cleanupdb - De-initialize the key database.
191  */
192 static void fs_cleanupdb(void)
193 {
194         /* Mmmm nothing to do here? */
195         close(keydb_lockfile_fd);
196 }
197
198 /**
199  *      starttrans - Start a transaction.
200  */
201 static bool fs_starttrans(void)
202 {
203         struct flock lockstruct;
204         int remaining = 20;
205         lockstruct.l_type =
206             F_RDLCK | ((keydb_lockfile_readonly) ? 0 : F_WRLCK);
207         lockstruct.l_whence = SEEK_SET;
208         lockstruct.l_start = 0;
209         lockstruct.l_len = 1;
210
211         while (fcntl(keydb_lockfile_fd, F_SETLK, &lockstruct) == -1) {
212                 if (remaining-- == 0)
213                         return false;   /* Hope to hell that noodles DTRT */
214                 usleep(100);
215         }
216         return true;
217 }
218
219 /**
220  *      endtrans - End a transaction.
221  */
222 static void fs_endtrans(void)
223 {
224         struct flock lockstruct;
225
226         lockstruct.l_type = F_UNLCK;
227         lockstruct.l_whence = SEEK_SET;
228         lockstruct.l_start = 0;
229         lockstruct.l_len = 1;
230         fcntl(keydb_lockfile_fd, F_SETLK, &lockstruct);
231 }
232
233 static uint64_t fs_getfullkeyid(uint64_t keyid)
234 {
235         static char buffer[PATH_MAX];
236         DIR *d = NULL;
237         struct dirent *de = NULL;
238         uint64_t ret = 0;
239
240         keydir(buffer, sizeof(buffer), keyid);
241
242         d = opendir(buffer);
243         if (d) {
244                 do {
245                         de = readdir(d);
246                         if (de && de->d_name[0] != '.') {
247                                 ret = strtoull(de->d_name, NULL, 16);
248                         }
249                 } while (de && de->d_name[0] == '.');
250                 closedir(d);    
251         }
252
253         if (ret == 0) {
254                 subkeydir(buffer, sizeof(buffer), keyid);
255
256                 d = opendir(buffer);
257                 if (d) {
258                         do {
259                                 de = readdir(d);
260                                 if (de && de->d_name[0] != '.') {
261                                         ret = strtoull(de->d_name, NULL, 16);
262                                 }
263                         } while (de && de->d_name[0] == '.');
264                         closedir(d);
265                 }
266         }
267
268         return ret;
269 }
270
271 /**
272  *      fetch_key - Given a keyid fetch the key from storage.
273  *      @keyid: The keyid to fetch.
274  *      @publickey: A pointer to a structure to return the key in.
275  *      @intrans: If we're already in a transaction.
276  */
277 static int fs_fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
278               bool intrans)
279 {
280         static char buffer[PATH_MAX];
281         int ret = 0, fd;
282         struct openpgp_packet_list *packets = NULL;
283
284         if (!intrans)
285                 fs_starttrans();
286
287         if ((keyid >> 32) == 0)
288                 keyid = fs_getfullkeyid(keyid);
289
290         keypath(buffer, sizeof(buffer), keyid);
291         if ((fd = open(buffer, O_RDONLY)) != -1) {
292                 /* File is present, load it in... */
293                 read_openpgp_stream(file_fetchchar, &fd, &packets, 0);
294                 parse_keys(packets, publickey);
295                 free_packet_list(packets);
296                 packets = NULL;
297                 close(fd);
298                 ret = 1;
299         }
300
301         if (!intrans)
302                 fs_endtrans();
303         return ret;
304 }
305
306 /**
307  *      store_key - Takes a key and stores it.
308  *      @publickey: A pointer to the public key to store.
309  *      @intrans: If we're already in a transaction.
310  *      @update: If true the key exists and should be updated.
311  */
312 static int fs_store_key(struct openpgp_publickey *publickey, bool intrans,
313               bool update)
314 {
315         static char buffer[PATH_MAX];
316         static char wbuffer[PATH_MAX];
317         int ret = 0, fd;
318         struct openpgp_packet_list *packets = NULL;
319         struct openpgp_packet_list *list_end = NULL;
320         struct openpgp_publickey *next = NULL;
321         uint64_t keyid = get_keyid(publickey);
322         struct ll *wordlist = NULL, *wl = NULL;
323         struct skshash hash;
324         uint64_t *subkeyids = NULL;
325         uint32_t hashid;
326         int i = 0;
327
328
329         if (!intrans)
330                 fs_starttrans();
331
332         prove_path_to(keyid, "key");
333         keypath(buffer, sizeof(buffer), keyid);
334
335         if ((fd =
336              open(buffer, O_WRONLY | (update ? O_TRUNC : O_CREAT),
337                   0644)) != -1) {
338                 next = publickey->next;
339                 publickey->next = NULL;
340                 flatten_publickey(publickey, &packets, &list_end);
341                 publickey->next = next;
342
343                 write_openpgp_stream(file_putchar, &fd, packets);
344                 close(fd);
345                 free_packet_list(packets);
346                 packets = NULL;
347                 ret = 1;
348         }
349
350         if (ret) {
351                 wl = wordlist = makewordlistfromkey(wordlist, publickey);
352                 while (wl) {
353                         uint32_t hash = calchash((uint8_t *) (wl->object));
354                         prove_path_to(hash, "words");
355
356                         worddir(wbuffer, sizeof(wbuffer), wl->object, hash);
357                         mkdir(wbuffer, 0777);
358                         wordpath(wbuffer, sizeof(wbuffer), wl->object, hash,
359                                 keyid);
360                         link(buffer, wbuffer);
361
362                         wl = wl->next;
363                 }
364                 llfree(wordlist, free);
365                 
366                 subkeyids = keysubkeys(publickey);
367                 i = 0;
368                 while (subkeyids != NULL && subkeyids[i] != 0) {
369                         prove_path_to(subkeyids[i], "subkeys");
370
371                         subkeydir(wbuffer, sizeof(wbuffer), subkeyids[i]);
372                         mkdir(wbuffer, 0777);
373                         subkeypath(wbuffer, sizeof(wbuffer), subkeyids[i],
374                                 keyid);
375                         link(buffer, wbuffer);
376
377                         i++;
378                 }
379                 if (subkeyids != NULL) {
380                         free(subkeyids);
381                         subkeyids = NULL;
382                 }
383
384                 get_skshash(publickey, &hash);
385                 hashid = (hash.hash[0] << 24) + (hash.hash[1] << 16) +
386                                 (hash.hash[2] << 8) + hash.hash[3];
387                 prove_path_to(hashid, "skshash");
388                 skshashpath(wbuffer, sizeof(wbuffer), &hash);
389                 link(buffer, wbuffer);
390         }
391
392         if (!intrans)
393                 fs_endtrans();
394         return ret;
395 }
396
397 /**
398  *      delete_key - Given a keyid delete the key from storage.
399  *      @keyid: The keyid to delete.
400  *      @intrans: If we're already in a transaction.
401  */
402 static int fs_delete_key(uint64_t keyid, bool intrans)
403 {
404         static char buffer[PATH_MAX];
405         int ret;
406         struct openpgp_publickey *pk = NULL;
407         struct skshash hash;
408         struct ll *wordlist = NULL, *wl = NULL;
409         uint64_t *subkeyids = NULL;
410         int i = 0;
411
412         if ((keyid >> 32) == 0)
413                 keyid = fs_getfullkeyid(keyid);
414
415         if (!intrans)
416                 fs_starttrans();
417
418         ret = fs_fetch_key(keyid, &pk, true);
419
420         if (ret) {
421                 logthing(LOGTHING_DEBUG, "Wordlist for key %016" PRIX64,
422                          keyid);
423                 wl = wordlist = makewordlistfromkey(wordlist, pk);
424                 logthing(LOGTHING_DEBUG,
425                          "Wordlist for key %016" PRIX64 " done", keyid);
426                 while (wl) {
427                         uint32_t hash = calchash((uint8_t *) (wl->object));
428                         prove_path_to(hash, "words");
429
430                         wordpath(buffer, sizeof(buffer), wl->object, hash,
431                                 keyid);
432                         unlink(buffer);
433
434                         wl = wl->next;
435                 }
436
437                 subkeyids = keysubkeys(pk);
438                 i = 0;
439                 while (subkeyids != NULL && subkeyids[i] != 0) {
440                         prove_path_to(subkeyids[i], "subkeys");
441
442                         subkeypath(buffer, sizeof(buffer), subkeyids[i],
443                                 keyid);
444                         unlink(buffer);
445
446                         i++;
447                 }
448                 if (subkeyids != NULL) {
449                         free(subkeyids);
450                         subkeyids = NULL;
451                 }
452
453                 get_skshash(pk, &hash);
454                 skshashpath(buffer, sizeof(buffer), &hash);
455                 unlink(buffer);
456         }
457
458         keypath(buffer, sizeof(buffer), keyid);
459         unlink(buffer);
460
461         if (!intrans)
462                 fs_endtrans();
463         return 1;
464 }
465
466 static struct ll *internal_get_key_by_word(char *word, struct ll *mct)
467 {
468         struct ll *keys = NULL;
469         DIR *d = NULL;
470         char buffer[PATH_MAX];
471         uint32_t hash = calchash((uint8_t *) (word));
472         struct dirent *de;
473
474         worddir(buffer, sizeof(buffer), word, hash);
475         d = opendir(buffer);
476         logthing(LOGTHING_DEBUG, "Scanning for word %s in dir %s", word,
477                  buffer);
478         if (d)
479                 do {
480                         de = readdir(d);
481                         if (de && de->d_name[0] != '.') {
482                                 if ((!mct)
483                                     || (llfind(mct, de->d_name,
484                                         (int (*)(const void *, const void *))
485                                                     strcmp) !=
486                                         NULL)) {
487                                         logthing(LOGTHING_DEBUG,
488                                                  "Found %s // %s", word,
489                                                  de->d_name);
490                                         keys =
491                                             lladd(keys,
492                                                   strdup(de->d_name));
493                                 }
494                         }
495                 } while (de);
496         closedir(d);
497
498         return keys;
499 }
500
501 /*
502  *      fetch_key_text - Trys to find the keys that contain the supplied text.
503  *      @search: The text to search for.
504  *      @publickey: A pointer to a structure to return the key in.
505  */
506 static int fs_fetch_key_text(const char *search,
507                    struct openpgp_publickey **publickey)
508 {
509         struct ll *wordlist = NULL, *wl = NULL;
510         struct ll *keylist = NULL;
511         char      *searchtext = NULL;
512         int addedkeys = 0;
513
514         logthing(LOGTHING_DEBUG, "Search was '%s'", search);
515
516         searchtext = strdup(search);
517         wl = wordlist = makewordlist(wordlist, searchtext);
518
519         keylist = internal_get_key_by_word(wordlist->object, NULL);
520
521         if (!keylist) {
522                 llfree(wordlist, NULL);
523                 free(searchtext);
524                 searchtext = NULL;
525                 return 0;
526         }
527
528         wl = wl->next;
529         while (wl) {
530                 struct ll *nkl =
531                     internal_get_key_by_word(wl->object, keylist);
532                 if (!nkl) {
533                         llfree(wordlist, NULL);
534                         llfree(keylist, free);
535                         free(searchtext);
536                         searchtext = NULL;
537                         return 0;
538                 }
539                 llfree(keylist, free);
540                 keylist = nkl;
541                 wl = wl->next;
542         }
543
544         llfree(wordlist, NULL);
545
546         /* Now add the keys... */
547         wl = keylist;
548         while (wl) {
549                 logthing(LOGTHING_DEBUG, "Adding key: %s", wl->object);
550                 addedkeys +=
551                     fs_fetch_key(strtoull(wl->object, NULL, 16), publickey,
552                               false);
553                 if (addedkeys >= config.maxkeys)
554                         break;
555                 wl = wl->next;
556         }
557
558         llfree(keylist, free);
559         free(searchtext);
560         searchtext = NULL;
561
562         return addedkeys;
563 }
564
565 /**
566  *      fetch_key_skshash - Given an SKS hash fetch the key from storage.
567  *      @hash: The hash to fetch.
568  *      @publickey: A pointer to a structure to return the key in.
569  *      @intrans: If we're already in a transaction.
570  */
571 static int fs_fetch_key_skshash(const struct skshash *hash,
572               struct openpgp_publickey **publickey)
573 {
574         static char buffer[PATH_MAX];
575         int ret = 0, fd;
576         struct openpgp_packet_list *packets = NULL;
577
578         skshashpath(buffer, sizeof(buffer), hash);
579         if ((fd = open(buffer, O_RDONLY)) != -1) {
580                 read_openpgp_stream(file_fetchchar, &fd, &packets, 0);
581                 parse_keys(packets, publickey);
582                 free_packet_list(packets);
583                 packets = NULL;
584                 close(fd);
585                 ret = 1;
586         }
587
588         return ret;
589 }
590
591 /**
592  *      iterate_keys - call a function once for each key in the db.
593  *      @iterfunc: The function to call.
594  *      @ctx: A context pointer
595  *
596  *      Calls iterfunc once for each key in the database. ctx is passed
597  *      unaltered to iterfunc. This function is intended to aid database dumps
598  *      and statistic calculations.
599  *
600  *      Returns the number of keys we iterated over.
601  */
602 static int fs_iterate_keys(void (*iterfunc)(void *ctx,
603                 struct openpgp_publickey *key), void *ctx)
604 {
605         return 0;
606 }
607
608 /*
609  * Include the basic keydb routines.
610  */
611 #define NEED_KEYID2UID 1
612 #define NEED_GETKEYSIGS 1
613 #define NEED_UPDATEKEYS 1
614 #include "keydb.c"
615
616 struct dbfuncs keydb_fs_funcs = {
617         .initdb                 = fs_initdb,
618         .cleanupdb              = fs_cleanupdb,
619         .starttrans             = fs_starttrans,
620         .endtrans               = fs_endtrans,
621         .fetch_key              = fs_fetch_key,
622         .fetch_key_text         = fs_fetch_key_text,
623         .fetch_key_skshash      = fs_fetch_key_skshash,
624         .store_key              = fs_store_key,
625         .update_keys            = generic_update_keys,
626         .delete_key             = fs_delete_key,
627         .getkeysigs             = generic_getkeysigs,
628         .cached_getkeysigs      = generic_cached_getkeysigs,
629         .keyid2uid              = generic_keyid2uid,
630         .getfullkeyid           = fs_getfullkeyid,
631         .iterate_keys           = fs_iterate_keys,
632 };