]> git.sommitrealweird.co.uk Git - onak.git/blob - keydb_fs.c
01101f0eaeca4683a077e71fbc7ed45c651ce8ca
[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,
351                                         (int (*)(const void *, const void *))
352                                                     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         char      *searchtext = NULL;
379         int addedkeys = 0;
380
381         logthing(LOGTHING_CRITICAL, "Search was '%s'", search);
382
383         searchtext = strdup(search);
384         wl = wordlist = makewordlist(wordlist, searchtext);
385
386         keylist = internal_get_key_by_word(wordlist->object, NULL);
387
388         if (!keylist) {
389                 llfree(wordlist, NULL);
390                 free(searchtext);
391                 searchtext = NULL;
392                 return 0;
393         }
394
395         wl = wl->next;
396         while (wl) {
397                 struct ll *nkl =
398                     internal_get_key_by_word(wl->object, keylist);
399                 if (!nkl) {
400                         llfree(wordlist, NULL);
401                         llfree(keylist, free);
402                         free(searchtext);
403                         searchtext = NULL;
404                         return 0;
405                 }
406                 llfree(keylist, free);
407                 keylist = nkl;
408                 wl = wl->next;
409         }
410
411         llfree(wordlist, NULL);
412
413         /* Now add the keys... */
414         wl = keylist;
415         while (wl) {
416                 logthing(LOGTHING_CRITICAL, "Adding key: %s", wl->object);
417                 addedkeys +=
418                     fetch_key(strtoull(wl->object, NULL, 16), publickey,
419                               false);
420                 if (addedkeys >= config.maxkeys)
421                         break;
422                 wl = wl->next;
423         }
424
425         llfree(keylist, free);
426         free(searchtext);
427         searchtext = NULL;
428
429         return addedkeys;
430 }
431
432 /*
433  *      dumpdb - dump the key database
434  *      @filenamebase: The base filename to use for the dump.
435  */
436 int dumpdb(char *filenamebase)
437 {
438         return 0;
439 }
440
441 uint64_t getfullkeyid(uint64_t keyid)
442 {
443         static char buffer[PATH_MAX];
444         DIR *d;
445         struct dirent *de;
446         uint64_t ret = 0;
447
448         keydir(buffer, keyid);
449
450         d = opendir(buffer);
451         do {
452                 de = readdir(d);
453                 if (de)
454                         if (de && de->d_name[0] != '.') {
455                                 ret = strtoull(de->d_name, NULL, 16);
456                         }
457         } while (de && de->d_name[0] == '.');
458         closedir(d);
459         return ret;
460 }
461
462 /*
463  * Include the basic keydb routines.
464  */
465 #define NEED_KEYID2UID 1
466 #define NEED_GETKEYSIGS 1
467 #include "keydb.c"