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"
28 #include "onak-conf.h"
32 * dbenv - our database environment.
34 static DB_ENV *dbenv = NULL;
37 * dbconn - our connection to the key database.
39 static DB *dbconn = NULL;
42 * worddb - our connection to the word database.
44 static DB *worddb = NULL;
47 * txn - our current transaction id.
49 static DB_TXN *txn = NULL;
52 * makewordlist - Takes a string and splits it into a set of unique words.
53 * @wordlist: The current word list.
54 * @words: The string to split and add.
56 * We take words and split it on non alpha numeric characters. These get
57 * added to the word list if they're not already present. If the wordlist
58 * is NULL then we start a new list, otherwise it's search for already
59 * added words. Note that words is modified in the process of scanning.
61 * Returns the new word list.
63 struct ll *makewordlist(struct ll *wordlist, char *word)
69 * Walk through the words string, spliting on non alphanumerics and
70 * then checking if the word already exists in the list. If not then
74 while (end != NULL && *end != 0) {
76 while (*start != 0 && !isalnum(*start)) {
80 while (*end != 0 && isalnum(*end)) {
84 if (end - start > 1) {
90 if (llfind(wordlist, start,
92 wordlist = lladd(wordlist,
102 * initdb - Initialize the key database.
104 * This function should be called before any of the other functions in
105 * this file are called in order to allow the DB to be initialized ready
113 ret = db_env_create(&dbenv, 0);
115 fprintf(stderr, "db_env_create: %s\n", db_strerror(ret));
120 * This is a bit of a kludge. Either we run a separate process for
121 * deadlock detection or we do this every time we run. What we really
122 * want to do is specify that our locks are exclusive locks when we
123 * start to do an update.
125 ret = lock_detect(dbenv,
128 NULL); /* If non null int* for number broken */
130 ret = dbenv->open(dbenv, config.db_dir,
131 DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
136 dbenv->err(dbenv, ret, "%s", config.db_dir);
140 ret = db_create(&dbconn, dbenv, 0);
142 fprintf(stderr, "db_create: %s\n", db_strerror(ret));
146 ret = dbconn->open(dbconn, "keydb.db",
152 dbconn->err(dbconn, ret, "keydb.db");
156 ret = db_create(&worddb, dbenv, 0);
158 fprintf(stderr, "db_create: %s\n", db_strerror(ret));
161 ret = worddb->set_flags(worddb, DB_DUP);
163 ret = worddb->open(worddb, "worddb", NULL, DB_BTREE,
167 worddb->err(worddb, ret, "worddb");
175 * cleanupdb - De-initialize the key database.
177 * This function should be called upon program exit to allow the DB to
178 * cleanup after itself.
182 worddb->close(worddb, 0);
184 dbconn->close(dbconn, 0);
186 dbenv->close(dbenv, 0);
191 * starttrans - Start a transaction.
193 * Start a transaction. Intended to be used if we're about to perform many
194 * operations on the database to help speed it all up, or if we want
195 * something to only succeed if all relevant operations are successful.
197 bool starttrans(void)
203 ret = txn_begin(dbenv,
204 NULL, /* No parent transaction */
208 dbenv->err(dbenv, ret, "starttrans():");
216 * endtrans - End a transaction.
218 * Ends a transaction.
226 ret = txn_commit(txn,
229 dbenv->err(dbenv, ret, "endtrans():");
238 * fetch_key - Given a keyid fetch the key from storage.
239 * @keyid: The keyid to fetch.
240 * @publickey: A pointer to a structure to return the key in.
241 * @intrans: If we're already in a transaction.
243 * We use the hex representation of the keyid as the filename to fetch the
244 * key from. The key is stored in the file as a binary OpenPGP stream of
245 * packets, so we can just use read_openpgp_stream() to read the packets
246 * in and then parse_keys() to parse the packets into a publickey
249 int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
252 struct openpgp_packet_list *packets = NULL;
256 struct buffer_ctx fetchbuf;
258 memset(&key, 0, sizeof(key));
259 memset(&data, 0, sizeof(data));
264 key.size = sizeof(keyid);
272 ret = dbconn->get(dbconn,
279 fetchbuf.buffer = data.data;
281 fetchbuf.size = data.size;
282 read_openpgp_stream(buffer_fetchchar, &fetchbuf,
284 parse_keys(packets, publickey);
285 free_packet_list(packets);
288 } else if (ret != DB_NOTFOUND) {
289 dbconn->err(dbconn, ret, "Problem retrieving key");
299 int worddb_cmp(const char *d1, const char *d2)
301 return memcmp(d1, d2, 12);
305 * fetch_key_text - Trys to find the keys that contain the supplied text.
306 * @search: The text to search for.
307 * @publickey: A pointer to a structure to return the key in.
309 * This function searches for the supplied text and returns the keys that
312 int fetch_key_text(const char *search, struct openpgp_publickey **publickey)
320 char *searchtext = NULL;
321 struct ll *wordlist = NULL;
322 struct ll *curword = NULL;
323 struct ll *keylist = NULL;
324 struct ll *newkeylist = NULL;
327 searchtext = strdup(search);
328 wordlist = makewordlist(wordlist, searchtext);
332 ret = worddb->cursor(worddb,
337 for (curword = wordlist; curword != NULL; curword = curword->next) {
338 memset(&key, 0, sizeof(key));
339 memset(&data, 0, sizeof(data));
340 key.data = curword->object;
341 key.size = strlen(curword->object);
342 data.flags = DB_DBT_MALLOC;
343 ret = cursor->c_get(cursor,
347 while (ret == 0 && strncmp(key.data, curword->object,
349 ((char *) curword->object)[key.size] == 0) {
351 for (i = 4; i < 12; i++) {
353 keyid += ((unsigned char *)
357 if (keylist == NULL ||
358 llfind(keylist, data.data,
359 worddb_cmp) != NULL) {
360 newkeylist = lladd(newkeylist, data.data);
366 ret = cursor->c_get(cursor,
371 llfree(keylist, free);
372 keylist = newkeylist;
374 if (data.data != NULL) {
379 llfree(wordlist, NULL);
382 for (newkeylist = keylist;
383 newkeylist != NULL && numkeys < config.maxkeys;
384 newkeylist = newkeylist->next) {
387 for (i = 4; i < 12; i++) {
389 keyid += ((unsigned char *)
390 newkeylist->object)[i];
393 numkeys += fetch_key(keyid,
397 llfree(keylist, free);
402 ret = cursor->c_close(cursor);
411 * store_key - Takes a key and stores it.
412 * @publickey: A pointer to the public key to store.
413 * @intrans: If we're already in a transaction.
414 * @update: If true the key exists and should be updated.
416 * Again we just use the hex representation of the keyid as the filename
417 * to store the key to. We flatten the public key to a list of OpenPGP
418 * packets and then use write_openpgp_stream() to write the stream out to
419 * the file. If update is true then we delete the old key first, otherwise
420 * we trust that it doesn't exist.
422 int store_key(struct openpgp_publickey *publickey, bool intrans, bool update)
424 struct openpgp_packet_list *packets = NULL;
425 struct openpgp_packet_list *list_end = NULL;
426 struct openpgp_publickey *next = NULL;
429 struct buffer_ctx storebuf;
434 char *primary = NULL;
435 unsigned char worddb_data[12];
436 struct ll *wordlist = NULL;
437 struct ll *curword = NULL;
438 bool deadlock = false;
440 keyid = get_keyid(publickey);
447 * Delete the key if we already have it.
449 * TODO: Can we optimize this perhaps? Possibly when other data is
450 * involved as well? I suspect this is easiest and doesn't make a lot
451 * of difference though - the largest chunk of data is the keydata and
452 * it definitely needs updated.
455 deadlock = (delete_key(keyid, true) == -1);
459 * Convert the key to a flat set of binary data.
462 next = publickey->next;
463 publickey->next = NULL;
464 flatten_publickey(publickey, &packets, &list_end);
465 publickey->next = next;
468 storebuf.size = 8192;
469 storebuf.buffer = malloc(8192);
471 write_openpgp_stream(buffer_putchar, &storebuf, packets);
474 * Now we have the key data store it in the DB; the keyid is
477 memset(&key, 0, sizeof(key));
478 memset(&data, 0, sizeof(data));
480 key.size = sizeof(keyid);
482 data.size = storebuf.offset;
483 data.data = storebuf.buffer;
485 ret = dbconn->put(dbconn,
491 dbconn->err(dbconn, ret, "Problem storing key");
492 if (ret == DB_LOCK_DEADLOCK) {
497 free(storebuf.buffer);
498 storebuf.buffer = NULL;
502 free_packet_list(packets);
507 * Walk through our uids storing the words into the db with the keyid.
510 uids = keyuids(publickey, &primary);
513 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
514 wordlist = makewordlist(wordlist, uids[i]);
517 for (curword = wordlist; curword != NULL && !deadlock;
518 curword = curword->next) {
519 memset(&key, 0, sizeof(key));
520 memset(&data, 0, sizeof(data));
521 key.data = curword->object;
522 key.size = strlen(key.data);
523 data.data = worddb_data;
524 data.size = sizeof(worddb_data);
527 * Our data is the key creation time followed by the
530 worddb_data[ 0] = publickey->publickey->data[1];
531 worddb_data[ 1] = publickey->publickey->data[2];
532 worddb_data[ 2] = publickey->publickey->data[3];
533 worddb_data[ 3] = publickey->publickey->data[4];
534 worddb_data[ 4] = (keyid >> 56) & 0xFF;
535 worddb_data[ 5] = (keyid >> 48) & 0xFF;
536 worddb_data[ 6] = (keyid >> 40) & 0xFF;
537 worddb_data[ 7] = (keyid >> 32) & 0xFF;
538 worddb_data[ 8] = (keyid >> 24) & 0xFF;
539 worddb_data[ 9] = (keyid >> 16) & 0xFF;
540 worddb_data[10] = (keyid >> 8) & 0xFF;
541 worddb_data[11] = keyid & 0xFF;
542 ret = worddb->put(worddb,
548 worddb->err(worddb, ret,
549 "Problem storing key");
550 if (ret == DB_LOCK_DEADLOCK) {
557 * Free our UID and word lists.
559 llfree(wordlist, NULL);
560 for (i = 0; uids[i] != NULL; i++) {
572 return deadlock ? -1 : 0 ;
576 * delete_key - Given a keyid delete the key from storage.
577 * @keyid: The keyid to delete.
578 * @intrans: If we're already in a transaction.
580 * This function deletes a public key from whatever storage mechanism we
581 * are using. Returns 0 if the key existed.
583 int delete_key(uint64_t keyid, bool intrans)
585 struct openpgp_publickey *publickey = NULL;
591 char *primary = NULL;
592 unsigned char worddb_data[12];
593 struct ll *wordlist = NULL;
594 struct ll *curword = NULL;
595 bool deadlock = false;
603 fetch_key(keyid, &publickey, true);
606 * Walk through the uids removing the words from the worddb.
608 if (publickey != NULL) {
609 uids = keyuids(publickey, &primary);
612 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
613 wordlist = makewordlist(wordlist, uids[i]);
616 ret = worddb->cursor(worddb,
621 for (curword = wordlist; curword != NULL && !deadlock;
622 curword = curword->next) {
623 memset(&key, 0, sizeof(key));
624 memset(&data, 0, sizeof(data));
625 key.data = curword->object;
626 key.size = strlen(key.data);
627 data.data = worddb_data;
628 data.size = sizeof(worddb_data);
631 * Our data is the key creation time followed by the
634 worddb_data[ 0] = publickey->publickey->data[1];
635 worddb_data[ 1] = publickey->publickey->data[2];
636 worddb_data[ 2] = publickey->publickey->data[3];
637 worddb_data[ 3] = publickey->publickey->data[4];
638 worddb_data[ 4] = (keyid >> 56) & 0xFF;
639 worddb_data[ 5] = (keyid >> 48) & 0xFF;
640 worddb_data[ 6] = (keyid >> 40) & 0xFF;
641 worddb_data[ 7] = (keyid >> 32) & 0xFF;
642 worddb_data[ 8] = (keyid >> 24) & 0xFF;
643 worddb_data[ 9] = (keyid >> 16) & 0xFF;
644 worddb_data[10] = (keyid >> 8) & 0xFF;
645 worddb_data[11] = keyid & 0xFF;
647 ret = cursor->c_get(cursor,
653 ret = cursor->c_del(cursor, 0);
655 worddb->err(worddb, ret,
656 "Problem deleting word.");
661 worddb->err(worddb, ret,
662 "Problem deleting word.");
663 if (ret == DB_LOCK_DEADLOCK) {
668 ret = cursor->c_close(cursor);
672 * Free our UID and word lists.
674 llfree(wordlist, NULL);
675 for (i = 0; uids[i] != NULL; i++) {
681 free_publickey(publickey);
687 key.size = sizeof(keyid);
699 return deadlock ? (-1) : (ret == DB_NOTFOUND);
703 * Include the basic keydb routines.
705 #define NEED_GETFULLKEYID 1
706 #define NEED_GETKEYSIGS 1
707 #define NEED_KEYID2UID 1