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