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