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.24 2004/03/28 21:27:03 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 * numdb - The number of database files we have.
42 static int numdbs = 16;
45 * dbconn - our connections to the key database files.
47 static DB **dbconns = NULL;
50 * worddb - our connection to the word database.
52 static DB *worddb = NULL;
55 * txn - our current transaction id.
57 static DB_TXN *txn = NULL;
59 DB *keydb(uint64_t keyid)
65 return(dbconns[keytrun % numdbs]);
69 * makewordlist - Takes a string and splits it into a set of unique words.
70 * @wordlist: The current word list.
71 * @words: The string to split and add.
73 * We take words and split it on non alpha numeric characters. These get
74 * added to the word list if they're not already present. If the wordlist
75 * is NULL then we start a new list, otherwise it's search for already
76 * added words. Note that words is modified in the process of scanning.
78 * Returns the new word list.
80 struct ll *makewordlist(struct ll *wordlist, char *word)
86 * Walk through the words string, spliting on non alphanumerics and
87 * then checking if the word already exists in the list. If not then
91 while (end != NULL && *end != 0) {
93 while (*start != 0 && !isalnum(*start)) {
97 while (*end != 0 && isalnum(*end)) {
101 if (end - start > 1) {
107 if (llfind(wordlist, start,
109 wordlist = lladd(wordlist,
119 * initdb - Initialize the key database.
121 * This function should be called before any of the other functions in
122 * this file are called in order to allow the DB to be initialized ready
125 void initdb(bool readonly)
133 snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", config.db_dir);
134 numdb = fopen(buf, "r");
136 if (fgets(buf, sizeof(buf), numdb) != NULL) {
140 } else if (!readonly) {
141 logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s",
143 numdb = fopen(buf, "w");
145 fprintf(numdb, "%d", numdbs);
148 logthing(LOGTHING_ERROR,
149 "Couldn't write num_keydb: %s",
154 dbconns = malloc(sizeof (DB *) * numdbs);
155 if (dbconns == NULL) {
156 logthing(LOGTHING_CRITICAL,
157 "Couldn't allocate memory for dbconns");
161 ret = db_env_create(&dbenv, 0);
163 logthing(LOGTHING_CRITICAL,
164 "db_env_create: %s", db_strerror(ret));
169 * Enable deadlock detection so that we don't block indefinitely on
170 * anything. What we really want is simple 2 state locks, but I'm not
171 * sure how to make the standard DB functions do that yet.
173 ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT);
175 logthing(LOGTHING_CRITICAL,
176 "db_env_create: %s", db_strerror(ret));
180 ret = dbenv->open(dbenv, config.db_dir,
181 DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
186 logthing(LOGTHING_CRITICAL,
187 "Error opening db environment: %s (%s)",
193 for (i = 0; i < numdbs; i++) {
194 ret = db_create(&dbconns[i], dbenv, 0);
196 logthing(LOGTHING_CRITICAL,
197 "db_create: %s", db_strerror(ret));
201 snprintf(buf, 1023, "keydb.%d.db", i);
206 ret = dbconns[i]->open(dbconns[i], buf,
212 logthing(LOGTHING_CRITICAL,
213 "Error opening key database: %s (%s)",
220 ret = db_create(&worddb, dbenv, 0);
222 logthing(LOGTHING_CRITICAL, "db_create: %s", db_strerror(ret));
225 ret = worddb->set_flags(worddb, DB_DUP);
227 ret = worddb->open(worddb, "worddb", NULL, DB_BTREE,
231 logthing(LOGTHING_CRITICAL,
232 "Error opening word database: %s (%s)",
242 * cleanupdb - De-initialize the key database.
244 * This function should be called upon program exit to allow the DB to
245 * cleanup after itself.
251 txn_checkpoint(dbenv, 0, 0, 0);
252 worddb->close(worddb, 0);
254 for (i = 0; i < numdbs; i++) {
255 dbconns[i]->close(dbconns[i], 0);
258 dbenv->close(dbenv, 0);
263 * starttrans - Start a transaction.
265 * Start a transaction. Intended to be used if we're about to perform many
266 * operations on the database to help speed it all up, or if we want
267 * something to only succeed if all relevant operations are successful.
269 bool starttrans(void)
273 assert(dbenv != NULL);
276 ret = txn_begin(dbenv,
277 NULL, /* No parent transaction */
281 logthing(LOGTHING_CRITICAL,
282 "Error starting transaction: %s",
291 * endtrans - End a transaction.
293 * Ends a transaction.
299 assert(dbenv != NULL);
302 ret = txn_commit(txn,
305 logthing(LOGTHING_CRITICAL,
306 "Error ending transaction: %s",
316 * fetch_key - Given a keyid fetch the key from storage.
317 * @keyid: The keyid to fetch.
318 * @publickey: A pointer to a structure to return the key in.
319 * @intrans: If we're already in a transaction.
321 * We use the hex representation of the keyid as the filename to fetch the
322 * key from. The key is stored in the file as a binary OpenPGP stream of
323 * packets, so we can just use read_openpgp_stream() to read the packets
324 * in and then parse_keys() to parse the packets into a publickey
327 int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
330 struct openpgp_packet_list *packets = NULL;
334 struct buffer_ctx fetchbuf;
336 memset(&key, 0, sizeof(key));
337 memset(&data, 0, sizeof(data));
342 key.size = sizeof(keyid);
350 ret = keydb(keyid)->get(keydb(keyid),
357 fetchbuf.buffer = data.data;
359 fetchbuf.size = data.size;
360 read_openpgp_stream(buffer_fetchchar, &fetchbuf,
362 parse_keys(packets, publickey);
363 free_packet_list(packets);
366 } else if (ret != DB_NOTFOUND) {
367 logthing(LOGTHING_ERROR,
368 "Problem retrieving key: %s",
379 int worddb_cmp(const char *d1, const char *d2)
381 return memcmp(d1, d2, 12);
385 * fetch_key_text - Trys to find the keys that contain the supplied text.
386 * @search: The text to search for.
387 * @publickey: A pointer to a structure to return the key in.
389 * This function searches for the supplied text and returns the keys that
392 int fetch_key_text(const char *search, struct openpgp_publickey **publickey)
400 char *searchtext = NULL;
401 struct ll *wordlist = NULL;
402 struct ll *curword = NULL;
403 struct ll *keylist = NULL;
404 struct ll *newkeylist = NULL;
407 searchtext = strdup(search);
408 wordlist = makewordlist(wordlist, searchtext);
412 ret = worddb->cursor(worddb,
417 for (curword = wordlist; curword != NULL; curword = curword->next) {
418 memset(&key, 0, sizeof(key));
419 memset(&data, 0, sizeof(data));
420 key.data = curword->object;
421 key.size = strlen(curword->object);
422 data.flags = DB_DBT_MALLOC;
423 ret = cursor->c_get(cursor,
427 while (ret == 0 && strncmp(key.data, curword->object,
429 ((char *) curword->object)[key.size] == 0) {
431 for (i = 4; i < 12; i++) {
433 keyid += ((unsigned char *)
437 if (keylist == NULL ||
438 llfind(keylist, data.data,
439 worddb_cmp) != NULL) {
440 newkeylist = lladd(newkeylist, data.data);
446 ret = cursor->c_get(cursor,
451 llfree(keylist, free);
452 keylist = newkeylist;
454 if (data.data != NULL) {
459 llfree(wordlist, NULL);
462 for (newkeylist = keylist;
463 newkeylist != NULL && numkeys < config.maxkeys;
464 newkeylist = newkeylist->next) {
467 for (i = 4; i < 12; i++) {
469 keyid += ((unsigned char *)
470 newkeylist->object)[i];
473 numkeys += fetch_key(keyid,
477 llfree(keylist, free);
482 ret = cursor->c_close(cursor);
491 * store_key - Takes a key and stores it.
492 * @publickey: A pointer to the public key to store.
493 * @intrans: If we're already in a transaction.
494 * @update: If true the key exists and should be updated.
496 * Again we just use the hex representation of the keyid as the filename
497 * to store the key to. We flatten the public key to a list of OpenPGP
498 * packets and then use write_openpgp_stream() to write the stream out to
499 * the file. If update is true then we delete the old key first, otherwise
500 * we trust that it doesn't exist.
502 int store_key(struct openpgp_publickey *publickey, bool intrans, bool update)
504 struct openpgp_packet_list *packets = NULL;
505 struct openpgp_packet_list *list_end = NULL;
506 struct openpgp_publickey *next = NULL;
509 struct buffer_ctx storebuf;
514 char *primary = NULL;
515 unsigned char worddb_data[12];
516 struct ll *wordlist = NULL;
517 struct ll *curword = NULL;
518 bool deadlock = false;
520 keyid = get_keyid(publickey);
527 * Delete the key if we already have it.
529 * TODO: Can we optimize this perhaps? Possibly when other data is
530 * involved as well? I suspect this is easiest and doesn't make a lot
531 * of difference though - the largest chunk of data is the keydata and
532 * it definitely needs updated.
535 deadlock = (delete_key(keyid, true) == -1);
539 * Convert the key to a flat set of binary data.
542 next = publickey->next;
543 publickey->next = NULL;
544 flatten_publickey(publickey, &packets, &list_end);
545 publickey->next = next;
548 storebuf.size = 8192;
549 storebuf.buffer = malloc(8192);
551 write_openpgp_stream(buffer_putchar, &storebuf, packets);
554 * Now we have the key data store it in the DB; the keyid is
557 memset(&key, 0, sizeof(key));
558 memset(&data, 0, sizeof(data));
560 key.size = sizeof(keyid);
562 data.size = storebuf.offset;
563 data.data = storebuf.buffer;
565 ret = keydb(keyid)->put(keydb(keyid),
571 logthing(LOGTHING_ERROR,
572 "Problem storing key: %s",
574 if (ret == DB_LOCK_DEADLOCK) {
579 free(storebuf.buffer);
580 storebuf.buffer = NULL;
584 free_packet_list(packets);
589 * Walk through our uids storing the words into the db with the keyid.
592 uids = keyuids(publickey, &primary);
595 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
596 wordlist = makewordlist(wordlist, uids[i]);
599 for (curword = wordlist; curword != NULL && !deadlock;
600 curword = curword->next) {
601 memset(&key, 0, sizeof(key));
602 memset(&data, 0, sizeof(data));
603 key.data = curword->object;
604 key.size = strlen(key.data);
605 data.data = worddb_data;
606 data.size = sizeof(worddb_data);
609 * Our data is the key creation time followed by the
612 worddb_data[ 0] = publickey->publickey->data[1];
613 worddb_data[ 1] = publickey->publickey->data[2];
614 worddb_data[ 2] = publickey->publickey->data[3];
615 worddb_data[ 3] = publickey->publickey->data[4];
616 worddb_data[ 4] = (keyid >> 56) & 0xFF;
617 worddb_data[ 5] = (keyid >> 48) & 0xFF;
618 worddb_data[ 6] = (keyid >> 40) & 0xFF;
619 worddb_data[ 7] = (keyid >> 32) & 0xFF;
620 worddb_data[ 8] = (keyid >> 24) & 0xFF;
621 worddb_data[ 9] = (keyid >> 16) & 0xFF;
622 worddb_data[10] = (keyid >> 8) & 0xFF;
623 worddb_data[11] = keyid & 0xFF;
624 ret = worddb->put(worddb,
630 logthing(LOGTHING_ERROR,
631 "Problem storing word: %s",
633 if (ret == DB_LOCK_DEADLOCK) {
640 * Free our UID and word lists.
642 llfree(wordlist, NULL);
643 for (i = 0; uids[i] != NULL; i++) {
655 return deadlock ? -1 : 0 ;
659 * delete_key - Given a keyid delete the key from storage.
660 * @keyid: The keyid to delete.
661 * @intrans: If we're already in a transaction.
663 * This function deletes a public key from whatever storage mechanism we
664 * are using. Returns 0 if the key existed.
666 int delete_key(uint64_t keyid, bool intrans)
668 struct openpgp_publickey *publickey = NULL;
674 char *primary = NULL;
675 unsigned char worddb_data[12];
676 struct ll *wordlist = NULL;
677 struct ll *curword = NULL;
678 bool deadlock = false;
686 fetch_key(keyid, &publickey, true);
689 * Walk through the uids removing the words from the worddb.
691 if (publickey != NULL) {
692 uids = keyuids(publickey, &primary);
695 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
696 wordlist = makewordlist(wordlist, uids[i]);
699 ret = worddb->cursor(worddb,
704 for (curword = wordlist; curword != NULL && !deadlock;
705 curword = curword->next) {
706 memset(&key, 0, sizeof(key));
707 memset(&data, 0, sizeof(data));
708 key.data = curword->object;
709 key.size = strlen(key.data);
710 data.data = worddb_data;
711 data.size = sizeof(worddb_data);
714 * Our data is the key creation time followed by the
717 worddb_data[ 0] = publickey->publickey->data[1];
718 worddb_data[ 1] = publickey->publickey->data[2];
719 worddb_data[ 2] = publickey->publickey->data[3];
720 worddb_data[ 3] = publickey->publickey->data[4];
721 worddb_data[ 4] = (keyid >> 56) & 0xFF;
722 worddb_data[ 5] = (keyid >> 48) & 0xFF;
723 worddb_data[ 6] = (keyid >> 40) & 0xFF;
724 worddb_data[ 7] = (keyid >> 32) & 0xFF;
725 worddb_data[ 8] = (keyid >> 24) & 0xFF;
726 worddb_data[ 9] = (keyid >> 16) & 0xFF;
727 worddb_data[10] = (keyid >> 8) & 0xFF;
728 worddb_data[11] = keyid & 0xFF;
730 ret = cursor->c_get(cursor,
736 ret = cursor->c_del(cursor, 0);
738 logthing(LOGTHING_ERROR,
739 "Problem deleting word: %s",
745 logthing(LOGTHING_ERROR,
746 "Problem deleting word: %s",
748 if (ret == DB_LOCK_DEADLOCK) {
753 ret = cursor->c_close(cursor);
757 * Free our UID and word lists.
759 llfree(wordlist, NULL);
760 for (i = 0; uids[i] != NULL; i++) {
766 free_publickey(publickey);
772 key.size = sizeof(keyid);
774 keydb(keyid)->del(keydb(keyid),
784 return deadlock ? (-1) : (ret == DB_NOTFOUND);
788 * dumpdb - dump the key database
789 * @filenamebase: The base filename to use for the dump.
791 * Dumps the database into one or more files, which contain pure OpenPGP
792 * that can be reimported into onak or gpg. filenamebase provides a base
793 * file name for the dump; several files may be created, all of which will
794 * begin with this string and then have a unique number and a .pgp
797 int dumpdb(char *filenamebase)
807 for (i = 0; i < numdbs; i++) {
808 ret = dbconns[i]->cursor(dbconns[i],
813 snprintf(filename, 1023, "%s.%d.pgp", filenamebase, i);
814 fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0640);
816 logthing(LOGTHING_ERROR,
817 "Error opening keydump file (%s): %s",
821 memset(&key, 0, sizeof(key));
822 memset(&data, 0, sizeof(data));
823 ret = cursor->c_get(cursor, &key, &data, DB_NEXT);
825 write(fd, data.data, data.size);
826 memset(&key, 0, sizeof(key));
827 memset(&data, 0, sizeof(data));
828 ret = cursor->c_get(cursor, &key, &data,
831 if (ret != DB_NOTFOUND) {
832 logthing(LOGTHING_ERROR,
833 "Problem reading key: %s",
839 ret = cursor->c_close(cursor);
847 * Include the basic keydb routines.
849 #define NEED_GETFULLKEYID 1
850 #define NEED_GETKEYSIGS 1
851 #define NEED_KEYID2UID 1