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