]> git.sommitrealweird.co.uk Git - onak.git/blob - keydb_fs.c
First cut at cleanup infrastructure.
[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 static int keydb_lockfile_fd = -1;
34 static bool keydb_lockfile_readonly;
35
36 /*****************************************************************************/
37
38 /* Helper functions */
39
40 #define FNV_offset_basis 2166136261ul
41 #define FNV_mixing_prime 16777619ul
42
43 static uint32_t calchash(uint8_t * ptr)
44 {
45         register uint32_t h = FNV_offset_basis;
46         register uint32_t p = FNV_mixing_prime;
47         register uint32_t n = strlen((char *) ptr);
48         register uint8_t *c = ptr;
49         while (n--) {
50                 h *= p;
51                 h ^= *c++;
52         }
53         return h ? h : 1;       /* prevent a hash of zero happening */
54 }
55
56
57 void keypath(char *buffer, uint64_t _keyid)
58 {
59         uint64_t keyid = _keyid << 32;
60         snprintf(buffer, PATH_MAX, "%s/key/%02X/%02X/%08X/%016llX",
61                  config.db_dir, (uint8_t) ((keyid >> 56) & 0xFF),
62                  (uint8_t) ((keyid >> 48) & 0xFF),
63                  (uint32_t) (keyid >> 32), _keyid);
64 }
65
66 void keydir(char *buffer, uint64_t _keyid)
67 {
68         uint64_t keyid = _keyid << 32;
69         snprintf(buffer, PATH_MAX, "%s/key/%02X/%02X/%08X", config.db_dir,
70                  (uint8_t) ((keyid >> 56) & 0xFF),
71                  (uint8_t) ((keyid >> 48) & 0xFF),
72                  (uint32_t) (keyid >> 32));
73 }
74
75 void prove_path_to(uint64_t keyid, char *what)
76 {
77         static char buffer[1024];
78         snprintf(buffer, PATH_MAX, "%s/%s", config.db_dir, what);
79         mkdir(buffer, 0777);
80
81         snprintf(buffer, PATH_MAX, "%s/%s/%02X", config.db_dir, what,
82                  (uint8_t) ((keyid >> 24) & 0xFF));
83         mkdir(buffer, 0777);
84
85         snprintf(buffer, PATH_MAX, "%s/%s/%02X/%02X", config.db_dir, what,
86                  (uint8_t) ((keyid >> 24) & 0xFF),
87                  (uint8_t) ((keyid >> 16) & 0xFF));
88         mkdir(buffer, 0777);
89
90         snprintf(buffer, PATH_MAX, "%s/%s/%02X/%02X/%08X", config.db_dir, what,
91                  (uint8_t) ((keyid >> 24) & 0xFF),
92                  (uint8_t) ((keyid >> 16) & 0xFF), (uint32_t) (keyid));
93         mkdir(buffer, 0777);
94 }
95
96 void wordpath(char *buffer, char *word, uint32_t hash, uint64_t keyid)
97 {
98         snprintf(buffer, PATH_MAX, "%s/words/%02X/%02X/%08X/%s/%016llX",
99                  config.db_dir, (uint8_t) ((hash >> 24) & 0xFF),
100                  (uint8_t) ((hash >> 16) & 0xFF), hash, word, keyid);
101 }
102
103 void worddir(char *buffer, char *word, uint32_t hash)
104 {
105         snprintf(buffer, PATH_MAX, "%s/words/%02X/%02X/%08X/%s", config.db_dir,
106                  (uint8_t) ((hash >> 24) & 0xFF),
107                  (uint8_t) ((hash >> 16) & 0xFF), hash, word);
108 }
109
110 void subkeypath(char *buffer, uint64_t subkey, uint64_t keyid)
111 {
112         snprintf(buffer, PATH_MAX, "%s/subkeys/%02X/%02X/%08X/%016llX",
113                  config.db_dir,
114                  (uint8_t) ((subkey >> 24) & 0xFF),
115                  (uint8_t) ((subkey >> 16) & 0xFF),
116                  (uint32_t) (subkey & 0xFFFFFFFF),
117                  keyid);
118 }
119
120 void subkeydir(char *buffer, uint64_t subkey)
121 {
122         snprintf(buffer, PATH_MAX, "%s/subkeys/%02X/%02X/%08X",
123                  config.db_dir,
124                  (uint8_t) ((subkey >> 24) & 0xFF),
125                  (uint8_t) ((subkey >> 16) & 0xFF),
126                  (uint32_t) (subkey & 0xFFFFFFFF));
127 }
128
129 /*****************************************************************************/
130
131 /**
132  *      initdb - Initialize the key database.
133  */
134 void initdb(bool readonly)
135 {
136         char buffer[PATH_MAX];
137
138         keydb_lockfile_readonly = readonly;
139
140         snprintf(buffer, PATH_MAX, "%s/.lock", config.db_dir);
141
142         if (access(config.db_dir, R_OK | W_OK | X_OK) == -1) {
143                 if (errno != ENOENT) {
144                         logthing(LOGTHING_CRITICAL,
145                                  "Unable to access keydb_fs root of '%s'. (%s)",
146                                  config.db_dir, strerror(errno));
147                         exit(1);        /* Lacking rwx on the key dir */
148                 }
149                 mkdir(config.db_dir, 0777);
150                 keydb_lockfile_fd = open(buffer, O_RDWR | O_CREAT, 0600);
151         }
152         chdir(config.db_dir);
153         if (keydb_lockfile_fd == -1)
154                 keydb_lockfile_fd = open(buffer,
155                                          (keydb_lockfile_readonly) ?
156                                          O_RDONLY : O_RDWR);
157         if (keydb_lockfile_fd == -1)
158                 keydb_lockfile_fd = open(buffer, O_RDWR | O_CREAT, 0600);
159         if (keydb_lockfile_fd == -1) {
160                 logthing(LOGTHING_CRITICAL,
161                          "Unable to open lockfile '%s'. (%s)",
162                          buffer, strerror(errno));
163                 exit(1);        /* Lacking rwx on the key dir */
164         }
165 }
166
167 /**
168  *      cleanupdb - De-initialize the key database.
169  */
170 void cleanupdb(void)
171 {
172         /* Mmmm nothing to do here? */
173         close(keydb_lockfile_fd);
174 }
175
176 /**
177  *      starttrans - Start a transaction.
178  */
179 bool starttrans(void)
180 {
181         struct flock lockstruct;
182         int remaining = 20;
183         lockstruct.l_type =
184             F_RDLCK | ((keydb_lockfile_readonly) ? 0 : F_WRLCK);
185         lockstruct.l_whence = SEEK_SET;
186         lockstruct.l_start = 0;
187         lockstruct.l_len = 1;
188
189         while (fcntl(keydb_lockfile_fd, F_SETLK, &lockstruct) == -1) {
190                 if (remaining-- == 0)
191                         return false;   /* Hope to hell that noodles DTRT */
192                 usleep(100);
193         }
194         return true;
195 }
196
197 /**
198  *      endtrans - End a transaction.
199  */
200 void endtrans(void)
201 {
202         struct flock lockstruct;
203
204         lockstruct.l_type = F_UNLCK;
205         lockstruct.l_whence = SEEK_SET;
206         lockstruct.l_start = 0;
207         lockstruct.l_len = 1;
208         fcntl(keydb_lockfile_fd, F_SETLK, &lockstruct);
209 }
210
211 /**
212  *      fetch_key - Given a keyid fetch the key from storage.
213  *      @keyid: The keyid to fetch.
214  *      @publickey: A pointer to a structure to return the key in.
215  *      @intrans: If we're already in a transaction.
216  */
217 int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
218               bool intrans)
219 {
220         static char buffer[PATH_MAX];
221         int ret = 0, fd;
222         struct openpgp_packet_list *packets = NULL;
223
224         if (!intrans)
225                 starttrans();
226
227         if ((keyid >> 32) == 0)
228                 keyid = getfullkeyid(keyid);
229
230         keypath(buffer, keyid);
231         if ((fd = open(buffer, O_RDONLY)) != -1) {
232                 /* File is present, load it in... */
233                 read_openpgp_stream(file_fetchchar, &fd, &packets, 0);
234                 parse_keys(packets, publickey);
235                 free_packet_list(packets);
236                 packets = NULL;
237                 close(fd);
238                 ret = 1;
239         }
240
241         if (!intrans)
242                 endtrans();
243         return ret;
244 }
245
246 /**
247  *      store_key - Takes a key and stores it.
248  *      @publickey: A pointer to the public key to store.
249  *      @intrans: If we're already in a transaction.
250  *      @update: If true the key exists and should be updated.
251  */
252 int store_key(struct openpgp_publickey *publickey, bool intrans,
253               bool update)
254 {
255         static char buffer[PATH_MAX];
256         static char wbuffer[PATH_MAX];
257         int ret = 0, fd;
258         struct openpgp_packet_list *packets = NULL;
259         struct openpgp_packet_list *list_end = NULL;
260         struct openpgp_publickey *next = NULL;
261         uint64_t keyid = get_keyid(publickey);
262         struct ll *wordlist = NULL, *wl = NULL;
263         uint64_t *subkeyids = NULL;
264         int i = 0;
265
266
267         if (!intrans)
268                 starttrans();
269
270         prove_path_to(keyid, "key");
271         keypath(buffer, keyid);
272
273         if ((fd =
274              open(buffer, O_WRONLY | (update ? O_TRUNC : O_CREAT),
275                   0644)) != -1) {
276                 next = publickey->next;
277                 publickey->next = NULL;
278                 flatten_publickey(publickey, &packets, &list_end);
279                 publickey->next = next;
280
281                 write_openpgp_stream(file_putchar, &fd, packets);
282                 close(fd);
283                 free_packet_list(packets);
284                 packets = NULL;
285                 ret = 1;
286         }
287
288         if (ret) {
289                 wl = wordlist = makewordlistfromkey(wordlist, publickey);
290                 while (wl) {
291                         uint32_t hash = calchash((uint8_t *) (wl->object));
292                         prove_path_to(hash, "words");
293
294                         worddir(wbuffer, wl->object, hash);
295                         mkdir(wbuffer, 0777);
296                         wordpath(wbuffer, wl->object, hash, keyid);
297                         link(buffer, wbuffer);
298
299                         wl = wl->next;
300                 }
301                 llfree(wordlist, free);
302                 
303                 subkeyids = keysubkeys(publickey);
304                 i = 0;
305                 while (subkeyids != NULL && subkeyids[i] != 0) {
306                         prove_path_to(subkeyids[i], "subkeys");
307
308                         subkeydir(wbuffer, subkeyids[i]);
309                         mkdir(wbuffer, 0777);
310                         subkeypath(wbuffer, subkeyids[i], keyid);
311                         link(buffer, wbuffer);
312
313                         i++;
314                 }
315                 if (subkeyids != NULL) {
316                         free(subkeyids);
317                         subkeyids = NULL;
318                 }
319         }
320
321         if (!intrans)
322                 endtrans();
323         return ret;
324 }
325
326 /**
327  *      delete_key - Given a keyid delete the key from storage.
328  *      @keyid: The keyid to delete.
329  *      @intrans: If we're already in a transaction.
330  */
331 int delete_key(uint64_t keyid, bool intrans)
332 {
333         static char buffer[PATH_MAX];
334         int ret;
335         struct openpgp_publickey *pk = NULL;
336         struct ll *wordlist = NULL, *wl = NULL;
337         uint64_t *subkeyids = NULL;
338         int i = 0;
339
340         if ((keyid >> 32) == 0)
341                 keyid = getfullkeyid(keyid);
342
343         if (!intrans)
344                 starttrans();
345
346         ret = fetch_key(keyid, &pk, true);
347
348         if (ret) {
349                 logthing(LOGTHING_DEBUG, "Wordlist for key %016llX",
350                          keyid);
351                 wl = wordlist = makewordlistfromkey(wordlist, pk);
352                 logthing(LOGTHING_DEBUG,
353                          "Wordlist for key %016llX done", keyid);
354                 while (wl) {
355                         uint32_t hash = calchash((uint8_t *) (wl->object));
356                         prove_path_to(hash, "words");
357
358                         wordpath(buffer, wl->object, hash, keyid);
359                         unlink(buffer);
360
361                         wl = wl->next;
362                 }
363
364                 subkeyids = keysubkeys(pk);
365                 i = 0;
366                 while (subkeyids != NULL && subkeyids[i] != 0) {
367                         prove_path_to(subkeyids[i], "subkeys");
368
369                         subkeypath(buffer, subkeyids[i], keyid);
370                         unlink(buffer);
371
372                         i++;
373                 }
374                 if (subkeyids != NULL) {
375                         free(subkeyids);
376                         subkeyids = NULL;
377                 }
378
379         }
380
381         keypath(buffer, keyid);
382         unlink(buffer);
383
384         if (!intrans)
385                 endtrans();
386         return 1;
387 }
388
389 static struct ll *internal_get_key_by_word(char *word, struct ll *mct)
390 {
391         struct ll *keys = NULL;
392         DIR *d = NULL;
393         char buffer[PATH_MAX];
394         uint32_t hash = calchash((uint8_t *) (word));
395         struct dirent *de;
396
397         worddir(buffer, word, hash);
398         d = opendir(buffer);
399         logthing(LOGTHING_DEBUG, "Scanning for word %s in dir %s", word,
400                  buffer);
401         if (d)
402                 do {
403                         de = readdir(d);
404                         if (de && de->d_name[0] != '.') {
405                                 if ((!mct)
406                                     || (llfind(mct, de->d_name,
407                                         (int (*)(const void *, const void *))
408                                                     strcmp) !=
409                                         NULL)) {
410                                         logthing(LOGTHING_DEBUG,
411                                                  "Found %s // %s", word,
412                                                  de->d_name);
413                                         keys =
414                                             lladd(keys,
415                                                   strdup(de->d_name));
416                                 }
417                         }
418                 } while (de);
419         closedir(d);
420
421         return keys;
422 }
423
424 /*
425  *      fetch_key_text - Trys to find the keys that contain the supplied text.
426  *      @search: The text to search for.
427  *      @publickey: A pointer to a structure to return the key in.
428  */
429 int fetch_key_text(const char *search,
430                    struct openpgp_publickey **publickey)
431 {
432         struct ll *wordlist = NULL, *wl = NULL;
433         struct ll *keylist = NULL;
434         char      *searchtext = NULL;
435         int addedkeys = 0;
436
437         logthing(LOGTHING_DEBUG, "Search was '%s'", search);
438
439         searchtext = strdup(search);
440         wl = wordlist = makewordlist(wordlist, searchtext);
441
442         keylist = internal_get_key_by_word(wordlist->object, NULL);
443
444         if (!keylist) {
445                 llfree(wordlist, NULL);
446                 free(searchtext);
447                 searchtext = NULL;
448                 return 0;
449         }
450
451         wl = wl->next;
452         while (wl) {
453                 struct ll *nkl =
454                     internal_get_key_by_word(wl->object, keylist);
455                 if (!nkl) {
456                         llfree(wordlist, NULL);
457                         llfree(keylist, free);
458                         free(searchtext);
459                         searchtext = NULL;
460                         return 0;
461                 }
462                 llfree(keylist, free);
463                 keylist = nkl;
464                 wl = wl->next;
465         }
466
467         llfree(wordlist, NULL);
468
469         /* Now add the keys... */
470         wl = keylist;
471         while (wl) {
472                 logthing(LOGTHING_DEBUG, "Adding key: %s", wl->object);
473                 addedkeys +=
474                     fetch_key(strtoull(wl->object, NULL, 16), publickey,
475                               false);
476                 if (addedkeys >= config.maxkeys)
477                         break;
478                 wl = wl->next;
479         }
480
481         llfree(keylist, free);
482         free(searchtext);
483         searchtext = NULL;
484
485         return addedkeys;
486 }
487
488 /*
489  *      dumpdb - dump the key database
490  *      @filenamebase: The base filename to use for the dump.
491  */
492 int dumpdb(char *filenamebase)
493 {
494         return 0;
495 }
496
497 uint64_t getfullkeyid(uint64_t keyid)
498 {
499         static char buffer[PATH_MAX];
500         DIR *d = NULL;
501         struct dirent *de = NULL;
502         uint64_t ret = 0;
503
504         keydir(buffer, keyid);
505
506         d = opendir(buffer);
507         if (d) {
508                 do {
509                         de = readdir(d);
510                         if (de && de->d_name[0] != '.') {
511                                 ret = strtoull(de->d_name, NULL, 16);
512                         }
513                 } while (de && de->d_name[0] == '.');
514                 closedir(d);    
515         }
516
517         if (ret == 0) {
518                 subkeydir(buffer, keyid);
519
520                 d = opendir(buffer);
521                 if (d) {
522                         do {
523                                 de = readdir(d);
524                                 if (de && de->d_name[0] != '.') {
525                                         ret = strtoull(de->d_name, NULL, 16);
526                                 }
527                         } while (de && de->d_name[0] == '.');
528                         closedir(d);
529                 }
530         }
531         
532         return ret;
533 }
534
535 /*
536  * Include the basic keydb routines.
537  */
538 #define NEED_KEYID2UID 1
539 #define NEED_GETKEYSIGS 1
540 #include "keydb.c"