2 * keydb_pg.c - Routines to store and fetch keys in a PostGres database.
4 * Jonathan McDowell <noodles@earth.li>
6 * Copyright 2002 Project Purple
8 * $Id: keydb_pg.c,v 1.14 2004/03/23 12:33:47 noodles Exp $
11 #include <postgresql/libpq-fe.h>
12 #include <postgresql/libpq/libpq-fs.h>
14 #include <sys/types.h>
26 #include "decodekey.h"
27 #include "keystructs.h"
30 #include "onak-conf.h"
34 * dbconn - our connection to the database.
36 static PGconn *dbconn = NULL;
39 * keydb_fetchchar - Fetches a char from a file.
41 static int keydb_fetchchar(void *fd, size_t count, unsigned char *c)
43 return (!lo_read(dbconn, *(int *) fd, (char *) c, count));
47 * keydb_putchar - Puts a char to a file.
49 static int keydb_putchar(void *fd, size_t count, unsigned char *c)
51 return !(lo_write(dbconn, *(int *) fd, (char *) c, count));
55 * initdb - Initialize the key database.
57 * This function should be called before any of the other functions in
58 * this file are called in order to allow the DB to be initialized ready
61 void initdb(bool readonly)
63 dbconn = PQsetdbLogin(config.pg_dbhost, // host
67 config.pg_dbname, // database
68 config.pg_dbuser, //login
69 config.pg_dbpass); // password
71 if (PQstatus(dbconn) == CONNECTION_BAD) {
72 logthing(LOGTHING_CRITICAL, "Connection to database failed.");
73 logthing(LOGTHING_CRITICAL, "%s", PQerrorMessage(dbconn));
81 * cleanupdb - De-initialize the key database.
83 * This function should be called upon program exit to allow the DB to
84 * cleanup after itself.
93 * starttrans - Start a transaction.
95 * Start a transaction. Intended to be used if we're about to perform many
96 * operations on the database to help speed it all up, or if we want
97 * something to only succeed if all relevant operations are successful.
101 PGresult *result = NULL;
103 result = PQexec(dbconn, "BEGIN");
110 * endtrans - End a transaction.
112 * Ends a transaction.
116 PGresult *result = NULL;
118 result = PQexec(dbconn, "COMMIT");
125 * fetch_key - Given a keyid fetch the key from storage.
126 * @keyid: The keyid to fetch.
127 * @publickey: A pointer to a structure to return the key in.
128 * @intrans: If we're already in a transaction.
130 * We use the hex representation of the keyid as the filename to fetch the
131 * key from. The key is stored in the file as a binary OpenPGP stream of
132 * packets, so we can just use read_openpgp_stream() to read the packets
133 * in and then parse_keys() to parse the packets into a publickey
136 int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
139 struct openpgp_packet_list *packets = NULL;
140 PGresult *result = NULL;
142 char statement[1024];
149 result = PQexec(dbconn, "BEGIN");
153 if (keyid > 0xFFFFFFFF) {
154 snprintf(statement, 1023,
155 "SELECT keydata FROM onak_keys WHERE keyid = '%llX'",
158 snprintf(statement, 1023,
159 "SELECT keydata FROM onak_keys WHERE keyid "
163 result = PQexec(dbconn, statement);
165 if (PQresultStatus(result) == PGRES_TUPLES_OK) {
166 numkeys = PQntuples(result);
167 for (i = 0; i < numkeys && numkeys <= config.maxkeys; i++) {
168 oids = PQgetvalue(result, i, 0);
169 key_oid = (Oid) atoi(oids);
171 fd = lo_open(dbconn, key_oid, INV_READ);
173 logthing(LOGTHING_ERROR,
174 "Can't open large object.");
176 read_openpgp_stream(keydb_fetchchar, &fd,
178 parse_keys(packets, publickey);
179 lo_close(dbconn, fd);
180 free_packet_list(packets);
184 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
185 logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
191 result = PQexec(dbconn, "COMMIT");
198 * fetch_key_text - Trys to find the keys that contain the supplied text.
199 * @search: The text to search for.
200 * @publickey: A pointer to a structure to return the key in.
202 * This function searches for the supplied text and returns the keys that
205 int fetch_key_text(const char *search, struct openpgp_publickey **publickey)
207 struct openpgp_packet_list *packets = NULL;
208 PGresult *result = NULL;
210 char statement[1024];
215 char *newsearch = NULL;
217 result = PQexec(dbconn, "BEGIN");
220 newsearch = malloc(strlen(search) * 2 + 1);
221 memset(newsearch, 0, strlen(search) * 2 + 1);
222 PQescapeString(newsearch, search, strlen(search));
223 snprintf(statement, 1023,
224 "SELECT DISTINCT onak_keys.keydata FROM onak_keys, "
225 "onak_uids WHERE onak_keys.keyid = onak_uids.keyid "
226 "AND onak_uids.uid LIKE '%%%s%%'",
228 result = PQexec(dbconn, statement);
232 if (PQresultStatus(result) == PGRES_TUPLES_OK) {
233 numkeys = PQntuples(result);
234 for (i = 0; i < numkeys && numkeys <= config.maxkeys; i++) {
235 oids = PQgetvalue(result, i, 0);
236 key_oid = (Oid) atoi(oids);
238 fd = lo_open(dbconn, key_oid, INV_READ);
240 logthing(LOGTHING_ERROR,
241 "Can't open large object.");
243 read_openpgp_stream(keydb_fetchchar, &fd,
246 parse_keys(packets, publickey);
247 lo_close(dbconn, fd);
248 free_packet_list(packets);
252 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
253 logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
258 result = PQexec(dbconn, "COMMIT");
264 * store_key - Takes a key and stores it.
265 * @publickey: A pointer to the public key to store.
266 * @intrans: If we're already in a transaction.
267 * @update: If true the key exists and should be updated.
269 * Again we just use the hex representation of the keyid as the filename
270 * to store the key to. We flatten the public key to a list of OpenPGP
271 * packets and then use write_openpgp_stream() to write the stream out to
272 * the file. If update is true then we delete the old key first, otherwise
273 * we trust that it doesn't exist.
275 int store_key(struct openpgp_publickey *publickey, bool intrans, bool update)
277 struct openpgp_packet_list *packets = NULL;
278 struct openpgp_packet_list *list_end = NULL;
279 struct openpgp_publickey *next = NULL;
280 struct openpgp_signedpacket_list *curuid = NULL;
281 PGresult *result = NULL;
282 char statement[1024];
286 char *primary = NULL;
287 char *safeuid = NULL;
291 result = PQexec(dbconn, "BEGIN");
296 * Delete the key if we already have it.
298 * TODO: Can we optimize this perhaps? Possibly when other data is
299 * involved as well? I suspect this is easiest and doesn't make a lot
300 * of difference though - the largest chunk of data is the keydata and
301 * it definitely needs updated.
304 delete_key(get_keyid(publickey), true);
307 next = publickey->next;
308 publickey->next = NULL;
309 flatten_publickey(publickey, &packets, &list_end);
310 publickey->next = next;
312 key_oid = lo_creat(dbconn, INV_READ | INV_WRITE);
314 logthing(LOGTHING_ERROR, "Can't create key OID");
316 fd = lo_open(dbconn, key_oid, INV_WRITE);
317 write_openpgp_stream(keydb_putchar, &fd, packets);
318 lo_close(dbconn, fd);
320 free_packet_list(packets);
323 snprintf(statement, 1023,
324 "INSERT INTO onak_keys (keyid, keydata) VALUES "
326 get_keyid(publickey),
328 result = PQexec(dbconn, statement);
330 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
331 logthing(LOGTHING_ERROR, "Problem storing key in DB.");
332 logthing(LOGTHING_ERROR, "%s", PQresultErrorMessage(result));
336 uids = keyuids(publickey, &primary);
338 for (i = 0; uids[i] != NULL; i++) {
339 safeuid = malloc(strlen(uids[i]) * 2 + 1);
340 if (safeuid != NULL) {
341 memset(safeuid, 0, strlen(uids[i]) * 2 + 1);
342 PQescapeString(safeuid, uids[i],
345 snprintf(statement, 1023,
346 "INSERT INTO onak_uids "
348 "VALUES ('%llX', '%s', '%c')",
349 get_keyid(publickey),
351 (uids[i] == primary) ? 't' : 'f');
352 result = PQexec(dbconn, statement);
357 if (uids[i] != NULL) {
362 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
363 logthing(LOGTHING_ERROR,
364 "Problem storing key in DB.");
365 logthing(LOGTHING_ERROR, "%s",
366 PQresultErrorMessage(result));
369 * TODO: Check result.
377 for (curuid = publickey->uids; curuid != NULL; curuid = curuid->next) {
378 for (packets = curuid->sigs; packets != NULL;
379 packets = packets->next) {
380 snprintf(statement, 1023,
381 "INSERT INTO onak_sigs (signer, signee) "
382 "VALUES ('%llX', '%llX')",
383 sig_keyid(packets->packet),
384 get_keyid(publickey));
385 result = PQexec(dbconn, statement);
391 result = PQexec(dbconn, "COMMIT");
399 * delete_key - Given a keyid delete the key from storage.
400 * @keyid: The keyid to delete.
401 * @intrans: If we're already in a transaction.
403 * This function deletes a public key from whatever storage mechanism we
404 * are using. Returns 0 if the key existed.
406 int delete_key(uint64_t keyid, bool intrans)
408 PGresult *result = NULL;
410 char statement[1024];
416 result = PQexec(dbconn, "BEGIN");
420 snprintf(statement, 1023,
421 "SELECT keydata FROM onak_keys WHERE keyid = '%llX'",
423 result = PQexec(dbconn, statement);
425 if (PQresultStatus(result) == PGRES_TUPLES_OK) {
427 i = PQntuples(result);
429 oids = PQgetvalue(result, i-1, 0);
430 key_oid = (Oid) atoi(oids);
431 lo_unlink(dbconn, key_oid);
436 snprintf(statement, 1023,
437 "DELETE FROM onak_keys WHERE keyid = '%llX'",
439 result = PQexec(dbconn, statement);
442 snprintf(statement, 1023,
443 "DELETE FROM onak_sigs WHERE signee = '%llX'",
445 result = PQexec(dbconn, statement);
448 snprintf(statement, 1023,
449 "DELETE FROM onak_uids WHERE keyid = '%llX'",
451 result = PQexec(dbconn, statement);
452 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
453 logthing(LOGTHING_ERROR,
454 "Problem retrieving key (%llX) from DB.",
461 result = PQexec(dbconn, "COMMIT");
468 * keyid2uid - Takes a keyid and returns the primary UID for it.
469 * @keyid: The keyid to lookup.
471 char *keyid2uid(uint64_t keyid)
473 PGresult *result = NULL;
474 char statement[1024];
477 snprintf(statement, 1023,
478 "SELECT uid FROM onak_uids WHERE keyid = '%llX' AND pri = 't'",
480 result = PQexec(dbconn, statement);
483 * Technically we only expect one response to the query; a key only has
484 * one primary ID. Better to return something than nothing though.
486 * TODO: Log if we get more than one response? Needs logging framework
489 if (PQresultStatus(result) == PGRES_TUPLES_OK &&
490 PQntuples(result) >= 1) {
491 uid = strdup(PQgetvalue(result, 0, 0));
492 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
493 logthing(LOGTHING_ERROR,
494 "Problem retrieving key (%llX) from DB.",
504 * getkeysigs - Gets a linked list of the signatures on a key.
505 * @keyid: The keyid to get the sigs for.
506 * @revoked: If the key is revoked.
508 * This function gets the list of signatures on a key. Used for key
509 * indexing and doing stats bits.
511 struct ll *getkeysigs(uint64_t keyid, bool *revoked)
513 struct ll *sigs = NULL;
514 PGresult *result = NULL;
516 char statement[1024];
519 bool intrans = false;
523 result = PQexec(dbconn, "BEGIN");
527 snprintf(statement, 1023,
528 "SELECT DISTINCT signer FROM onak_sigs WHERE signee = '%llX'",
530 result = PQexec(dbconn, statement);
532 if (PQresultStatus(result) == PGRES_TUPLES_OK) {
533 numsigs = PQntuples(result);
534 for (i = 0; i < numsigs; i++) {
537 str = PQgetvalue(result, i, 0);
538 while (str[j] != 0) {
540 if (str[j] >= '0' && str[j] <= '9') {
541 signer += str[j] - '0';
543 signer += str[j] - 'A' + 10;
547 sigs = lladd(sigs, createandaddtohash(signer));
549 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
550 logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
556 result = PQexec(dbconn, "COMMIT");
561 * TODO: What do we do about revocations? We don't have the details
562 * stored in a separate table, so we'd have to grab the key and decode
563 * it, which we're trying to avoid by having a signers table.
565 if (revoked != NULL) {
573 * dumpdb - dump the key database
574 * @filenamebase: The base filename to use for the dump.
576 * Dumps the database into one or more files, which contain pure OpenPGP
577 * that can be reimported into onak or gpg. filenamebase provides a base
578 * file name for the dump; several files may be created, all of which will
579 * begin with this string and then have a unique number and a .pgp
582 int dumpdb(char *filenamebase)
588 * Include the basic keydb routines.
590 #define NEED_GETFULLKEYID 1