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 <libpq-fe.h>
13 //#include <libpq/libpq-fs.h>
14 #include <sys/types.h>
26 #include "decodekey.h"
27 #include "keystructs.h"
29 #include "onak-conf.h"
33 * dbconn - our connection to the database.
35 static PGconn *dbconn = NULL;
38 * keydb_fetchchar - Fetches a char from a file.
40 static int keydb_fetchchar(void *fd, size_t count, unsigned char *c)
42 return (!lo_read(dbconn, *(int *) fd, c, count));
46 * keydb_putchar - Puts a char to a file.
48 static int keydb_putchar(void *fd, size_t count, unsigned char *c)
50 return !(lo_write(dbconn, *(int *) fd, c, count));
54 * initdb - Initialize the key database.
56 * This function should be called before any of the other functions in
57 * this file are called in order to allow the DB to be initialized ready
62 dbconn = PQsetdbLogin(config.pg_dbhost, // host
66 config.pg_dbname, // database
67 config.pg_dbuser, //login
68 config.pg_dbpass); // password
70 if (PQstatus(dbconn) == CONNECTION_BAD) {
71 fprintf(stderr, "Connection to database failed.\n");
72 fprintf(stderr, "%s\n", PQerrorMessage(dbconn));
80 * cleanupdb - De-initialize the key database.
82 * This function should be called upon program exit to allow the DB to
83 * cleanup after itself.
92 * starttrans - Start a transaction.
94 * Start a transaction. Intended to be used if we're about to perform many
95 * operations on the database to help speed it all up, or if we want
96 * something to only succeed if all relevant operations are successful.
100 PGresult *result = NULL;
102 result = PQexec(dbconn, "BEGIN");
109 * endtrans - End a transaction.
111 * Ends a transaction.
115 PGresult *result = NULL;
117 result = PQexec(dbconn, "COMMIT");
124 * fetch_key - Given a keyid fetch the key from storage.
125 * @keyid: The keyid to fetch.
126 * @publickey: A pointer to a structure to return the key in.
127 * @intrans: If we're already in a transaction.
129 * We use the hex representation of the keyid as the filename to fetch the
130 * key from. The key is stored in the file as a binary OpenPGP stream of
131 * packets, so we can just use read_openpgp_stream() to read the packets
132 * in and then parse_keys() to parse the packets into a publickey
135 int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
138 struct openpgp_packet_list *packets = NULL;
139 PGresult *result = NULL;
141 char statement[1024];
148 result = PQexec(dbconn, "BEGIN");
152 if (keyid > 0xFFFFFFFF) {
153 snprintf(statement, 1023,
154 "SELECT keydata FROM onak_keys WHERE keyid = '%llX'",
157 snprintf(statement, 1023,
158 "SELECT keydata FROM onak_keys WHERE keyid "
162 result = PQexec(dbconn, statement);
164 if (PQresultStatus(result) == PGRES_TUPLES_OK) {
165 numkeys = PQntuples(result);
166 for (i = 0; i < numkeys && numkeys <= config.maxkeys; i++) {
167 oids = PQgetvalue(result, i, 0);
168 key_oid = (Oid) atoi(oids);
170 fd = lo_open(dbconn, key_oid, INV_READ);
172 fprintf(stderr, "Can't open large object.\n");
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 fprintf(stderr, "Problem retrieving key from DB.\n");
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 fprintf(stderr, "Can't open large object.\n");
240 read_openpgp_stream(keydb_fetchchar, &fd,
242 parse_keys(packets, publickey);
243 lo_close(dbconn, fd);
244 free_packet_list(packets);
248 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
249 fprintf(stderr, "Problem retrieving key from DB.\n");
254 result = PQexec(dbconn, "COMMIT");
260 * store_key - Takes a key and stores it.
261 * @publickey: A pointer to the public key to store.
262 * @intrans: If we're already in a transaction.
263 * @update: If true the key exists and should be updated.
265 * Again we just use the hex representation of the keyid as the filename
266 * to store the key to. We flatten the public key to a list of OpenPGP
267 * packets and then use write_openpgp_stream() to write the stream out to
268 * the file. If update is true then we delete the old key first, otherwise
269 * we trust that it doesn't exist.
271 int store_key(struct openpgp_publickey *publickey, bool intrans, bool update)
273 struct openpgp_packet_list *packets = NULL;
274 struct openpgp_packet_list *list_end = NULL;
275 struct openpgp_publickey *next = NULL;
276 struct openpgp_signedpacket_list *curuid = NULL;
277 PGresult *result = NULL;
278 char statement[1024];
282 char *primary = NULL;
283 char *safeuid = NULL;
287 result = PQexec(dbconn, "BEGIN");
292 * Delete the key if we already have it.
294 * TODO: Can we optimize this perhaps? Possibly when other data is
295 * involved as well? I suspect this is easiest and doesn't make a lot
296 * of difference though - the largest chunk of data is the keydata and
297 * it definitely needs updated.
300 delete_key(get_keyid(publickey), true);
303 next = publickey->next;
304 publickey->next = NULL;
305 flatten_publickey(publickey, &packets, &list_end);
306 publickey->next = next;
308 key_oid = lo_creat(dbconn, INV_READ | INV_WRITE);
310 fprintf(stderr, "Can't create key OID\n");
312 fd = lo_open(dbconn, key_oid, INV_WRITE);
313 write_openpgp_stream(keydb_putchar, &fd, packets);
314 lo_close(dbconn, fd);
316 free_packet_list(packets);
319 snprintf(statement, 1023,
320 "INSERT INTO onak_keys (keyid, keydata) VALUES "
322 get_keyid(publickey),
324 result = PQexec(dbconn, statement);
326 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
327 fprintf(stderr, "Problem storing key in DB.\n");
328 fprintf(stderr, "%s\n", PQresultErrorMessage(result));
332 uids = keyuids(publickey, &primary);
334 for (i = 0; uids[i] != NULL; i++) {
335 safeuid = malloc(strlen(uids[i]) * 2 + 1);
336 if (safeuid != NULL) {
337 memset(safeuid, 0, strlen(uids[i]) * 2 + 1);
338 PQescapeString(safeuid, uids[i],
341 snprintf(statement, 1023,
342 "INSERT INTO onak_uids "
344 "VALUES ('%llX', '%s', '%c')",
345 get_keyid(publickey),
347 (uids[i] == primary) ? 't' : 'f');
348 result = PQexec(dbconn, statement);
353 if (uids[i] != NULL) {
358 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
359 fprintf(stderr, "Problem storing key in DB.\n");
360 fprintf(stderr, "%s\n",
361 PQresultErrorMessage(result));
364 * TODO: Check result.
372 for (curuid = publickey->uids; curuid != NULL; curuid = curuid->next) {
373 for (packets = curuid->sigs; packets != NULL;
374 packets = packets->next) {
375 snprintf(statement, 1023,
376 "INSERT INTO onak_sigs (signer, signee) "
377 "VALUES ('%llX', '%llX')",
378 sig_keyid(packets->packet),
379 get_keyid(publickey));
380 result = PQexec(dbconn, statement);
386 result = PQexec(dbconn, "COMMIT");
394 * delete_key - Given a keyid delete the key from storage.
395 * @keyid: The keyid to delete.
396 * @intrans: If we're already in a transaction.
398 * This function deletes a public key from whatever storage mechanism we
399 * are using. Returns 0 if the key existed.
401 int delete_key(uint64_t keyid, bool intrans)
403 PGresult *result = NULL;
405 char statement[1024];
411 result = PQexec(dbconn, "BEGIN");
415 snprintf(statement, 1023,
416 "SELECT keydata FROM onak_keys WHERE keyid = '%llX'",
418 result = PQexec(dbconn, statement);
420 if (PQresultStatus(result) == PGRES_TUPLES_OK) {
422 i = PQntuples(result);
424 oids = PQgetvalue(result, i-1, 0);
425 key_oid = (Oid) atoi(oids);
426 lo_unlink(dbconn, key_oid);
431 snprintf(statement, 1023,
432 "DELETE FROM onak_keys WHERE keyid = '%llX'",
434 result = PQexec(dbconn, statement);
437 snprintf(statement, 1023,
438 "DELETE FROM onak_sigs WHERE signee = '%llX'",
440 result = PQexec(dbconn, statement);
443 snprintf(statement, 1023,
444 "DELETE FROM onak_uids WHERE keyid = '%llX'",
446 result = PQexec(dbconn, statement);
447 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
448 fprintf(stderr, "Problem retrieving key (%llX) from DB.\n",
455 result = PQexec(dbconn, "COMMIT");
462 * keyid2uid - Takes a keyid and returns the primary UID for it.
463 * @keyid: The keyid to lookup.
465 char *keyid2uid(uint64_t keyid)
467 PGresult *result = NULL;
468 char statement[1024];
471 snprintf(statement, 1023,
472 "SELECT uid FROM onak_uids WHERE keyid = '%llX' AND pri = 't'",
474 result = PQexec(dbconn, statement);
477 * Technically we only expect one response to the query; a key only has
478 * one primary ID. Better to return something than nothing though.
480 * TODO: Log if we get more than one response? Needs logging framework
483 if (PQresultStatus(result) == PGRES_TUPLES_OK &&
484 PQntuples(result) >= 1) {
485 uid = strdup(PQgetvalue(result, 0, 0));
486 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
487 fprintf(stderr, "Problem retrieving key (%llX) from DB.\n",
497 * getkeysigs - Gets a linked list of the signatures on a key.
498 * @keyid: The keyid to get the sigs for.
500 * This function gets the list of signatures on a key. Used for key
501 * indexing and doing stats bits.
503 struct ll *getkeysigs(uint64_t keyid)
505 struct ll *sigs = NULL;
506 PGresult *result = NULL;
508 char statement[1024];
511 bool intrans = false;
515 result = PQexec(dbconn, "BEGIN");
519 snprintf(statement, 1023,
520 "SELECT DISTINCT signer FROM onak_sigs WHERE signee = '%llX'",
522 result = PQexec(dbconn, statement);
524 if (PQresultStatus(result) == PGRES_TUPLES_OK) {
525 numsigs = PQntuples(result);
526 for (i = 0; i < numsigs; i++) {
529 str = PQgetvalue(result, i, 0);
530 while (str[j] != 0) {
532 if (str[j] >= '0' && str[j] <= '9') {
533 signer += str[j] - '0';
535 signer += str[j] - 'A' + 10;
539 sigs = lladd(sigs, createandaddtohash(signer));
541 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
542 fprintf(stderr, "Problem retrieving key from DB.\n");
548 result = PQexec(dbconn, "COMMIT");
555 * Include the basic keydb routines.
557 #define NEED_GETFULLKEYID 1