]> the.earth.li Git - onak.git/blob - keydb_db4.c
Skip signature hash verification for X509 signatures
[onak.git] / keydb_db4.c
1 /*
2  * keydb_db4.c - Routines to store and fetch keys in a DB4 database.
3  *
4  * Copyright 2002-2008 Jonathan McDowell <noodles@earth.li>
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program; if not, write to the Free Software Foundation, Inc., 51
17  * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 #include <sys/types.h>
21 #include <sys/stat.h>
22 #include <sys/uio.h>
23 #include <ctype.h>
24 #include <errno.h>
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30
31 #include <db.h>
32
33 #include "charfuncs.h"
34 #include "keyarray.h"
35 #include "keydb.h"
36 #include "keyid.h"
37 #include "decodekey.h"
38 #include "keystructs.h"
39 #include "mem.h"
40 #include "log.h"
41 #include "onak-conf.h"
42 #include "parsekey.h"
43 #include "wordlist.h"
44
45 #define DB4_UPGRADE_FILE "db_upgrade.lck"
46
47 /**
48  *      dbenv - our database environment.
49  */
50 static DB_ENV *dbenv = NULL;
51
52 /**
53  *      numdb - The number of database files we have.
54  */
55 static int numdbs = 16;
56
57 /**
58  *      dbconn - our connections to the key database files.
59  */
60 static DB **dbconns = NULL;
61
62 /**
63  *      worddb - our connection to the word database.
64  */
65 static DB *worddb = NULL;
66
67 /**
68  *      id32db - our connection to the 32bit ID database.
69  */
70 static DB *id32db = NULL;
71
72 /**
73  *      skshashdb - our connection to the SKS hash database.
74  */
75 static DB *skshashdb = NULL;
76
77 /**
78  *      subkeydb - our connection to the subkey ID lookup database.
79  */
80 static DB *subkeydb = NULL;
81
82 /**
83  *      txn - our current transaction id.
84  */
85 static DB_TXN *txn = NULL;
86
87 DB *keydb(uint64_t keyid)
88 {
89         uint64_t keytrun;
90
91         keytrun = keyid >> 8;
92
93         return(dbconns[keytrun % numdbs]);
94 }
95
96 /**
97  *      db4_errfunc - Direct DB errors to logfile
98  *
99  *      Basic function to take errors from the DB library and output them to
100  *      the logfile rather than stderr.
101  */
102 #if (DB_VERSION_MAJOR == 4) && (DB_VERSION_MINOR < 3)
103 static void db4_errfunc(const char *errpfx, const char *errmsg)
104 #else
105 static void db4_errfunc(const DB_ENV *edbenv, const char *errpfx,
106                 const char *errmsg)
107 #endif
108 {
109         if (errpfx) {
110                 logthing(LOGTHING_DEBUG, "db4 error: %s:%s", errpfx, errmsg);
111         } else {
112                 logthing(LOGTHING_DEBUG, "db4 error: %s", errmsg);
113         }
114
115         return;
116 }
117
118 /**
119  *      starttrans - Start a transaction.
120  *
121  *      Start a transaction. Intended to be used if we're about to perform many
122  *      operations on the database to help speed it all up, or if we want
123  *      something to only succeed if all relevant operations are successful.
124  */
125 static bool db4_starttrans(void)
126 {
127         int ret;
128
129         log_assert(dbenv != NULL);
130         log_assert(txn == NULL);
131
132         ret = dbenv->txn_begin(dbenv,
133                 NULL, /* No parent transaction */
134                 &txn,
135                 0);
136         if (ret != 0) {
137                 logthing(LOGTHING_CRITICAL,
138                                 "Error starting transaction: %s",
139                                 db_strerror(ret));
140                 exit(1);
141         }
142
143         return true;
144 }
145
146 /**
147  *      endtrans - End a transaction.
148  *
149  *      Ends a transaction.
150  */
151 static void db4_endtrans(void)
152 {
153         int ret;
154
155         log_assert(dbenv != NULL);
156         log_assert(txn != NULL);
157
158         ret = txn->commit(txn,
159                 0);
160         if (ret != 0) {
161                 logthing(LOGTHING_CRITICAL,
162                                 "Error ending transaction: %s",
163                                 db_strerror(ret));
164                 exit(1);
165         }
166         txn = NULL;
167
168         return;
169 }
170
171 /**
172  *      cleanupdb - De-initialize the key database.
173  *
174  *      This function should be called upon program exit to allow the DB to
175  *      cleanup after itself.
176  */
177 static void db4_cleanupdb(void)
178 {
179         int i = 0;
180
181         if (dbenv != NULL) {
182                 dbenv->txn_checkpoint(dbenv, 0, 0, 0);
183                 if (subkeydb != NULL) {
184                         subkeydb->close(subkeydb, 0);
185                         subkeydb = NULL;
186                 }
187                 if (skshashdb != NULL) {
188                         skshashdb->close(skshashdb, 0);
189                         skshashdb = NULL;
190                 }
191                 if (id32db != NULL) {
192                         id32db->close(id32db, 0);
193                         id32db = NULL;
194                 }
195                 if (worddb != NULL) {
196                         worddb->close(worddb, 0);
197                         worddb = NULL;
198                 }
199                 for (i = 0; i < numdbs; i++) {
200                         if (dbconns[i] != NULL) {
201                                 dbconns[i]->close(dbconns[i], 0);
202                                 dbconns[i] = NULL;
203                         }
204                 }
205                 free(dbconns);
206                 dbconns = NULL;
207                 dbenv->close(dbenv, 0);
208                 dbenv = NULL;
209         }
210 }
211
212 /**
213  *      db4_upgradedb - Upgrade a DB4 database
214  *
215  *      Called if we discover we need to upgrade our DB4 database; ie if
216  *      we're running with a newer version of db4 than the database was
217  *      created with.
218  */
219 static int db4_upgradedb(int numdb)
220 {
221         DB *curdb = NULL;
222         int ret;
223         int i;
224         char buf[1024];
225         int lockfile_fd;
226         struct stat statbuf;
227
228         snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir,
229                         DB4_UPGRADE_FILE);
230         lockfile_fd = open(buf, O_RDWR | O_CREAT | O_EXCL, 0600);
231         if (lockfile_fd < 0) {
232                 if (errno == EEXIST) {
233                         while (stat(buf, &statbuf) == 0) ;
234                         return 0;
235                 } else {
236                         logthing(LOGTHING_CRITICAL, "Couldn't open database "
237                                 "update lock file: %s", strerror(errno));
238                         return -1;
239                 }
240         }
241         snprintf(buf, sizeof(buf) - 1, "%d", getpid());
242         write(lockfile_fd, buf, strlen(buf));
243         close(lockfile_fd);
244
245         logthing(LOGTHING_NOTICE, "Upgrading DB4 database");
246         ret = db_env_create(&dbenv, 0);
247         dbenv->set_errcall(dbenv, &db4_errfunc);
248         dbenv->remove(dbenv, config.db_dir, 0);
249         dbenv = NULL;
250         for (i = 0; i < numdb; i++) {
251                 ret = db_create(&curdb, NULL, 0);
252                 if (ret == 0) {
253                         snprintf(buf, sizeof(buf) - 1, "%s/keydb.%d.db",
254                                 config.db_dir, i);
255                         logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
256                         ret = curdb->upgrade(curdb, buf, 0);
257                         curdb->close(curdb, 0);
258                 } else {
259                         logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
260                                 buf,
261                                 db_strerror(ret));
262                 }
263         }
264
265         ret = db_create(&curdb, NULL, 0);
266         if (ret == 0) {
267                 snprintf(buf, sizeof(buf) - 1, "%s/worddb", config.db_dir);
268                 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
269                 ret = curdb->upgrade(curdb, buf, 0);
270                 curdb->close(curdb, 0);
271         } else {
272                 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
273                         buf,
274                         db_strerror(ret));
275         }
276
277         ret = db_create(&curdb, NULL, 0);
278         if (ret == 0) {
279                 snprintf(buf, sizeof(buf) - 1, "%s/id32db", config.db_dir);
280                 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
281                 ret = curdb->upgrade(curdb, buf, 0);
282                 curdb->close(curdb, 0);
283         } else {
284                 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
285                         buf,
286                         db_strerror(ret));
287         }
288
289         ret = db_create(&curdb, NULL, 0);
290         if (ret == 0) {
291                 snprintf(buf, sizeof(buf) - 1, "%s/skshashdb", config.db_dir);
292                 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
293                 ret = curdb->upgrade(curdb, buf, 0);
294                 curdb->close(curdb, 0);
295         } else {
296                 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
297                         buf,
298                         db_strerror(ret));
299         }
300
301         ret = db_create(&curdb, NULL, 0);
302         if (ret == 0) {
303                 snprintf(buf, sizeof(buf) - 1, "%s/subkeydb", config.db_dir);
304                 logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
305                 ret = curdb->upgrade(curdb, buf, 0);
306                 curdb->close(curdb, 0);
307         } else {
308                 logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
309                         buf,
310                         db_strerror(ret));
311         }
312
313         snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir,
314                         DB4_UPGRADE_FILE);
315         unlink(buf);
316
317         return ret;
318 }
319
320 /**
321  *      initdb - Initialize the key database.
322  *
323  *      This function should be called before any of the other functions in
324  *      this file are called in order to allow the DB to be initialized ready
325  *      for access.
326  */
327 static void db4_initdb(bool readonly)
328 {
329         char       buf[1024];
330         FILE      *numdb = NULL;
331         int        ret = 0;
332         int        i = 0;
333         uint32_t   flags = 0;
334         struct stat statbuf;
335         int        maxlocks;
336
337         snprintf(buf, sizeof(buf) - 1, "%s/%s", config.db_dir,
338                         DB4_UPGRADE_FILE);
339         ret = stat(buf, &statbuf);
340         while ((ret == 0) || (errno != ENOENT)) {
341                 if (ret != 0) {
342                         logthing(LOGTHING_CRITICAL, "Couldn't stat upgrade "
343                                 "lock file: %s (%d)", strerror(errno), ret);
344                         exit(1);
345                 }
346                 logthing(LOGTHING_DEBUG, "DB4 upgrade in progress; waiting.");
347                 sleep(5);
348                 ret = stat(buf, &statbuf);
349         }
350         ret = 0;
351
352         snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", config.db_dir);
353         numdb = fopen(buf, "r");
354         if (numdb != NULL) {
355                 if (fgets(buf, sizeof(buf), numdb) != NULL) {
356                         numdbs = atoi(buf);
357                 }
358                 fclose(numdb);
359         } else if (!readonly) {
360                 logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s",
361                                 strerror(errno));
362                 numdb = fopen(buf, "w");
363                 if (numdb != NULL) {
364                         fprintf(numdb, "%d", numdbs);
365                         fclose(numdb);
366                 } else {
367                         logthing(LOGTHING_ERROR,
368                                 "Couldn't write num_keydb: %s",
369                                 strerror(errno));
370                 }
371         }
372
373         dbconns = calloc(numdbs, sizeof (DB *));
374         if (dbconns == NULL) {
375                 logthing(LOGTHING_CRITICAL,
376                                 "Couldn't allocate memory for dbconns");
377                 ret = 1;
378         }
379
380         if (ret == 0) {
381                 ret = db_env_create(&dbenv, 0);
382                 if (ret != 0) {
383                         logthing(LOGTHING_CRITICAL,
384                                 "db_env_create: %s", db_strerror(ret));
385                 }
386         }
387
388         /*
389          * Up the number of locks we're allowed at once. We base this on
390          * the maximum number of keys we're going to return.
391          */
392         maxlocks = config.maxkeys * 16;
393         if (maxlocks < 1000) {
394                 maxlocks = 1000;
395         }
396         dbenv->set_lk_max_locks(dbenv, maxlocks);
397         dbenv->set_lk_max_objects(dbenv, maxlocks);
398
399         /*
400          * Enable deadlock detection so that we don't block indefinitely on
401          * anything. What we really want is simple 2 state locks, but I'm not
402          * sure how to make the standard DB functions do that yet.
403          */
404         if (ret == 0) {
405                 dbenv->set_errcall(dbenv, &db4_errfunc);
406                 ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT);
407                 if (ret != 0) {
408                         logthing(LOGTHING_CRITICAL,
409                                 "db_env_create: %s", db_strerror(ret));
410                 }
411         }
412
413         if (ret == 0) {
414                 ret = dbenv->open(dbenv, config.db_dir,
415                                 DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
416                                 DB_INIT_TXN |
417                                 DB_CREATE,
418                                 0);
419 #ifdef DB_VERSION_MISMATCH
420                 if (ret == DB_VERSION_MISMATCH) {
421                         dbenv->close(dbenv, 0);
422                         dbenv = NULL;
423                         ret = db4_upgradedb(numdbs);
424                         if (ret == 0) {
425                                 ret = db_env_create(&dbenv, 0);
426                         }
427                         if (ret == 0) {
428                                 dbenv->set_errcall(dbenv, &db4_errfunc);
429                                 dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT);
430                                 ret = dbenv->open(dbenv, config.db_dir,
431                                         DB_INIT_LOG | DB_INIT_MPOOL |
432                                         DB_INIT_LOCK | DB_INIT_TXN |
433                                         DB_CREATE | DB_RECOVER,
434                                         0);
435
436                                 if (ret == 0) {
437                                         dbenv->txn_checkpoint(dbenv,
438                                                         0,
439                                                         0,
440                                                         DB_FORCE);
441                                 }
442                         }
443                 }
444 #endif
445                 if (ret != 0) {
446                         logthing(LOGTHING_CRITICAL,
447                                         "Error opening db environment: %s (%s)",
448                                         config.db_dir,
449                                         db_strerror(ret));
450                         dbenv->close(dbenv, 0);
451                         dbenv = NULL;
452                 }
453         }
454
455         if (ret == 0) {
456                 db4_starttrans();
457
458                 for (i = 0; !ret && i < numdbs; i++) {
459                         ret = db_create(&dbconns[i], dbenv, 0);
460                         if (ret != 0) {
461                                 logthing(LOGTHING_CRITICAL,
462                                         "db_create: %s", db_strerror(ret));
463                         }
464
465                         if (ret == 0) {
466                                 snprintf(buf, 1023, "keydb.%d.db", i);
467                                 flags = DB_CREATE;
468                                 if (readonly) {
469                                         flags = DB_RDONLY;
470                                 }
471                                 ret = dbconns[i]->open(dbconns[i],
472                                                 txn,
473                                                 buf,
474                                                 "keydb",
475                                                 DB_HASH,
476                                                 flags,
477                                                 0664);
478                                 if (ret != 0) {
479                                         logthing(LOGTHING_CRITICAL,
480                                                 "Error opening key database:"
481                                                 " %s (%s)",
482                                                 buf,
483                                                 db_strerror(ret));
484                                 }
485                         }
486                 }
487
488         }
489
490         if (ret == 0) {
491                 ret = db_create(&worddb, dbenv, 0);
492                 if (ret != 0) {
493                         logthing(LOGTHING_CRITICAL, "db_create: %s",
494                                         db_strerror(ret));
495                 }
496         }
497
498         if (ret == 0) {
499                 ret = worddb->set_flags(worddb, DB_DUP);
500         }
501
502         if (ret == 0) {
503                 ret = worddb->open(worddb, txn, "worddb", "worddb", DB_BTREE,
504                                 flags,
505                                 0664);
506                 if (ret != 0) {
507                         logthing(LOGTHING_CRITICAL,
508                                         "Error opening word database: %s (%s)",
509                                         "worddb",
510                                         db_strerror(ret));
511                 }
512         }
513
514         if (ret == 0) {
515                 ret = db_create(&id32db, dbenv, 0);
516                 if (ret != 0) {
517                         logthing(LOGTHING_CRITICAL, "db_create: %s",
518                                         db_strerror(ret));
519                 }
520         }
521
522         if (ret == 0) {
523                 ret = id32db->set_flags(id32db, DB_DUP);
524         }
525
526         if (ret == 0) {
527                 ret = id32db->open(id32db, txn, "id32db", "id32db", DB_HASH,
528                                 flags,
529                                 0664);
530                 if (ret != 0) {
531                         logthing(LOGTHING_CRITICAL,
532                                         "Error opening id32 database: %s (%s)",
533                                         "id32db",
534                                         db_strerror(ret));
535                 }
536         }
537
538         if (ret == 0) {
539                 ret = db_create(&skshashdb, dbenv, 0);
540                 if (ret != 0) {
541                         logthing(LOGTHING_CRITICAL, "db_create: %s",
542                                         db_strerror(ret));
543                 }
544         }
545
546         if (ret == 0) {
547                 ret = skshashdb->open(skshashdb, txn, "skshashdb",
548                                 "skshashdb", DB_HASH,
549                                 flags,
550                                 0664);
551                 if (ret != 0) {
552                         logthing(LOGTHING_CRITICAL,
553                                 "Error opening skshash database: %s (%s)",
554                                 "skshashdb",
555                                 db_strerror(ret));
556                 }
557         }
558
559         if (ret == 0) {
560                 ret = db_create(&subkeydb, dbenv, 0);
561                 if (ret != 0) {
562                         logthing(LOGTHING_CRITICAL, "db_create: %s",
563                                         db_strerror(ret));
564                 }
565         }
566
567         if (ret == 0) {
568                 ret = subkeydb->open(subkeydb, txn, "subkeydb", "subkeydb",
569                                 DB_HASH,
570                                 flags,
571                                 0664);
572                 if (ret != 0) {
573                         logthing(LOGTHING_CRITICAL,
574                                 "Error opening subkey database: %s (%s)",
575                                 "subkeydb",
576                                 db_strerror(ret));
577                 }
578         }
579
580         if (txn != NULL) {
581                 db4_endtrans();
582         }
583
584         if (ret != 0) {
585                 db4_cleanupdb();
586                 logthing(LOGTHING_CRITICAL,
587                                 "Error opening database; exiting");
588                 exit(EXIT_FAILURE);
589         }
590         
591         return;
592 }
593
594 /**
595  *      getfullkeyid - Maps a 32bit key id to a 64bit one.
596  *      @keyid: The 32bit keyid.
597  *
598  *      This function maps a 32bit key id to the full 64bit one. It returns the
599  *      full keyid. If the key isn't found a keyid of 0 is returned.
600  */
601 static uint64_t db4_getfullkeyid(uint64_t keyid)
602 {
603         DBT       key, data;
604         DBC      *cursor = NULL;
605         uint32_t  shortkeyid = 0;
606         int       ret = 0;
607
608         if (keyid < 0x100000000LL) {
609                 ret = id32db->cursor(id32db,
610                                 txn,
611                                 &cursor,
612                                 0);   /* flags */
613
614                 shortkeyid = keyid & 0xFFFFFFFF;
615
616                 memset(&key, 0, sizeof(key));
617                 memset(&data, 0, sizeof(data));
618                 key.data = &shortkeyid;
619                 key.size = sizeof(shortkeyid);
620                 data.flags = DB_DBT_MALLOC;
621
622                 ret = cursor->c_get(cursor,
623                         &key,
624                         &data,
625                         DB_SET);
626
627                 if (ret == 0) {
628                         keyid = *(uint64_t *) data.data;
629
630                         if (data.data != NULL) {
631                                 free(data.data);
632                                 data.data = NULL;
633                         }
634                 }
635
636                 ret = cursor->c_close(cursor);
637                 cursor = NULL;
638         }
639         
640         return keyid;
641 }
642
643 /**
644  *      fetch_key_id - Given a keyid fetch the key from storage.
645  *      @keyid: The keyid to fetch.
646  *      @publickey: A pointer to a structure to return the key in.
647  *      @intrans: If we're already in a transaction.
648  *
649  *      We use the hex representation of the keyid as the filename to fetch the
650  *      key from. The key is stored in the file as a binary OpenPGP stream of
651  *      packets, so we can just use read_openpgp_stream() to read the packets
652  *      in and then parse_keys() to parse the packets into a publickey
653  *      structure.
654  */
655 static int db4_fetch_key_id(uint64_t keyid,
656                 struct openpgp_publickey **publickey,
657                 bool intrans)
658 {
659         struct openpgp_packet_list *packets = NULL;
660         DBT key, data;
661         int ret = 0;
662         int numkeys = 0;
663         struct buffer_ctx fetchbuf;
664
665         if (keyid < 0x100000000LL) {
666                 keyid = db4_getfullkeyid(keyid);
667         }
668
669         memset(&key, 0, sizeof(key));
670         memset(&data, 0, sizeof(data));
671
672         data.size = 0;
673         data.data = NULL;
674
675         key.size = sizeof(keyid);
676         key.data = &keyid;
677
678         if (!intrans) {
679                 db4_starttrans();
680         }
681
682         ret = keydb(keyid)->get(keydb(keyid),
683                         txn,
684                         &key,
685                         &data,
686                         0); /* flags*/
687
688         if (ret == DB_NOTFOUND) {
689                 /* If we didn't find the key ID see if it's a subkey ID */
690                 memset(&key, 0, sizeof(key));
691                 memset(&data, 0, sizeof(data));
692                 data.size = 0;
693                 data.data = NULL;
694                 key.size = sizeof(keyid);
695                 key.data = &keyid;
696
697                 ret = subkeydb->get(subkeydb,
698                         txn,
699                         &key,
700                         &data,
701                         0); /* flags*/
702
703                 if (ret == 0) {
704                         /* We got a subkey match; retrieve the actual key */
705                         keyid = *(uint64_t *) data.data;
706
707                         memset(&key, 0, sizeof(key));
708                         memset(&data, 0, sizeof(data));
709                         data.size = 0;
710                         data.data = NULL;
711                         key.size = sizeof(keyid);
712                         key.data = &keyid;
713
714                         ret = keydb(keyid)->get(keydb(keyid),
715                                 txn,
716                                 &key,
717                                 &data,
718                                 0); /* flags*/
719                 }
720         }
721
722         if (ret == 0) {
723                 fetchbuf.buffer = data.data;
724                 fetchbuf.offset = 0;
725                 fetchbuf.size = data.size;
726                 read_openpgp_stream(buffer_fetchchar, &fetchbuf,
727                                 &packets, 0);
728                 parse_keys(packets, publickey);
729                 free_packet_list(packets);
730                 packets = NULL;
731                 numkeys++;
732         } else if (ret != DB_NOTFOUND) {
733                 logthing(LOGTHING_ERROR,
734                                 "Problem retrieving key: %s",
735                                 db_strerror(ret));
736         }
737
738         if (!intrans) {
739                 db4_endtrans();
740         }
741
742         return (numkeys);
743 }
744
745 int worddb_cmp(const void *d1, const void *d2)
746 {
747         return memcmp(d1, d2, 12);
748 }
749
750 /**
751  *      fetch_key_text - Trys to find the keys that contain the supplied text.
752  *      @search: The text to search for.
753  *      @publickey: A pointer to a structure to return the key in.
754  *
755  *      This function searches for the supplied text and returns the keys that
756  *      contain it.
757  */
758 static int db4_fetch_key_text(const char *search,
759                 struct openpgp_publickey **publickey)
760 {
761         DBC *cursor = NULL;
762         DBT key, data;
763         int ret;
764         uint64_t keyid;
765         int i;
766         int numkeys;
767         char *searchtext = NULL;
768         struct ll *wordlist = NULL;
769         struct ll *curword = NULL;
770         struct keyarray keylist = { NULL, 0, 0 };
771         struct keyarray newkeylist = { NULL, 0, 0 };
772         int firstpass = 1;
773
774         numkeys = 0;
775         searchtext = strdup(search);
776         wordlist = makewordlist(wordlist, searchtext);
777
778         for (curword = wordlist; curword != NULL; curword = curword->next) {
779                 db4_starttrans();
780
781                 ret = worddb->cursor(worddb,
782                                 txn,
783                                 &cursor,
784                                 0);   /* flags */
785
786                 memset(&key, 0, sizeof(key));
787                 memset(&data, 0, sizeof(data));
788                 key.data = curword->object;
789                 key.size = strlen(curword->object);
790                 data.flags = DB_DBT_MALLOC;
791                 ret = cursor->c_get(cursor,
792                                 &key,
793                                 &data,
794                                 DB_SET);
795                 while (ret == 0 && strncmp(key.data, curword->object,
796                                         key.size) == 0 &&
797                                 ((char *) curword->object)[key.size] == 0) {
798                         keyid = 0;
799                         for (i = 4; i < 12; i++) {
800                                 keyid <<= 8;
801                                 keyid += ((unsigned char *)
802                                                 data.data)[i];
803                         }
804
805                         /*
806                          * Only add the keys containing this word if this is
807                          * our first pass (ie we have no existing key list),
808                          * or the key contained a previous word.
809                          */
810                         if (firstpass || array_find(&keylist, keyid)) {
811                                 array_add(&newkeylist, keyid);
812                         }
813
814                         free(data.data);
815                         data.data = NULL;
816
817                         ret = cursor->c_get(cursor,
818                                         &key,
819                                         &data,
820                                         DB_NEXT);
821                 }
822                 array_free(&keylist);
823                 keylist = newkeylist;
824                 newkeylist.keys = NULL;
825                 newkeylist.count = newkeylist.size = 0;
826                 if (data.data != NULL) {
827                         free(data.data);
828                         data.data = NULL;
829                 }
830                 ret = cursor->c_close(cursor);
831                 cursor = NULL;
832                 firstpass = 0;
833                 db4_endtrans();
834         }
835         llfree(wordlist, NULL);
836         wordlist = NULL;
837
838         if (keylist.count > config.maxkeys) {
839                 keylist.count = config.maxkeys;
840         }
841         
842         db4_starttrans();
843         for (i = 0; i < keylist.count; i++) {
844                 numkeys += db4_fetch_key_id(keylist.keys[i],
845                         publickey,
846                         true);
847         }
848         array_free(&keylist);
849         free(searchtext);
850         searchtext = NULL;
851
852         db4_endtrans();
853         
854         return (numkeys);
855 }
856
857 static int db4_fetch_key_skshash(const struct skshash *hash,
858                 struct openpgp_publickey **publickey)
859 {
860         DBT       key, data;
861         DBC      *cursor = NULL;
862         uint64_t  keyid = 0;
863         int       ret = 0;
864
865         ret = skshashdb->cursor(skshashdb,
866                         txn,
867                         &cursor,
868                         0);   /* flags */
869
870         memset(&key, 0, sizeof(key));
871         memset(&data, 0, sizeof(data));
872         key.data = (void *) hash->hash;
873         key.size = sizeof(hash->hash);
874         data.flags = DB_DBT_MALLOC;
875
876         ret = cursor->c_get(cursor,
877                 &key,
878                 &data,
879                 DB_SET);
880
881         if (ret == 0) {
882                 keyid = *(uint64_t *) data.data;
883
884                 if (data.data != NULL) {
885                         free(data.data);
886                         data.data = NULL;
887                 }
888         }
889
890         ret = cursor->c_close(cursor);
891         cursor = NULL;
892
893         return db4_fetch_key_id(keyid, publickey, false);
894 }
895
896 /**
897  *      delete_key - Given a keyid delete the key from storage.
898  *      @keyid: The keyid to delete.
899  *      @intrans: If we're already in a transaction.
900  *
901  *      This function deletes a public key from whatever storage mechanism we
902  *      are using. Returns 0 if the key existed.
903  */
904 static int db4_delete_key(uint64_t keyid, bool intrans)
905 {
906         struct openpgp_publickey *publickey = NULL;
907         DBT key, data;
908         DBC *cursor = NULL;
909         uint32_t   shortkeyid = 0;
910         uint64_t  *subkeyids = NULL;
911         int ret = 0;
912         int i;
913         char **uids = NULL;
914         char *primary = NULL;
915         unsigned char worddb_data[12];
916         struct ll *wordlist = NULL;
917         struct ll *curword  = NULL;
918         bool deadlock = false;
919         struct skshash hash;
920
921         if (!intrans) {
922                 db4_starttrans();
923         }
924
925         db4_fetch_key_id(keyid, &publickey, true);
926
927         /*
928          * Walk through the uids removing the words from the worddb.
929          */
930         if (publickey != NULL) {
931                 uids = keyuids(publickey, &primary);
932         }
933         if (uids != NULL) {
934                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
935                         wordlist = makewordlist(wordlist, uids[i]);
936                 }
937                                 
938                 ret = worddb->cursor(worddb,
939                         txn,
940                         &cursor,
941                         0);   /* flags */
942
943                 for (curword = wordlist; curword != NULL && !deadlock;
944                                 curword = curword->next) {
945                         memset(&key, 0, sizeof(key));
946                         memset(&data, 0, sizeof(data));
947                         key.data = curword->object;
948                         key.size = strlen(key.data);
949                         data.data = worddb_data;
950                         data.size = sizeof(worddb_data);
951
952                         /*
953                          * Our data is the key creation time followed by the
954                          * key id.
955                          */
956                         worddb_data[ 0] = publickey->publickey->data[1];
957                         worddb_data[ 1] = publickey->publickey->data[2];
958                         worddb_data[ 2] = publickey->publickey->data[3];
959                         worddb_data[ 3] = publickey->publickey->data[4];
960                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
961                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
962                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
963                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
964                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
965                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
966                         worddb_data[10] = (keyid >>  8) & 0xFF;
967                         worddb_data[11] = keyid & 0xFF;
968
969                         ret = cursor->c_get(cursor,
970                                 &key,
971                                 &data,
972                                 DB_GET_BOTH);
973
974                         if (ret == 0) {
975                                 ret = cursor->c_del(cursor, 0);
976                         }
977
978                         if (ret != 0) {
979                                 logthing(LOGTHING_ERROR,
980                                         "Problem deleting word: %s "
981                                         "(0x%016" PRIX64 ")",
982                                         db_strerror(ret),
983                                         keyid);
984                                 if (ret == DB_LOCK_DEADLOCK) {
985                                         deadlock = true;
986                                 }
987                         }
988                 }
989                 ret = cursor->c_close(cursor);
990                 cursor = NULL;
991
992                 ret = skshashdb->cursor(skshashdb,
993                         txn,
994                         &cursor,
995                         0);   /* flags */
996                 get_skshash(publickey, &hash);
997
998                 memset(&key, 0, sizeof(key));
999                 memset(&data, 0, sizeof(data));
1000                 key.data = hash.hash;
1001                 key.size = sizeof(hash.hash);
1002                 data.data = &keyid;
1003                 data.size = sizeof(keyid);
1004
1005                 ret = cursor->c_get(cursor,
1006                         &key,
1007                         &data,
1008                         DB_GET_BOTH);
1009
1010                 if (ret == 0) {
1011                         ret = cursor->c_del(cursor, 0);
1012                 }
1013
1014                 if (ret != 0) {
1015                         logthing(LOGTHING_ERROR,
1016                                 "Problem deleting skshash: %s "
1017                                 "(0x%016" PRIX64 ")",
1018                                 db_strerror(ret),
1019                                 keyid);
1020                         if (ret == DB_LOCK_DEADLOCK) {
1021                                 deadlock = true;
1022                         }
1023                 }
1024
1025                 ret = cursor->c_close(cursor);
1026                 cursor = NULL;
1027
1028                 /*
1029                  * Free our UID and word lists.
1030                  */
1031                 llfree(wordlist, NULL);
1032                 for (i = 0; uids[i] != NULL; i++) {
1033                         free(uids[i]);
1034                         uids[i] = NULL;
1035                 }
1036                 free(uids);
1037                 uids = NULL;
1038                 free_publickey(publickey);
1039                 publickey = NULL;
1040         }
1041
1042         if (!deadlock) {
1043                 ret = id32db->cursor(id32db,
1044                         txn,
1045                         &cursor,
1046                         0);   /* flags */
1047
1048                 shortkeyid = keyid & 0xFFFFFFFF;
1049
1050                 memset(&key, 0, sizeof(key));
1051                 memset(&data, 0, sizeof(data));
1052                 key.data = &shortkeyid;
1053                 key.size = sizeof(shortkeyid);
1054                 data.data = &keyid;
1055                 data.size = sizeof(keyid);
1056
1057                 ret = cursor->c_get(cursor,
1058                         &key,
1059                         &data,
1060                         DB_GET_BOTH);
1061
1062                 if (ret == 0) {
1063                         ret = cursor->c_del(cursor, 0);
1064                 }
1065
1066                 if (ret != 0) {
1067                         logthing(LOGTHING_ERROR,
1068                                 "Problem deleting short keyid: %s "
1069                                 "(0x%016" PRIX64 ")",
1070                                 db_strerror(ret),
1071                                 keyid);
1072                         if (ret == DB_LOCK_DEADLOCK) {
1073                                 deadlock = true;
1074                         }
1075                 }
1076
1077                 subkeyids = keysubkeys(publickey);
1078                 i = 0;
1079                 while (subkeyids != NULL && subkeyids[i] != 0) {
1080                         memset(&key, 0, sizeof(key));
1081                         key.data = &subkeyids[i];
1082                         key.size = sizeof(subkeyids[i]);
1083                         subkeydb->del(subkeydb, txn, &key, 0);
1084                         if (ret != 0) {
1085                                 logthing(LOGTHING_ERROR,
1086                                         "Problem deleting subkey id: %s "
1087                                         "(0x%016" PRIX64 ")",
1088                                         db_strerror(ret),
1089                                         keyid);
1090                                 if (ret == DB_LOCK_DEADLOCK) {
1091                                         deadlock = true;
1092                                 }
1093                         }
1094
1095                         shortkeyid = subkeyids[i++] & 0xFFFFFFFF;
1096
1097                         memset(&key, 0, sizeof(key));
1098                         memset(&data, 0, sizeof(data));
1099                         key.data = &shortkeyid;
1100                         key.size = sizeof(shortkeyid);
1101                         data.data = &keyid;
1102                         data.size = sizeof(keyid);
1103
1104                         ret = cursor->c_get(cursor,
1105                                 &key,
1106                                 &data,
1107                                 DB_GET_BOTH);
1108
1109                         if (ret == 0) {
1110                                 ret = cursor->c_del(cursor, 0);
1111                         }
1112
1113                         if (ret != 0) {
1114                                 logthing(LOGTHING_ERROR,
1115                                         "Problem deleting short keyid: %s "
1116                                         "(0x%016" PRIX64 ")",
1117                                         db_strerror(ret),
1118                                         keyid);
1119                                 if (ret == DB_LOCK_DEADLOCK) {
1120                                         deadlock = true;
1121                                 }
1122                         }
1123                 }
1124                 if (subkeyids != NULL) {
1125                         free(subkeyids);
1126                         subkeyids = NULL;
1127                 }
1128                 ret = cursor->c_close(cursor);
1129                 cursor = NULL;
1130
1131         }
1132
1133         if (!deadlock) {
1134                 key.data = &keyid;
1135                 key.size = sizeof(keyid);
1136
1137                 keydb(keyid)->del(keydb(keyid),
1138                                 txn,
1139                                 &key,
1140                                 0); /* flags */
1141         }
1142
1143         if (!intrans) {
1144                 db4_endtrans();
1145         }
1146
1147         return deadlock ? (-1) : (ret == DB_NOTFOUND);
1148 }
1149
1150 /**
1151  *      store_key - Takes a key and stores it.
1152  *      @publickey: A pointer to the public key to store.
1153  *      @intrans: If we're already in a transaction.
1154  *      @update: If true the key exists and should be updated.
1155  *
1156  *      Again we just use the hex representation of the keyid as the filename
1157  *      to store the key to. We flatten the public key to a list of OpenPGP
1158  *      packets and then use write_openpgp_stream() to write the stream out to
1159  *      the file. If update is true then we delete the old key first, otherwise
1160  *      we trust that it doesn't exist.
1161  */
1162 static int db4_store_key(struct openpgp_publickey *publickey, bool intrans,
1163                 bool update)
1164 {
1165         struct     openpgp_packet_list *packets = NULL;
1166         struct     openpgp_packet_list *list_end = NULL;
1167         struct     openpgp_publickey *next = NULL;
1168         int        ret = 0;
1169         int        i = 0;
1170         struct     buffer_ctx storebuf;
1171         DBT        key;
1172         DBT        data;
1173         uint64_t   keyid = 0;
1174         uint32_t   shortkeyid = 0;
1175         uint64_t  *subkeyids = NULL;
1176         char     **uids = NULL;
1177         char      *primary = NULL;
1178         unsigned char worddb_data[12];
1179         struct ll *wordlist = NULL;
1180         struct ll *curword  = NULL;
1181         bool       deadlock = false;
1182         struct skshash hash;
1183
1184         if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
1185                 logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
1186                 return 0;
1187         }
1188
1189         if (!intrans) {
1190                 db4_starttrans();
1191         }
1192
1193         /*
1194          * Delete the key if we already have it.
1195          *
1196          * TODO: Can we optimize this perhaps? Possibly when other data is
1197          * involved as well? I suspect this is easiest and doesn't make a lot
1198          * of difference though - the largest chunk of data is the keydata and
1199          * it definitely needs updated.
1200          */
1201         if (update) {
1202                 deadlock = (db4_delete_key(keyid, true) == -1);
1203         }
1204
1205         /*
1206          * Convert the key to a flat set of binary data.
1207          */
1208         if (!deadlock) {
1209                 next = publickey->next;
1210                 publickey->next = NULL;
1211                 flatten_publickey(publickey, &packets, &list_end);
1212                 publickey->next = next;
1213
1214                 storebuf.offset = 0; 
1215                 storebuf.size = 8192;
1216                 storebuf.buffer = malloc(8192);
1217         
1218                 write_openpgp_stream(buffer_putchar, &storebuf, packets);
1219
1220                 /*
1221                  * Now we have the key data store it in the DB; the keyid is
1222                  * the key.
1223                  */
1224                 memset(&key, 0, sizeof(key));
1225                 memset(&data, 0, sizeof(data));
1226                 key.data = &keyid;
1227                 key.size = sizeof(keyid);
1228                 data.size = storebuf.offset;
1229                 data.data = storebuf.buffer;
1230
1231                 ret = keydb(keyid)->put(keydb(keyid),
1232                                 txn,
1233                                 &key,
1234                                 &data,
1235                                 0); /* flags*/
1236                 if (ret != 0) {
1237                         logthing(LOGTHING_ERROR,
1238                                         "Problem storing key: %s",
1239                                         db_strerror(ret));
1240                         if (ret == DB_LOCK_DEADLOCK) {
1241                                 deadlock = true;
1242                         }
1243                 }
1244
1245                 free(storebuf.buffer);
1246                 storebuf.buffer = NULL;
1247                 storebuf.size = 0;
1248                 storebuf.offset = 0; 
1249         
1250                 free_packet_list(packets);
1251                 packets = NULL;
1252         }
1253
1254         /*
1255          * Walk through our uids storing the words into the db with the keyid.
1256          */
1257         if (!deadlock) {
1258                 uids = keyuids(publickey, &primary);
1259         }
1260         if (uids != NULL) {
1261                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
1262                         wordlist = makewordlist(wordlist, uids[i]);
1263                 }
1264
1265                 for (curword = wordlist; curword != NULL && !deadlock;
1266                                 curword = curword->next) {
1267                         memset(&key, 0, sizeof(key));
1268                         memset(&data, 0, sizeof(data));
1269                         key.data = curword->object;
1270                         key.size = strlen(key.data);
1271                         data.data = worddb_data;
1272                         data.size = sizeof(worddb_data);
1273
1274                         /*
1275                          * Our data is the key creation time followed by the
1276                          * key id.
1277                          */
1278                         worddb_data[ 0] = publickey->publickey->data[1];
1279                         worddb_data[ 1] = publickey->publickey->data[2];
1280                         worddb_data[ 2] = publickey->publickey->data[3];
1281                         worddb_data[ 3] = publickey->publickey->data[4];
1282                         worddb_data[ 4] = (keyid >> 56) & 0xFF;
1283                         worddb_data[ 5] = (keyid >> 48) & 0xFF;
1284                         worddb_data[ 6] = (keyid >> 40) & 0xFF;
1285                         worddb_data[ 7] = (keyid >> 32) & 0xFF;
1286                         worddb_data[ 8] = (keyid >> 24) & 0xFF;
1287                         worddb_data[ 9] = (keyid >> 16) & 0xFF;
1288                         worddb_data[10] = (keyid >>  8) & 0xFF;
1289                         worddb_data[11] = keyid & 0xFF; 
1290                         ret = worddb->put(worddb,
1291                                 txn,
1292                                 &key,
1293                                 &data,
1294                                 0);
1295                         if (ret != 0) {
1296                                 logthing(LOGTHING_ERROR,
1297                                         "Problem storing word: %s",
1298                                         db_strerror(ret));
1299                                 if (ret == DB_LOCK_DEADLOCK) {
1300                                         deadlock = true;
1301                                 }
1302                         }
1303                 }
1304
1305                 /*
1306                  * Free our UID and word lists.
1307                  */
1308                 llfree(wordlist, NULL);
1309                 for (i = 0; uids[i] != NULL; i++) {
1310                         free(uids[i]);
1311                         uids[i] = NULL;
1312                 }
1313                 free(uids);
1314                 uids = NULL;
1315         }
1316
1317         /*
1318          * Write the truncated 32 bit keyid so we can lookup the full id for
1319          * queries.
1320          */
1321         if (!deadlock) {
1322                 shortkeyid = keyid & 0xFFFFFFFF;
1323
1324                 memset(&key, 0, sizeof(key));
1325                 memset(&data, 0, sizeof(data));
1326                 key.data = &shortkeyid;
1327                 key.size = sizeof(shortkeyid);
1328                 data.data = &keyid;
1329                 data.size = sizeof(keyid);
1330
1331                 ret = id32db->put(id32db,
1332                         txn,
1333                         &key,
1334                         &data,
1335                         0);
1336                 if (ret != 0) {
1337                         logthing(LOGTHING_ERROR,
1338                                 "Problem storing short keyid: %s",
1339                                 db_strerror(ret));
1340                         if (ret == DB_LOCK_DEADLOCK) {
1341                                 deadlock = true;
1342                         }
1343                 }
1344         }
1345
1346         if (!deadlock) {
1347                 subkeyids = keysubkeys(publickey);
1348                 i = 0;
1349                 while (subkeyids != NULL && subkeyids[i] != 0) {
1350                         /* Store the subkey ID -> main key ID mapping */
1351                         memset(&key, 0, sizeof(key));
1352                         memset(&data, 0, sizeof(data));
1353                         key.data = &subkeyids[i];
1354                         key.size = sizeof(subkeyids[i]);
1355                         data.data = &keyid;
1356                         data.size = sizeof(keyid);
1357
1358                         ret = subkeydb->put(subkeydb,
1359                                 txn,
1360                                 &key,
1361                                 &data,
1362                                 0);
1363                         if (ret != 0) {
1364                                 logthing(LOGTHING_ERROR,
1365                                         "Problem storing subkey keyid: %s",
1366                                         db_strerror(ret));
1367                                 if (ret == DB_LOCK_DEADLOCK) {
1368                                         deadlock = true;
1369                                 }
1370                         }
1371
1372                         /* Store the short subkey ID -> main key ID mapping */
1373                         shortkeyid = subkeyids[i++] & 0xFFFFFFFF;
1374
1375                         memset(&key, 0, sizeof(key));
1376                         memset(&data, 0, sizeof(data));
1377                         key.data = &shortkeyid;
1378                         key.size = sizeof(shortkeyid);
1379                         data.data = &keyid;
1380                         data.size = sizeof(keyid);
1381
1382                         ret = id32db->put(id32db,
1383                                 txn,
1384                                 &key,
1385                                 &data,
1386                                 0);
1387                         if (ret != 0) {
1388                                 logthing(LOGTHING_ERROR,
1389                                         "Problem storing short keyid: %s",
1390                                         db_strerror(ret));
1391                                 if (ret == DB_LOCK_DEADLOCK) {
1392                                         deadlock = true;
1393                                 }
1394                         }
1395                 }
1396                 if (subkeyids != NULL) {
1397                         free(subkeyids);
1398                         subkeyids = NULL;
1399                 }
1400         }
1401
1402         if (!deadlock) {
1403                 get_skshash(publickey, &hash);
1404                 memset(&key, 0, sizeof(key));
1405                 memset(&data, 0, sizeof(data));
1406                 key.data = hash.hash;
1407                 key.size = sizeof(hash.hash);
1408                 data.data = &keyid;
1409                 data.size = sizeof(keyid);
1410
1411                 ret = skshashdb->put(skshashdb,
1412                         txn,
1413                         &key,
1414                         &data,
1415                         0);
1416                 if (ret != 0) {
1417                         logthing(LOGTHING_ERROR,
1418                                 "Problem storing SKS hash: %s",
1419                                 db_strerror(ret));
1420                         if (ret == DB_LOCK_DEADLOCK) {
1421                                 deadlock = true;
1422                         }
1423                 }
1424         }
1425
1426         if (!intrans) {
1427                 db4_endtrans();
1428         }
1429
1430         return deadlock ? -1 : 0 ;
1431 }
1432
1433 /**
1434  *      iterate_keys - call a function once for each key in the db.
1435  *      @iterfunc: The function to call.
1436  *      @ctx: A context pointer
1437  *
1438  *      Calls iterfunc once for each key in the database. ctx is passed
1439  *      unaltered to iterfunc. This function is intended to aid database dumps
1440  *      and statistic calculations.
1441  *
1442  *      Returns the number of keys we iterated over.
1443  */
1444 static int db4_iterate_keys(void (*iterfunc)(void *ctx,
1445                 struct openpgp_publickey *key), void *ctx)
1446 {
1447         DBT                         dbkey, data;
1448         DBC                        *cursor = NULL;
1449         int                         ret = 0;
1450         int                         i = 0;
1451         int                         numkeys = 0;
1452         struct buffer_ctx           fetchbuf;
1453         struct openpgp_packet_list *packets = NULL;
1454         struct openpgp_publickey   *key = NULL;
1455
1456         for (i = 0; i < numdbs; i++) {
1457                 ret = dbconns[i]->cursor(dbconns[i],
1458                         NULL,
1459                         &cursor,
1460                         0);   /* flags */
1461
1462                 memset(&dbkey, 0, sizeof(dbkey));
1463                 memset(&data, 0, sizeof(data));
1464                 ret = cursor->c_get(cursor, &dbkey, &data, DB_NEXT);
1465                 while (ret == 0) {
1466                         fetchbuf.buffer = data.data;
1467                         fetchbuf.offset = 0;
1468                         fetchbuf.size = data.size;
1469                         read_openpgp_stream(buffer_fetchchar, &fetchbuf,
1470                                 &packets, 0);
1471                         parse_keys(packets, &key);
1472
1473                         iterfunc(ctx, key);
1474                         
1475                         free_publickey(key);
1476                         key = NULL;
1477                         free_packet_list(packets);
1478                         packets = NULL;
1479                         
1480                         memset(&dbkey, 0, sizeof(dbkey));
1481                         memset(&data, 0, sizeof(data));
1482                         ret = cursor->c_get(cursor, &dbkey, &data,
1483                                         DB_NEXT);
1484                         numkeys++;
1485                 }
1486                 if (ret != DB_NOTFOUND) {
1487                         logthing(LOGTHING_ERROR,
1488                                 "Problem reading key: %s",
1489                                 db_strerror(ret));
1490                 }
1491
1492                 ret = cursor->c_close(cursor);
1493                 cursor = NULL;
1494         }
1495         
1496         return numkeys;
1497 }
1498
1499 /*
1500  * Include the basic keydb routines.
1501  */
1502 #define NEED_GETKEYSIGS 1
1503 #define NEED_KEYID2UID 1
1504 #define NEED_UPDATEKEYS 1
1505 #define NEED_GET_FP 1
1506 #include "keydb.c"
1507
1508 struct dbfuncs keydb_db4_funcs = {
1509         .initdb                 = db4_initdb,
1510         .cleanupdb              = db4_cleanupdb,
1511         .starttrans             = db4_starttrans,
1512         .endtrans               = db4_endtrans,
1513         .fetch_key_id           = db4_fetch_key_id,
1514         .fetch_key_fp           = generic_fetch_key_fp,
1515         .fetch_key_text         = db4_fetch_key_text,
1516         .fetch_key_skshash      = db4_fetch_key_skshash,
1517         .store_key              = db4_store_key,
1518         .update_keys            = generic_update_keys,
1519         .delete_key             = db4_delete_key,
1520         .getkeysigs             = generic_getkeysigs,
1521         .cached_getkeysigs      = generic_cached_getkeysigs,
1522         .keyid2uid              = generic_keyid2uid,
1523         .getfullkeyid           = db4_getfullkeyid,
1524         .iterate_keys           = db4_iterate_keys,
1525 };