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