/*
* keydb_pg.c - Routines to store and fetch keys in a PostGres database.
*
- * Jonathan McDowell <noodles@earth.li>
+ * Copyright 2002-2004 Jonathan McDowell <noodles@earth.li>
*
- * Copyright 2002-2004 Project Purple
+ * This program is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <https://www.gnu.org/licenses/>.
*/
#include <postgresql/libpq-fe.h>
#include "onak-conf.h"
#include "parsekey.h"
-/**
- * dbconn - our connection to the database.
- */
-static PGconn *dbconn = NULL;
+struct pg_fc_ctx {
+ PGconn *dbconn;
+ int fd;
+};
/**
* keydb_fetchchar - Fetches a char from a file.
*/
-static int keydb_fetchchar(void *fd, size_t count, unsigned char *c)
+static int keydb_fetchchar(void *_ctx, size_t count, void *c)
{
- return (!lo_read(dbconn, *(int *) fd, (char *) c, count));
-}
+ struct pg_fc_ctx *ctx = (struct pg_fc_ctx *) _ctx;
-/**
- * keydb_putchar - Puts a char to a file.
- */
-static int keydb_putchar(void *fd, size_t count, unsigned char *c)
-{
- return !(lo_write(dbconn, *(int *) fd, (char *) c, count));
+ return (!lo_read(ctx->dbconn, ctx->fd, (char *) c, count));
}
/**
- * initdb - Initialize the key database.
- *
- * This function should be called before any of the other functions in
- * this file are called in order to allow the DB to be initialized ready
- * for access.
+ * keydb_putchar - Puts a char to a file.
*/
-static void pg_initdb(bool readonly)
+static int keydb_putchar(void *_ctx, size_t count, void *c)
{
- dbconn = PQsetdbLogin(config.pg_dbhost, // host
- NULL, // port
- NULL, // options
- NULL, // tty
- config.pg_dbname, // database
- config.pg_dbuser, //login
- config.pg_dbpass); // password
-
- if (PQstatus(dbconn) == CONNECTION_BAD) {
- logthing(LOGTHING_CRITICAL, "Connection to database failed.");
- logthing(LOGTHING_CRITICAL, "%s", PQerrorMessage(dbconn));
- PQfinish(dbconn);
- dbconn = NULL;
- exit(1);
- }
-}
+ struct pg_fc_ctx *ctx = (struct pg_fc_ctx *) _ctx;
-/**
- * cleanupdb - De-initialize the key database.
- *
- * This function should be called upon program exit to allow the DB to
- * cleanup after itself.
- */
-static void pg_cleanupdb(void)
-{
- PQfinish(dbconn);
- dbconn = NULL;
+ return !(lo_write(ctx->dbconn, ctx->fd, (char *) c, count));
}
/**
* operations on the database to help speed it all up, or if we want
* something to only succeed if all relevant operations are successful.
*/
-static bool pg_starttrans(void)
+static bool pg_starttrans(struct onak_dbctx *dbctx)
{
+ PGconn *dbconn = (PGconn *) dbctx->priv;
PGresult *result = NULL;
result = PQexec(dbconn, "BEGIN");
*
* Ends a transaction.
*/
-static void pg_endtrans(void)
+static void pg_endtrans(struct onak_dbctx *dbctx)
{
+ PGconn *dbconn = (PGconn *) dbctx->priv;
PGresult *result = NULL;
result = PQexec(dbconn, "COMMIT");
}
/**
- * fetch_key - Given a keyid fetch the key from storage.
+ * fetch_key_id - Given a keyid fetch the key from storage.
* @keyid: The keyid to fetch.
* @publickey: A pointer to a structure to return the key in.
* @intrans: If we're already in a transaction.
* in and then parse_keys() to parse the packets into a publickey
* structure.
*/
-static int pg_fetch_key(uint64_t keyid, struct openpgp_publickey **publickey,
+static int pg_fetch_key_id(struct onak_dbctx *dbctx,
+ uint64_t keyid,
+ struct openpgp_publickey **publickey,
bool intrans)
{
struct openpgp_packet_list *packets = NULL;
+ PGconn *dbconn = (PGconn *) dbctx->priv;
PGresult *result = NULL;
char *oids = NULL;
char statement[1024];
- int fd = -1;
int i = 0;
int numkeys = 0;
Oid key_oid;
+ struct pg_fc_ctx fcctx;
if (!intrans) {
result = PQexec(dbconn, "BEGIN");
if (keyid > 0xFFFFFFFF) {
snprintf(statement, 1023,
- "SELECT keydata FROM onak_keys WHERE keyid = '%llX'",
+ "SELECT keydata FROM onak_keys WHERE keyid = '%"
+ PRIX64 "'",
keyid);
} else {
snprintf(statement, 1023,
"SELECT keydata FROM onak_keys WHERE keyid "
- "LIKE '%%%llX'",
+ "LIKE '%%%" PRIX64 "'",
keyid);
}
result = PQexec(dbconn, statement);
oids = PQgetvalue(result, i, 0);
key_oid = (Oid) atoi(oids);
- fd = lo_open(dbconn, key_oid, INV_READ);
- if (fd < 0) {
+ fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
+ if (fcctx.fd < 0) {
logthing(LOGTHING_ERROR,
"Can't open large object.");
} else {
- read_openpgp_stream(keydb_fetchchar, &fd,
+ fcctx.dbconn = dbconn;
+ read_openpgp_stream(keydb_fetchchar, &fcctx,
&packets, 0);
parse_keys(packets, publickey);
- lo_close(dbconn, fd);
+ lo_close(dbconn, fcctx.fd);
free_packet_list(packets);
packets = NULL;
}
* This function searches for the supplied text and returns the keys that
* contain it.
*/
-static int pg_fetch_key_text(const char *search,
+static int pg_fetch_key_text(struct onak_dbctx *dbctx,
+ const char *search,
struct openpgp_publickey **publickey)
{
struct openpgp_packet_list *packets = NULL;
+ PGconn *dbconn = (PGconn *) dbctx->priv;
PGresult *result = NULL;
char *oids = NULL;
char statement[1024];
- int fd = -1;
int i = 0;
int numkeys = 0;
Oid key_oid;
char *newsearch = NULL;
+ struct pg_fc_ctx fcctx;
result = PQexec(dbconn, "BEGIN");
PQclear(result);
newsearch = malloc(strlen(search) * 2 + 1);
memset(newsearch, 0, strlen(search) * 2 + 1);
- PQescapeString(newsearch, search, strlen(search));
+ PQescapeStringConn(dbconn, newsearch, search, strlen(search), NULL);
snprintf(statement, 1023,
"SELECT DISTINCT onak_keys.keydata FROM onak_keys, "
"onak_uids WHERE onak_keys.keyid = onak_uids.keyid "
oids = PQgetvalue(result, i, 0);
key_oid = (Oid) atoi(oids);
- fd = lo_open(dbconn, key_oid, INV_READ);
- if (fd < 0) {
+ fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
+ if (fcctx.fd < 0) {
logthing(LOGTHING_ERROR,
"Can't open large object.");
} else {
- read_openpgp_stream(keydb_fetchchar, &fd,
+ fcctx.dbconn = dbconn;
+ read_openpgp_stream(keydb_fetchchar, &fcctx,
&packets,
0);
parse_keys(packets, publickey);
- lo_close(dbconn, fd);
+ lo_close(dbconn, fcctx.fd);
free_packet_list(packets);
packets = NULL;
}
return (numkeys);
}
+/**
+ * delete_key - Given a keyid delete the key from storage.
+ * @fp: The fingerprint of the key to delete.
+ * @intrans: If we're already in a transaction.
+ *
+ * This function deletes a public key from whatever storage mechanism we
+ * are using. Returns 0 if the key existed.
+ */
+static int pg_delete_key(struct onak_dbctx *dbctx,
+ struct openpgp_fingerprint *fp, bool intrans)
+{
+ PGconn *dbconn = (PGconn *) dbctx->priv;
+ PGresult *result = NULL;
+ char *oids = NULL;
+ char statement[1024];
+ int found = 1;
+ int i;
+ Oid key_oid;
+ uint64_t keyid;
+
+ if (!intrans) {
+ result = PQexec(dbconn, "BEGIN");
+ PQclear(result);
+ }
+
+ keyid = fingerprint2keyid(fp);
+
+ snprintf(statement, 1023,
+ "SELECT keydata FROM onak_keys WHERE keyid = '%"
+ PRIX64 "'",
+ keyid);
+ result = PQexec(dbconn, statement);
+
+ if (PQresultStatus(result) == PGRES_TUPLES_OK) {
+ found = 0;
+ i = PQntuples(result);
+ while (i > 0) {
+ oids = PQgetvalue(result, i-1, 0);
+ key_oid = (Oid) atoi(oids);
+ lo_unlink(dbconn, key_oid);
+ i--;
+ }
+ PQclear(result);
+
+ snprintf(statement, 1023,
+ "DELETE FROM onak_keys WHERE keyid = '%" PRIX64 "'",
+ keyid);
+ result = PQexec(dbconn, statement);
+ PQclear(result);
+
+ snprintf(statement, 1023,
+ "DELETE FROM onak_sigs WHERE signee = '%" PRIX64 "'",
+ keyid);
+ result = PQexec(dbconn, statement);
+ PQclear(result);
+
+ snprintf(statement, 1023,
+ "DELETE FROM onak_uids WHERE keyid = '%" PRIX64 "'",
+ keyid);
+ result = PQexec(dbconn, statement);
+ } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
+ logthing(LOGTHING_ERROR,
+ "Problem retrieving key (%" PRIX64
+ ") from DB.",
+ keyid);
+ }
+
+ PQclear(result);
+
+ if (!intrans) {
+ result = PQexec(dbconn, "COMMIT");
+ PQclear(result);
+ }
+ return (found);
+}
+
/**
* store_key - Takes a key and stores it.
* @publickey: A pointer to the public key to store.
* the file. If update is true then we delete the old key first, otherwise
* we trust that it doesn't exist.
*/
-static int pg_store_key(struct openpgp_publickey *publickey, bool intrans,
+static int pg_store_key(struct onak_dbctx *dbctx,
+ struct openpgp_publickey *publickey, bool intrans,
bool update)
{
struct openpgp_packet_list *packets = NULL;
struct openpgp_packet_list *list_end = NULL;
struct openpgp_publickey *next = NULL;
struct openpgp_signedpacket_list *curuid = NULL;
+ PGconn *dbconn = (PGconn *) dbctx->priv;
PGresult *result = NULL;
char statement[1024];
Oid key_oid;
- int fd;
char **uids = NULL;
char *primary = NULL;
char *safeuid = NULL;
int i;
+ uint64_t keyid;
+ struct pg_fc_ctx fcctx;
+ struct openpgp_fingerprint fp;
if (!intrans) {
result = PQexec(dbconn, "BEGIN");
PQclear(result);
}
+ if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
+ logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
+ return 0;
+ }
+
/*
* Delete the key if we already have it.
*
* it definitely needs updated.
*/
if (update) {
- pg_delete_key(get_keyid(publickey), true);
+ get_fingerprint(publickey->publickey, &fp);
+ pg_delete_key(dbctx, &fp, true);
}
next = publickey->next;
if (key_oid == 0) {
logthing(LOGTHING_ERROR, "Can't create key OID");
} else {
- fd = lo_open(dbconn, key_oid, INV_WRITE);
- write_openpgp_stream(keydb_putchar, &fd, packets);
- lo_close(dbconn, fd);
+ fcctx.fd = lo_open(dbconn, key_oid, INV_WRITE);
+ fcctx.dbconn = dbconn;
+ write_openpgp_stream(keydb_putchar, &fcctx, packets);
+ lo_close(dbconn, fcctx.fd);
}
free_packet_list(packets);
packets = NULL;
snprintf(statement, 1023,
"INSERT INTO onak_keys (keyid, keydata) VALUES "
- "('%llX', '%d')",
- get_keyid(publickey),
+ "('%" PRIX64 "', '%d')",
+ keyid,
key_oid);
result = PQexec(dbconn, statement);
safeuid = malloc(strlen(uids[i]) * 2 + 1);
if (safeuid != NULL) {
memset(safeuid, 0, strlen(uids[i]) * 2 + 1);
- PQescapeString(safeuid, uids[i],
- strlen(uids[i]));
+ PQescapeStringConn(dbconn, safeuid, uids[i],
+ strlen(uids[i]), NULL);
snprintf(statement, 1023,
"INSERT INTO onak_uids "
"(keyid, uid, pri) "
- "VALUES ('%llX', '%s', '%c')",
- get_keyid(publickey),
+ "VALUES ('%" PRIX64 "', '%s', '%c')",
+ keyid,
safeuid,
(uids[i] == primary) ? 't' : 'f');
result = PQexec(dbconn, statement);
packets = packets->next) {
snprintf(statement, 1023,
"INSERT INTO onak_sigs (signer, signee) "
- "VALUES ('%llX', '%llX')",
+ "VALUES ('%" PRIX64 "', '%" PRIX64 "')",
sig_keyid(packets->packet),
- get_keyid(publickey));
+ keyid);
result = PQexec(dbconn, statement);
PQclear(result);
}
return 0;
}
-/**
- * delete_key - Given a keyid delete the key from storage.
- * @keyid: The keyid to delete.
- * @intrans: If we're already in a transaction.
- *
- * This function deletes a public key from whatever storage mechanism we
- * are using. Returns 0 if the key existed.
- */
-static int pg_delete_key(uint64_t keyid, bool intrans)
-{
- PGresult *result = NULL;
- char *oids = NULL;
- char statement[1024];
- int found = 1;
- int i;
- Oid key_oid;
-
- if (!intrans) {
- result = PQexec(dbconn, "BEGIN");
- PQclear(result);
- }
-
- snprintf(statement, 1023,
- "SELECT keydata FROM onak_keys WHERE keyid = '%llX'",
- keyid);
- result = PQexec(dbconn, statement);
-
- if (PQresultStatus(result) == PGRES_TUPLES_OK) {
- found = 0;
- i = PQntuples(result);
- while (i > 0) {
- oids = PQgetvalue(result, i-1, 0);
- key_oid = (Oid) atoi(oids);
- lo_unlink(dbconn, key_oid);
- i--;
- }
- PQclear(result);
-
- snprintf(statement, 1023,
- "DELETE FROM onak_keys WHERE keyid = '%llX'",
- keyid);
- result = PQexec(dbconn, statement);
- PQclear(result);
-
- snprintf(statement, 1023,
- "DELETE FROM onak_sigs WHERE signee = '%llX'",
- keyid);
- result = PQexec(dbconn, statement);
- PQclear(result);
-
- snprintf(statement, 1023,
- "DELETE FROM onak_uids WHERE keyid = '%llX'",
- keyid);
- result = PQexec(dbconn, statement);
- } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
- logthing(LOGTHING_ERROR,
- "Problem retrieving key (%llX) from DB.",
- keyid);
- }
-
- PQclear(result);
-
- if (!intrans) {
- result = PQexec(dbconn, "COMMIT");
- PQclear(result);
- }
- return (found);
-}
-
/**
* keyid2uid - Takes a keyid and returns the primary UID for it.
* @keyid: The keyid to lookup.
*/
-static char *pg_keyid2uid(uint64_t keyid)
+static char *pg_keyid2uid(struct onak_dbctx *dbctx, uint64_t keyid)
{
+ PGconn *dbconn = (PGconn *) dbctx->priv;
PGresult *result = NULL;
char statement[1024];
char *uid = NULL;
snprintf(statement, 1023,
- "SELECT uid FROM onak_uids WHERE keyid = '%llX' AND pri = 't'",
+ "SELECT uid FROM onak_uids WHERE keyid = '%" PRIX64
+ "' AND pri = 't'",
keyid);
result = PQexec(dbconn, statement);
uid = strdup(PQgetvalue(result, 0, 0));
} else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
logthing(LOGTHING_ERROR,
- "Problem retrieving key (%llX) from DB.",
+ "Problem retrieving key (%" PRIX64
+ ") from DB.",
keyid);
}
* This function gets the list of signatures on a key. Used for key
* indexing and doing stats bits.
*/
-static struct ll *pg_getkeysigs(uint64_t keyid, bool *revoked)
+static struct ll *pg_getkeysigs(struct onak_dbctx *dbctx,
+ uint64_t keyid, bool *revoked)
{
struct ll *sigs = NULL;
+ PGconn *dbconn = (PGconn *) dbctx->priv;
PGresult *result = NULL;
uint64_t signer;
char statement[1024];
}
snprintf(statement, 1023,
- "SELECT DISTINCT signer FROM onak_sigs WHERE signee = '%llX'",
+ "SELECT DISTINCT signer FROM onak_sigs WHERE signee = '%"
+ PRIX64 "'",
keyid);
result = PQexec(dbconn, statement);
*
* Returns the number of keys we iterated over.
*/
-static int pg_iterate_keys(void (*iterfunc)(void *ctx,
+static int pg_iterate_keys(struct onak_dbctx *dbctx,
+ void (*iterfunc)(void *ctx,
struct openpgp_publickey *key), void *ctx)
{
struct openpgp_packet_list *packets = NULL;
struct openpgp_publickey *key = NULL;
+ PGconn *dbconn = (PGconn *) dbctx->priv;
PGresult *result = NULL;
char *oids = NULL;
- char statement[1024];
- int fd = -1;
int i = 0;
int numkeys = 0;
Oid key_oid;
+ struct pg_fc_ctx fcctx;
result = PQexec(dbconn, "SELECT keydata FROM onak_keys;");
oids = PQgetvalue(result, i, 0);
key_oid = (Oid) atoi(oids);
- fd = lo_open(dbconn, key_oid, INV_READ);
- if (fd < 0) {
+ fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
+ if (fcctx.fd < 0) {
logthing(LOGTHING_ERROR,
"Can't open large object.");
} else {
- read_openpgp_stream(keydb_fetchchar, &fd,
+ fcctx.dbconn = dbconn;
+ read_openpgp_stream(keydb_fetchchar, &fcctx,
&packets, 0);
- parse_keys(packets, key);
- lo_close(dbconn, fd);
+ parse_keys(packets, &key);
+ lo_close(dbconn, fcctx.fd);
iterfunc(ctx, key);
/*
* Include the basic keydb routines.
*/
-#define NEED_GETFULLKEYID 1
#define NEED_UPDATEKEYS 1
+#define NEED_GET_FP 1
#include "keydb.c"
-struct dbfuncs keydb_pg_funcs = {
- .initdb = pg_initdb,
- .cleanupdb = pg_cleanupdb,
- .starttrans = pg_starttrans,
- .endtrans = pg_endtrans,
- .fetch_key = pg_fetch_key,
- .fetch_key_text = pg_fetch_key_text,
- .store_key = pg_store_key,
- .update_keys = generic_update_keys,
- .delete_key = pg_delete_key,
- .getkeysigs = pg_getkeysigs,
- .cached_getkeysigs = generic_cached_getkeysigs,
- .keyid2uid = pg_keyid2uid,
- .getfullkeyid = generic_getfullkeyid,
- .iterate_keys = pg_iterate_keys,
-};
+/**
+ * cleanupdb - De-initialize the key database.
+ *
+ * This function should be called upon program exit to allow the DB to
+ * cleanup after itself.
+ */
+static void pg_cleanupdb(struct onak_dbctx *dbctx)
+{
+ PGconn *dbconn = (PGconn *) dbctx->priv;
+
+ PQfinish(dbconn);
+ dbconn = NULL;
+
+ free(dbctx);
+}
+
+/**
+ * initdb - Initialize the key database.
+ *
+ * This function should be called before any of the other functions in
+ * this file are called in order to allow the DB to be initialized ready
+ * for access.
+ */
+struct onak_dbctx *keydb_pg_init(struct onak_db_config *dbcfg, bool readonly)
+{
+ struct onak_dbctx *dbctx;
+ PGconn *dbconn;
+
+ dbctx = malloc(sizeof(struct onak_dbctx));
+ if (dbctx == NULL) {
+ return NULL;
+ }
+ dbctx->config = dbcfg;
+
+ dbconn = PQsetdbLogin(dbcfg->hostname, // host
+ NULL, // port
+ NULL, // options
+ NULL, // tty
+ dbcfg->location, // database
+ dbcfg->username, //login
+ dbcfg->password); // password
+
+ if (PQstatus(dbconn) == CONNECTION_BAD) {
+ logthing(LOGTHING_CRITICAL, "Connection to database failed.");
+ logthing(LOGTHING_CRITICAL, "%s", PQerrorMessage(dbconn));
+ PQfinish(dbconn);
+ dbconn = NULL;
+ exit(1);
+ }
+
+ dbctx->priv = dbconn;
+
+ dbctx->cleanupdb = pg_cleanupdb;
+ dbctx->starttrans = pg_starttrans;
+ dbctx->endtrans = pg_endtrans;
+ dbctx->fetch_key_id = pg_fetch_key_id;
+ dbctx->fetch_key_fp = generic_fetch_key_fp;
+ dbctx->fetch_key_text = pg_fetch_key_text;
+ dbctx->store_key = pg_store_key;
+ dbctx->update_keys = generic_update_keys;
+ dbctx->delete_key = pg_delete_key;
+ dbctx->getkeysigs = pg_getkeysigs;
+ dbctx->cached_getkeysigs = generic_cached_getkeysigs;
+ dbctx->keyid2uid = pg_keyid2uid;
+ dbctx->iterate_keys = pg_iterate_keys;
+
+ return dbctx;
+}