From 54288021d24e0b40119d2ffae5a24ecd720d3aca Mon Sep 17 00:00:00 2001 From: Jonathan McDowell Date: Mon, 30 Sep 2013 17:31:03 +0100 Subject: [PATCH] Add HKP database backend A potential use case of onak is as a proxy server. Add an HKP backend that uses libcurl to make requests to a remote keyserver to fetch, search or store keys. The "db_dir" configuration parameter becomes the base host name for the remote keyserver e.g.: db_backend hkp db_dir the.earth.li In the future the addition of the ability to stack database backends should allow this to be used to turn onak into a caching keyserver. --- Makefile.in | 4 + configure.ac | 2 + debian/control | 2 +- keydb_hkp.c | 299 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 306 insertions(+), 1 deletion(-) create mode 100644 keydb_hkp.c diff --git a/Makefile.in b/Makefile.in index ead45eb..ec8b614 100644 --- a/Makefile.in +++ b/Makefile.in @@ -11,6 +11,7 @@ DBTYPE = @DBTYPE@ # LIBS = @LIBS@ @NETTLE_LIBS@ DB4LIBS = @DB4LIBS@ +CURLLIBS = @LIBCURL@ #MAKEDEPEND = makedepend -f- -- MAKEDEPEND = $(CC) -MM prefix ?= @prefix@ @@ -84,6 +85,9 @@ keydctl: keydctl.o onak-conf.o ll.o log.o libkeydb_db4.so: keydb_db4.o $(CORE_OBJS) $(CC) -shared $(DB4LIBS) -o libkeydb_db4.so keydb_db4.o $(CORE_OBJS) +libkeydb_hkp.so: keydb_hkp.o $(CORE_OBJS) + $(CC) -shared $(CURLLIBS) -o libkeydb_hkp.so keydb_hkp.o $(CORE_OBJS) + libkeydb_pg.so: keydb_pg.o $(CORE_OBJS) $(CC) -shared $(PQLIBS) -o libkeydb_pg.so keydb_pg.o $(CORE_OBJS) diff --git a/configure.ac b/configure.ac index 80e7d73..55def5c 100644 --- a/configure.ac +++ b/configure.ac @@ -15,6 +15,8 @@ AX_LIB_NETTLE(auto) dnl We should always have these backends available. backends="file fs keyd" +LIBCURL_CHECK_CONFIG(,,[have_libcurl="yes" backends="$backends hkp"],have_libcurl="no") + AC_CHECK_LIB(pq, PQsetdbLogin,[have_libpq="yes" backends="$backends pg"],have_libpq="no") AC_CHECK_HEADER(db.h, have_db_h="yes", have_db_h="no") diff --git a/debian/control b/debian/control index f8bb920..47988e5 100644 --- a/debian/control +++ b/debian/control @@ -3,7 +3,7 @@ Section: net Priority: optional Maintainer: Jonathan McDowell Uploaders: Ross Burton -Build-Depends: debhelper (>= 7), cdbs, libdb-dev, nettle-dev +Build-Depends: debhelper (>= 7), cdbs, libdb-dev, nettle-dev, libcurl4-gnutls-dev | libcurl-dev Standards-Version: 3.9.2.0 Homepage: http://www.earth.li/projectpurple/progs/onak.html Vcs-Bzr: http://www.earth.li/~noodles/bzr/onak/mainline diff --git a/keydb_hkp.c b/keydb_hkp.c new file mode 100644 index 0000000..f1dfe44 --- /dev/null +++ b/keydb_hkp.c @@ -0,0 +1,299 @@ +/* + * keydb_hkp.c - Routines to store and fetch keys from another keyserver. + * + * Copyright 2013 Jonathan McDowell + * + * 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, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include + +#include "armor.h" +#include "charfuncs.h" +#include "keydb.h" +#include "keystructs.h" +#include "log.h" +#include "mem.h" +#include "onak-conf.h" +#include "parsekey.h" + +static CURL *curl = NULL; + +/** + * initdb - Initialize the key database. + * + * We initialize CURL here. + */ +static void hkp_initdb(bool readonly) +{ + curl_global_init(CURL_GLOBAL_DEFAULT); + curl = curl_easy_init(); +} + +/** + * cleanupdb - De-initialize the key database. + * + * We cleanup CURL here. + */ +static void hkp_cleanupdb(void) +{ + if (curl) { + curl_easy_cleanup(curl); + curl = NULL; + } + curl_global_cleanup(); +} + +/** + * Receive data from a CURL request and process it into a buffer context. + */ +static size_t hkp_curl_recv_data(void *buffer, size_t size, size_t nmemb, + void *ctx) +{ + buffer_putchar(ctx, nmemb * size, buffer); + + return (nmemb * size); +} + +/** + * fetch_key - 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. + * + * We use the hex representation of the keyid as the filename to fetch the + * key from. The key is stored in the file as a binary OpenPGP stream of + * packets, so we can just use read_openpgp_stream() to read the packets + * in and then parse_keys() to parse the packets into a publickey + * structure. + */ +static int hkp_fetch_key(uint64_t keyid, struct openpgp_publickey **publickey, + bool intrans) +{ + struct openpgp_packet_list *packets = NULL; + char keyurl[1024]; + CURLcode res; + struct buffer_ctx buf; + + buf.offset = 0; + buf.size = 8192; + buf.buffer = malloc(8192); + + snprintf(keyurl, sizeof(keyurl), + "http://%s:11371/pks/lookup?op=get&search=0x%08" PRIX64, + config.db_dir, keyid); + + curl_easy_setopt(curl, CURLOPT_URL, keyurl); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, + hkp_curl_recv_data); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf); + res = curl_easy_perform(curl); + + if (res == 0) { + buf.offset = 0; + dearmor_openpgp_stream(buffer_fetchchar, &buf, &packets); + parse_keys(packets, publickey); + free_packet_list(packets); + packets = NULL; + } else { + logthing(LOGTHING_ERROR, "Couldn't find key: %s (%d)", + curl_easy_strerror(res), res); + } + + free(buf.buffer); + buf.offset = buf.size = 0; + buf.buffer = NULL; + + return (res == 0) ? 1 : 0; +} + +/** + * fetch_key_text - Tries to find the keys that contain the supplied text. + * @search: The text to search for. + * @publickey: A pointer to a structure to return the key in. + * + * This function searches for the supplied text and returns the keys that + * contain it. + * + * TODO: Write for flat file access. Some sort of grep? + */ +static int hkp_fetch_key_text(const char *search, + struct openpgp_publickey **publickey) +{ + struct openpgp_packet_list *packets = NULL; + char keyurl[1024]; + CURLcode res; + struct buffer_ctx buf; + int count = 0; + + buf.offset = 0; + buf.size = 8192; + buf.buffer = malloc(8192); + + snprintf(keyurl, sizeof(keyurl), + "http://%s:11371/pks/lookup?op=get&search=%s", + config.db_dir, search); + + curl_easy_setopt(curl, CURLOPT_URL, keyurl); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, + hkp_curl_recv_data); + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf); + res = curl_easy_perform(curl); + + if (res == 0) { + buf.offset = 0; + dearmor_openpgp_stream(buffer_fetchchar, &buf, &packets); + count = parse_keys(packets, publickey); + free_packet_list(packets); + packets = NULL; + } else { + logthing(LOGTHING_ERROR, "Couldn't find key: %s (%d)", + curl_easy_strerror(res), res); + } + + free(buf.buffer); + buf.offset = buf.size = 0; + buf.buffer = NULL; + + return count; +} + +/** + * store_key - Takes a key and stores it. + * @publickey: A pointer to the public key to store. + * @intrans: If we're already in a transaction. + * @update: If true the key exists and should be updated. + * + */ +static int hkp_store_key(struct openpgp_publickey *publickey, bool intrans, + bool update) +{ + struct openpgp_packet_list *packets = NULL; + struct openpgp_packet_list *list_end = NULL; + char keyurl[1024]; + CURLcode res; + struct buffer_ctx buf; + char *addform; + + buf.offset = 0; + buf.size = 8192; + buf.buffer = malloc(8192); + buf.offset = snprintf(buf.buffer, buf.size, "keytextz"); + + flatten_publickey(publickey, &packets, &list_end); + armor_openpgp_stream(buffer_putchar, &buf, packets); + addform = curl_easy_escape(curl, buf.buffer, buf.offset); + addform[7] = '='; + + snprintf(keyurl, sizeof(keyurl), + "http://%s:11371/pks/add", + config.db_dir); + + curl_easy_setopt(curl, CURLOPT_URL, keyurl); + curl_easy_setopt(curl, CURLOPT_POSTFIELDS, addform); + curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, + hkp_curl_recv_data); + buf.offset = 0; + curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buf); + res = curl_easy_perform(curl); + + if (res != 0) { + logthing(LOGTHING_ERROR, "Couldn't send key: %s (%d)", + curl_easy_strerror(res), res); + } + + curl_free(addform); + + /* TODO: buf has any response text we might want to parse. */ + free(buf.buffer); + buf.offset = buf.size = 0; + buf.buffer = NULL; + + return (res == 0) ? 1 : 0; +} + +/** + * delete_key - Given a keyid delete the key from storage. + * @keyid: The keyid to delete. + * @intrans: If we're already in a transaction. + * + * No op for HKP. + */ +static int hkp_delete_key(uint64_t keyid, bool intrans) +{ + return -1; +} + +/** + * iterate_keys - call a function once for each key in the db. + * @iterfunc: The function to call. + * @ctx: A context pointer + * + * Not applicable for HKP backend. + */ +static int hkp_iterate_keys(void (*iterfunc)(void *ctx, + struct openpgp_publickey *key), void *ctx) +{ + return 0; +} + +/** + * starttrans - Start a transaction. + * + * This is just a no-op for HKP access. + */ +static bool hkp_starttrans(void) +{ + return true; +} + +/** + * endtrans - End a transaction. + * + * This is just a no-op for HKP access. + */ +static void hkp_endtrans(void) +{ + return; +} + +/* + * Include the basic keydb routines. + */ +#define NEED_KEYID2UID 1 +#define NEED_GETKEYSIGS 1 +#define NEED_GETFULLKEYID 1 +#define NEED_UPDATEKEYS 1 +#include "keydb.c" + +struct dbfuncs keydb_hkp_funcs = { + .initdb = hkp_initdb, + .cleanupdb = hkp_cleanupdb, + .starttrans = hkp_starttrans, + .endtrans = hkp_endtrans, + .fetch_key = hkp_fetch_key, + .fetch_key_text = hkp_fetch_key_text, + .store_key = hkp_store_key, + .update_keys = generic_update_keys, + .delete_key = hkp_delete_key, + .getkeysigs = generic_getkeysigs, + .cached_getkeysigs = generic_cached_getkeysigs, + .keyid2uid = generic_keyid2uid, + .getfullkeyid = generic_getfullkeyid, + .iterate_keys = hkp_iterate_keys, +}; -- 2.30.2