]> git.sommitrealweird.co.uk Git - onak.git/blob - keydb_db3.c
cscvs to tla changeset 38
[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                 numkeys++;
221         } else if (ret != DB_NOTFOUND) {
222                 dbconn->err(dbconn, ret, "Problem retrieving key");
223         }
224
225         return (numkeys);
226 }
227
228 int worddb_cmp(const char *d1, const char *d2)
229 {
230         return memcmp(d1, d2, 12);
231 }
232
233 /**
234  *      fetch_key_text - Trys to find the keys that contain the supplied text.
235  *      @search: The text to search for.
236  *      @publickey: A pointer to a structure to return the key in.
237  *
238  *      This function searches for the supplied text and returns the keys that
239  *      contain it.
240  */
241 int fetch_key_text(const char *search, struct openpgp_publickey **publickey)
242 {
243         DBC *cursor = NULL;
244         DBT key, data;
245         int ret;
246         uint64_t keyid;
247         int i;
248         int numkeys;
249         char *searchtext = NULL;
250         struct ll *wordlist = NULL;
251         struct ll *curword = NULL;
252         struct ll *keylist = NULL;
253         struct ll *newkeylist = NULL;
254
255         numkeys = 0;
256         searchtext = strdup(search);
257         wordlist = makewordlist(wordlist, searchtext);
258
259
260         ret = worddb->cursor(worddb,
261                         NULL, /* txn */
262                         &cursor,
263                         0);   /* flags */
264
265         for (curword = wordlist; curword != NULL; curword = curword->next) {
266                 memset(&key, 0, sizeof(key));
267                 memset(&data, 0, sizeof(data));
268                 key.data = curword->object;
269                 key.size = strlen(curword->object);
270                 data.flags = DB_DBT_MALLOC;
271                 ret = cursor->c_get(cursor,
272                                 &key,
273                                 &data,
274                                 DB_SET);
275                 while (ret == 0 && strncmp(key.data, curword->object,
276                                         key.size) == 0 &&
277                                 ((char *) curword->object)[key.size] == 0) {
278                         keyid = 0;
279                         for (i = 4; i < 12; i++) {
280                                 keyid <<= 8;
281                                 keyid += ((unsigned char *)
282                                                 data.data)[i];
283                         }
284
285                         if (keylist == NULL ||
286                                         llfind(keylist, data.data,
287                                                 worddb_cmp) != NULL) {
288                                 newkeylist = lladd(newkeylist, data.data);
289                         } else {
290                                 free(data.data);
291                                 data.data = NULL;
292                         }
293                         ret = cursor->c_get(cursor,
294                                         &key,
295                                         &data,
296                                         DB_NEXT);
297                 }
298                 llfree(keylist, free);
299                 keylist = newkeylist;
300                 newkeylist = NULL;
301         }
302         
303         for (newkeylist = keylist; newkeylist != NULL;
304                         newkeylist = newkeylist->next) {
305
306                         keyid = 0;
307                         for (i = 4; i < 12; i++) {
308                                 keyid <<= 8;
309                                 keyid += ((unsigned char *)
310                                                 newkeylist->object)[i];
311                         }
312
313                         numkeys += fetch_key(keyid,
314                                         publickey,
315                                         false);
316         }
317         llfree(keylist, free);
318         keylist = NULL;
319         free(searchtext);
320         searchtext = NULL;
321
322         ret = cursor->c_close(cursor);
323         cursor = NULL;
324         
325         return (numkeys);
326 }
327
328 /**
329  *      store_key - Takes a key and stores it.
330  *      @publickey: A pointer to the public key to store.
331  *      @intrans: If we're already in a transaction.
332  *      @update: If true the key exists and should be updated.
333  *
334  *      Again we just use the hex representation of the keyid as the filename
335  *      to store the key to. We flatten the public key to a list of OpenPGP
336  *      packets and then use write_openpgp_stream() to write the stream out to
337  *      the file. If update is true then we delete the old key first, otherwise
338  *      we trust that it doesn't exist.
339  */
340 int store_key(struct openpgp_publickey *publickey, bool intrans, bool update)
341 {
342         struct     openpgp_packet_list *packets = NULL;
343         struct     openpgp_packet_list *list_end = NULL;
344         struct     openpgp_publickey *next = NULL;
345         int        ret = 0;
346         int        i = 0;
347         struct     buffer_ctx storebuf;
348         DBT        key;
349         DBT        data;
350         uint64_t   keyid = 0;
351         char     **uids = NULL;
352         char      *primary = NULL;
353         unsigned char worddb_data[12];
354         struct ll *wordlist = NULL;
355         struct ll *curword  = NULL;
356
357         keyid = get_keyid(publickey);
358
359         /*
360          * Delete the key if we already have it.
361          *
362          * TODO: Can we optimize this perhaps? Possibly when other data is
363          * involved as well? I suspect this is easiest and doesn't make a lot
364          * of difference though - the largest chunk of data is the keydata and
365          * it definitely needs updated.
366          */
367         if (update) {
368                 delete_key(keyid, true);
369         }
370
371         /*
372          * Convert the key to a flat set of binary data.
373          */
374         next = publickey->next;
375         publickey->next = NULL;
376         flatten_publickey(publickey, &packets, &list_end);
377         publickey->next = next;
378
379         storebuf.offset = 0; 
380         storebuf.size = 8192;
381         storebuf.buffer = malloc(8192);
382         
383         write_openpgp_stream(buffer_putchar, &storebuf, packets);
384
385         /*
386          * Now we have the key data store it in the DB; the keyid is the key.
387          */
388         memset(&key, 0, sizeof(key));
389         memset(&data, 0, sizeof(data));
390         key.data = &keyid;
391         key.size = sizeof(keyid);
392         keyid &= 0xFFFFFFFF;
393         data.size = storebuf.offset;
394         data.data = storebuf.buffer;
395
396         ret = dbconn->put(dbconn,
397                         NULL, /* txn id */
398                         &key,
399                         &data,
400                         0); /* flags*/
401         if (ret != 0) {
402                 dbconn->err(dbconn, ret, "Problem storing key");
403         }
404
405         /*
406          * Walk through our uids storing the words into the db with the keyid.
407          */
408         uids = keyuids(publickey, &primary);
409         if (uids != NULL) {
410                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
411                         wordlist = makewordlist(wordlist, uids[i]);
412                 }
413
414                 for (curword = wordlist; curword != NULL;
415                                 curword = curword->next) {
416                         memset(&key, 0, sizeof(key));
417                         memset(&data, 0, sizeof(data));
418                         key.data = curword->object;
419                         key.size = strlen(key.data);
420                         data.data = worddb_data;
421                         data.size = sizeof(worddb_data);
422
423                         /*
424                          * Our data is the key creation time followed by the
425                          * key id.
426                          */
427                         worddb_data[ 0] = publickey->publickey->data[1];
428                         worddb_data[ 1] = publickey->publickey->data[2];
429                         worddb_data[ 2] = publickey->publickey->data[3];
430                         worddb_data[ 3] = publickey->publickey->data[4];
431                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
432                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
433                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
434                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
435                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
436                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
437                         worddb_data[10] = (keyid >>  8) & 0xFF;
438                         worddb_data[11] = keyid & 0xFF; 
439                         ret = worddb->put(worddb,
440                                 0,
441                                 &key,
442                                 &data,
443                                 0);
444                         if (ret != 0) {
445                                 worddb->err(worddb, ret,
446                                         "Problem storing key");
447                         }
448                 }
449
450                 /*
451                  * Free our UID and word lists.
452                  */
453                 llfree(wordlist, NULL);
454                 for (i = 0; uids[i] != NULL; i++) {
455                         free(uids[i]);
456                         uids[i] = NULL;
457                 }
458                 free(uids);
459                 uids = NULL;
460         }
461
462         return 0;
463 }
464
465 /**
466  *      delete_key - Given a keyid delete the key from storage.
467  *      @keyid: The keyid to delete.
468  *      @intrans: If we're already in a transaction.
469  *
470  *      This function deletes a public key from whatever storage mechanism we
471  *      are using. Returns 0 if the key existed.
472  */
473 int delete_key(uint64_t keyid, bool intrans)
474 {
475         struct openpgp_publickey *publickey = NULL;
476         DBT key, data;
477         DBC *cursor = NULL;
478         int ret = 0;
479         int i;
480         char **uids = NULL;
481         char *primary = NULL;
482         unsigned char worddb_data[12];
483         struct ll *wordlist = NULL;
484         struct ll *curword  = NULL;
485
486         keyid &= 0xFFFFFFFF;
487
488         fetch_key(keyid, &publickey, intrans);
489
490         /*
491          * Walk through the uids removing the words from the worddb.
492          */
493         if (publickey != NULL) {
494                 uids = keyuids(publickey, &primary);
495         }
496         if (uids != NULL) {
497                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
498                         wordlist = makewordlist(wordlist, uids[i]);
499                 }
500                                 
501                 ret = worddb->cursor(worddb,
502                         NULL, /* txn */
503                         &cursor,
504                         0);   /* flags */
505
506                 for (curword = wordlist; curword != NULL;
507                                 curword = curword->next) {
508                         memset(&key, 0, sizeof(key));
509                         memset(&data, 0, sizeof(data));
510                         key.data = curword->object;
511                         key.size = strlen(key.data);
512                         data.data = worddb_data;
513                         data.size = sizeof(worddb_data);
514
515                         /*
516                          * Our data is the key creation time followed by the
517                          * key id.
518                          */
519                         worddb_data[ 0] = publickey->publickey->data[1];
520                         worddb_data[ 1] = publickey->publickey->data[2];
521                         worddb_data[ 2] = publickey->publickey->data[3];
522                         worddb_data[ 3] = publickey->publickey->data[4];
523                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
524                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
525                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
526                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
527                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
528                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
529                         worddb_data[10] = (keyid >>  8) & 0xFF;
530                         worddb_data[11] = keyid & 0xFF; 
531
532                         ret = cursor->c_get(cursor,
533                                 &key,
534                                 &data,
535                                 DB_GET_BOTH);
536                         if (ret == 0) {
537                                 ret = cursor->c_del(cursor, 0);
538                         }
539
540                         if (ret != 0) {
541                                 worddb->err(worddb, ret,
542                                         "Problem deleting word.");
543                         }
544                 }
545                 ret = cursor->c_close(cursor);
546                 cursor = NULL;
547
548                 /*
549                  * Free our UID and word lists.
550                  */
551                 llfree(wordlist, NULL);
552                 for (i = 0; uids[i] != NULL; i++) {
553                         free(uids[i]);
554                         uids[i] = NULL;
555                 }
556                 free(uids);
557                 uids = NULL;
558                 free_publickey(publickey);
559                 publickey = NULL;
560         }
561
562         key.data = &keyid;
563         key.size = sizeof(keyid);
564
565         dbconn->del(dbconn,
566                         NULL, /* txn id */
567                         &key,
568                         0); /* flags */
569
570         return (ret == DB_NOTFOUND);
571 }
572
573 /*
574  * Include the basic keydb routines.
575  */
576 #define NEED_GETFULLKEYID 1
577 #define NEED_GETKEYSIGS 1
578 #define NEED_KEYID2UID 1
579 #include "keydb.c"