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 "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, c, count));
45 * keydb_putchar - Puts a char to a file.
47 static int keydb_putchar(void *fd, unsigned char c)
49 return !(lo_write(dbconn, *(int *) fd, &c, sizeof(c)));
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
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 fprintf(stderr, "Connection to database failed.\n");
71 fprintf(stderr, "%s\n", 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 * fetch_key - Given a keyid fetch the key from storage.
92 * @keyid: The keyid to fetch.
93 * @publickey: A pointer to a structure to return the key in.
95 * We use the hex representation of the keyid as the filename to fetch the
96 * key from. The key is stored in the file as a binary OpenPGP stream of
97 * packets, so we can just use read_openpgp_stream() to read the packets
98 * in and then parse_keys() to parse the packets into a publickey
101 int fetch_key(uint64_t keyid, struct openpgp_publickey **publickey)
103 struct openpgp_packet_list *packets = NULL;
104 PGresult *result = NULL;
106 char statement[1024];
112 result = PQexec(dbconn, "BEGIN");
115 if (keyid > 0xFFFFFFFF) {
116 snprintf(statement, 1023,
117 "SELECT keydata FROM onak_keys WHERE keyid = '%llX'",
120 snprintf(statement, 1023,
121 "SELECT keydata FROM onak_keys WHERE keyid "
125 result = PQexec(dbconn, statement);
127 if (PQresultStatus(result) == PGRES_TUPLES_OK) {
128 numkeys = PQntuples(result);
129 for (i = 0; i < numkeys && numkeys <= config.maxkeys; i++) {
130 oids = PQgetvalue(result, i, 0);
131 key_oid = (Oid) atoi(oids);
133 fd = lo_open(dbconn, key_oid, INV_READ);
135 fprintf(stderr, "Can't open large object.\n");
137 read_openpgp_stream(keydb_fetchchar, &fd,
139 parse_keys(packets, publickey);
140 lo_close(dbconn, fd);
143 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
144 fprintf(stderr, "Problem retrieving key from DB.\n");
149 result = PQexec(dbconn, "COMMIT");
155 * fetch_key_text - Trys to find the keys that contain the supplied text.
156 * @search: The text to search for.
157 * @publickey: A pointer to a structure to return the key in.
159 * This function searches for the supplied text and returns the keys that
162 int fetch_key_text(const char *search, struct openpgp_publickey **publickey)
164 struct openpgp_packet_list *packets = NULL;
165 PGresult *result = NULL;
167 char statement[1024];
172 char *dodgychar = NULL;
174 result = PQexec(dbconn, "BEGIN");
178 * TODO: We really want to use PQescapeString, but this isn't supported
179 * by the version of Postgresql in Debian Stable. Roll on Woody and for
182 dodgychar = strchr(search, '\'');
183 while (dodgychar != NULL) {
185 dodgychar = strchr(search, '\'');
187 dodgychar = strchr(search, '\\');
188 while (dodgychar != NULL) {
190 dodgychar = strchr(search, '\\');
194 snprintf(statement, 1023,
195 "SELECT DISTINCT onak_keys.keydata FROM onak_keys, "
196 "onak_uids WHERE onak_keys.keyid = onak_uids.keyid "
197 "AND onak_uids.uid LIKE '%%%s%%'",
199 result = PQexec(dbconn, statement);
201 if (PQresultStatus(result) == PGRES_TUPLES_OK) {
202 numkeys = PQntuples(result);
203 for (i = 0; i < numkeys && numkeys <= config.maxkeys; i++) {
204 oids = PQgetvalue(result, i, 0);
205 key_oid = (Oid) atoi(oids);
207 fd = lo_open(dbconn, key_oid, INV_READ);
209 fprintf(stderr, "Can't open large object.\n");
211 read_openpgp_stream(keydb_fetchchar, &fd,
213 parse_keys(packets, publickey);
214 lo_close(dbconn, fd);
217 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
218 fprintf(stderr, "Problem retrieving key from DB.\n");
223 result = PQexec(dbconn, "COMMIT");
229 * store_key - Takes a key and stores it.
230 * @publickey: A pointer to the public key to store.
232 * Again we just use the hex representation of the keyid as the filename
233 * to store the key to. We flatten the public key to a list of OpenPGP
234 * packets and then use write_openpgp_stream() to write the stream out to
237 int store_key(struct openpgp_publickey *publickey)
239 struct openpgp_packet_list *packets = NULL;
240 struct openpgp_packet_list *list_end = NULL;
241 struct openpgp_publickey *next = NULL;
242 PGresult *result = NULL;
243 char statement[1024];
247 char *primary = NULL;
248 char *dodgychar = NULL;
253 * Delete the key if we already have it.
255 * TODO: Can we optimize this perhaps? Possibly when other data is
256 * involved as well? I suspect this is easiest and doesn't make a lot
257 * of difference though - the largest chunk of data is the keydata and
258 * it definitely needs updated.
260 delete_key(get_keyid(publickey));
262 result = PQexec(dbconn, "BEGIN");
265 next = publickey->next;
266 publickey->next = NULL;
267 flatten_publickey(publickey, &packets, &list_end);
268 publickey->next = next;
270 key_oid = lo_creat(dbconn, INV_READ | INV_WRITE);
272 fprintf(stderr, "Can't create key OID\n");
274 fd = lo_open(dbconn, key_oid, INV_WRITE);
275 write_openpgp_stream(keydb_putchar, &fd, packets);
276 lo_close(dbconn, fd);
279 snprintf(statement, 1023,
280 "INSERT INTO onak_keys (keyid, keydata) VALUES "
282 get_keyid(publickey),
284 result = PQexec(dbconn, statement);
286 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
287 fprintf(stderr, "Problem storing key in DB.\n");
288 fprintf(stderr, "%s\n", PQresultErrorMessage(result));
292 uids = keyuids(publickey, &primary);
294 for (i = 0; uids[i] != NULL; i++) {
296 * TODO: We really want to use PQescapeString, but this
297 * isn't supported by the version of Postgresql in
298 * Debian Stable. Roll on Woody and for now kludge it.
300 dodgychar = strchr(uids[i], '\'');
301 while (dodgychar != NULL) {
303 dodgychar = strchr(uids[i], '\'');
305 dodgychar = strchr(uids[i], '\\');
306 while (dodgychar != NULL) {
308 dodgychar = strchr(uids[i], '\\');
311 snprintf(statement, 1023,
312 "INSERT INTO onak_uids (keyid, uid, pri) "
313 "VALUES ('%llX', '%s', '%c')",
314 get_keyid(publickey),
316 (uids[i] == primary) ? 't' : 'f');
317 result = PQexec(dbconn, statement);
319 if (PQresultStatus(result) != PGRES_COMMAND_OK) {
320 fprintf(stderr, "Problem storing key in DB.\n");
321 fprintf(stderr, "%s\n",
322 PQresultErrorMessage(result));
325 * TODO: Check result.
331 result = PQexec(dbconn, "COMMIT");
338 * delete_key - Given a keyid delete the key from storage.
339 * @keyid: The keyid to delete.
341 * This function deletes a public key from whatever storage mechanism we
342 * are using. Returns 0 if the key existed.
344 int delete_key(uint64_t keyid)
346 PGresult *result = NULL;
348 char statement[1024];
353 result = PQexec(dbconn, "BEGIN");
356 snprintf(statement, 1023,
357 "SELECT keydata FROM onak_keys WHERE keyid = '%llX'",
359 result = PQexec(dbconn, statement);
361 if (PQresultStatus(result) == PGRES_TUPLES_OK) {
363 i = PQntuples(result);
365 oids = PQgetvalue(result, i-1, 0);
366 key_oid = (Oid) atoi(oids);
367 lo_unlink(dbconn, key_oid);
372 snprintf(statement, 1023,
373 "DELETE FROM onak_keys WHERE keyid = '%llX'",
375 result = PQexec(dbconn, statement);
378 snprintf(statement, 1023,
379 "DELETE FROM onak_uids WHERE keyid = '%llX'",
381 result = PQexec(dbconn, statement);
382 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
383 fprintf(stderr, "Problem retrieving key (%llX) from DB.\n",
389 result = PQexec(dbconn, "COMMIT");
395 * keyid2uid - Takes a keyid and returns the primary UID for it.
396 * @keyid: The keyid to lookup.
398 char *keyid2uid(uint64_t keyid)
400 PGresult *result = NULL;
401 char statement[1024];
404 snprintf(statement, 1023,
405 "SELECT uid FROM onak_uids WHERE keyid = '%llX' AND pri = 't'",
407 result = PQexec(dbconn, statement);
410 * Technically we only expect one response to the query; a key only has
411 * one primary ID. Better to return something than nothing though.
413 * TODO: Log if we get more than one response? Needs logging framework
416 if (PQresultStatus(result) == PGRES_TUPLES_OK &&
417 PQntuples(result) >= 1) {
418 uid = strdup(PQgetvalue(result, 0, 0));
419 } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
420 fprintf(stderr, "Problem retrieving key (%llX) from DB.\n",
430 * Include the basic keydb routines.
432 #define NEED_GETKEYSIGS 1