]> git.sommitrealweird.co.uk Git - onak.git/blob - keydb_db3.c
cscvs to tla changeset 132
[onak.git] / keydb_db3.c
1 /*
2  * keydb_db3.c - Routines to store and fetch keys in a DB3 database.
3  *
4  * Jonathan McDowell <noodles@earth.li>
5  *
6  * Copyright 2002 Project Purple
7  *
8  * $Id: keydb_db3.c,v 1.26 2004/05/27 03:33:24 noodles Exp $
9  */
10
11 #include <assert.h>
12 #include <sys/types.h>
13 #include <sys/uio.h>
14 #include <ctype.h>
15 #include <errno.h>
16 #include <fcntl.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <unistd.h>
21
22 #include <db.h>
23
24 #include "charfuncs.h"
25 #include "keydb.h"
26 #include "keyid.h"
27 #include "decodekey.h"
28 #include "keystructs.h"
29 #include "mem.h"
30 #include "log.h"
31 #include "onak-conf.h"
32 #include "parsekey.h"
33 #include "wordlist.h"
34
35 /**
36  *      dbenv - our database environment.
37  */
38 static DB_ENV *dbenv = NULL;
39
40 /**
41  *      numdb - The number of database files we have.
42  */
43 static int numdbs = 16;
44
45 /**
46  *      dbconn - our connections to the key database files.
47  */
48 static DB **dbconns = NULL;
49
50 /**
51  *      worddb - our connection to the word database.
52  */
53 static DB *worddb = NULL;
54
55 /**
56  *      txn - our current transaction id.
57  */
58 static DB_TXN *txn = NULL;
59
60 DB *keydb(uint64_t keyid)
61 {
62         uint64_t keytrun;
63
64         keytrun = keyid >> 8;
65
66         return(dbconns[keytrun % numdbs]);
67 }
68
69 /**
70  *      initdb - Initialize the key database.
71  *
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
74  *      for access.
75  */
76 void initdb(bool readonly)
77 {
78         char       buf[1024];
79         FILE      *numdb = NULL;
80         int        ret = 0;
81         int        i = 0;
82         u_int32_t  flags = 0;
83
84         snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", config.db_dir);
85         numdb = fopen(buf, "r");
86         if (numdb != NULL) {
87                 if (fgets(buf, sizeof(buf), numdb) != NULL) {
88                         numdbs = atoi(buf);
89                 }
90                 fclose(numdb);
91         } else if (!readonly) {
92                 logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s",
93                                 strerror(errno));
94                 numdb = fopen(buf, "w");
95                 if (numdb != NULL) {
96                         fprintf(numdb, "%d", numdbs);
97                         fclose(numdb);
98                 } else {
99                         logthing(LOGTHING_ERROR,
100                                 "Couldn't write num_keydb: %s",
101                                 strerror(errno));
102                 }
103         }
104
105         dbconns = malloc(sizeof (DB *) * numdbs);
106         if (dbconns == NULL) {
107                 logthing(LOGTHING_CRITICAL,
108                                 "Couldn't allocate memory for dbconns");
109                 exit(1);
110         }
111
112         ret = db_env_create(&dbenv, 0);
113         if (ret != 0) {
114                 logthing(LOGTHING_CRITICAL,
115                         "db_env_create: %s", db_strerror(ret));
116                 exit(1);
117         }
118
119         /*
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.
123          */
124         ret = dbenv->set_lk_detect(dbenv, DB_LOCK_DEFAULT);
125         if (ret != 0) {
126                 logthing(LOGTHING_CRITICAL,
127                         "db_env_create: %s", db_strerror(ret));
128                 exit(1);
129         }
130
131         ret = dbenv->open(dbenv, config.db_dir,
132                         DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
133                         DB_INIT_TXN |
134                         DB_CREATE,
135                         0);
136         if (ret != 0) {
137                 logthing(LOGTHING_CRITICAL,
138                                 "Error opening db environment: %s (%s)",
139                                 config.db_dir,
140                                 db_strerror(ret));
141                 exit(1);
142         }
143
144         for (i = 0; i < numdbs; i++) {
145                 ret = db_create(&dbconns[i], dbenv, 0);
146                 if (ret != 0) {
147                         logthing(LOGTHING_CRITICAL,
148                                 "db_create: %s", db_strerror(ret));
149                         exit(1);
150                 }
151
152                 snprintf(buf, 1023, "keydb.%d.db", i);
153                 flags = DB_CREATE;
154                 if (readonly) {
155                         flags = DB_RDONLY;
156                 }
157                 ret = dbconns[i]->open(dbconns[i], buf,
158                         NULL,
159                         DB_HASH,
160                         flags,
161                         0664);
162                 if (ret != 0) {
163                         logthing(LOGTHING_CRITICAL,
164                                 "Error opening key database: %s (%s)",
165                                 buf,
166                                 db_strerror(ret));
167                         exit(1);
168                 }
169         }
170
171         ret = db_create(&worddb, dbenv, 0);
172         if (ret != 0) {
173                 logthing(LOGTHING_CRITICAL, "db_create: %s", db_strerror(ret));
174                 exit(1);
175         }
176         ret = worddb->set_flags(worddb, DB_DUP);
177
178         ret = worddb->open(worddb, "worddb", NULL, DB_BTREE,
179                         flags,
180                         0664);
181         if (ret != 0) {
182                 logthing(LOGTHING_CRITICAL,
183                                 "Error opening word database: %s (%s)",
184                                 "worddb",
185                                 db_strerror(ret));
186                 exit(1);
187         }
188         
189         return;
190 }
191
192 /**
193  *      cleanupdb - De-initialize the key database.
194  *
195  *      This function should be called upon program exit to allow the DB to
196  *      cleanup after itself.
197  */
198 void cleanupdb(void)
199 {
200         int i = 0;
201
202         txn_checkpoint(dbenv, 0, 0, 0);
203         worddb->close(worddb, 0);
204         worddb = NULL;
205         for (i = 0; i < numdbs; i++) {
206                 dbconns[i]->close(dbconns[i], 0);
207                 dbconns[i] = NULL;
208         }
209         dbenv->close(dbenv, 0);
210         dbenv = NULL;
211 }
212
213 /**
214  *      starttrans - Start a transaction.
215  *
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.
219  */
220 bool starttrans(void)
221 {
222         int ret;
223
224         assert(dbenv != NULL);
225         assert(txn == NULL);
226
227         ret = txn_begin(dbenv,
228                 NULL, /* No parent transaction */
229                 &txn,
230                 0);
231         if (ret != 0) {
232                 logthing(LOGTHING_CRITICAL,
233                                 "Error starting transaction: %s",
234                                 db_strerror(ret));
235                 exit(1);
236         }
237
238         return true;
239 }
240
241 /**
242  *      endtrans - End a transaction.
243  *
244  *      Ends a transaction.
245  */
246 void endtrans(void)
247 {
248         int ret;
249
250         assert(dbenv != NULL);
251         assert(txn != NULL);
252
253         ret = txn_commit(txn,
254                 0);
255         if (ret != 0) {
256                 logthing(LOGTHING_CRITICAL,
257                                 "Error ending transaction: %s",
258                                 db_strerror(ret));
259                 exit(1);
260         }
261         txn = NULL;
262
263         return;
264 }
265
266 /**
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.
271  *
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
276  *      structure.
277  */
278 int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
279                 bool intrans)
280 {
281         struct openpgp_packet_list *packets = NULL;
282         DBT key, data;
283         int ret = 0;
284         int numkeys = 0;
285         struct buffer_ctx fetchbuf;
286
287         memset(&key, 0, sizeof(key));
288         memset(&data, 0, sizeof(data));
289
290         data.size = 0;
291         data.data = NULL;
292
293         key.size = sizeof(keyid);
294         key.data = &keyid;
295         keyid &= 0xFFFFFFFF;
296
297         if (!intrans) {
298                 starttrans();
299         }
300
301         ret = keydb(keyid)->get(keydb(keyid),
302                         txn,
303                         &key,
304                         &data,
305                         0); /* flags*/
306         
307         if (ret == 0) {
308                 fetchbuf.buffer = data.data;
309                 fetchbuf.offset = 0;
310                 fetchbuf.size = data.size;
311                 read_openpgp_stream(buffer_fetchchar, &fetchbuf,
312                                 &packets, 0);
313                 parse_keys(packets, publickey);
314                 free_packet_list(packets);
315                 packets = NULL;
316                 numkeys++;
317         } else if (ret != DB_NOTFOUND) {
318                 logthing(LOGTHING_ERROR,
319                                 "Problem retrieving key: %s",
320                                 db_strerror(ret));
321         }
322
323         if (!intrans) {
324                 endtrans();
325         }
326
327         return (numkeys);
328 }
329
330 int worddb_cmp(const char *d1, const char *d2)
331 {
332         return memcmp(d1, d2, 12);
333 }
334
335 /**
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.
339  *
340  *      This function searches for the supplied text and returns the keys that
341  *      contain it.
342  */
343 int fetch_key_text(const char *search, struct openpgp_publickey **publickey)
344 {
345         DBC *cursor = NULL;
346         DBT key, data;
347         int ret;
348         uint64_t keyid;
349         int i;
350         int numkeys;
351         char *searchtext = NULL;
352         struct ll *wordlist = NULL;
353         struct ll *curword = NULL;
354         struct ll *keylist = NULL;
355         struct ll *newkeylist = NULL;
356
357         numkeys = 0;
358         searchtext = strdup(search);
359         wordlist = makewordlist(wordlist, searchtext);
360
361         starttrans();
362
363         ret = worddb->cursor(worddb,
364                         txn,
365                         &cursor,
366                         0);   /* flags */
367
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,
375                                 &key,
376                                 &data,
377                                 DB_SET);
378                 while (ret == 0 && strncmp(key.data, curword->object,
379                                         key.size) == 0 &&
380                                 ((char *) curword->object)[key.size] == 0) {
381                         keyid = 0;
382                         for (i = 4; i < 12; i++) {
383                                 keyid <<= 8;
384                                 keyid += ((unsigned char *)
385                                                 data.data)[i];
386                         }
387
388                         if (keylist == NULL ||
389                                         llfind(keylist, data.data,
390                                                 worddb_cmp) != NULL) {
391                                 newkeylist = lladd(newkeylist, data.data);
392                                 data.data = NULL;
393                         } else {
394                                 free(data.data);
395                                 data.data = NULL;
396                         }
397                         ret = cursor->c_get(cursor,
398                                         &key,
399                                         &data,
400                                         DB_NEXT);
401                 }
402                 llfree(keylist, free);
403                 keylist = newkeylist;
404                 newkeylist = NULL;
405                 if (data.data != NULL) {
406                         free(data.data);
407                         data.data = NULL;
408                 }
409         }
410         llfree(wordlist, NULL);
411         wordlist = NULL;
412         
413         for (newkeylist = keylist;
414                         newkeylist != NULL && numkeys < config.maxkeys;
415                         newkeylist = newkeylist->next) {
416
417                         keyid = 0;
418                         for (i = 4; i < 12; i++) {
419                                 keyid <<= 8;
420                                 keyid += ((unsigned char *)
421                                                 newkeylist->object)[i];
422                         }
423
424                         numkeys += fetch_key(keyid,
425                                         publickey,
426                                         true);
427         }
428         llfree(keylist, free);
429         keylist = NULL;
430         free(searchtext);
431         searchtext = NULL;
432
433         ret = cursor->c_close(cursor);
434         cursor = NULL;
435
436         endtrans();
437         
438         return (numkeys);
439 }
440
441 /**
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.
446  *
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.
452  */
453 int store_key(struct openpgp_publickey *publickey, bool intrans, bool update)
454 {
455         struct     openpgp_packet_list *packets = NULL;
456         struct     openpgp_packet_list *list_end = NULL;
457         struct     openpgp_publickey *next = NULL;
458         int        ret = 0;
459         int        i = 0;
460         struct     buffer_ctx storebuf;
461         DBT        key;
462         DBT        data;
463         uint64_t   keyid = 0;
464         char     **uids = NULL;
465         char      *primary = NULL;
466         unsigned char worddb_data[12];
467         struct ll *wordlist = NULL;
468         struct ll *curword  = NULL;
469         bool       deadlock = false;
470
471         keyid = get_keyid(publickey);
472
473         if (!intrans) {
474                 starttrans();
475         }
476
477         /*
478          * Delete the key if we already have it.
479          *
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.
484          */
485         if (update) {
486                 deadlock = (delete_key(keyid, true) == -1);
487         }
488
489         /*
490          * Convert the key to a flat set of binary data.
491          */
492         if (!deadlock) {
493                 next = publickey->next;
494                 publickey->next = NULL;
495                 flatten_publickey(publickey, &packets, &list_end);
496                 publickey->next = next;
497
498                 storebuf.offset = 0; 
499                 storebuf.size = 8192;
500                 storebuf.buffer = malloc(8192);
501         
502                 write_openpgp_stream(buffer_putchar, &storebuf, packets);
503
504                 /*
505                  * Now we have the key data store it in the DB; the keyid is
506                  * the key.
507                  */
508                 memset(&key, 0, sizeof(key));
509                 memset(&data, 0, sizeof(data));
510                 key.data = &keyid;
511                 key.size = sizeof(keyid);
512                 keyid &= 0xFFFFFFFF;
513                 data.size = storebuf.offset;
514                 data.data = storebuf.buffer;
515
516                 ret = keydb(keyid)->put(keydb(keyid),
517                                 txn,
518                                 &key,
519                                 &data,
520                                 0); /* flags*/
521                 if (ret != 0) {
522                         logthing(LOGTHING_ERROR,
523                                         "Problem storing key: %s",
524                                         db_strerror(ret));
525                         if (ret == DB_LOCK_DEADLOCK) {
526                                 deadlock = true;
527                         }
528                 }
529
530                 free(storebuf.buffer);
531                 storebuf.buffer = NULL;
532                 storebuf.size = 0;
533                 storebuf.offset = 0; 
534         
535                 free_packet_list(packets);
536                 packets = NULL;
537         }
538
539         /*
540          * Walk through our uids storing the words into the db with the keyid.
541          */
542         if (!deadlock) {
543                 uids = keyuids(publickey, &primary);
544         }
545         if (uids != NULL) {
546                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
547                         wordlist = makewordlist(wordlist, uids[i]);
548                 }
549
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);
558
559                         /*
560                          * Our data is the key creation time followed by the
561                          * key id.
562                          */
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,
576                                 txn,
577                                 &key,
578                                 &data,
579                                 0);
580                         if (ret != 0) {
581                                 logthing(LOGTHING_ERROR,
582                                         "Problem storing word: %s",
583                                         db_strerror(ret));
584                                 if (ret == DB_LOCK_DEADLOCK) {
585                                         deadlock = true;
586                                 }
587                         }
588                 }
589
590                 /*
591                  * Free our UID and word lists.
592                  */
593                 llfree(wordlist, NULL);
594                 for (i = 0; uids[i] != NULL; i++) {
595                         free(uids[i]);
596                         uids[i] = NULL;
597                 }
598                 free(uids);
599                 uids = NULL;
600         }
601
602         if (!intrans) {
603                 endtrans();
604         }
605
606         return deadlock ? -1 : 0 ;
607 }
608
609 /**
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.
613  *
614  *      This function deletes a public key from whatever storage mechanism we
615  *      are using. Returns 0 if the key existed.
616  */
617 int delete_key(uint64_t keyid, bool intrans)
618 {
619         struct openpgp_publickey *publickey = NULL;
620         DBT key, data;
621         DBC *cursor = NULL;
622         int ret = 0;
623         int i;
624         char **uids = 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;
630
631         keyid &= 0xFFFFFFFF;
632
633         if (!intrans) {
634                 starttrans();
635         }
636
637         fetch_key(keyid, &publickey, true);
638
639         /*
640          * Walk through the uids removing the words from the worddb.
641          */
642         if (publickey != NULL) {
643                 uids = keyuids(publickey, &primary);
644         }
645         if (uids != NULL) {
646                 for (i = 0; ret == 0 && uids[i] != NULL; i++) {
647                         wordlist = makewordlist(wordlist, uids[i]);
648                 }
649                                 
650                 ret = worddb->cursor(worddb,
651                         txn,
652                         &cursor,
653                         0);   /* flags */
654
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);
663
664                         /*
665                          * Our data is the key creation time followed by the
666                          * key id.
667                          */
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; 
680
681                         ret = cursor->c_get(cursor,
682                                 &key,
683                                 &data,
684                                 DB_GET_BOTH);
685
686                         if (ret == 0) {
687                                 ret = cursor->c_del(cursor, 0);
688                                 if (ret != 0) {
689                                         logthing(LOGTHING_ERROR,
690                                                 "Problem deleting word: %s",
691                                                 db_strerror(ret));
692                                 }
693                         }
694
695                         if (ret != 0) {
696                                 logthing(LOGTHING_ERROR,
697                                         "Problem deleting word: %s",
698                                         db_strerror(ret));
699                                 if (ret == DB_LOCK_DEADLOCK) {
700                                         deadlock = true;
701                                 }
702                         }
703                 }
704                 ret = cursor->c_close(cursor);
705                 cursor = NULL;
706
707                 /*
708                  * Free our UID and word lists.
709                  */
710                 llfree(wordlist, NULL);
711                 for (i = 0; uids[i] != NULL; i++) {
712                         free(uids[i]);
713                         uids[i] = NULL;
714                 }
715                 free(uids);
716                 uids = NULL;
717                 free_publickey(publickey);
718                 publickey = NULL;
719         }
720
721         if (!deadlock) {
722                 key.data = &keyid;
723                 key.size = sizeof(keyid);
724
725                 keydb(keyid)->del(keydb(keyid),
726                                 txn,
727                                 &key,
728                                 0); /* flags */
729         }
730
731         if (!intrans) {
732                 endtrans();
733         }
734
735         return deadlock ? (-1) : (ret == DB_NOTFOUND);
736 }
737
738 /**
739  *      dumpdb - dump the key database
740  *      @filenamebase: The base filename to use for the dump.
741  *
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
746  *      extension.
747  */
748 int dumpdb(char *filenamebase)
749 {
750         DBT   key, data;
751         DBC  *cursor = NULL;
752         int   ret = 0;
753         int   fd = -1;
754         int   i = 0;
755         char  filename[1024];
756
757         filename[1023] = 0;
758         for (i = 0; i < numdbs; i++) {
759                 ret = dbconns[i]->cursor(dbconns[i],
760                         NULL,
761                         &cursor,
762                         0);   /* flags */
763
764                 snprintf(filename, 1023, "%s.%d.pgp", filenamebase, i);
765                 fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0640);
766                 if (fd == -1) {
767                         logthing(LOGTHING_ERROR,
768                                 "Error opening keydump file (%s): %s",
769                                 filename,
770                                 strerror(errno));
771                 } else {
772                         memset(&key, 0, sizeof(key));
773                         memset(&data, 0, sizeof(data));
774                         ret = cursor->c_get(cursor, &key, &data, DB_NEXT);
775                         while (ret == 0) {
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,
780                                                 DB_NEXT);
781                         }
782                         if (ret != DB_NOTFOUND) {
783                                 logthing(LOGTHING_ERROR,
784                                         "Problem reading key: %s",
785                                         db_strerror(ret));
786                         }
787                         close(fd);
788                 }
789
790                 ret = cursor->c_close(cursor);
791                 cursor = NULL;
792         }
793         
794         return 0;
795 }
796
797 /*
798  * Include the basic keydb routines.
799  */
800 #define NEED_GETFULLKEYID 1
801 #define NEED_GETKEYSIGS 1
802 #define NEED_KEYID2UID 1
803 #include "keydb.c"