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
10 #include <sys/types.h>
22 #include "charfuncs.h"
25 #include "decodekey.h"
26 #include "keystructs.h"
29 #include "onak-conf.h"
33 * dbenv - our database environment.
35 static DB_ENV *dbenv = NULL;
38 * dbconn - our connection to the key database.
40 static DB *dbconn = NULL;
43 * worddb - our connection to the word database.
45 static DB *worddb = NULL;
48 * txn - our current transaction id.
50 static DB_TXN *txn = NULL;
53 * makewordlist - Takes a string and splits it into a set of unique words.
54 * @wordlist: The current word list.
55 * @words: The string to split and add.
57 * We take words and split it on non alpha numeric characters. These get
58 * added to the word list if they're not already present. If the wordlist
59 * is NULL then we start a new list, otherwise it's search for already
60 * added words. Note that words is modified in the process of scanning.
62 * Returns the new word list.
64 struct ll *makewordlist(struct ll *wordlist, char *word)
70 * Walk through the words string, spliting on non alphanumerics and
71 * then checking if the word already exists in the list. If not then
75 while (end != NULL && *end != 0) {
77 while (*start != 0 && !isalnum(*start)) {
81 while (*end != 0 && isalnum(*end)) {
85 if (end - start > 1) {
91 if (llfind(wordlist, start,
93 wordlist = lladd(wordlist,
103 * initdb - Initialize the key database.
105 * This function should be called before any of the other functions in
106 * this file are called in order to allow the DB to be initialized ready
113 ret = db_env_create(&dbenv, 0);
115 logthing(LOGTHING_CRITICAL,
116 "db_env_create: %s", db_strerror(ret));
121 * Enable deadlock detection so that we don't block indefinitely on
122 * anything. What we really want is simple 2 state locks, but I'm not
123 * sure how to make the standard DB functions do that yet.
125 ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT);
127 logthing(LOGTHING_CRITICAL,
128 "db_env_create: %s", db_strerror(ret));
132 ret = dbenv->open(dbenv, config.db_dir,
133 DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
138 dbenv->err(dbenv, ret, "%s", config.db_dir);
142 ret = db_create(&dbconn, dbenv, 0);
144 logthing(LOGTHING_CRITICAL,
145 "db_create: %s", db_strerror(ret));
149 ret = dbconn->open(dbconn, "keydb.db",
155 dbconn->err(dbconn, ret, "keydb.db");
159 ret = db_create(&worddb, dbenv, 0);
161 logthing(LOGTHING_CRITICAL, "db_create: %s", db_strerror(ret));
164 ret = worddb->set_flags(worddb, DB_DUP);
166 ret = worddb->open(worddb, "worddb", NULL, DB_BTREE,
170 worddb->err(worddb, ret, "worddb");
178 * cleanupdb - De-initialize the key database.
180 * This function should be called upon program exit to allow the DB to
181 * cleanup after itself.
185 worddb->close(worddb, 0);
187 dbconn->close(dbconn, 0);
189 dbenv->close(dbenv, 0);
194 * starttrans - Start a transaction.
196 * Start a transaction. Intended to be used if we're about to perform many
197 * operations on the database to help speed it all up, or if we want
198 * something to only succeed if all relevant operations are successful.
200 bool starttrans(void)
204 assert(dbenv != NULL);
207 ret = txn_begin(dbenv,
208 NULL, /* No parent transaction */
212 dbenv->err(dbenv, ret, "starttrans():");
220 * endtrans - End a transaction.
222 * Ends a transaction.
228 assert(dbenv != NULL);
231 ret = txn_commit(txn,
234 dbenv->err(dbenv, ret, "endtrans():");
243 * fetch_key - Given a keyid fetch the key from storage.
244 * @keyid: The keyid to fetch.
245 * @publickey: A pointer to a structure to return the key in.
246 * @intrans: If we're already in a transaction.
248 * We use the hex representation of the keyid as the filename to fetch the
249 * key from. The key is stored in the file as a binary OpenPGP stream of
250 * packets, so we can just use read_openpgp_stream() to read the packets
251 * in and then parse_keys() to parse the packets into a publickey
254 int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
257 struct openpgp_packet_list *packets = NULL;
261 struct buffer_ctx fetchbuf;
263 memset(&key, 0, sizeof(key));
264 memset(&data, 0, sizeof(data));
269 key.size = sizeof(keyid);
277 ret = dbconn->get(dbconn,
284 fetchbuf.buffer = data.data;
286 fetchbuf.size = data.size;
287 read_openpgp_stream(buffer_fetchchar, &fetchbuf,
289 parse_keys(packets, publickey);
290 free_packet_list(packets);
293 } else if (ret != DB_NOTFOUND) {
294 dbconn->err(dbconn, ret, "Problem retrieving key");
304 int worddb_cmp(const char *d1, const char *d2)
306 return memcmp(d1, d2, 12);
310 * fetch_key_text - Trys to find the keys that contain the supplied text.
311 * @search: The text to search for.
312 * @publickey: A pointer to a structure to return the key in.
314 * This function searches for the supplied text and returns the keys that
317 int fetch_key_text(const char *search, struct openpgp_publickey **publickey)
325 char *searchtext = NULL;
326 struct ll *wordlist = NULL;
327 struct ll *curword = NULL;
328 struct ll *keylist = NULL;
329 struct ll *newkeylist = NULL;
332 searchtext = strdup(search);
333 wordlist = makewordlist(wordlist, searchtext);
337 ret = worddb->cursor(worddb,
342 for (curword = wordlist; curword != NULL; curword = curword->next) {
343 memset(&key, 0, sizeof(key));
344 memset(&data, 0, sizeof(data));
345 key.data = curword->object;
346 key.size = strlen(curword->object);
347 data.flags = DB_DBT_MALLOC;
348 ret = cursor->c_get(cursor,
352 while (ret == 0 && strncmp(key.data, curword->object,
354 ((char *) curword->object)[key.size] == 0) {
356 for (i = 4; i < 12; i++) {
358 keyid += ((unsigned char *)
362 if (keylist == NULL ||
363 llfind(keylist, data.data,
364 worddb_cmp) != NULL) {
365 newkeylist = lladd(newkeylist, data.data);
371 ret = cursor->c_get(cursor,
376 llfree(keylist, free);
377 keylist = newkeylist;
379 if (data.data != NULL) {
384 llfree(wordlist, NULL);
387 for (newkeylist = keylist;
388 newkeylist != NULL && numkeys < config.maxkeys;
389 newkeylist = newkeylist->next) {
392 for (i = 4; i < 12; i++) {
394 keyid += ((unsigned char *)
395 newkeylist->object)[i];
398 numkeys += fetch_key(keyid,
402 llfree(keylist, free);
407 ret = cursor->c_close(cursor);
416 * store_key - Takes a key and stores it.
417 * @publickey: A pointer to the public key to store.
418 * @intrans: If we're already in a transaction.
419 * @update: If true the key exists and should be updated.
421 * Again we just use the hex representation of the keyid as the filename
422 * to store the key to. We flatten the public key to a list of OpenPGP
423 * packets and then use write_openpgp_stream() to write the stream out to
424 * the file. If update is true then we delete the old key first, otherwise
425 * we trust that it doesn't exist.
427 int store_key(struct openpgp_publickey *publickey, bool intrans, bool update)
429 struct openpgp_packet_list *packets = NULL;
430 struct openpgp_packet_list *list_end = NULL;
431 struct openpgp_publickey *next = NULL;
434 struct buffer_ctx storebuf;
439 char *primary = NULL;
440 unsigned char worddb_data[12];
441 struct ll *wordlist = NULL;
442 struct ll *curword = NULL;
443 bool deadlock = false;
445 keyid = get_keyid(publickey);
452 * Delete the key if we already have it.
454 * TODO: Can we optimize this perhaps? Possibly when other data is
455 * involved as well? I suspect this is easiest and doesn't make a lot
456 * of difference though - the largest chunk of data is the keydata and
457 * it definitely needs updated.
460 deadlock = (delete_key(keyid, true) == -1);
464 * Convert the key to a flat set of binary data.
467 next = publickey->next;
468 publickey->next = NULL;
469 flatten_publickey(publickey, &packets, &list_end);
470 publickey->next = next;
473 storebuf.size = 8192;
474 storebuf.buffer = malloc(8192);
476 write_openpgp_stream(buffer_putchar, &storebuf, packets);
479 * Now we have the key data store it in the DB; the keyid is
482 memset(&key, 0, sizeof(key));
483 memset(&data, 0, sizeof(data));
485 key.size = sizeof(keyid);
487 data.size = storebuf.offset;
488 data.data = storebuf.buffer;
490 ret = dbconn->put(dbconn,
496 dbconn->err(dbconn, ret, "Problem storing key");
497 if (ret == DB_LOCK_DEADLOCK) {
502 free(storebuf.buffer);
503 storebuf.buffer = NULL;
507 free_packet_list(packets);
512 * Walk through our uids storing the words into the db with the keyid.
515 uids = keyuids(publickey, &primary);
518 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
519 wordlist = makewordlist(wordlist, uids[i]);
522 for (curword = wordlist; curword != NULL && !deadlock;
523 curword = curword->next) {
524 memset(&key, 0, sizeof(key));
525 memset(&data, 0, sizeof(data));
526 key.data = curword->object;
527 key.size = strlen(key.data);
528 data.data = worddb_data;
529 data.size = sizeof(worddb_data);
532 * Our data is the key creation time followed by the
535 worddb_data[ 0] = publickey->publickey->data[1];
536 worddb_data[ 1] = publickey->publickey->data[2];
537 worddb_data[ 2] = publickey->publickey->data[3];
538 worddb_data[ 3] = publickey->publickey->data[4];
539 worddb_data[ 4] = (keyid >> 56) & 0xFF;
540 worddb_data[ 5] = (keyid >> 48) & 0xFF;
541 worddb_data[ 6] = (keyid >> 40) & 0xFF;
542 worddb_data[ 7] = (keyid >> 32) & 0xFF;
543 worddb_data[ 8] = (keyid >> 24) & 0xFF;
544 worddb_data[ 9] = (keyid >> 16) & 0xFF;
545 worddb_data[10] = (keyid >> 8) & 0xFF;
546 worddb_data[11] = keyid & 0xFF;
547 ret = worddb->put(worddb,
553 worddb->err(worddb, ret,
554 "Problem storing key");
555 if (ret == DB_LOCK_DEADLOCK) {
562 * Free our UID and word lists.
564 llfree(wordlist, NULL);
565 for (i = 0; uids[i] != NULL; i++) {
577 return deadlock ? -1 : 0 ;
581 * delete_key - Given a keyid delete the key from storage.
582 * @keyid: The keyid to delete.
583 * @intrans: If we're already in a transaction.
585 * This function deletes a public key from whatever storage mechanism we
586 * are using. Returns 0 if the key existed.
588 int delete_key(uint64_t keyid, bool intrans)
590 struct openpgp_publickey *publickey = NULL;
596 char *primary = NULL;
597 unsigned char worddb_data[12];
598 struct ll *wordlist = NULL;
599 struct ll *curword = NULL;
600 bool deadlock = false;
608 fetch_key(keyid, &publickey, true);
611 * Walk through the uids removing the words from the worddb.
613 if (publickey != NULL) {
614 uids = keyuids(publickey, &primary);
617 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
618 wordlist = makewordlist(wordlist, uids[i]);
621 ret = worddb->cursor(worddb,
626 for (curword = wordlist; curword != NULL && !deadlock;
627 curword = curword->next) {
628 memset(&key, 0, sizeof(key));
629 memset(&data, 0, sizeof(data));
630 key.data = curword->object;
631 key.size = strlen(key.data);
632 data.data = worddb_data;
633 data.size = sizeof(worddb_data);
636 * Our data is the key creation time followed by the
639 worddb_data[ 0] = publickey->publickey->data[1];
640 worddb_data[ 1] = publickey->publickey->data[2];
641 worddb_data[ 2] = publickey->publickey->data[3];
642 worddb_data[ 3] = publickey->publickey->data[4];
643 worddb_data[ 4] = (keyid >> 56) & 0xFF;
644 worddb_data[ 5] = (keyid >> 48) & 0xFF;
645 worddb_data[ 6] = (keyid >> 40) & 0xFF;
646 worddb_data[ 7] = (keyid >> 32) & 0xFF;
647 worddb_data[ 8] = (keyid >> 24) & 0xFF;
648 worddb_data[ 9] = (keyid >> 16) & 0xFF;
649 worddb_data[10] = (keyid >> 8) & 0xFF;
650 worddb_data[11] = keyid & 0xFF;
652 ret = cursor->c_get(cursor,
658 ret = cursor->c_del(cursor, 0);
660 worddb->err(worddb, ret,
661 "Problem deleting word.");
666 worddb->err(worddb, ret,
667 "Problem deleting word.");
668 if (ret == DB_LOCK_DEADLOCK) {
673 ret = cursor->c_close(cursor);
677 * Free our UID and word lists.
679 llfree(wordlist, NULL);
680 for (i = 0; uids[i] != NULL; i++) {
686 free_publickey(publickey);
692 key.size = sizeof(keyid);
704 return deadlock ? (-1) : (ret == DB_NOTFOUND);
708 * dumpdb - dump the key database
709 * @filenamebase: The base filename to use for the dump.
711 * Dumps the database into one or more files, which contain pure OpenPGP
712 * that can be reimported into onak or gpg. filenamebase provides a base
713 * file name for the dump; several files may be created, all of which will
714 * begin with this string and then have a unique number and a .pgp
717 int dumpdb(char *filenamebase)
726 ret = dbconn->cursor(dbconn,
731 fd = open(filenamebase, O_CREAT | O_WRONLY | O_TRUNC, 0640);
732 memset(&key, 0, sizeof(key));
733 memset(&data, 0, sizeof(data));
734 ret = cursor->c_get(cursor, &key, &data, DB_NEXT);
736 write(fd, data.data, data.size);
737 memset(&key, 0, sizeof(key));
738 memset(&data, 0, sizeof(data));
739 ret = cursor->c_get(cursor, &key, &data, DB_NEXT);
741 dbconn->err(dbconn, ret, "Problem reading key");
745 ret = cursor->c_close(cursor);
754 * Include the basic keydb routines.
756 #define NEED_GETFULLKEYID 1
757 #define NEED_GETKEYSIGS 1
758 #define NEED_KEYID2UID 1