2 * keydb_db3.c - Routines to store and fetch keys in a DB3 database.
4 * Jonathan McDowell <noodles@earth.li>
6 * Copyright 2002 Project Purple
8 * $Id: keydb_db3.c,v 1.16 2003/06/04 20:57:08 noodles Exp $
12 #include <sys/types.h>
24 #include "charfuncs.h"
27 #include "decodekey.h"
28 #include "keystructs.h"
31 #include "onak-conf.h"
35 * dbenv - our database environment.
37 static DB_ENV *dbenv = NULL;
40 * dbconn - our connection to the key database.
42 static DB *dbconn = NULL;
45 * worddb - our connection to the word database.
47 static DB *worddb = NULL;
50 * txn - our current transaction id.
52 static DB_TXN *txn = NULL;
55 * makewordlist - Takes a string and splits it into a set of unique words.
56 * @wordlist: The current word list.
57 * @words: The string to split and add.
59 * We take words and split it on non alpha numeric characters. These get
60 * added to the word list if they're not already present. If the wordlist
61 * is NULL then we start a new list, otherwise it's search for already
62 * added words. Note that words is modified in the process of scanning.
64 * Returns the new word list.
66 struct ll *makewordlist(struct ll *wordlist, char *word)
72 * Walk through the words string, spliting on non alphanumerics and
73 * then checking if the word already exists in the list. If not then
77 while (end != NULL && *end != 0) {
79 while (*start != 0 && !isalnum(*start)) {
83 while (*end != 0 && isalnum(*end)) {
87 if (end - start > 1) {
93 if (llfind(wordlist, start,
95 wordlist = lladd(wordlist,
105 * initdb - Initialize the key database.
107 * This function should be called before any of the other functions in
108 * this file are called in order to allow the DB to be initialized ready
115 ret = db_env_create(&dbenv, 0);
117 logthing(LOGTHING_CRITICAL,
118 "db_env_create: %s", db_strerror(ret));
123 * Enable deadlock detection so that we don't block indefinitely on
124 * anything. What we really want is simple 2 state locks, but I'm not
125 * sure how to make the standard DB functions do that yet.
127 ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT);
129 logthing(LOGTHING_CRITICAL,
130 "db_env_create: %s", db_strerror(ret));
134 ret = dbenv->open(dbenv, config.db_dir,
135 DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
140 logthing(LOGTHING_CRITICAL,
141 "Erroring opening db environment: %s (%s)",
147 ret = db_create(&dbconn, dbenv, 0);
149 logthing(LOGTHING_CRITICAL,
150 "db_create: %s", db_strerror(ret));
154 ret = dbconn->open(dbconn, "keydb.db",
160 logthing(LOGTHING_CRITICAL,
161 "Error opening key database: %s (%s)",
167 ret = db_create(&worddb, dbenv, 0);
169 logthing(LOGTHING_CRITICAL, "db_create: %s", db_strerror(ret));
172 ret = worddb->set_flags(worddb, DB_DUP);
174 ret = worddb->open(worddb, "worddb", NULL, DB_BTREE,
178 logthing(LOGTHING_CRITICAL,
179 "Error opening word database: %s (%s)",
189 * cleanupdb - De-initialize the key database.
191 * This function should be called upon program exit to allow the DB to
192 * cleanup after itself.
196 txn_checkpoint(dbenv, 0, 0, 0);
197 worddb->close(worddb, 0);
199 dbconn->close(dbconn, 0);
201 dbenv->close(dbenv, 0);
206 * starttrans - Start a transaction.
208 * Start a transaction. Intended to be used if we're about to perform many
209 * operations on the database to help speed it all up, or if we want
210 * something to only succeed if all relevant operations are successful.
212 bool starttrans(void)
216 assert(dbenv != NULL);
219 ret = txn_begin(dbenv,
220 NULL, /* No parent transaction */
224 logthing(LOGTHING_CRITICAL,
225 "Error starting transaction: %s",
234 * endtrans - End a transaction.
236 * Ends a transaction.
242 assert(dbenv != NULL);
245 ret = txn_commit(txn,
248 logthing(LOGTHING_CRITICAL,
249 "Error ending transaction: %s",
259 * fetch_key - Given a keyid fetch the key from storage.
260 * @keyid: The keyid to fetch.
261 * @publickey: A pointer to a structure to return the key in.
262 * @intrans: If we're already in a transaction.
264 * We use the hex representation of the keyid as the filename to fetch the
265 * key from. The key is stored in the file as a binary OpenPGP stream of
266 * packets, so we can just use read_openpgp_stream() to read the packets
267 * in and then parse_keys() to parse the packets into a publickey
270 int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
273 struct openpgp_packet_list *packets = NULL;
277 struct buffer_ctx fetchbuf;
279 memset(&key, 0, sizeof(key));
280 memset(&data, 0, sizeof(data));
285 key.size = sizeof(keyid);
293 ret = dbconn->get(dbconn,
300 fetchbuf.buffer = data.data;
302 fetchbuf.size = data.size;
303 read_openpgp_stream(buffer_fetchchar, &fetchbuf,
305 parse_keys(packets, publickey);
306 free_packet_list(packets);
309 } else if (ret != DB_NOTFOUND) {
310 logthing(LOGTHING_ERROR,
311 "Problem retrieving key: %s",
322 int worddb_cmp(const char *d1, const char *d2)
324 return memcmp(d1, d2, 12);
328 * fetch_key_text - Trys to find the keys that contain the supplied text.
329 * @search: The text to search for.
330 * @publickey: A pointer to a structure to return the key in.
332 * This function searches for the supplied text and returns the keys that
335 int fetch_key_text(const char *search, struct openpgp_publickey **publickey)
343 char *searchtext = NULL;
344 struct ll *wordlist = NULL;
345 struct ll *curword = NULL;
346 struct ll *keylist = NULL;
347 struct ll *newkeylist = NULL;
350 searchtext = strdup(search);
351 wordlist = makewordlist(wordlist, searchtext);
355 ret = worddb->cursor(worddb,
360 for (curword = wordlist; curword != NULL; curword = curword->next) {
361 memset(&key, 0, sizeof(key));
362 memset(&data, 0, sizeof(data));
363 key.data = curword->object;
364 key.size = strlen(curword->object);
365 data.flags = DB_DBT_MALLOC;
366 ret = cursor->c_get(cursor,
370 while (ret == 0 && strncmp(key.data, curword->object,
372 ((char *) curword->object)[key.size] == 0) {
374 for (i = 4; i < 12; i++) {
376 keyid += ((unsigned char *)
380 if (keylist == NULL ||
381 llfind(keylist, data.data,
382 worddb_cmp) != NULL) {
383 newkeylist = lladd(newkeylist, data.data);
389 ret = cursor->c_get(cursor,
394 llfree(keylist, free);
395 keylist = newkeylist;
397 if (data.data != NULL) {
402 llfree(wordlist, NULL);
405 for (newkeylist = keylist;
406 newkeylist != NULL && numkeys < config.maxkeys;
407 newkeylist = newkeylist->next) {
410 for (i = 4; i < 12; i++) {
412 keyid += ((unsigned char *)
413 newkeylist->object)[i];
416 numkeys += fetch_key(keyid,
420 llfree(keylist, free);
425 ret = cursor->c_close(cursor);
434 * store_key - Takes a key and stores it.
435 * @publickey: A pointer to the public key to store.
436 * @intrans: If we're already in a transaction.
437 * @update: If true the key exists and should be updated.
439 * Again we just use the hex representation of the keyid as the filename
440 * to store the key to. We flatten the public key to a list of OpenPGP
441 * packets and then use write_openpgp_stream() to write the stream out to
442 * the file. If update is true then we delete the old key first, otherwise
443 * we trust that it doesn't exist.
445 int store_key(struct openpgp_publickey *publickey, bool intrans, bool update)
447 struct openpgp_packet_list *packets = NULL;
448 struct openpgp_packet_list *list_end = NULL;
449 struct openpgp_publickey *next = NULL;
452 struct buffer_ctx storebuf;
457 char *primary = NULL;
458 unsigned char worddb_data[12];
459 struct ll *wordlist = NULL;
460 struct ll *curword = NULL;
461 bool deadlock = false;
463 keyid = get_keyid(publickey);
470 * Delete the key if we already have it.
472 * TODO: Can we optimize this perhaps? Possibly when other data is
473 * involved as well? I suspect this is easiest and doesn't make a lot
474 * of difference though - the largest chunk of data is the keydata and
475 * it definitely needs updated.
478 deadlock = (delete_key(keyid, true) == -1);
482 * Convert the key to a flat set of binary data.
485 next = publickey->next;
486 publickey->next = NULL;
487 flatten_publickey(publickey, &packets, &list_end);
488 publickey->next = next;
491 storebuf.size = 8192;
492 storebuf.buffer = malloc(8192);
494 write_openpgp_stream(buffer_putchar, &storebuf, packets);
497 * Now we have the key data store it in the DB; the keyid is
500 memset(&key, 0, sizeof(key));
501 memset(&data, 0, sizeof(data));
503 key.size = sizeof(keyid);
505 data.size = storebuf.offset;
506 data.data = storebuf.buffer;
508 ret = dbconn->put(dbconn,
514 logthing(LOGTHING_ERROR,
515 "Problem storing key: %s",
517 if (ret == DB_LOCK_DEADLOCK) {
522 free(storebuf.buffer);
523 storebuf.buffer = NULL;
527 free_packet_list(packets);
532 * Walk through our uids storing the words into the db with the keyid.
535 uids = keyuids(publickey, &primary);
538 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
539 wordlist = makewordlist(wordlist, uids[i]);
542 for (curword = wordlist; curword != NULL && !deadlock;
543 curword = curword->next) {
544 memset(&key, 0, sizeof(key));
545 memset(&data, 0, sizeof(data));
546 key.data = curword->object;
547 key.size = strlen(key.data);
548 data.data = worddb_data;
549 data.size = sizeof(worddb_data);
552 * Our data is the key creation time followed by the
555 worddb_data[ 0] = publickey->publickey->data[1];
556 worddb_data[ 1] = publickey->publickey->data[2];
557 worddb_data[ 2] = publickey->publickey->data[3];
558 worddb_data[ 3] = publickey->publickey->data[4];
559 worddb_data[ 4] = (keyid >> 56) & 0xFF;
560 worddb_data[ 5] = (keyid >> 48) & 0xFF;
561 worddb_data[ 6] = (keyid >> 40) & 0xFF;
562 worddb_data[ 7] = (keyid >> 32) & 0xFF;
563 worddb_data[ 8] = (keyid >> 24) & 0xFF;
564 worddb_data[ 9] = (keyid >> 16) & 0xFF;
565 worddb_data[10] = (keyid >> 8) & 0xFF;
566 worddb_data[11] = keyid & 0xFF;
567 ret = worddb->put(worddb,
573 logthing(LOGTHING_ERROR,
574 "Problem storing word: %s",
576 if (ret == DB_LOCK_DEADLOCK) {
583 * Free our UID and word lists.
585 llfree(wordlist, NULL);
586 for (i = 0; uids[i] != NULL; i++) {
598 return deadlock ? -1 : 0 ;
602 * delete_key - Given a keyid delete the key from storage.
603 * @keyid: The keyid to delete.
604 * @intrans: If we're already in a transaction.
606 * This function deletes a public key from whatever storage mechanism we
607 * are using. Returns 0 if the key existed.
609 int delete_key(uint64_t keyid, bool intrans)
611 struct openpgp_publickey *publickey = NULL;
617 char *primary = NULL;
618 unsigned char worddb_data[12];
619 struct ll *wordlist = NULL;
620 struct ll *curword = NULL;
621 bool deadlock = false;
629 fetch_key(keyid, &publickey, true);
632 * Walk through the uids removing the words from the worddb.
634 if (publickey != NULL) {
635 uids = keyuids(publickey, &primary);
638 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
639 wordlist = makewordlist(wordlist, uids[i]);
642 ret = worddb->cursor(worddb,
647 for (curword = wordlist; curword != NULL && !deadlock;
648 curword = curword->next) {
649 memset(&key, 0, sizeof(key));
650 memset(&data, 0, sizeof(data));
651 key.data = curword->object;
652 key.size = strlen(key.data);
653 data.data = worddb_data;
654 data.size = sizeof(worddb_data);
657 * Our data is the key creation time followed by the
660 worddb_data[ 0] = publickey->publickey->data[1];
661 worddb_data[ 1] = publickey->publickey->data[2];
662 worddb_data[ 2] = publickey->publickey->data[3];
663 worddb_data[ 3] = publickey->publickey->data[4];
664 worddb_data[ 4] = (keyid >> 56) & 0xFF;
665 worddb_data[ 5] = (keyid >> 48) & 0xFF;
666 worddb_data[ 6] = (keyid >> 40) & 0xFF;
667 worddb_data[ 7] = (keyid >> 32) & 0xFF;
668 worddb_data[ 8] = (keyid >> 24) & 0xFF;
669 worddb_data[ 9] = (keyid >> 16) & 0xFF;
670 worddb_data[10] = (keyid >> 8) & 0xFF;
671 worddb_data[11] = keyid & 0xFF;
673 ret = cursor->c_get(cursor,
679 ret = cursor->c_del(cursor, 0);
681 logthing(LOGTHING_ERROR,
682 "Problem deleting word: %s",
688 logthing(LOGTHING_ERROR,
689 "Problem deleting word: %s",
691 if (ret == DB_LOCK_DEADLOCK) {
696 ret = cursor->c_close(cursor);
700 * Free our UID and word lists.
702 llfree(wordlist, NULL);
703 for (i = 0; uids[i] != NULL; i++) {
709 free_publickey(publickey);
715 key.size = sizeof(keyid);
727 return deadlock ? (-1) : (ret == DB_NOTFOUND);
731 * dumpdb - dump the key database
732 * @filenamebase: The base filename to use for the dump.
734 * Dumps the database into one or more files, which contain pure OpenPGP
735 * that can be reimported into onak or gpg. filenamebase provides a base
736 * file name for the dump; several files may be created, all of which will
737 * begin with this string and then have a unique number and a .pgp
740 int dumpdb(char *filenamebase)
749 ret = dbconn->cursor(dbconn,
754 fd = open(filenamebase, O_CREAT | O_WRONLY | O_TRUNC, 0640);
755 memset(&key, 0, sizeof(key));
756 memset(&data, 0, sizeof(data));
757 ret = cursor->c_get(cursor, &key, &data, DB_NEXT);
759 write(fd, data.data, data.size);
760 memset(&key, 0, sizeof(key));
761 memset(&data, 0, sizeof(data));
762 ret = cursor->c_get(cursor, &key, &data, DB_NEXT);
764 if (ret != DB_NOTFOUND) {
765 logthing(LOGTHING_ERROR, "Problem reading key: %s",
771 ret = cursor->c_close(cursor);
780 * Include the basic keydb routines.
782 #define NEED_GETFULLKEYID 1
783 #define NEED_GETKEYSIGS 1
784 #define NEED_KEYID2UID 1