]> git.sommitrealweird.co.uk Git - onak.git/blob - keydb_fs.c
7e739d834623c1193bdf283c70362efbe9d1467d
[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.1 2004/05/27 03:37:58 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
283
284                 llfree(wordlist, NULL);
285         }
286
287         if (!intrans)
288                 endtrans();
289         return ret;
290 }
291
292 /**
293  *      delete_key - Given a keyid delete the key from storage.
294  *      @keyid: The keyid to delete.
295  *      @intrans: If we're already in a transaction.
296  */
297 int delete_key(uint64_t keyid, bool intrans)
298 {
299         static char buffer[PATH_MAX];
300         int ret;
301         struct openpgp_publickey *pk = NULL;
302         struct ll *wordlist = NULL, *wl = NULL;
303
304         if ((keyid >> 32) == 0)
305                 keyid = getfullkeyid(keyid);
306
307         if (!intrans)
308                 starttrans();
309
310         ret = fetch_key(keyid, &pk, true);
311
312         if (ret) {
313                 logthing(LOGTHING_CRITICAL, "Wordlist for key %016llX",
314                          keyid);
315                 wl = wordlist = makewordlistfromkey(wordlist, pk);
316                 logthing(LOGTHING_CRITICAL,
317                          "Wordlist for key %016llX done", keyid);
318                 while (wl) {
319                         uint32_t hash = calchash((uint8_t *) (wl->object));
320                         prove_path_to(hash, "words");
321
322                         wordpath(buffer, wl->object, hash, keyid);
323                         unlink(buffer);
324
325                         wl = wl->next;
326                 }
327         }
328
329         keypath(buffer, keyid);
330         unlink(buffer);
331
332         if (!intrans)
333                 endtrans();
334         return 1;
335 }
336
337 static struct ll *internal_get_key_by_word(char *word, struct ll *mct)
338 {
339         struct ll *keys = NULL;
340         DIR *d = NULL;
341         char buffer[PATH_MAX];
342         uint32_t hash = calchash((uint8_t *) (word));
343         struct dirent *de;
344
345         worddir(buffer, word, hash);
346         d = opendir(buffer);
347         logthing(LOGTHING_CRITICAL, "Scanning for word %s in dir %s", word,
348                  buffer);
349         if (d)
350                 do {
351                         de = readdir(d);
352                         if (de && de->d_name[0] != '.') {
353                                 if ((!mct)
354                                     || (llfind(mct, de->d_name, strcmp) !=
355                                         NULL)) {
356                                         logthing(LOGTHING_CRITICAL,
357                                                  "Found %s // %s", word,
358                                                  de->d_name);
359                                         keys =
360                                             lladd(keys,
361                                                   strdup(de->d_name));
362                                 }
363                         }
364                 } while (de);
365         closedir(d);
366
367         return keys;
368 }
369
370 /*
371  *      fetch_key_text - Trys to find the keys that contain the supplied text.
372  *      @search: The text to search for.
373  *      @publickey: A pointer to a structure to return the key in.
374  */
375 int fetch_key_text(const char *search,
376                    struct openpgp_publickey **publickey)
377 {
378         struct ll *wordlist = NULL, *wl = NULL;
379         struct ll *keylist = NULL;
380         int addedkeys = 0;
381
382         logthing(LOGTHING_CRITICAL, "Search was '%s'", search);
383
384         wl = wordlist = makewordlist(wordlist, search);
385
386         keylist = internal_get_key_by_word(wordlist->object, NULL);
387
388         if (!keylist) {
389                 llfree(wordlist, NULL);
390                 return 0;
391         }
392
393         wl = wl->next;
394         while (wl) {
395                 struct ll *nkl =
396                     internal_get_key_by_word(wl->object, keylist);
397                 if (!nkl) {
398                         llfree(wordlist, NULL);
399                         llfree(keylist, free);
400                         return 0;
401                 }
402                 llfree(keylist, free);
403                 keylist = nkl;
404                 wl = wl->next;
405         }
406
407         llfree(wordlist, NULL);
408
409         /* Now add the keys... */
410         wl = keylist;
411         while (wl) {
412                 logthing(LOGTHING_CRITICAL, "Adding key: %s", wl->object);
413                 addedkeys +=
414                     fetch_key(strtoull(wl->object, NULL, 16), publickey,
415                               false);
416                 if (addedkeys >= config.maxkeys)
417                         break;
418                 wl = wl->next;
419         }
420
421         llfree(keylist, free);
422
423         return addedkeys;
424 }
425
426 /*
427  *      dumpdb - dump the key database
428  *      @filenamebase: The base filename to use for the dump.
429  */
430 int dumpdb(char *filenamebase)
431 {
432         return 0;
433 }
434
435 uint64_t getfullkeyid(uint64_t keyid)
436 {
437         static char buffer[PATH_MAX];
438         DIR *d;
439         struct dirent *de;
440         uint64_t ret = 0;
441
442         keydir(buffer, keyid);
443
444         d = opendir(buffer);
445         do {
446                 de = readdir(d);
447                 if (de)
448                         if (de && de->d_name[0] != '.') {
449                                 ret = strtoull(de->d_name, NULL, 16);
450                         }
451         } while (de && de->d_name[0] == '.');
452         closedir(d);
453         return ret;
454 }
455
456 /*
457  * Include the basic keydb routines.
458  */
459 #define NEED_KEYID2UID 1
460 #define NEED_GETKEYSIGS 1
461 #include "keydb.c"