]> the.earth.li Git - onak.git/blob - keydb_pg.c
Add config option to specify keyd socket directory
[onak.git] / keydb_pg.c
1 /*
2  * keydb_pg.c - Routines to store and fetch keys in a PostGres database.
3  *
4  * Copyright 2002-2004 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 <postgresql/libpq-fe.h>
21 #include <postgresql/libpq/libpq-fs.h>
22
23 #include <sys/types.h>
24 #include <sys/uio.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31
32 #include "hash.h"
33 #include "keydb.h"
34 #include "keyid.h"
35 #include "decodekey.h"
36 #include "keystructs.h"
37 #include "log.h"
38 #include "mem.h"
39 #include "onak-conf.h"
40 #include "parsekey.h"
41
42 struct pg_fc_ctx {
43         PGconn *dbconn;
44         int fd;
45 };
46
47 /**
48  *      keydb_fetchchar - Fetches a char from a file.
49  */
50 static int keydb_fetchchar(void *_ctx, size_t count, void *c)
51 {
52         struct pg_fc_ctx *ctx = (struct pg_fc_ctx *) _ctx;
53
54         return (!lo_read(ctx->dbconn, ctx->fd, (char *) c, count));
55 }
56
57 /**
58  *      keydb_putchar - Puts a char to a file.
59  */
60 static int keydb_putchar(void *_ctx, size_t count, void *c)
61 {
62         struct pg_fc_ctx *ctx = (struct pg_fc_ctx *) _ctx;
63
64         return !(lo_write(ctx->dbconn, ctx->fd, (char *) c, count));
65 }
66
67 /**
68  *      starttrans - Start a transaction.
69  *
70  *      Start a transaction. Intended to be used if we're about to perform many
71  *      operations on the database to help speed it all up, or if we want
72  *      something to only succeed if all relevant operations are successful.
73  */
74 static bool pg_starttrans(struct onak_dbctx *dbctx)
75 {
76         PGconn *dbconn = (PGconn *) dbctx->priv;
77         PGresult *result = NULL;
78         
79         result = PQexec(dbconn, "BEGIN");
80         PQclear(result);
81
82         return true;
83 }
84
85 /**
86  *      endtrans - End a transaction.
87  *
88  *      Ends a transaction.
89  */
90 static void pg_endtrans(struct onak_dbctx *dbctx)
91 {
92         PGconn *dbconn = (PGconn *) dbctx->priv;
93         PGresult *result = NULL;
94
95         result = PQexec(dbconn, "COMMIT");
96         PQclear(result);
97
98         return;
99 }
100
101 /**
102  *      fetch_key_id - Given a keyid fetch the key from storage.
103  *      @keyid: The keyid to fetch.
104  *      @publickey: A pointer to a structure to return the key in.
105  *      @intrans: If we're already in a transaction.
106  *
107  *      We use the hex representation of the keyid as the filename to fetch the
108  *      key from. The key is stored in the file as a binary OpenPGP stream of
109  *      packets, so we can just use read_openpgp_stream() to read the packets
110  *      in and then parse_keys() to parse the packets into a publickey
111  *      structure.
112  */
113 static int pg_fetch_key_id(struct onak_dbctx *dbctx,
114                 uint64_t keyid,
115                 struct openpgp_publickey **publickey,
116                 bool intrans)
117 {
118         struct openpgp_packet_list *packets = NULL;
119         PGconn *dbconn = (PGconn *) dbctx->priv;
120         PGresult *result = NULL;
121         char *oids = NULL;
122         char statement[1024];
123         int i = 0;
124         int numkeys = 0;
125         Oid key_oid;
126         struct pg_fc_ctx fcctx;
127
128         if (!intrans) {
129                 result = PQexec(dbconn, "BEGIN");
130                 PQclear(result);
131         }
132         
133         if (keyid > 0xFFFFFFFF) {
134                 snprintf(statement, 1023,
135                         "SELECT keydata FROM onak_keys WHERE keyid = '%"
136                         PRIX64 "'",
137                         keyid);
138         } else {
139                 snprintf(statement, 1023,
140                         "SELECT keydata FROM onak_keys WHERE keyid "
141                         "LIKE '%%%" PRIX64 "'",
142                         keyid);
143         }
144         result = PQexec(dbconn, statement);
145
146         if (PQresultStatus(result) == PGRES_TUPLES_OK) {
147                 numkeys = PQntuples(result);
148                 for (i = 0; i < numkeys && numkeys <= config.maxkeys; i++) {
149                         oids = PQgetvalue(result, i, 0);
150                         key_oid = (Oid) atoi(oids);
151
152                         fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
153                         if (fcctx.fd < 0) {
154                                 logthing(LOGTHING_ERROR,
155                                                 "Can't open large object.");
156                         } else {
157                                 fcctx.dbconn = dbconn;
158                                 read_openpgp_stream(keydb_fetchchar, &fcctx,
159                                                 &packets, 0);
160                                 parse_keys(packets, publickey);
161                                 lo_close(dbconn, fcctx.fd);
162                                 free_packet_list(packets);
163                                 packets = NULL;
164                         }
165                 }
166         } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
167                 logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
168         }
169
170         PQclear(result);
171
172         if (!intrans) {
173                 result = PQexec(dbconn, "COMMIT");
174                 PQclear(result);
175         }
176         return (numkeys);
177 }
178
179 /**
180  *      fetch_key_text - Trys to find the keys that contain the supplied text.
181  *      @search: The text to search for.
182  *      @publickey: A pointer to a structure to return the key in.
183  *
184  *      This function searches for the supplied text and returns the keys that
185  *      contain it.
186  */
187 static int pg_fetch_key_text(struct onak_dbctx *dbctx,
188                 const char *search,
189                 struct openpgp_publickey **publickey)
190 {
191         struct openpgp_packet_list *packets = NULL;
192         PGconn *dbconn = (PGconn *) dbctx->priv;
193         PGresult *result = NULL;
194         char *oids = NULL;
195         char statement[1024];
196         int i = 0;
197         int numkeys = 0;
198         Oid key_oid;
199         char *newsearch = NULL;
200         struct pg_fc_ctx fcctx;
201
202         result = PQexec(dbconn, "BEGIN");
203         PQclear(result);
204
205         newsearch = malloc(strlen(search) * 2 + 1);
206         memset(newsearch, 0, strlen(search) * 2 + 1);
207         PQescapeStringConn(dbconn, newsearch, search, strlen(search), NULL);
208         snprintf(statement, 1023,
209                         "SELECT DISTINCT onak_keys.keydata FROM onak_keys, "
210                         "onak_uids WHERE onak_keys.keyid = onak_uids.keyid "
211                         "AND onak_uids.uid LIKE '%%%s%%'",
212                         newsearch);
213         result = PQexec(dbconn, statement);
214         free(newsearch);
215         newsearch = NULL;
216
217         if (PQresultStatus(result) == PGRES_TUPLES_OK) {
218                 numkeys = PQntuples(result);
219                 for (i = 0; i < numkeys && numkeys <= config.maxkeys; i++) {
220                         oids = PQgetvalue(result, i, 0);
221                         key_oid = (Oid) atoi(oids);
222
223                         fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
224                         if (fcctx.fd < 0) {
225                                 logthing(LOGTHING_ERROR,
226                                                 "Can't open large object.");
227                         } else {
228                                 fcctx.dbconn = dbconn;
229                                 read_openpgp_stream(keydb_fetchchar, &fcctx,
230                                                 &packets,
231                                                 0);
232                                 parse_keys(packets, publickey);
233                                 lo_close(dbconn, fcctx.fd);
234                                 free_packet_list(packets);
235                                 packets = NULL;
236                         }
237                 }
238         } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
239                 logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
240         }
241
242         PQclear(result);
243
244         result = PQexec(dbconn, "COMMIT");
245         PQclear(result);
246         return (numkeys);
247 }
248
249 /**
250  *      delete_key - Given a keyid delete the key from storage.
251  *      @keyid: The keyid to delete.
252  *      @intrans: If we're already in a transaction.
253  *
254  *      This function deletes a public key from whatever storage mechanism we
255  *      are using. Returns 0 if the key existed.
256  */
257 static int pg_delete_key(struct onak_dbctx *dbctx, uint64_t keyid, bool intrans)
258 {
259         PGconn *dbconn = (PGconn *) dbctx->priv;
260         PGresult *result = NULL;
261         char *oids = NULL;
262         char statement[1024];
263         int found = 1;
264         int i;
265         Oid key_oid;
266
267         if (!intrans) {
268                 result = PQexec(dbconn, "BEGIN");
269                 PQclear(result);
270         }
271         
272         snprintf(statement, 1023,
273                         "SELECT keydata FROM onak_keys WHERE keyid = '%"
274                         PRIX64 "'",
275                         keyid);
276         result = PQexec(dbconn, statement);
277
278         if (PQresultStatus(result) == PGRES_TUPLES_OK) {
279                 found = 0;
280                 i = PQntuples(result);
281                 while (i > 0) {
282                         oids = PQgetvalue(result, i-1, 0);
283                         key_oid = (Oid) atoi(oids);
284                         lo_unlink(dbconn, key_oid);
285                         i--;
286                 }
287                 PQclear(result);
288
289                 snprintf(statement, 1023,
290                         "DELETE FROM onak_keys WHERE keyid = '%" PRIX64 "'",
291                         keyid);
292                 result = PQexec(dbconn, statement);
293                 PQclear(result);
294
295                 snprintf(statement, 1023,
296                         "DELETE FROM onak_sigs WHERE signee = '%" PRIX64 "'",
297                         keyid);
298                 result = PQexec(dbconn, statement);
299                 PQclear(result);
300
301                 snprintf(statement, 1023,
302                         "DELETE FROM onak_uids WHERE keyid = '%" PRIX64 "'",
303                         keyid);
304                 result = PQexec(dbconn, statement);
305         } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
306                 logthing(LOGTHING_ERROR,
307                                 "Problem retrieving key (%" PRIX64
308                                 ") from DB.",
309                                 keyid);
310         }
311
312         PQclear(result);
313
314         if (!intrans) {
315                 result = PQexec(dbconn, "COMMIT");
316                 PQclear(result);
317         }
318         return (found);
319 }
320
321 /**
322  *      store_key - Takes a key and stores it.
323  *      @publickey: A pointer to the public key to store.
324  *      @intrans: If we're already in a transaction.
325  *      @update: If true the key exists and should be updated.
326  *
327  *      Again we just use the hex representation of the keyid as the filename
328  *      to store the key to. We flatten the public key to a list of OpenPGP
329  *      packets and then use write_openpgp_stream() to write the stream out to
330  *      the file. If update is true then we delete the old key first, otherwise
331  *      we trust that it doesn't exist.
332  */
333 static int pg_store_key(struct onak_dbctx *dbctx,
334                 struct openpgp_publickey *publickey, bool intrans,
335                 bool update)
336 {
337         struct openpgp_packet_list *packets = NULL;
338         struct openpgp_packet_list *list_end = NULL;
339         struct openpgp_publickey *next = NULL;
340         struct openpgp_signedpacket_list *curuid = NULL;
341         PGconn *dbconn = (PGconn *) dbctx->priv;
342         PGresult *result = NULL;
343         char statement[1024];
344         Oid key_oid;
345         char **uids = NULL;
346         char *primary = NULL;
347         char *safeuid = NULL;
348         int i;
349         uint64_t keyid;
350         struct pg_fc_ctx fcctx;
351
352         if (!intrans) {
353                 result = PQexec(dbconn, "BEGIN");
354                 PQclear(result);
355         }
356
357         if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
358                 logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
359                 return 0;
360         }
361
362         /*
363          * Delete the key if we already have it.
364          *
365          * TODO: Can we optimize this perhaps? Possibly when other data is
366          * involved as well? I suspect this is easiest and doesn't make a lot
367          * of difference though - the largest chunk of data is the keydata and
368          * it definitely needs updated.
369          */
370         if (update) {
371                 pg_delete_key(dbctx, keyid, true);
372         }
373
374         next = publickey->next;
375         publickey->next = NULL;
376         flatten_publickey(publickey, &packets, &list_end);
377         publickey->next = next;
378                 
379         key_oid = lo_creat(dbconn, INV_READ | INV_WRITE);
380         if (key_oid == 0) {
381                 logthing(LOGTHING_ERROR, "Can't create key OID");
382         } else {
383                 fcctx.fd = lo_open(dbconn, key_oid, INV_WRITE);
384                 fcctx.dbconn = dbconn;
385                 write_openpgp_stream(keydb_putchar, &fcctx, packets);
386                 lo_close(dbconn, fcctx.fd);
387         }
388         free_packet_list(packets);
389         packets = NULL;
390
391         snprintf(statement, 1023, 
392                         "INSERT INTO onak_keys (keyid, keydata) VALUES "
393                         "('%" PRIX64 "', '%d')", 
394                         keyid,
395                         key_oid);
396         result = PQexec(dbconn, statement);
397
398         if (PQresultStatus(result) != PGRES_COMMAND_OK) {
399                 logthing(LOGTHING_ERROR, "Problem storing key in DB.");
400                 logthing(LOGTHING_ERROR, "%s", PQresultErrorMessage(result));
401         }
402         PQclear(result);
403
404         uids = keyuids(publickey, &primary);
405         if (uids != NULL) {
406                 for (i = 0; uids[i] != NULL; i++) {
407                         safeuid = malloc(strlen(uids[i]) * 2 + 1);
408                         if (safeuid != NULL) {
409                                 memset(safeuid, 0, strlen(uids[i]) * 2 + 1);
410                                 PQescapeStringConn(dbconn, safeuid, uids[i],
411                                                 strlen(uids[i]), NULL);
412
413                                 snprintf(statement, 1023,
414                                         "INSERT INTO onak_uids "
415                                         "(keyid, uid, pri) "
416                                         "VALUES ('%" PRIX64 "', '%s', '%c')",
417                                         keyid,
418                                         safeuid,
419                                         (uids[i] == primary) ? 't' : 'f');
420                                 result = PQexec(dbconn, statement);
421
422                                 free(safeuid);
423                                 safeuid = NULL;
424                         }
425                         if (uids[i] != NULL) {
426                                 free(uids[i]);
427                                 uids[i] = NULL;
428                         }
429
430                         if (PQresultStatus(result) != PGRES_COMMAND_OK) {
431                                 logthing(LOGTHING_ERROR,
432                                                 "Problem storing key in DB.");
433                                 logthing(LOGTHING_ERROR, "%s",
434                                                 PQresultErrorMessage(result));
435                         }
436                         /*
437                          * TODO: Check result.
438                          */
439                         PQclear(result);
440                 }
441                 free(uids);
442                 uids = NULL;
443         }
444
445         for (curuid = publickey->uids; curuid != NULL; curuid = curuid->next) {
446                 for (packets = curuid->sigs; packets != NULL; 
447                                 packets = packets->next) {
448                         snprintf(statement, 1023,
449                                 "INSERT INTO onak_sigs (signer, signee) "
450                                 "VALUES ('%" PRIX64 "', '%" PRIX64 "')",
451                                 sig_keyid(packets->packet),
452                                 keyid);
453                         result = PQexec(dbconn, statement);
454                         PQclear(result);
455                 }
456         }
457
458         if (!intrans) {
459                 result = PQexec(dbconn, "COMMIT");
460                 PQclear(result);
461         }
462         
463         return 0;
464 }
465
466 /**
467  *      keyid2uid - Takes a keyid and returns the primary UID for it.
468  *      @keyid: The keyid to lookup.
469  */
470 static char *pg_keyid2uid(struct onak_dbctx *dbctx, uint64_t keyid)
471 {
472         PGconn *dbconn = (PGconn *) dbctx->priv;
473         PGresult *result = NULL;
474         char statement[1024];
475         char *uid = NULL;
476
477         snprintf(statement, 1023,
478                 "SELECT uid FROM onak_uids WHERE keyid = '%" PRIX64
479                 "' AND pri = 't'",
480                 keyid);
481         result = PQexec(dbconn, statement);
482
483         /*
484          * Technically we only expect one response to the query; a key only has
485          * one primary ID. Better to return something than nothing though.
486          *
487          * TODO: Log if we get more than one response? Needs logging framework
488          * first though.
489          */
490         if (PQresultStatus(result) == PGRES_TUPLES_OK &&
491                         PQntuples(result) >= 1) {
492                 uid = strdup(PQgetvalue(result, 0, 0));
493         } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
494                 logthing(LOGTHING_ERROR,
495                                 "Problem retrieving key (%" PRIX64
496                                 ") from DB.",
497                                 keyid);
498         }
499
500         PQclear(result);
501
502         return uid;
503 }
504
505 /**
506  *      getkeysigs - Gets a linked list of the signatures on a key.
507  *      @keyid: The keyid to get the sigs for.
508  *      @revoked: If the key is revoked.
509  *
510  *      This function gets the list of signatures on a key. Used for key 
511  *      indexing and doing stats bits.
512  */
513 static struct ll *pg_getkeysigs(struct onak_dbctx *dbctx,
514                         uint64_t keyid, bool *revoked)
515 {
516         struct ll *sigs = NULL;
517         PGconn *dbconn = (PGconn *) dbctx->priv;
518         PGresult *result = NULL;
519         uint64_t signer;
520         char statement[1024];
521         int i, j;
522         int numsigs = 0;
523         bool intrans = false;
524         char *str;
525
526         if (!intrans) {
527                 result = PQexec(dbconn, "BEGIN");
528                 PQclear(result);
529         }
530
531         snprintf(statement, 1023,
532                 "SELECT DISTINCT signer FROM onak_sigs WHERE signee = '%"
533                 PRIX64 "'",
534                 keyid);
535         result = PQexec(dbconn, statement);
536
537         if (PQresultStatus(result) == PGRES_TUPLES_OK) {
538                 numsigs = PQntuples(result);
539                 for (i = 0; i < numsigs;  i++) {
540                         j = 0;
541                         signer = 0;
542                         str = PQgetvalue(result, i, 0);
543                         while (str[j] != 0) {
544                                 signer <<= 4;
545                                 if (str[j] >= '0' && str[j] <= '9') {
546                                         signer += str[j] - '0';
547                                 } else {
548                                         signer += str[j] - 'A' + 10;
549                                 }
550                                 j++;
551                         }
552                         sigs = lladd(sigs, createandaddtohash(signer));
553                 }
554         } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
555                 logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
556         }
557
558         PQclear(result);
559
560         if (!intrans) {
561                 result = PQexec(dbconn, "COMMIT");
562                 PQclear(result);
563         }
564
565         /*
566          * TODO: What do we do about revocations? We don't have the details
567          * stored in a separate table, so we'd have to grab the key and decode
568          * it, which we're trying to avoid by having a signers table.
569          */
570         if (revoked != NULL) {
571                 *revoked = false;
572         }
573         
574         return sigs;
575 }
576
577 /**
578  *      iterate_keys - call a function once for each key in the db.
579  *      @iterfunc: The function to call.
580  *      @ctx: A context pointer
581  *
582  *      Calls iterfunc once for each key in the database. ctx is passed
583  *      unaltered to iterfunc. This function is intended to aid database dumps
584  *      and statistic calculations.
585  *
586  *      Returns the number of keys we iterated over.
587  */
588 static int pg_iterate_keys(struct onak_dbctx *dbctx,
589                 void (*iterfunc)(void *ctx,
590                 struct openpgp_publickey *key), void *ctx)
591 {
592         struct openpgp_packet_list *packets = NULL;
593         struct openpgp_publickey *key = NULL;
594         PGconn *dbconn = (PGconn *) dbctx->priv;
595         PGresult *result = NULL;
596         char *oids = NULL;
597         int i = 0;
598         int numkeys = 0;
599         Oid key_oid;
600         struct pg_fc_ctx fcctx;
601
602         result = PQexec(dbconn, "SELECT keydata FROM onak_keys;");
603
604         if (PQresultStatus(result) == PGRES_TUPLES_OK) {
605                 numkeys = PQntuples(result);
606                 for (i = 0; i < numkeys; i++) {
607                         oids = PQgetvalue(result, i, 0);
608                         key_oid = (Oid) atoi(oids);
609
610                         fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
611                         if (fcctx.fd < 0) {
612                                 logthing(LOGTHING_ERROR,
613                                                 "Can't open large object.");
614                         } else {
615                                 fcctx.dbconn = dbconn;
616                                 read_openpgp_stream(keydb_fetchchar, &fcctx,
617                                                 &packets, 0);
618                                 parse_keys(packets, &key);
619                                 lo_close(dbconn, fcctx.fd);
620
621                                 iterfunc(ctx, key);
622                                         
623                                 free_publickey(key);
624                                 key = NULL;
625                                 free_packet_list(packets);
626                                 packets = NULL;
627                         }
628                 }
629         } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
630                 logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
631         }
632
633         PQclear(result);
634
635         return (numkeys);
636 }
637
638 /*
639  * Include the basic keydb routines.
640  */
641 #define NEED_GETFULLKEYID 1
642 #define NEED_UPDATEKEYS 1
643 #define NEED_GET_FP 1
644 #include "keydb.c"
645
646 /**
647  *      cleanupdb - De-initialize the key database.
648  *
649  *      This function should be called upon program exit to allow the DB to
650  *      cleanup after itself.
651  */
652 static void pg_cleanupdb(struct onak_dbctx *dbctx)
653 {
654         PGconn *dbconn = (PGconn *) dbctx->priv;
655
656         PQfinish(dbconn);
657         dbconn = NULL;
658
659         free(dbctx);
660 }
661
662 /**
663  *      initdb - Initialize the key database.
664  *
665  *      This function should be called before any of the other functions in
666  *      this file are called in order to allow the DB to be initialized ready
667  *      for access.
668  */
669 struct onak_dbctx *keydb_pg_init(bool readonly)
670 {
671         struct onak_dbctx *dbctx;
672         PGconn *dbconn;
673
674         dbctx = malloc(sizeof(struct onak_dbctx));
675         if (dbctx == NULL) {
676                 return NULL;
677         }
678
679         dbconn = PQsetdbLogin(config.pg_dbhost, // host
680                         NULL, // port
681                         NULL, // options
682                         NULL, // tty
683                         config.pg_dbname, // database
684                         config.pg_dbuser,  //login
685                         config.pg_dbpass); // password
686
687         if (PQstatus(dbconn) == CONNECTION_BAD) {
688                 logthing(LOGTHING_CRITICAL, "Connection to database failed.");
689                 logthing(LOGTHING_CRITICAL, "%s", PQerrorMessage(dbconn));
690                 PQfinish(dbconn);
691                 dbconn = NULL;
692                 exit(1);
693         }
694
695         dbctx->priv = dbconn;
696
697         dbctx->cleanupdb                = pg_cleanupdb;
698         dbctx->starttrans               = pg_starttrans;
699         dbctx->endtrans                 = pg_endtrans;
700         dbctx->fetch_key_id             = pg_fetch_key_id;
701         dbctx->fetch_key_fp             = generic_fetch_key_fp;
702         dbctx->fetch_key_text           = pg_fetch_key_text;
703         dbctx->store_key                = pg_store_key;
704         dbctx->update_keys              = generic_update_keys;
705         dbctx->delete_key               = pg_delete_key;
706         dbctx->getkeysigs               = pg_getkeysigs;
707         dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
708         dbctx->keyid2uid                = pg_keyid2uid;
709         dbctx->getfullkeyid             = generic_getfullkeyid;
710         dbctx->iterate_keys             = pg_iterate_keys;
711
712         return dbctx;
713 }