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 * txn - our current transaction id.
58 static DB_TXN *txn = NULL;
60 DB *keydb(uint64_t keyid)
66 return(dbconns[keytrun % numdbs]);
70 * initdb - Initialize the key database.
72 * This function should be called before any of the other functions in
73 * this file are called in order to allow the DB to be initialized ready
76 void initdb(bool readonly)
84 snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", config.db_dir);
85 numdb = fopen(buf, "r");
87 if (fgets(buf, sizeof(buf), numdb) != NULL) {
91 } else if (!readonly) {
92 logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s",
94 numdb = fopen(buf, "w");
96 fprintf(numdb, "%d", numdbs);
99 logthing(LOGTHING_ERROR,
100 "Couldn't write num_keydb: %s",
105 dbconns = malloc(sizeof (DB *) * numdbs);
106 if (dbconns == NULL) {
107 logthing(LOGTHING_CRITICAL,
108 "Couldn't allocate memory for dbconns");
112 ret = db_env_create(&dbenv, 0);
114 logthing(LOGTHING_CRITICAL,
115 "db_env_create: %s", db_strerror(ret));
120 * Enable deadlock detection so that we don't block indefinitely on
121 * anything. What we really want is simple 2 state locks, but I'm not
122 * sure how to make the standard DB functions do that yet.
124 ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT);
126 logthing(LOGTHING_CRITICAL,
127 "db_env_create: %s", db_strerror(ret));
131 ret = dbenv->open(dbenv, config.db_dir,
132 DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
137 logthing(LOGTHING_CRITICAL,
138 "Error opening db environment: %s (%s)",
144 for (i = 0; i < numdbs; i++) {
145 ret = db_create(&dbconns[i], dbenv, 0);
147 logthing(LOGTHING_CRITICAL,
148 "db_create: %s", db_strerror(ret));
152 snprintf(buf, 1023, "keydb.%d.db", i);
157 ret = dbconns[i]->open(dbconns[i], buf,
163 logthing(LOGTHING_CRITICAL,
164 "Error opening key database: %s (%s)",
171 ret = db_create(&worddb, dbenv, 0);
173 logthing(LOGTHING_CRITICAL, "db_create: %s", db_strerror(ret));
176 ret = worddb->set_flags(worddb, DB_DUP);
178 ret = worddb->open(worddb, "worddb", NULL, DB_BTREE,
182 logthing(LOGTHING_CRITICAL,
183 "Error opening word database: %s (%s)",
193 * cleanupdb - De-initialize the key database.
195 * This function should be called upon program exit to allow the DB to
196 * cleanup after itself.
202 txn_checkpoint(dbenv, 0, 0, 0);
203 worddb->close(worddb, 0);
205 for (i = 0; i < numdbs; i++) {
206 dbconns[i]->close(dbconns[i], 0);
209 dbenv->close(dbenv, 0);
214 * starttrans - Start a transaction.
216 * Start a transaction. Intended to be used if we're about to perform many
217 * operations on the database to help speed it all up, or if we want
218 * something to only succeed if all relevant operations are successful.
220 bool starttrans(void)
224 assert(dbenv != NULL);
227 ret = txn_begin(dbenv,
228 NULL, /* No parent transaction */
232 logthing(LOGTHING_CRITICAL,
233 "Error starting transaction: %s",
242 * endtrans - End a transaction.
244 * Ends a transaction.
250 assert(dbenv != NULL);
253 ret = txn_commit(txn,
256 logthing(LOGTHING_CRITICAL,
257 "Error ending transaction: %s",
267 * fetch_key - Given a keyid fetch the key from storage.
268 * @keyid: The keyid to fetch.
269 * @publickey: A pointer to a structure to return the key in.
270 * @intrans: If we're already in a transaction.
272 * We use the hex representation of the keyid as the filename to fetch the
273 * key from. The key is stored in the file as a binary OpenPGP stream of
274 * packets, so we can just use read_openpgp_stream() to read the packets
275 * in and then parse_keys() to parse the packets into a publickey
278 int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
281 struct openpgp_packet_list *packets = NULL;
285 struct buffer_ctx fetchbuf;
287 memset(&key, 0, sizeof(key));
288 memset(&data, 0, sizeof(data));
293 key.size = sizeof(keyid);
301 ret = keydb(keyid)->get(keydb(keyid),
308 fetchbuf.buffer = data.data;
310 fetchbuf.size = data.size;
311 read_openpgp_stream(buffer_fetchchar, &fetchbuf,
313 parse_keys(packets, publickey);
314 free_packet_list(packets);
317 } else if (ret != DB_NOTFOUND) {
318 logthing(LOGTHING_ERROR,
319 "Problem retrieving key: %s",
330 int worddb_cmp(const char *d1, const char *d2)
332 return memcmp(d1, d2, 12);
336 * fetch_key_text - Trys to find the keys that contain the supplied text.
337 * @search: The text to search for.
338 * @publickey: A pointer to a structure to return the key in.
340 * This function searches for the supplied text and returns the keys that
343 int fetch_key_text(const char *search, struct openpgp_publickey **publickey)
351 char *searchtext = NULL;
352 struct ll *wordlist = NULL;
353 struct ll *curword = NULL;
354 struct ll *keylist = NULL;
355 struct ll *newkeylist = NULL;
358 searchtext = strdup(search);
359 wordlist = makewordlist(wordlist, searchtext);
363 ret = worddb->cursor(worddb,
368 for (curword = wordlist; curword != NULL; curword = curword->next) {
369 memset(&key, 0, sizeof(key));
370 memset(&data, 0, sizeof(data));
371 key.data = curword->object;
372 key.size = strlen(curword->object);
373 data.flags = DB_DBT_MALLOC;
374 ret = cursor->c_get(cursor,
378 while (ret == 0 && strncmp(key.data, curword->object,
380 ((char *) curword->object)[key.size] == 0) {
382 for (i = 4; i < 12; i++) {
384 keyid += ((unsigned char *)
388 if (keylist == NULL ||
389 llfind(keylist, data.data,
390 worddb_cmp) != NULL) {
391 newkeylist = lladd(newkeylist, data.data);
397 ret = cursor->c_get(cursor,
402 llfree(keylist, free);
403 keylist = newkeylist;
405 if (data.data != NULL) {
410 llfree(wordlist, NULL);
413 for (newkeylist = keylist;
414 newkeylist != NULL && numkeys < config.maxkeys;
415 newkeylist = newkeylist->next) {
418 for (i = 4; i < 12; i++) {
420 keyid += ((unsigned char *)
421 newkeylist->object)[i];
424 numkeys += fetch_key(keyid,
428 llfree(keylist, free);
433 ret = cursor->c_close(cursor);
442 * store_key - Takes a key and stores it.
443 * @publickey: A pointer to the public key to store.
444 * @intrans: If we're already in a transaction.
445 * @update: If true the key exists and should be updated.
447 * Again we just use the hex representation of the keyid as the filename
448 * to store the key to. We flatten the public key to a list of OpenPGP
449 * packets and then use write_openpgp_stream() to write the stream out to
450 * the file. If update is true then we delete the old key first, otherwise
451 * we trust that it doesn't exist.
453 int store_key(struct openpgp_publickey *publickey, bool intrans, bool update)
455 struct openpgp_packet_list *packets = NULL;
456 struct openpgp_packet_list *list_end = NULL;
457 struct openpgp_publickey *next = NULL;
460 struct buffer_ctx storebuf;
465 char *primary = NULL;
466 unsigned char worddb_data[12];
467 struct ll *wordlist = NULL;
468 struct ll *curword = NULL;
469 bool deadlock = false;
471 keyid = get_keyid(publickey);
478 * Delete the key if we already have it.
480 * TODO: Can we optimize this perhaps? Possibly when other data is
481 * involved as well? I suspect this is easiest and doesn't make a lot
482 * of difference though - the largest chunk of data is the keydata and
483 * it definitely needs updated.
486 deadlock = (delete_key(keyid, true) == -1);
490 * Convert the key to a flat set of binary data.
493 next = publickey->next;
494 publickey->next = NULL;
495 flatten_publickey(publickey, &packets, &list_end);
496 publickey->next = next;
499 storebuf.size = 8192;
500 storebuf.buffer = malloc(8192);
502 write_openpgp_stream(buffer_putchar, &storebuf, packets);
505 * Now we have the key data store it in the DB; the keyid is
508 memset(&key, 0, sizeof(key));
509 memset(&data, 0, sizeof(data));
511 key.size = sizeof(keyid);
513 data.size = storebuf.offset;
514 data.data = storebuf.buffer;
516 ret = keydb(keyid)->put(keydb(keyid),
522 logthing(LOGTHING_ERROR,
523 "Problem storing key: %s",
525 if (ret == DB_LOCK_DEADLOCK) {
530 free(storebuf.buffer);
531 storebuf.buffer = NULL;
535 free_packet_list(packets);
540 * Walk through our uids storing the words into the db with the keyid.
543 uids = keyuids(publickey, &primary);
546 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
547 wordlist = makewordlist(wordlist, uids[i]);
550 for (curword = wordlist; curword != NULL && !deadlock;
551 curword = curword->next) {
552 memset(&key, 0, sizeof(key));
553 memset(&data, 0, sizeof(data));
554 key.data = curword->object;
555 key.size = strlen(key.data);
556 data.data = worddb_data;
557 data.size = sizeof(worddb_data);
560 * Our data is the key creation time followed by the
563 worddb_data[ 0] = publickey->publickey->data[1];
564 worddb_data[ 1] = publickey->publickey->data[2];
565 worddb_data[ 2] = publickey->publickey->data[3];
566 worddb_data[ 3] = publickey->publickey->data[4];
567 worddb_data[ 4] = (keyid >> 56) & 0xFF;
568 worddb_data[ 5] = (keyid >> 48) & 0xFF;
569 worddb_data[ 6] = (keyid >> 40) & 0xFF;
570 worddb_data[ 7] = (keyid >> 32) & 0xFF;
571 worddb_data[ 8] = (keyid >> 24) & 0xFF;
572 worddb_data[ 9] = (keyid >> 16) & 0xFF;
573 worddb_data[10] = (keyid >> 8) & 0xFF;
574 worddb_data[11] = keyid & 0xFF;
575 ret = worddb->put(worddb,
581 logthing(LOGTHING_ERROR,
582 "Problem storing word: %s",
584 if (ret == DB_LOCK_DEADLOCK) {
591 * Free our UID and word lists.
593 llfree(wordlist, NULL);
594 for (i = 0; uids[i] != NULL; i++) {
606 return deadlock ? -1 : 0 ;
610 * delete_key - Given a keyid delete the key from storage.
611 * @keyid: The keyid to delete.
612 * @intrans: If we're already in a transaction.
614 * This function deletes a public key from whatever storage mechanism we
615 * are using. Returns 0 if the key existed.
617 int delete_key(uint64_t keyid, bool intrans)
619 struct openpgp_publickey *publickey = NULL;
625 char *primary = NULL;
626 unsigned char worddb_data[12];
627 struct ll *wordlist = NULL;
628 struct ll *curword = NULL;
629 bool deadlock = false;
637 fetch_key(keyid, &publickey, true);
640 * Walk through the uids removing the words from the worddb.
642 if (publickey != NULL) {
643 uids = keyuids(publickey, &primary);
646 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
647 wordlist = makewordlist(wordlist, uids[i]);
650 ret = worddb->cursor(worddb,
655 for (curword = wordlist; curword != NULL && !deadlock;
656 curword = curword->next) {
657 memset(&key, 0, sizeof(key));
658 memset(&data, 0, sizeof(data));
659 key.data = curword->object;
660 key.size = strlen(key.data);
661 data.data = worddb_data;
662 data.size = sizeof(worddb_data);
665 * Our data is the key creation time followed by the
668 worddb_data[ 0] = publickey->publickey->data[1];
669 worddb_data[ 1] = publickey->publickey->data[2];
670 worddb_data[ 2] = publickey->publickey->data[3];
671 worddb_data[ 3] = publickey->publickey->data[4];
672 worddb_data[ 4] = (keyid >> 56) & 0xFF;
673 worddb_data[ 5] = (keyid >> 48) & 0xFF;
674 worddb_data[ 6] = (keyid >> 40) & 0xFF;
675 worddb_data[ 7] = (keyid >> 32) & 0xFF;
676 worddb_data[ 8] = (keyid >> 24) & 0xFF;
677 worddb_data[ 9] = (keyid >> 16) & 0xFF;
678 worddb_data[10] = (keyid >> 8) & 0xFF;
679 worddb_data[11] = keyid & 0xFF;
681 ret = cursor->c_get(cursor,
687 ret = cursor->c_del(cursor, 0);
689 logthing(LOGTHING_ERROR,
690 "Problem deleting word: %s",
696 logthing(LOGTHING_ERROR,
697 "Problem deleting word: %s",
699 if (ret == DB_LOCK_DEADLOCK) {
704 ret = cursor->c_close(cursor);
708 * Free our UID and word lists.
710 llfree(wordlist, NULL);
711 for (i = 0; uids[i] != NULL; i++) {
717 free_publickey(publickey);
723 key.size = sizeof(keyid);
725 keydb(keyid)->del(keydb(keyid),
735 return deadlock ? (-1) : (ret == DB_NOTFOUND);
739 * dumpdb - dump the key database
740 * @filenamebase: The base filename to use for the dump.
742 * Dumps the database into one or more files, which contain pure OpenPGP
743 * that can be reimported into onak or gpg. filenamebase provides a base
744 * file name for the dump; several files may be created, all of which will
745 * begin with this string and then have a unique number and a .pgp
748 int dumpdb(char *filenamebase)
758 for (i = 0; i < numdbs; i++) {
759 ret = dbconns[i]->cursor(dbconns[i],
764 snprintf(filename, 1023, "%s.%d.pgp", filenamebase, i);
765 fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0640);
767 logthing(LOGTHING_ERROR,
768 "Error opening keydump file (%s): %s",
772 memset(&key, 0, sizeof(key));
773 memset(&data, 0, sizeof(data));
774 ret = cursor->c_get(cursor, &key, &data, DB_NEXT);
776 write(fd, data.data, data.size);
777 memset(&key, 0, sizeof(key));
778 memset(&data, 0, sizeof(data));
779 ret = cursor->c_get(cursor, &key, &data,
782 if (ret != DB_NOTFOUND) {
783 logthing(LOGTHING_ERROR,
784 "Problem reading key: %s",
790 ret = cursor->c_close(cursor);
798 * Include the basic keydb routines.
800 #define NEED_GETFULLKEYID 1
801 #define NEED_GETKEYSIGS 1
802 #define NEED_KEYID2UID 1