cscvs to tla changeset 47
[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 "decodekey.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;
313                         newkeylist != NULL && numkeys < config.maxkeys;
314                         newkeylist = newkeylist->next) {
315
316                         keyid = 0;
317                         for (i = 4; i < 12; i++) {
318                                 keyid <<= 8;
319                                 keyid += ((unsigned char *)
320                                                 newkeylist->object)[i];
321                         }
322
323                         numkeys += fetch_key(keyid,
324                                         publickey,
325                                         false);
326         }
327         llfree(keylist, free);
328         keylist = NULL;
329         free(searchtext);
330         searchtext = NULL;
331
332         ret = cursor->c_close(cursor);
333         cursor = NULL;
334         
335         return (numkeys);
336 }
337
338 /**
339  *      store_key - Takes a key and stores it.
340  *      @publickey: A pointer to the public key to store.
341  *      @intrans: If we're already in a transaction.
342  *      @update: If true the key exists and should be updated.
343  *
344  *      Again we just use the hex representation of the keyid as the filename
345  *      to store the key to. We flatten the public key to a list of OpenPGP
346  *      packets and then use write_openpgp_stream() to write the stream out to
347  *      the file. If update is true then we delete the old key first, otherwise
348  *      we trust that it doesn't exist.
349  */
350 int store_key(struct openpgp_publickey *publickey, bool intrans, bool update)
351 {
352         struct     openpgp_packet_list *packets = NULL;
353         struct     openpgp_packet_list *list_end = NULL;
354         struct     openpgp_publickey *next = NULL;
355         int        ret = 0;
356         int        i = 0;
357         struct     buffer_ctx storebuf;
358         DBT        key;
359         DBT        data;
360         uint64_t   keyid = 0;
361         char     **uids = NULL;
362         char      *primary = NULL;
363         unsigned char worddb_data[12];
364         struct ll *wordlist = NULL;
365         struct ll *curword  = NULL;
366
367         keyid = get_keyid(publickey);
368
369         /*
370          * Delete the key if we already have it.
371          *
372          * TODO: Can we optimize this perhaps? Possibly when other data is
373          * involved as well? I suspect this is easiest and doesn't make a lot
374          * of difference though - the largest chunk of data is the keydata and
375          * it definitely needs updated.
376          */
377         if (update) {
378                 delete_key(keyid, true);
379         }
380
381         /*
382          * Convert the key to a flat set of binary data.
383          */
384         next = publickey->next;
385         publickey->next = NULL;
386         flatten_publickey(publickey, &packets, &list_end);
387         publickey->next = next;
388
389         storebuf.offset = 0; 
390         storebuf.size = 8192;
391         storebuf.buffer = malloc(8192);
392         
393         write_openpgp_stream(buffer_putchar, &storebuf, packets);
394
395         /*
396          * Now we have the key data store it in the DB; the keyid is the key.
397          */
398         memset(&key, 0, sizeof(key));
399         memset(&data, 0, sizeof(data));
400         key.data = &keyid;
401         key.size = sizeof(keyid);
402         keyid &= 0xFFFFFFFF;
403         data.size = storebuf.offset;
404         data.data = storebuf.buffer;
405
406         ret = dbconn->put(dbconn,
407                         NULL, /* txn id */
408                         &key,
409                         &data,
410                         0); /* flags*/
411         if (ret != 0) {
412                 dbconn->err(dbconn, ret, "Problem storing key");
413         }
414
415         free(storebuf.buffer);
416         storebuf.buffer = NULL;
417         storebuf.size = 0;
418         storebuf.offset = 0; 
419
420         free_packet_list(packets);
421         packets = NULL;
422
423         /*
424          * Walk through our uids storing the words into the db with the keyid.
425          */
426         uids = keyuids(publickey, &primary);
427         if (uids != NULL) {
428                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
429                         wordlist = makewordlist(wordlist, uids[i]);
430                 }
431
432                 for (curword = wordlist; curword != NULL;
433                                 curword = curword->next) {
434                         memset(&key, 0, sizeof(key));
435                         memset(&data, 0, sizeof(data));
436                         key.data = curword->object;
437                         key.size = strlen(key.data);
438                         data.data = worddb_data;
439                         data.size = sizeof(worddb_data);
440
441                         /*
442                          * Our data is the key creation time followed by the
443                          * key id.
444                          */
445                         worddb_data[ 0] = publickey->publickey->data[1];
446                         worddb_data[ 1] = publickey->publickey->data[2];
447                         worddb_data[ 2] = publickey->publickey->data[3];
448                         worddb_data[ 3] = publickey->publickey->data[4];
449                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
450                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
451                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
452                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
453                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
454                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
455                         worddb_data[10] = (keyid >>  8) & 0xFF;
456                         worddb_data[11] = keyid & 0xFF; 
457                         ret = worddb->put(worddb,
458                                 0,
459                                 &key,
460                                 &data,
461                                 0);
462                         if (ret != 0) {
463                                 worddb->err(worddb, ret,
464                                         "Problem storing key");
465                         }
466                 }
467
468                 /*
469                  * Free our UID and word lists.
470                  */
471                 llfree(wordlist, NULL);
472                 for (i = 0; uids[i] != NULL; i++) {
473                         free(uids[i]);
474                         uids[i] = NULL;
475                 }
476                 free(uids);
477                 uids = NULL;
478         }
479
480         return 0;
481 }
482
483 /**
484  *      delete_key - Given a keyid delete the key from storage.
485  *      @keyid: The keyid to delete.
486  *      @intrans: If we're already in a transaction.
487  *
488  *      This function deletes a public key from whatever storage mechanism we
489  *      are using. Returns 0 if the key existed.
490  */
491 int delete_key(uint64_t keyid, bool intrans)
492 {
493         struct openpgp_publickey *publickey = NULL;
494         DBT key, data;
495         DBC *cursor = NULL;
496         int ret = 0;
497         int i;
498         char **uids = NULL;
499         char *primary = NULL;
500         unsigned char worddb_data[12];
501         struct ll *wordlist = NULL;
502         struct ll *curword  = NULL;
503
504         keyid &= 0xFFFFFFFF;
505
506         fetch_key(keyid, &publickey, intrans);
507
508         /*
509          * Walk through the uids removing the words from the worddb.
510          */
511         if (publickey != NULL) {
512                 uids = keyuids(publickey, &primary);
513         }
514         if (uids != NULL) {
515                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
516                         wordlist = makewordlist(wordlist, uids[i]);
517                 }
518                                 
519                 ret = worddb->cursor(worddb,
520                         NULL, /* txn */
521                         &cursor,
522                         0);   /* flags */
523
524                 for (curword = wordlist; curword != NULL;
525                                 curword = curword->next) {
526                         memset(&key, 0, sizeof(key));
527                         memset(&data, 0, sizeof(data));
528                         key.data = curword->object;
529                         key.size = strlen(key.data);
530                         data.data = worddb_data;
531                         data.size = sizeof(worddb_data);
532
533                         /*
534                          * Our data is the key creation time followed by the
535                          * key id.
536                          */
537                         worddb_data[ 0] = publickey->publickey->data[1];
538                         worddb_data[ 1] = publickey->publickey->data[2];
539                         worddb_data[ 2] = publickey->publickey->data[3];
540                         worddb_data[ 3] = publickey->publickey->data[4];
541                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
542                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
543                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
544                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
545                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
546                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
547                         worddb_data[10] = (keyid >>  8) & 0xFF;
548                         worddb_data[11] = keyid & 0xFF; 
549
550                         ret = cursor->c_get(cursor,
551                                 &key,
552                                 &data,
553                                 DB_GET_BOTH);
554                         if (ret == 0) {
555                                 ret = cursor->c_del(cursor, 0);
556                         }
557
558                         if (ret != 0) {
559                                 worddb->err(worddb, ret,
560                                         "Problem deleting word.");
561                         }
562                 }
563                 ret = cursor->c_close(cursor);
564                 cursor = NULL;
565
566                 /*
567                  * Free our UID and word lists.
568                  */
569                 llfree(wordlist, NULL);
570                 for (i = 0; uids[i] != NULL; i++) {
571                         free(uids[i]);
572                         uids[i] = NULL;
573                 }
574                 free(uids);
575                 uids = NULL;
576                 free_publickey(publickey);
577                 publickey = NULL;
578         }
579
580         key.data = &keyid;
581         key.size = sizeof(keyid);
582
583         dbconn->del(dbconn,
584                         NULL, /* txn id */
585                         &key,
586                         0); /* flags */
587
588         return (ret == DB_NOTFOUND);
589 }
590
591 /*
592  * Include the basic keydb routines.
593  */
594 #define NEED_GETFULLKEYID 1
595 #define NEED_GETKEYSIGS 1
596 #define NEED_KEYID2UID 1
597 #include "keydb.c"