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
9 #include <postgresql/libpq-fe.h>
10 #include <postgresql/libpq/libpq-fs.h>
12 #include <sys/types.h>
24 #include "decodekey.h"
25 #include "keystructs.h"
28 #include "onak-conf.h"
32 * dbconn - our connection to the database.
34 static PGconn *dbconn = NULL;
37 * keydb_fetchchar - Fetches a char from a file.
39 static int keydb_fetchchar(void *fd, size_t count, unsigned char *c)
41 return (!lo_read(dbconn, *(int *) fd, (char *) c, count));
45 * keydb_putchar - Puts a char to a file.
47 static int keydb_putchar(void *fd, size_t count, unsigned char *c)
49 return !(lo_write(dbconn, *(int *) fd, (char *) c, count));
53 * initdb - Initialize the key database.
55 * This function should be called before any of the other functions in
56 * this file are called in order to allow the DB to be initialized ready
59 void initdb(bool readonly)
61 dbconn = PQsetdbLogin(config.pg_dbhost, // host
65 config.pg_dbname, // database
66 config.pg_dbuser, //login
67 config.pg_dbpass); // password
69 if (PQstatus(dbconn) == CONNECTION_BAD) {
70 logthing(LOGTHING_CRITICAL, "Connection to database failed.");
71 logthing(LOGTHING_CRITICAL, "%s", PQerrorMessage(dbconn));
79 * cleanupdb - De-initialize the key database.
81 * This function should be called upon program exit to allow the DB to
82 * cleanup after itself.
91 * starttrans - Start a transaction.
93 * Start a transaction. Intended to be used if we're about to perform many
94 * operations on the database to help speed it all up, or if we want
95 * something to only succeed if all relevant operations are successful.
99 PGresult *result = NULL;
101 result = PQexec(dbconn, "BEGIN");
108 * endtrans - End a transaction.
110 * Ends a transaction.
114 PGresult *result = NULL;
116 result = PQexec(dbconn, "COMMIT");
123 * fetch_key - Given a keyid fetch the key from storage.
124 * @keyid: The keyid to fetch.
125 * @publickey: A pointer to a structure to return the key in.
126 * @intrans: If we're already in a transaction.
128 * We use the hex representation of the keyid as the filename to fetch the
129 * key from. The key is stored in the file as a binary OpenPGP stream of
130 * packets, so we can just use read_openpgp_stream() to read the packets
131 * in and then parse_keys() to parse the packets into a publickey
134 int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
137 struct openpgp_packet_list *packets = NULL;
138 PGresult *result = NULL;
140 char statement[1024];
147 result = PQexec(dbconn, "BEGIN");
151 if (keyid > 0xFFFFFFFF) {
152 snprintf(statement, 1023,
153 "SELECT keydata FROM onak_keys WHERE keyid = '%llX'",
156 snprintf(statement, 1023,
157 "SELECT keydata FROM onak_keys WHERE keyid "
161 result = PQexec(dbconn, statement);
163 if (PQresultStatus(result) == PGRES_TUPLES_OK) {
164 numkeys = PQntuples(result);
165 for (i = 0; i < numkeys && numkeys <= config.maxkeys; i++) {
166 oids = PQgetvalue(result, i, 0);
167 key_oid = (Oid) atoi(oids);
169 fd = lo_open(dbconn, key_oid, INV_READ);
171 logthing(LOGTHING_ERROR,
172 "Can't open large object.");
174 read_openpgp_stream(keydb_fetchchar, &fd,
176 parse_keys(packets, publickey);
177 lo_close(dbconn, fd);
178 free_packet_list(packets);
182 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
183 logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
189 result = PQexec(dbconn, "COMMIT");
196 * fetch_key_text - Trys to find the keys that contain the supplied text.
197 * @search: The text to search for.
198 * @publickey: A pointer to a structure to return the key in.
200 * This function searches for the supplied text and returns the keys that
203 int fetch_key_text(const char *search, struct openpgp_publickey **publickey)
205 struct openpgp_packet_list *packets = NULL;
206 PGresult *result = NULL;
208 char statement[1024];
213 char *newsearch = NULL;
215 result = PQexec(dbconn, "BEGIN");
218 newsearch = malloc(strlen(search) * 2 + 1);
219 memset(newsearch, 0, strlen(search) * 2 + 1);
220 PQescapeString(newsearch, search, strlen(search));
221 snprintf(statement, 1023,
222 "SELECT DISTINCT onak_keys.keydata FROM onak_keys, "
223 "onak_uids WHERE onak_keys.keyid = onak_uids.keyid "
224 "AND onak_uids.uid LIKE '%%%s%%'",
226 result = PQexec(dbconn, statement);
230 if (PQresultStatus(result) == PGRES_TUPLES_OK) {
231 numkeys = PQntuples(result);
232 for (i = 0; i < numkeys && numkeys <= config.maxkeys; i++) {
233 oids = PQgetvalue(result, i, 0);
234 key_oid = (Oid) atoi(oids);
236 fd = lo_open(dbconn, key_oid, INV_READ);
238 logthing(LOGTHING_ERROR,
239 "Can't open large object.");
241 read_openpgp_stream(keydb_fetchchar, &fd,
244 parse_keys(packets, publickey);
245 lo_close(dbconn, fd);
246 free_packet_list(packets);
250 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
251 logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
256 result = PQexec(dbconn, "COMMIT");
262 * store_key - Takes a key and stores it.
263 * @publickey: A pointer to the public key to store.
264 * @intrans: If we're already in a transaction.
265 * @update: If true the key exists and should be updated.
267 * Again we just use the hex representation of the keyid as the filename
268 * to store the key to. We flatten the public key to a list of OpenPGP
269 * packets and then use write_openpgp_stream() to write the stream out to
270 * the file. If update is true then we delete the old key first, otherwise
271 * we trust that it doesn't exist.
273 int store_key(struct openpgp_publickey *publickey, bool intrans, bool update)
275 struct openpgp_packet_list *packets = NULL;
276 struct openpgp_packet_list *list_end = NULL;
277 struct openpgp_publickey *next = NULL;
278 struct openpgp_signedpacket_list *curuid = NULL;
279 PGresult *result = NULL;
280 char statement[1024];
284 char *primary = NULL;
285 char *safeuid = NULL;
289 result = PQexec(dbconn, "BEGIN");
294 * Delete the key if we already have it.
296 * TODO: Can we optimize this perhaps? Possibly when other data is
297 * involved as well? I suspect this is easiest and doesn't make a lot
298 * of difference though - the largest chunk of data is the keydata and
299 * it definitely needs updated.
302 delete_key(get_keyid(publickey), true);
305 next = publickey->next;
306 publickey->next = NULL;
307 flatten_publickey(publickey, &packets, &list_end);
308 publickey->next = next;
310 key_oid = lo_creat(dbconn, INV_READ | INV_WRITE);
312 logthing(LOGTHING_ERROR, "Can't create key OID");
314 fd = lo_open(dbconn, key_oid, INV_WRITE);
315 write_openpgp_stream(keydb_putchar, &fd, packets);
316 lo_close(dbconn, fd);
318 free_packet_list(packets);
321 snprintf(statement, 1023,
322 "INSERT INTO onak_keys (keyid, keydata) VALUES "
324 get_keyid(publickey),
326 result = PQexec(dbconn, statement);
328 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
329 logthing(LOGTHING_ERROR, "Problem storing key in DB.");
330 logthing(LOGTHING_ERROR, "%s", PQresultErrorMessage(result));
334 uids = keyuids(publickey, &primary);
336 for (i = 0; uids[i] != NULL; i++) {
337 safeuid = malloc(strlen(uids[i]) * 2 + 1);
338 if (safeuid != NULL) {
339 memset(safeuid, 0, strlen(uids[i]) * 2 + 1);
340 PQescapeString(safeuid, uids[i],
343 snprintf(statement, 1023,
344 "INSERT INTO onak_uids "
346 "VALUES ('%llX', '%s', '%c')",
347 get_keyid(publickey),
349 (uids[i] == primary) ? 't' : 'f');
350 result = PQexec(dbconn, statement);
355 if (uids[i] != NULL) {
360 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
361 logthing(LOGTHING_ERROR,
362 "Problem storing key in DB.");
363 logthing(LOGTHING_ERROR, "%s",
364 PQresultErrorMessage(result));
367 * TODO: Check result.
375 for (curuid = publickey->uids; curuid != NULL; curuid = curuid->next) {
376 for (packets = curuid->sigs; packets != NULL;
377 packets = packets->next) {
378 snprintf(statement, 1023,
379 "INSERT INTO onak_sigs (signer, signee) "
380 "VALUES ('%llX', '%llX')",
381 sig_keyid(packets->packet),
382 get_keyid(publickey));
383 result = PQexec(dbconn, statement);
389 result = PQexec(dbconn, "COMMIT");
397 * delete_key - Given a keyid delete the key from storage.
398 * @keyid: The keyid to delete.
399 * @intrans: If we're already in a transaction.
401 * This function deletes a public key from whatever storage mechanism we
402 * are using. Returns 0 if the key existed.
404 int delete_key(uint64_t keyid, bool intrans)
406 PGresult *result = NULL;
408 char statement[1024];
414 result = PQexec(dbconn, "BEGIN");
418 snprintf(statement, 1023,
419 "SELECT keydata FROM onak_keys WHERE keyid = '%llX'",
421 result = PQexec(dbconn, statement);
423 if (PQresultStatus(result) == PGRES_TUPLES_OK) {
425 i = PQntuples(result);
427 oids = PQgetvalue(result, i-1, 0);
428 key_oid = (Oid) atoi(oids);
429 lo_unlink(dbconn, key_oid);
434 snprintf(statement, 1023,
435 "DELETE FROM onak_keys WHERE keyid = '%llX'",
437 result = PQexec(dbconn, statement);
440 snprintf(statement, 1023,
441 "DELETE FROM onak_sigs WHERE signee = '%llX'",
443 result = PQexec(dbconn, statement);
446 snprintf(statement, 1023,
447 "DELETE FROM onak_uids WHERE keyid = '%llX'",
449 result = PQexec(dbconn, statement);
450 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
451 logthing(LOGTHING_ERROR,
452 "Problem retrieving key (%llX) from DB.",
459 result = PQexec(dbconn, "COMMIT");
466 * keyid2uid - Takes a keyid and returns the primary UID for it.
467 * @keyid: The keyid to lookup.
469 char *keyid2uid(uint64_t keyid)
471 PGresult *result = NULL;
472 char statement[1024];
475 snprintf(statement, 1023,
476 "SELECT uid FROM onak_uids WHERE keyid = '%llX' AND pri = 't'",
478 result = PQexec(dbconn, statement);
481 * Technically we only expect one response to the query; a key only has
482 * one primary ID. Better to return something than nothing though.
484 * TODO: Log if we get more than one response? Needs logging framework
487 if (PQresultStatus(result) == PGRES_TUPLES_OK &&
488 PQntuples(result) >= 1) {
489 uid = strdup(PQgetvalue(result, 0, 0));
490 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
491 logthing(LOGTHING_ERROR,
492 "Problem retrieving key (%llX) from DB.",
502 * getkeysigs - Gets a linked list of the signatures on a key.
503 * @keyid: The keyid to get the sigs for.
504 * @revoked: If the key is revoked.
506 * This function gets the list of signatures on a key. Used for key
507 * indexing and doing stats bits.
509 struct ll *getkeysigs(uint64_t keyid, bool *revoked)
511 struct ll *sigs = NULL;
512 PGresult *result = NULL;
514 char statement[1024];
517 bool intrans = false;
521 result = PQexec(dbconn, "BEGIN");
525 snprintf(statement, 1023,
526 "SELECT DISTINCT signer FROM onak_sigs WHERE signee = '%llX'",
528 result = PQexec(dbconn, statement);
530 if (PQresultStatus(result) == PGRES_TUPLES_OK) {
531 numsigs = PQntuples(result);
532 for (i = 0; i < numsigs; i++) {
535 str = PQgetvalue(result, i, 0);
536 while (str[j] != 0) {
538 if (str[j] >= '0' && str[j] <= '9') {
539 signer += str[j] - '0';
541 signer += str[j] - 'A' + 10;
545 sigs = lladd(sigs, createandaddtohash(signer));
547 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
548 logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
554 result = PQexec(dbconn, "COMMIT");
559 * TODO: What do we do about revocations? We don't have the details
560 * stored in a separate table, so we'd have to grab the key and decode
561 * it, which we're trying to avoid by having a signers table.
563 if (revoked != NULL) {
571 * dumpdb - dump the key database
572 * @filenamebase: The base filename to use for the dump.
574 * Dumps the database into one or more files, which contain pure OpenPGP
575 * that can be reimported into onak or gpg. filenamebase provides a base
576 * file name for the dump; several files may be created, all of which will
577 * begin with this string and then have a unique number and a .pgp
580 int dumpdb(char *filenamebase)
586 * Include the basic keydb routines.
588 #define NEED_GETFULLKEYID 1