]> git.sommitrealweird.co.uk Git - onak.git/blob - keydb_db3.c
6d98d3881f0f253eaa63137ee31bb0bcf715b939
[onak.git] / keydb_db3.c
1 /*
2  * keydb_db3.c - Routines to store and fetch keys in a DB3 database.
3  *
4  * Jonathan McDowell <noodles@earth.li>
5  *
6  * Copyright 2002 Project Purple
7  */
8
9 #include <sys/types.h>
10 #include <sys/uio.h>
11 #include <ctype.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
19 #include <db.h>
20
21 #include "charfuncs.h"
22 #include "keydb.h"
23 #include "keyid.h"
24 #include "keyindex.h"
25 #include "keystructs.h"
26 #include "mem.h"
27 #include "onak-conf.h"
28 #include "parsekey.h"
29
30 /**
31  *      dbconn - our connection to the key database.
32  */
33 static DB *dbconn = NULL;
34
35 /**
36  *      worddb - our connection to the word database.
37  */
38 static DB *worddb = NULL;
39
40 /**
41  *      makewordlist - Takes a string and splits it into a set of unique words.
42  *      @wordlist: The current word list.
43  *      @words: The string to split and add.
44  *
45  *      We take words and split it on non alpha numeric characters. These get
46  *      added to the word list if they're not already present. If the wordlist
47  *      is NULL then we start a new list, otherwise it's search for already
48  *      added words. Note that words is modified in the process of scanning.
49  *
50  *      Returns the new word list.
51  */
52 struct ll *makewordlist(struct ll *wordlist, char *word)
53 {
54         char *start = NULL;
55         char *end = NULL;
56
57         /*
58          * Walk through the words string, spliting on non alphanumerics and
59          * then checking if the word already exists in the list. If not then
60          * we add it.
61          */
62         end = word;
63         while (end != NULL && *end != 0) {
64                 start = end;
65                 while (*start != 0 && !isalnum(*start)) {
66                         start++;
67                 }
68                 end = start;
69                 while (*end != 0 && isalnum(*end)) {
70                         *end = tolower(*end);
71                         end++;
72                 }
73                 if (end - start > 1) {
74                         if (*end != 0) {
75                                 *end = 0;
76                                 end++;
77                         }
78                         
79                         if (llfind(wordlist, start,
80                                         strcmp) == NULL) {
81                                 wordlist = lladd(wordlist,
82                                                 start);
83                         }
84                 }
85         }
86
87         return wordlist;
88 }
89
90 /**
91  *      initdb - Initialize the key database.
92  *
93  *      This function should be called before any of the other functions in
94  *      this file are called in order to allow the DB to be initialized ready
95  *      for access.
96  */
97 void initdb(void)
98 {
99         char buf[1024];
100         int ret = 0;
101
102         strcpy(buf, config.db_dir);
103         strcat(buf, "/keydb.db");
104         
105         ret = db_create(&dbconn, NULL, 0);
106         if (ret != 0) {
107                 fprintf(stderr, "db_create: %s\n", db_strerror(ret));
108                 exit(1);
109         }
110
111         ret = dbconn->open(dbconn, buf, NULL, DB_HASH,
112                         DB_CREATE,
113                         0664);
114         if (ret != 0) {
115                 dbconn->err(dbconn, ret, "%s", buf);
116                 exit(1);
117         }
118
119         strcpy(buf, config.db_dir);
120         strcat(buf, "/worddb");
121         
122         ret = db_create(&worddb, NULL, 0);
123         if (ret != 0) {
124                 fprintf(stderr, "db_create: %s\n", db_strerror(ret));
125                 exit(1);
126         }
127         ret = worddb->set_flags(worddb, DB_DUP);
128
129         ret = worddb->open(worddb, buf, NULL, DB_BTREE,
130                         DB_CREATE,
131                         0664);
132         if (ret != 0) {
133                 worddb->err(worddb, ret, "%s", buf);
134                 exit(1);
135         }
136         
137         return;
138 }
139
140 /**
141  *      cleanupdb - De-initialize the key database.
142  *
143  *      This function should be called upon program exit to allow the DB to
144  *      cleanup after itself.
145  */
146 void cleanupdb(void)
147 {
148         worddb->close(worddb, 0);
149         worddb = NULL;
150         dbconn->close(dbconn, 0);
151         dbconn = NULL;
152 }
153
154 /**
155  *      starttrans - Start a transaction.
156  *
157  *      Start a transaction. Intended to be used if we're about to perform many
158  *      operations on the database to help speed it all up, or if we want
159  *      something to only succeed if all relevant operations are successful.
160  */
161 bool starttrans(void)
162 {
163         return true;
164 }
165
166 /**
167  *      endtrans - End a transaction.
168  *
169  *      Ends a transaction.
170  */
171 void endtrans(void)
172 {
173         return;
174 }
175
176 /**
177  *      fetch_key - Given a keyid fetch the key from storage.
178  *      @keyid: The keyid to fetch.
179  *      @publickey: A pointer to a structure to return the key in.
180  *      @intrans: If we're already in a transaction.
181  *
182  *      We use the hex representation of the keyid as the filename to fetch the
183  *      key from. The key is stored in the file as a binary OpenPGP stream of
184  *      packets, so we can just use read_openpgp_stream() to read the packets
185  *      in and then parse_keys() to parse the packets into a publickey
186  *      structure.
187  */
188 int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
189                 bool intrans)
190 {
191         struct openpgp_packet_list *packets = NULL;
192         DBT key, data;
193         int ret = 0;
194         int numkeys = 0;
195         struct buffer_ctx fetchbuf;
196
197         memset(&key, 0, sizeof(key));
198         memset(&data, 0, sizeof(data));
199
200         data.size = 0;
201         data.data = NULL;
202
203         key.size = sizeof(keyid);
204         key.data = &keyid;
205         keyid &= 0xFFFFFFFF;
206
207         ret = dbconn->get(dbconn,
208                         NULL, /* txn id */
209                         &key,
210                         &data,
211                         0); /* flags*/
212         
213         if (ret == 0) {
214                 fetchbuf.buffer = data.data;
215                 fetchbuf.offset = 0;
216                 fetchbuf.size = data.size;
217                 read_openpgp_stream(buffer_fetchchar, &fetchbuf,
218                                 &packets);
219                 parse_keys(packets, publickey);
220                 free_packet_list(packets);
221                 packets = NULL;
222                 numkeys++;
223         } else if (ret != DB_NOTFOUND) {
224                 dbconn->err(dbconn, ret, "Problem retrieving key");
225         }
226
227         return (numkeys);
228 }
229
230 int worddb_cmp(const char *d1, const char *d2)
231 {
232         return memcmp(d1, d2, 12);
233 }
234
235 /**
236  *      fetch_key_text - Trys to find the keys that contain the supplied text.
237  *      @search: The text to search for.
238  *      @publickey: A pointer to a structure to return the key in.
239  *
240  *      This function searches for the supplied text and returns the keys that
241  *      contain it.
242  */
243 int fetch_key_text(const char *search, struct openpgp_publickey **publickey)
244 {
245         DBC *cursor = NULL;
246         DBT key, data;
247         int ret;
248         uint64_t keyid;
249         int i;
250         int numkeys;
251         char *searchtext = NULL;
252         struct ll *wordlist = NULL;
253         struct ll *curword = NULL;
254         struct ll *keylist = NULL;
255         struct ll *newkeylist = NULL;
256
257         numkeys = 0;
258         searchtext = strdup(search);
259         wordlist = makewordlist(wordlist, searchtext);
260
261
262         ret = worddb->cursor(worddb,
263                         NULL, /* txn */
264                         &cursor,
265                         0);   /* flags */
266
267         for (curword = wordlist; curword != NULL; curword = curword->next) {
268                 memset(&key, 0, sizeof(key));
269                 memset(&data, 0, sizeof(data));
270                 key.data = curword->object;
271                 key.size = strlen(curword->object);
272                 data.flags = DB_DBT_MALLOC;
273                 ret = cursor->c_get(cursor,
274                                 &key,
275                                 &data,
276                                 DB_SET);
277                 while (ret == 0 && strncmp(key.data, curword->object,
278                                         key.size) == 0 &&
279                                 ((char *) curword->object)[key.size] == 0) {
280                         keyid = 0;
281                         for (i = 4; i < 12; i++) {
282                                 keyid <<= 8;
283                                 keyid += ((unsigned char *)
284                                                 data.data)[i];
285                         }
286
287                         if (keylist == NULL ||
288                                         llfind(keylist, data.data,
289                                                 worddb_cmp) != NULL) {
290                                 newkeylist = lladd(newkeylist, data.data);
291                                 data.data = NULL;
292                         } else {
293                                 free(data.data);
294                                 data.data = NULL;
295                         }
296                         ret = cursor->c_get(cursor,
297                                         &key,
298                                         &data,
299                                         DB_NEXT);
300                 }
301                 llfree(keylist, free);
302                 keylist = newkeylist;
303                 newkeylist = NULL;
304                 if (data.data != NULL) {
305                         free(data.data);
306                         data.data = NULL;
307                 }
308         }
309         llfree(wordlist, NULL);
310         wordlist = NULL;
311         
312         for (newkeylist = keylist; newkeylist != NULL;
313                         newkeylist = newkeylist->next) {
314
315                         keyid = 0;
316                         for (i = 4; i < 12; i++) {
317                                 keyid <<= 8;
318                                 keyid += ((unsigned char *)
319                                                 newkeylist->object)[i];
320                         }
321
322                         numkeys += fetch_key(keyid,
323                                         publickey,
324                                         false);
325         }
326         llfree(keylist, free);
327         keylist = NULL;
328         free(searchtext);
329         searchtext = NULL;
330
331         ret = cursor->c_close(cursor);
332         cursor = NULL;
333         
334         return (numkeys);
335 }
336
337 /**
338  *      store_key - Takes a key and stores it.
339  *      @publickey: A pointer to the public key to store.
340  *      @intrans: If we're already in a transaction.
341  *      @update: If true the key exists and should be updated.
342  *
343  *      Again we just use the hex representation of the keyid as the filename
344  *      to store the key to. We flatten the public key to a list of OpenPGP
345  *      packets and then use write_openpgp_stream() to write the stream out to
346  *      the file. If update is true then we delete the old key first, otherwise
347  *      we trust that it doesn't exist.
348  */
349 int store_key(struct openpgp_publickey *publickey, bool intrans, bool update)
350 {
351         struct     openpgp_packet_list *packets = NULL;
352         struct     openpgp_packet_list *list_end = NULL;
353         struct     openpgp_publickey *next = NULL;
354         int        ret = 0;
355         int        i = 0;
356         struct     buffer_ctx storebuf;
357         DBT        key;
358         DBT        data;
359         uint64_t   keyid = 0;
360         char     **uids = NULL;
361         char      *primary = NULL;
362         unsigned char worddb_data[12];
363         struct ll *wordlist = NULL;
364         struct ll *curword  = NULL;
365
366         keyid = get_keyid(publickey);
367
368         /*
369          * Delete the key if we already have it.
370          *
371          * TODO: Can we optimize this perhaps? Possibly when other data is
372          * involved as well? I suspect this is easiest and doesn't make a lot
373          * of difference though - the largest chunk of data is the keydata and
374          * it definitely needs updated.
375          */
376         if (update) {
377                 delete_key(keyid, true);
378         }
379
380         /*
381          * Convert the key to a flat set of binary data.
382          */
383         next = publickey->next;
384         publickey->next = NULL;
385         flatten_publickey(publickey, &packets, &list_end);
386         publickey->next = next;
387
388         storebuf.offset = 0; 
389         storebuf.size = 8192;
390         storebuf.buffer = malloc(8192);
391         
392         write_openpgp_stream(buffer_putchar, &storebuf, packets);
393
394         /*
395          * Now we have the key data store it in the DB; the keyid is the key.
396          */
397         memset(&key, 0, sizeof(key));
398         memset(&data, 0, sizeof(data));
399         key.data = &keyid;
400         key.size = sizeof(keyid);
401         keyid &= 0xFFFFFFFF;
402         data.size = storebuf.offset;
403         data.data = storebuf.buffer;
404
405         ret = dbconn->put(dbconn,
406                         NULL, /* txn id */
407                         &key,
408                         &data,
409                         0); /* flags*/
410         if (ret != 0) {
411                 dbconn->err(dbconn, ret, "Problem storing key");
412         }
413
414         free(storebuf.buffer);
415         storebuf.buffer = NULL;
416         storebuf.size = 0;
417         storebuf.offset = 0; 
418
419         free_packet_list(packets);
420         packets = NULL;
421
422         /*
423          * Walk through our uids storing the words into the db with the keyid.
424          */
425         uids = keyuids(publickey, &primary);
426         if (uids != NULL) {
427                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
428                         wordlist = makewordlist(wordlist, uids[i]);
429                 }
430
431                 for (curword = wordlist; curword != NULL;
432                                 curword = curword->next) {
433                         memset(&key, 0, sizeof(key));
434                         memset(&data, 0, sizeof(data));
435                         key.data = curword->object;
436                         key.size = strlen(key.data);
437                         data.data = worddb_data;
438                         data.size = sizeof(worddb_data);
439
440                         /*
441                          * Our data is the key creation time followed by the
442                          * key id.
443                          */
444                         worddb_data[ 0] = publickey->publickey->data[1];
445                         worddb_data[ 1] = publickey->publickey->data[2];
446                         worddb_data[ 2] = publickey->publickey->data[3];
447                         worddb_data[ 3] = publickey->publickey->data[4];
448                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
449                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
450                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
451                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
452                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
453                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
454                         worddb_data[10] = (keyid >>  8) & 0xFF;
455                         worddb_data[11] = keyid & 0xFF; 
456                         ret = worddb->put(worddb,
457                                 0,
458                                 &key,
459                                 &data,
460                                 0);
461                         if (ret != 0) {
462                                 worddb->err(worddb, ret,
463                                         "Problem storing key");
464                         }
465                 }
466
467                 /*
468                  * Free our UID and word lists.
469                  */
470                 llfree(wordlist, NULL);
471                 for (i = 0; uids[i] != NULL; i++) {
472                         free(uids[i]);
473                         uids[i] = NULL;
474                 }
475                 free(uids);
476                 uids = NULL;
477         }
478
479         return 0;
480 }
481
482 /**
483  *      delete_key - Given a keyid delete the key from storage.
484  *      @keyid: The keyid to delete.
485  *      @intrans: If we're already in a transaction.
486  *
487  *      This function deletes a public key from whatever storage mechanism we
488  *      are using. Returns 0 if the key existed.
489  */
490 int delete_key(uint64_t keyid, bool intrans)
491 {
492         struct openpgp_publickey *publickey = NULL;
493         DBT key, data;
494         DBC *cursor = NULL;
495         int ret = 0;
496         int i;
497         char **uids = NULL;
498         char *primary = NULL;
499         unsigned char worddb_data[12];
500         struct ll *wordlist = NULL;
501         struct ll *curword  = NULL;
502
503         keyid &= 0xFFFFFFFF;
504
505         fetch_key(keyid, &publickey, intrans);
506
507         /*
508          * Walk through the uids removing the words from the worddb.
509          */
510         if (publickey != NULL) {
511                 uids = keyuids(publickey, &primary);
512         }
513         if (uids != NULL) {
514                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
515                         wordlist = makewordlist(wordlist, uids[i]);
516                 }
517                                 
518                 ret = worddb->cursor(worddb,
519                         NULL, /* txn */
520                         &cursor,
521                         0);   /* flags */
522
523                 for (curword = wordlist; curword != NULL;
524                                 curword = curword->next) {
525                         memset(&key, 0, sizeof(key));
526                         memset(&data, 0, sizeof(data));
527                         key.data = curword->object;
528                         key.size = strlen(key.data);
529                         data.data = worddb_data;
530                         data.size = sizeof(worddb_data);
531
532                         /*
533                          * Our data is the key creation time followed by the
534                          * key id.
535                          */
536                         worddb_data[ 0] = publickey->publickey->data[1];
537                         worddb_data[ 1] = publickey->publickey->data[2];
538                         worddb_data[ 2] = publickey->publickey->data[3];
539                         worddb_data[ 3] = publickey->publickey->data[4];
540                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
541                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
542                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
543                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
544                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
545                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
546                         worddb_data[10] = (keyid >>  8) & 0xFF;
547                         worddb_data[11] = keyid & 0xFF; 
548
549                         ret = cursor->c_get(cursor,
550                                 &key,
551                                 &data,
552                                 DB_GET_BOTH);
553                         if (ret == 0) {
554                                 ret = cursor->c_del(cursor, 0);
555                         }
556
557                         if (ret != 0) {
558                                 worddb->err(worddb, ret,
559                                         "Problem deleting word.");
560                         }
561                 }
562                 ret = cursor->c_close(cursor);
563                 cursor = NULL;
564
565                 /*
566                  * Free our UID and word lists.
567                  */
568                 llfree(wordlist, NULL);
569                 for (i = 0; uids[i] != NULL; i++) {
570                         free(uids[i]);
571                         uids[i] = NULL;
572                 }
573                 free(uids);
574                 uids = NULL;
575                 free_publickey(publickey);
576                 publickey = NULL;
577         }
578
579         key.data = &keyid;
580         key.size = sizeof(keyid);
581
582         dbconn->del(dbconn,
583                         NULL, /* txn id */
584                         &key,
585                         0); /* flags */
586
587         return (ret == DB_NOTFOUND);
588 }
589
590 /*
591  * Include the basic keydb routines.
592  */
593 #define NEED_GETFULLKEYID 1
594 #define NEED_GETKEYSIGS 1
595 #define NEED_KEYID2UID 1
596 #include "keydb.c"