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