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.26 2004/05/27 03:33:24 noodles Exp $
12 #include <sys/types.h>
24 #include "charfuncs.h"
27 #include "decodekey.h"
28 #include "keystructs.h"
31 #include "onak-conf.h"
36 * dbenv - our database environment.
38 static DB_ENV *dbenv = NULL;
41 * numdb - The number of database files we have.
43 static int numdbs = 16;
46 * dbconn - our connections to the key database files.
48 static DB **dbconns = NULL;
51 * worddb - our connection to the word database.
53 static DB *worddb = NULL;
56 * id32db - our connection to the 32bit ID database.
58 static DB *id32db = NULL;
61 * txn - our current transaction id.
63 static DB_TXN *txn = NULL;
65 DB *keydb(uint64_t keyid)
71 return(dbconns[keytrun % numdbs]);
75 * initdb - Initialize the key database.
77 * This function should be called before any of the other functions in
78 * this file are called in order to allow the DB to be initialized ready
81 void initdb(bool readonly)
89 snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", config.db_dir);
90 numdb = fopen(buf, "r");
92 if (fgets(buf, sizeof(buf), numdb) != NULL) {
96 } else if (!readonly) {
97 logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s",
99 numdb = fopen(buf, "w");
101 fprintf(numdb, "%d", numdbs);
104 logthing(LOGTHING_ERROR,
105 "Couldn't write num_keydb: %s",
110 dbconns = malloc(sizeof (DB *) * numdbs);
111 if (dbconns == NULL) {
112 logthing(LOGTHING_CRITICAL,
113 "Couldn't allocate memory for dbconns");
117 ret = db_env_create(&dbenv, 0);
119 logthing(LOGTHING_CRITICAL,
120 "db_env_create: %s", db_strerror(ret));
125 * Enable deadlock detection so that we don't block indefinitely on
126 * anything. What we really want is simple 2 state locks, but I'm not
127 * sure how to make the standard DB functions do that yet.
129 ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT);
131 logthing(LOGTHING_CRITICAL,
132 "db_env_create: %s", db_strerror(ret));
136 ret = dbenv->open(dbenv, config.db_dir,
137 DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
142 logthing(LOGTHING_CRITICAL,
143 "Error opening db environment: %s (%s)",
149 for (i = 0; i < numdbs; i++) {
150 ret = db_create(&dbconns[i], dbenv, 0);
152 logthing(LOGTHING_CRITICAL,
153 "db_create: %s", db_strerror(ret));
157 snprintf(buf, 1023, "keydb.%d.db", i);
162 ret = dbconns[i]->open(dbconns[i], buf,
168 logthing(LOGTHING_CRITICAL,
169 "Error opening key database: %s (%s)",
176 ret = db_create(&worddb, dbenv, 0);
178 logthing(LOGTHING_CRITICAL, "db_create: %s", db_strerror(ret));
181 ret = worddb->set_flags(worddb, DB_DUP);
183 ret = worddb->open(worddb, "worddb", NULL, DB_BTREE,
187 logthing(LOGTHING_CRITICAL,
188 "Error opening word database: %s (%s)",
194 ret = db_create(&id32db, dbenv, 0);
196 logthing(LOGTHING_CRITICAL, "db_create: %s", db_strerror(ret));
199 ret = id32db->set_flags(id32db, DB_DUP);
201 ret = id32db->open(id32db, "id32db", NULL, DB_HASH,
205 logthing(LOGTHING_CRITICAL,
206 "Error opening id32 database: %s (%s)",
216 * cleanupdb - De-initialize the key database.
218 * This function should be called upon program exit to allow the DB to
219 * cleanup after itself.
225 txn_checkpoint(dbenv, 0, 0, 0);
226 id32db->close(id32db, 0);
228 worddb->close(worddb, 0);
230 for (i = 0; i < numdbs; i++) {
231 dbconns[i]->close(dbconns[i], 0);
234 dbenv->close(dbenv, 0);
239 * starttrans - Start a transaction.
241 * Start a transaction. Intended to be used if we're about to perform many
242 * operations on the database to help speed it all up, or if we want
243 * something to only succeed if all relevant operations are successful.
245 bool starttrans(void)
249 assert(dbenv != NULL);
252 ret = txn_begin(dbenv,
253 NULL, /* No parent transaction */
257 logthing(LOGTHING_CRITICAL,
258 "Error starting transaction: %s",
267 * endtrans - End a transaction.
269 * Ends a transaction.
275 assert(dbenv != NULL);
278 ret = txn_commit(txn,
281 logthing(LOGTHING_CRITICAL,
282 "Error ending transaction: %s",
292 * fetch_key - Given a keyid fetch the key from storage.
293 * @keyid: The keyid to fetch.
294 * @publickey: A pointer to a structure to return the key in.
295 * @intrans: If we're already in a transaction.
297 * We use the hex representation of the keyid as the filename to fetch the
298 * key from. The key is stored in the file as a binary OpenPGP stream of
299 * packets, so we can just use read_openpgp_stream() to read the packets
300 * in and then parse_keys() to parse the packets into a publickey
303 int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
306 struct openpgp_packet_list *packets = NULL;
310 struct buffer_ctx fetchbuf;
312 if (keyid < 0x100000000LL) {
313 keyid = getfullkeyid(keyid);
316 memset(&key, 0, sizeof(key));
317 memset(&data, 0, sizeof(data));
322 key.size = sizeof(keyid);
329 ret = keydb(keyid)->get(keydb(keyid),
336 fetchbuf.buffer = data.data;
338 fetchbuf.size = data.size;
339 read_openpgp_stream(buffer_fetchchar, &fetchbuf,
341 parse_keys(packets, publickey);
342 free_packet_list(packets);
345 } else if (ret != DB_NOTFOUND) {
346 logthing(LOGTHING_ERROR,
347 "Problem retrieving key: %s",
358 int worddb_cmp(const void *d1, const void *d2)
360 return memcmp(d1, d2, 12);
364 * fetch_key_text - Trys to find the keys that contain the supplied text.
365 * @search: The text to search for.
366 * @publickey: A pointer to a structure to return the key in.
368 * This function searches for the supplied text and returns the keys that
371 int fetch_key_text(const char *search, struct openpgp_publickey **publickey)
379 char *searchtext = NULL;
380 struct ll *wordlist = NULL;
381 struct ll *curword = NULL;
382 struct ll *keylist = NULL;
383 struct ll *newkeylist = NULL;
386 searchtext = strdup(search);
387 wordlist = makewordlist(wordlist, searchtext);
391 ret = worddb->cursor(worddb,
396 for (curword = wordlist; curword != NULL; curword = curword->next) {
397 memset(&key, 0, sizeof(key));
398 memset(&data, 0, sizeof(data));
399 key.data = curword->object;
400 key.size = strlen(curword->object);
401 data.flags = DB_DBT_MALLOC;
402 ret = cursor->c_get(cursor,
406 while (ret == 0 && strncmp(key.data, curword->object,
408 ((char *) curword->object)[key.size] == 0) {
410 for (i = 4; i < 12; i++) {
412 keyid += ((unsigned char *)
416 if (keylist == NULL ||
417 llfind(keylist, data.data,
418 worddb_cmp) != NULL) {
419 newkeylist = lladd(newkeylist, data.data);
425 ret = cursor->c_get(cursor,
430 llfree(keylist, free);
431 keylist = newkeylist;
433 if (data.data != NULL) {
438 llfree(wordlist, NULL);
441 for (newkeylist = keylist;
442 newkeylist != NULL && numkeys < config.maxkeys;
443 newkeylist = newkeylist->next) {
446 for (i = 4; i < 12; i++) {
448 keyid += ((unsigned char *)
449 newkeylist->object)[i];
452 numkeys += fetch_key(keyid,
456 llfree(keylist, free);
461 ret = cursor->c_close(cursor);
470 * store_key - Takes a key and stores it.
471 * @publickey: A pointer to the public key to store.
472 * @intrans: If we're already in a transaction.
473 * @update: If true the key exists and should be updated.
475 * Again we just use the hex representation of the keyid as the filename
476 * to store the key to. We flatten the public key to a list of OpenPGP
477 * packets and then use write_openpgp_stream() to write the stream out to
478 * the file. If update is true then we delete the old key first, otherwise
479 * we trust that it doesn't exist.
481 int store_key(struct openpgp_publickey *publickey, bool intrans, bool update)
483 struct openpgp_packet_list *packets = NULL;
484 struct openpgp_packet_list *list_end = NULL;
485 struct openpgp_publickey *next = NULL;
488 struct buffer_ctx storebuf;
492 uint32_t shortkeyid = 0;
493 uint64_t *subkeyids = NULL;
495 char *primary = NULL;
496 unsigned char worddb_data[12];
497 struct ll *wordlist = NULL;
498 struct ll *curword = NULL;
499 bool deadlock = false;
501 keyid = get_keyid(publickey);
508 * Delete the key if we already have it.
510 * TODO: Can we optimize this perhaps? Possibly when other data is
511 * involved as well? I suspect this is easiest and doesn't make a lot
512 * of difference though - the largest chunk of data is the keydata and
513 * it definitely needs updated.
516 deadlock = (delete_key(keyid, true) == -1);
520 * Convert the key to a flat set of binary data.
523 next = publickey->next;
524 publickey->next = NULL;
525 flatten_publickey(publickey, &packets, &list_end);
526 publickey->next = next;
529 storebuf.size = 8192;
530 storebuf.buffer = malloc(8192);
532 write_openpgp_stream(buffer_putchar, &storebuf, packets);
535 * Now we have the key data store it in the DB; the keyid is
538 memset(&key, 0, sizeof(key));
539 memset(&data, 0, sizeof(data));
541 key.size = sizeof(keyid);
542 data.size = storebuf.offset;
543 data.data = storebuf.buffer;
545 ret = keydb(keyid)->put(keydb(keyid),
551 logthing(LOGTHING_ERROR,
552 "Problem storing key: %s",
554 if (ret == DB_LOCK_DEADLOCK) {
559 free(storebuf.buffer);
560 storebuf.buffer = NULL;
564 free_packet_list(packets);
569 * Walk through our uids storing the words into the db with the keyid.
572 uids = keyuids(publickey, &primary);
575 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
576 wordlist = makewordlist(wordlist, uids[i]);
579 for (curword = wordlist; curword != NULL && !deadlock;
580 curword = curword->next) {
581 memset(&key, 0, sizeof(key));
582 memset(&data, 0, sizeof(data));
583 key.data = curword->object;
584 key.size = strlen(key.data);
585 data.data = worddb_data;
586 data.size = sizeof(worddb_data);
589 * Our data is the key creation time followed by the
592 worddb_data[ 0] = publickey->publickey->data[1];
593 worddb_data[ 1] = publickey->publickey->data[2];
594 worddb_data[ 2] = publickey->publickey->data[3];
595 worddb_data[ 3] = publickey->publickey->data[4];
596 worddb_data[ 4] = (keyid >> 56) & 0xFF;
597 worddb_data[ 5] = (keyid >> 48) & 0xFF;
598 worddb_data[ 6] = (keyid >> 40) & 0xFF;
599 worddb_data[ 7] = (keyid >> 32) & 0xFF;
600 worddb_data[ 8] = (keyid >> 24) & 0xFF;
601 worddb_data[ 9] = (keyid >> 16) & 0xFF;
602 worddb_data[10] = (keyid >> 8) & 0xFF;
603 worddb_data[11] = keyid & 0xFF;
604 ret = worddb->put(worddb,
610 logthing(LOGTHING_ERROR,
611 "Problem storing word: %s",
613 if (ret == DB_LOCK_DEADLOCK) {
620 * Free our UID and word lists.
622 llfree(wordlist, NULL);
623 for (i = 0; uids[i] != NULL; i++) {
636 * Write the truncated 32 bit keyid so we can lookup the full id for
640 shortkeyid = keyid & 0xFFFFFFFF;
642 memset(&key, 0, sizeof(key));
643 memset(&data, 0, sizeof(data));
644 key.data = &shortkeyid;
645 key.size = sizeof(shortkeyid);
647 data.size = sizeof(keyid);
649 ret = id32db->put(id32db,
655 logthing(LOGTHING_ERROR,
656 "Problem storing short keyid: %s",
658 if (ret == DB_LOCK_DEADLOCK) {
665 subkeyids = keysubkeys(publickey);
667 while (subkeyids != NULL && subkeyids[i] != 0) {
668 shortkeyid = subkeyids[i++] & 0xFFFFFFFF;
670 memset(&key, 0, sizeof(key));
671 memset(&data, 0, sizeof(data));
672 key.data = &shortkeyid;
673 key.size = sizeof(shortkeyid);
675 data.size = sizeof(keyid);
677 ret = id32db->put(id32db,
683 logthing(LOGTHING_ERROR,
684 "Problem storing short keyid: %s",
686 if (ret == DB_LOCK_DEADLOCK) {
691 if (subkeyids != NULL) {
697 return deadlock ? -1 : 0 ;
701 * delete_key - Given a keyid delete the key from storage.
702 * @keyid: The keyid to delete.
703 * @intrans: If we're already in a transaction.
705 * This function deletes a public key from whatever storage mechanism we
706 * are using. Returns 0 if the key existed.
708 int delete_key(uint64_t keyid, bool intrans)
710 struct openpgp_publickey *publickey = NULL;
713 uint32_t shortkeyid = 0;
714 uint64_t *subkeyids = NULL;
718 char *primary = NULL;
719 unsigned char worddb_data[12];
720 struct ll *wordlist = NULL;
721 struct ll *curword = NULL;
722 bool deadlock = false;
728 fetch_key(keyid, &publickey, true);
731 * Walk through the uids removing the words from the worddb.
733 if (publickey != NULL) {
734 uids = keyuids(publickey, &primary);
737 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
738 wordlist = makewordlist(wordlist, uids[i]);
741 ret = worddb->cursor(worddb,
746 for (curword = wordlist; curword != NULL && !deadlock;
747 curword = curword->next) {
748 memset(&key, 0, sizeof(key));
749 memset(&data, 0, sizeof(data));
750 key.data = curword->object;
751 key.size = strlen(key.data);
752 data.data = worddb_data;
753 data.size = sizeof(worddb_data);
756 * Our data is the key creation time followed by the
759 worddb_data[ 0] = publickey->publickey->data[1];
760 worddb_data[ 1] = publickey->publickey->data[2];
761 worddb_data[ 2] = publickey->publickey->data[3];
762 worddb_data[ 3] = publickey->publickey->data[4];
763 worddb_data[ 4] = (keyid >> 56) & 0xFF;
764 worddb_data[ 5] = (keyid >> 48) & 0xFF;
765 worddb_data[ 6] = (keyid >> 40) & 0xFF;
766 worddb_data[ 7] = (keyid >> 32) & 0xFF;
767 worddb_data[ 8] = (keyid >> 24) & 0xFF;
768 worddb_data[ 9] = (keyid >> 16) & 0xFF;
769 worddb_data[10] = (keyid >> 8) & 0xFF;
770 worddb_data[11] = keyid & 0xFF;
772 ret = cursor->c_get(cursor,
778 ret = cursor->c_del(cursor, 0);
780 logthing(LOGTHING_ERROR,
781 "Problem deleting word: %s",
787 logthing(LOGTHING_ERROR,
788 "Problem deleting word: %s",
790 if (ret == DB_LOCK_DEADLOCK) {
795 ret = cursor->c_close(cursor);
799 * Free our UID and word lists.
801 llfree(wordlist, NULL);
802 for (i = 0; uids[i] != NULL; i++) {
808 free_publickey(publickey);
813 ret = id32db->cursor(id32db,
818 shortkeyid = keyid & 0xFFFFFFFF;
820 memset(&key, 0, sizeof(key));
821 memset(&data, 0, sizeof(data));
822 key.data = &shortkeyid;
823 key.size = sizeof(shortkeyid);
825 data.size = sizeof(keyid);
827 ret = cursor->c_get(cursor,
833 ret = cursor->c_del(cursor, 0);
835 logthing(LOGTHING_ERROR,
836 "Problem deleting short keyid: %s",
842 logthing(LOGTHING_ERROR,
843 "Problem deleting short keyid: %s",
845 if (ret == DB_LOCK_DEADLOCK) {
850 subkeyids = keysubkeys(publickey);
852 while (subkeyids != NULL && subkeyids[i] != 0) {
853 shortkeyid = subkeyids[i++] & 0xFFFFFFFF;
855 memset(&key, 0, sizeof(key));
856 memset(&data, 0, sizeof(data));
857 key.data = &shortkeyid;
858 key.size = sizeof(shortkeyid);
860 data.size = sizeof(keyid);
862 ret = cursor->c_get(cursor,
868 ret = cursor->c_del(cursor, 0);
870 logthing(LOGTHING_ERROR,
871 "Problem deleting short"
878 logthing(LOGTHING_ERROR,
879 "Problem deleting short keyid: %s",
881 if (ret == DB_LOCK_DEADLOCK) {
886 if (subkeyids != NULL) {
891 ret = cursor->c_close(cursor);
897 key.size = sizeof(keyid);
899 keydb(keyid)->del(keydb(keyid),
909 return deadlock ? (-1) : (ret == DB_NOTFOUND);
913 * dumpdb - dump the key database
914 * @filenamebase: The base filename to use for the dump.
916 * Dumps the database into one or more files, which contain pure OpenPGP
917 * that can be reimported into onak or gpg. filenamebase provides a base
918 * file name for the dump; several files may be created, all of which will
919 * begin with this string and then have a unique number and a .pgp
922 int dumpdb(char *filenamebase)
932 for (i = 0; i < numdbs; i++) {
933 ret = dbconns[i]->cursor(dbconns[i],
938 snprintf(filename, 1023, "%s.%d.pgp", filenamebase, i);
939 fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0640);
941 logthing(LOGTHING_ERROR,
942 "Error opening keydump file (%s): %s",
946 memset(&key, 0, sizeof(key));
947 memset(&data, 0, sizeof(data));
948 ret = cursor->c_get(cursor, &key, &data, DB_NEXT);
950 write(fd, data.data, data.size);
951 memset(&key, 0, sizeof(key));
952 memset(&data, 0, sizeof(data));
953 ret = cursor->c_get(cursor, &key, &data,
956 if (ret != DB_NOTFOUND) {
957 logthing(LOGTHING_ERROR,
958 "Problem reading key: %s",
964 ret = cursor->c_close(cursor);
972 * getfullkeyid - Maps a 32bit key id to a 64bit one.
973 * @keyid: The 32bit keyid.
975 * This function maps a 32bit key id to the full 64bit one. It returns the
976 * full keyid. If the key isn't found a keyid of 0 is returned.
978 uint64_t getfullkeyid(uint64_t keyid)
982 uint32_t shortkeyid = 0;
985 if (keyid < 0x100000000LL) {
986 ret = id32db->cursor(id32db,
991 shortkeyid = keyid & 0xFFFFFFFF;
993 memset(&key, 0, sizeof(key));
994 memset(&data, 0, sizeof(data));
995 key.data = &shortkeyid;
996 key.size = sizeof(shortkeyid);
997 data.flags = DB_DBT_MALLOC;
999 ret = cursor->c_get(cursor,
1005 keyid = *(uint64_t *) data.data;
1007 if (data.data != NULL) {
1013 ret = cursor->c_close(cursor);
1021 * Include the basic keydb routines.
1023 #define NEED_GETKEYSIGS 1
1024 #define NEED_KEYID2UID 1