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