]> the.earth.li Git - onak.git/commitdiff
Move key database backends into their own directory
authorJonathan McDowell <noodles@earth.li>
Tue, 27 Aug 2019 17:56:46 +0000 (18:56 +0100)
committerJonathan McDowell <noodles@earth.li>
Tue, 27 Aug 2019 17:56:46 +0000 (18:56 +0100)
These logically sit together and are separate from the core code, so
move them off to a separate directory. There are various bits of support
that should be hived off the core libonak and move in here too, but that
can wait until later.

34 files changed:
CMakeLists.txt
keyd.8 [deleted file]
keyd.c [deleted file]
keyd.h [deleted file]
keydb.c [deleted file]
keydb/CMakeLists.txt [new file with mode: 0644]
keydb/keyd.8 [new file with mode: 0644]
keydb/keyd.c [new file with mode: 0644]
keydb/keyd.h [new file with mode: 0644]
keydb/keydb.c [new file with mode: 0644]
keydb/keydb_db4.c [new file with mode: 0644]
keydb/keydb_dynamic.c [new file with mode: 0644]
keydb/keydb_file.c [new file with mode: 0644]
keydb/keydb_fs.c [new file with mode: 0644]
keydb/keydb_hkp.c [new file with mode: 0644]
keydb/keydb_keyd.c [new file with mode: 0644]
keydb/keydb_keyring.c [new file with mode: 0644]
keydb/keydb_pg.c [new file with mode: 0644]
keydb/keydb_stacked.c [new file with mode: 0644]
keydb/keydctl.8 [new file with mode: 0644]
keydb/keydctl.c [new file with mode: 0644]
keydb_db4.c [deleted file]
keydb_dynamic.c [deleted file]
keydb_file.c [deleted file]
keydb_fs.c [deleted file]
keydb_hkp.c [deleted file]
keydb_keyd.c [deleted file]
keydb_keyring.c [deleted file]
keydb_pg.c [deleted file]
keydb_stacked.c [deleted file]
keydctl.8 [deleted file]
keydctl.c [deleted file]
runtests
t/test-in.ini

index 4b2868860b787f04a840ecbf5c6a3e613b53502f..bf819d7e718453d6a106c73e69ef1b12b3c0d74f 100644 (file)
@@ -48,70 +48,29 @@ else()
        target_sources(libonak PRIVATE md5.c sha1.c)
 endif()
 
-# Backends
-
-# These have no dependencies and can always be compiled
-set(BACKENDS "file" "fs" "keyring" "stacked")
-
-# DB4 backend (add check for existence)
-LIST(APPEND BACKENDS db4)
-set(BACKEND_db4_LIBS db-5.3)
-
-# HKP backend - needs libcurl
-pkg_check_modules(CURL libcurl)
-if (CURL_FOUND)
-       LIST(APPEND BACKENDS hkp)
-       set(BACKEND_hkp_INC ${CURL_INCLUDE_DIRS})
-       set(BACKEND_hkp_LIBS ${CURL_LIBRARIES})
-endif()
-
-# PostgreSQL backend - needs libpq
-pkg_check_modules(POSTGRESQL libpq)
-if (POSTGRESQL_FOUND)
-       LIST(APPEND BACKENDS pg)
-       set(BACKEND_pg_INC ${POSTGRESQL_INCLUDE_DIRS})
-       set(BACKEND_pg_LIBS ${POSTGRESQL_LIBRARIES})
-endif()
+# Build files that have substitutions in them
+include_directories(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR})
+configure_file("${CMAKE_SOURCE_DIR}/build-config.h.in"
+       "${CMAKE_BINARY_DIR}/build-config.h" @ONLY)
 
-# keyd backend - can be disabled entirely
-if (KEYD STREQUAL "ON")
-       LIST(APPEND BACKENDS keyd)
-
-       add_executable(keyd keyd.c)
-       target_link_libraries(keyd libonak)
-       add_executable(keydctl keydctl.c onak-conf.c)
-       target_link_libraries(keydctl libonak)
-       target_compile_definitions(keydctl PRIVATE
-               CONFIGDIR="${CMAKE_INSTALL_FULL_SYSCONFDIR}")
-
-       pkg_check_modules(SYSTEMD libsystemd)
-       if (SYSTEMD_FOUND)
-               set(HAVE_SYSTEMD true)
-               target_include_directories(keyd SYSTEM PUBLIC
-                       ${SYSTEMD_INCLUDE_DIRS})
-               target_link_libraries(keyd ${SYSTEMD_LIBRARIES})
-       endif()
+configure_file("${CMAKE_SOURCE_DIR}/onak.ini.in"
+       "${CMAKE_BINARY_DIR}/onak.ini" @ONLY)
+install(FILES ${CMAKE_BINARY_DIR}/onak.ini
+       DESTINATION ${CMAKE_INSTALL_SYSCONFDIR})
 
-       install(TARGETS keydctl RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
-       install(TARGETS keyd RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
-       install(FILES keyd.8 keydctl.8
-               DESTINATION ${CMAKE_INSTALL_MANDIR}/man8/)
-endif()
+configure_file("${CMAKE_SOURCE_DIR}/onak-mail.pl.in"
+       "${CMAKE_BINARY_DIR}/onak-mail.pl" @ONLY)
+install(PROGRAMS ${CMAKE_BINARY_DIR}/onak-mail.pl
+       DESTINATION ${CMAKE_INSTALL_LIBDIR}/onak/)
+install(FILES onak-mail.pl.8 DESTINATION ${CMAKE_INSTALL_MANDIR}/man8/)
 
+# Key database backends
+add_subdirectory(keydb)
 
 # Now we have the DB type confirmed we can tidy up the libonak options
 
 if (DBTYPE STREQUAL "dynamic")
        LIST(APPEND LIBONAK_LIBRARIES "dl")
-       foreach(BACKEND IN LISTS BACKENDS)
-               add_library(keydb_${BACKEND} SHARED keydb_${BACKEND}.c)
-               target_include_directories(keydb_${BACKEND} SYSTEM PUBLIC
-                       ${BACKEND_${BACKEND}_INC})
-               target_link_libraries(keydb_${BACKEND} libonak
-                       ${BACKEND_${BACKEND}_LIBS})
-               install(TARGETS keydb_${BACKEND} LIBRARY DESTINATION
-                       ${CMAKE_INSTALL_LIBDIR}/onak/backends/)
-       endforeach(BACKEND)
 else()
        list (FIND BACKENDS ${DBTYPE} _index)
        if (${_index} LESS 0)
@@ -127,25 +86,9 @@ target_compile_definitions(libonak PRIVATE
        DBINIT=keydb_${DBTYPE}_init)
 
 # DB Backend related options are known, so finish off libonak configuration
-target_sources(libonak PRIVATE keydb_${DBTYPE}.c)
+target_sources(libonak PRIVATE keydb/keydb_${DBTYPE}.c)
 target_link_libraries(libonak ${LIBONAK_LIBRARIES})
 
-# Build files that have substitutions in them
-include_directories(${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR})
-configure_file("${CMAKE_SOURCE_DIR}/build-config.h.in"
-       "${CMAKE_BINARY_DIR}/build-config.h" @ONLY)
-
-configure_file("${CMAKE_SOURCE_DIR}/onak.ini.in"
-       "${CMAKE_BINARY_DIR}/onak.ini" @ONLY)
-install(FILES ${CMAKE_BINARY_DIR}/onak.ini
-       DESTINATION ${CMAKE_INSTALL_SYSCONFDIR})
-
-configure_file("${CMAKE_SOURCE_DIR}/onak-mail.pl.in"
-       "${CMAKE_BINARY_DIR}/onak-mail.pl" @ONLY)
-install(PROGRAMS ${CMAKE_BINARY_DIR}/onak-mail.pl
-       DESTINATION ${CMAKE_INSTALL_LIBDIR}/onak/)
-install(FILES onak-mail.pl.8 DESTINATION ${CMAKE_INSTALL_MANDIR}/man8/)
-
 # CGI directory
 add_subdirectory(cgi)
 
diff --git a/keyd.8 b/keyd.8
deleted file mode 100644 (file)
index 93cb31f..0000000
--- a/keyd.8
+++ /dev/null
@@ -1,42 +0,0 @@
-.TH KEYD 8
-.SH NAME
-keyd \- Backend key serving daemon for the onak PGP keyserver 
-.SH SYNOPSIS
-.PP
-.B keyd
-[
-.B options
-]
-.SH DESCRIPTION
-.PP
-keyd is a backend daemon for the onak PGP keyserver. It listens on a Unix
-socket for connections from the various CGI and mail front ends and
-handles talking to the backend database. It is intended to be of use in
-high load keyservers to reduce the time spent in connecting and
-disconnecting from the key database.
-.PP
-keyd is currently fairly alpha code; it is only recommended that you use
-it if you know what you are doing.
-.SS "Options"
-.TP
-\fB\-c \fIFILE\fR\fR
-Use \fIFILE\fR as the config file instead of the default.
-.TP
-\fB\-f\fR
-Run in the foreground rather than forking and daemonising.
-.TP
-\fB\-h\fR
-Display help text.
-.SH FILES
-.br
-.nf
-.\" set tabstop to longest possible filename, plus a wee bit
-.ta \w'/usr/lib/perl/getopts.pl   'u
-\fI/etc/onak.ini\fR    default configuration file
-.SH NOTES
-This man page could probably do with some more details.
-.SH "SEE ALSO"
-.BR onak (1)
-.SH AUTHOR
-onak was written by Jonathan McDowell <noodles@earth.li>. It can be found at
-http://www.earth.li/projectpurple/progs/onak.html
diff --git a/keyd.c b/keyd.c
deleted file mode 100644 (file)
index ddd1366..0000000
--- a/keyd.c
+++ /dev/null
@@ -1,709 +0,0 @@
-/*
- * keyd.c - key retrieval daemon
- *
- * Copyright 2004,2011 Jonathan McDowell <noodles@earth.li>
- *
- * 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 <errno.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <inttypes.h>
-#include <signal.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/select.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <time.h>
-#include <unistd.h>
-
-#include "build-config.h"
-
-#ifdef HAVE_SYSTEMD
-#include <systemd/sd-daemon.h>
-#endif
-
-#include "charfuncs.h"
-#include "cleanup.h"
-#include "keyd.h"
-#include "keydb.h"
-#include "keyid.h"
-#include "keystructs.h"
-#include "log.h"
-#include "mem.h"
-#include "onak-conf.h"
-#include "parsekey.h"
-
-/* Maximum number of clients we're prepared to accept at once */
-#define MAX_CLIENTS 16
-
-#ifdef HAVE_SYSTEMD
-static bool using_socket_activation = false;
-#endif
-
-static struct keyd_stats *stats;
-
-static void daemonize(void)
-{
-       pid_t pid;
-
-       pid = fork();
-
-       if (pid < 0) {
-               logthing(LOGTHING_CRITICAL,
-                       "Failed to fork into background: %d (%s)",
-                       errno,
-                       strerror(errno));
-               exit(EXIT_FAILURE);
-       } else if (pid > 0) {
-               logthing(LOGTHING_INFO, "Backgrounded as pid %d.", pid);
-               exit(EXIT_SUCCESS);
-       }
-
-       if (setsid() == -1) {
-               logthing(LOGTHING_CRITICAL,
-                       "Couldn't set process group leader: %d (%s)",
-                       errno,
-                       strerror(errno));
-               exit(EXIT_FAILURE);
-       }
-
-       if (!freopen("/dev/null", "r", stdin)) {
-               logthing(LOGTHING_CRITICAL,
-                       "Couldn't reopen stdin to NULL: %d (%s)",
-                       errno,
-                       strerror(errno));
-               exit(EXIT_FAILURE);
-       }
-       if (!freopen("/dev/null", "w", stdout)) {
-               logthing(LOGTHING_CRITICAL,
-                       "Couldn't reopen stdout to NULL: %d (%s)",
-                       errno,
-                       strerror(errno));
-               exit(EXIT_FAILURE);
-       }
-       if (!freopen("/dev/null", "w", stderr)) {
-               logthing(LOGTHING_CRITICAL,
-                       "Couldn't reopen stderr to NULL: %d (%s)",
-                       errno,
-                       strerror(errno));
-               exit(EXIT_FAILURE);
-       }
-
-       return;
-}
-
-static bool keyd_write_key(int fd, struct openpgp_publickey *key)
-{
-       struct openpgp_packet_list *packets = NULL;
-       struct openpgp_packet_list *list_end = NULL;
-       struct buffer_ctx           storebuf;
-       ssize_t written;
-       bool    ok = true;
-
-       storebuf.offset = 0;
-       storebuf.size = 8192;
-       storebuf.buffer = malloc(8192);
-
-       flatten_publickey(key,
-                               &packets,
-                               &list_end);
-       write_openpgp_stream(buffer_putchar,
-                               &storebuf,
-                               packets);
-       logthing(LOGTHING_TRACE,
-                               "Sending %d bytes.",
-                               storebuf.offset);
-       written = write(fd, &storebuf.offset,
-                       sizeof(storebuf.offset));
-       if (written == 0) {
-               ok = false;
-       } else {
-               written = write(fd, storebuf.buffer,
-                       storebuf.offset);
-               if (written != storebuf.offset) {
-                       ok = false;
-               }
-       }
-
-       free(storebuf.buffer);
-       storebuf.buffer = NULL;
-       storebuf.size = storebuf.offset = 0;
-       free_packet_list(packets);
-       packets = list_end = NULL;
-
-       return (ok);
-}
-
-static bool keyd_write_reply(int fd, enum keyd_reply _reply)
-{
-       uint32_t reply = _reply;
-       ssize_t written;
-       bool ok = true;
-
-       written = write(fd, &reply, sizeof(reply));
-       if (written != sizeof(reply)) {
-               ok = false;
-       }
-
-       return (ok);
-}
-
-static bool keyd_write_size(int fd, size_t size)
-{
-       ssize_t written;
-       bool ok = true;
-
-       written = write(fd, &size, sizeof(size));
-       if (written != sizeof(size)) {
-               ok = false;
-       }
-
-       return (ok);
-}
-
-static void iteratefunc(void *ctx, struct openpgp_publickey *key)
-{
-       int      *fd = (int *) ctx;
-       uint64_t  keyid;
-
-       if (key != NULL) {
-               get_keyid(key, &keyid);
-               logthing(LOGTHING_TRACE,
-                               "Iterating over 0x%016" PRIX64 ".",
-                               keyid);
-
-               keyd_write_key(*fd, key);
-       }
-
-       return;
-}
-
-static int sock_init(const char *sockname)
-{
-       struct sockaddr_un sock;
-       int                fd = -1;
-       int                ret = -1;
-#ifdef HAVE_SYSTEMD
-       int                n;
-
-       n = sd_listen_fds(0);
-       if (n > 1) {
-               logthing(LOGTHING_ERROR,
-                       "Too many file descriptors received from systemd.");
-       } else if (n == 1) {
-               fd = SD_LISTEN_FDS_START + 0;
-               if (sd_is_socket_unix(fd, SOCK_STREAM, 1, NULL, 0) <= 0) {
-                       logthing(LOGTHING_ERROR,
-                               "systemd passed an invalid socket.");
-                       fd = -1;
-               }
-               using_socket_activation = true;
-       } else {
-#endif
-               fd = socket(PF_UNIX, SOCK_STREAM, 0);
-               if (fd != -1) {
-                       ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
-               }
-
-               if (ret != -1) {
-                       sock.sun_family = AF_UNIX;
-                       strncpy(sock.sun_path, sockname,
-                                       sizeof(sock.sun_path) - 1);
-                       unlink(sockname);
-                       ret = bind(fd, (struct sockaddr *) &sock,
-                                       sizeof(sock));
-               }
-
-               if (ret != -1) {
-                       ret = listen(fd, 5);
-                       if (ret == -1) {
-                               close(fd);
-                               fd = -1;
-                       }
-               }
-#ifdef HAVE_SYSTEMD
-       }
-#endif
-
-       return fd;
-}
-
-static int sock_do(struct onak_dbctx *dbctx, int fd)
-{
-       uint32_t cmd = KEYD_CMD_UNKNOWN;
-       ssize_t  bytes = 0;
-       ssize_t  count = 0;
-       int      ret = 0;
-       uint64_t keyid = 0;
-       char     *search = NULL;
-       struct openpgp_publickey *key = NULL;
-       struct openpgp_packet_list *packets = NULL;
-       struct buffer_ctx storebuf;
-       struct skshash hash;
-       struct openpgp_fingerprint fingerprint;
-
-       /*
-        * Get the command from the client.
-        */
-       bytes = read(fd, &cmd, sizeof(cmd));
-
-       logthing(LOGTHING_DEBUG, "Read %d bytes, command: %d", bytes, cmd);
-
-       if (bytes != sizeof(cmd)) {
-               ret = 1;
-       }
-       
-       if (ret == 0) {
-               if (cmd < KEYD_CMD_LAST) {
-                       stats->command_stats[cmd]++;
-               } else {
-                       stats->command_stats[KEYD_CMD_UNKNOWN]++;
-               }
-               switch (cmd) {
-               case KEYD_CMD_VERSION:
-                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
-                               ret = 1;
-                       }
-                       if (ret == 0) {
-                               cmd = sizeof(keyd_version);
-                               bytes = write(fd, &cmd, sizeof(cmd));
-                               if (bytes != sizeof(cmd)) {
-                                       ret = 1;
-                               }
-                       }
-                       if (ret == 0) {
-                               bytes = write(fd, &keyd_version,
-                                       sizeof(keyd_version));
-                               if (bytes != sizeof(keyd_version)) {
-                                       ret = 1;
-                               }
-                       }
-                       break;
-               case KEYD_CMD_GET_ID:
-                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
-                               ret = 1;
-                       }
-                       if (ret == 0) {
-                               bytes = read(fd, &keyid, sizeof(keyid));
-                               if (bytes != sizeof(keyid)) {
-                                       ret = 1;
-                               }
-                       }
-                       if (ret == 0) {
-                               logthing(LOGTHING_INFO,
-                                               "Fetching 0x%" PRIX64
-                                               ", result: %d",
-                                               keyid,
-                                               dbctx->fetch_key_id(dbctx,
-                                                       keyid,
-                                                       &key, false));
-                               if (key != NULL) {
-                                       keyd_write_key(fd, key);
-                                       free_publickey(key);
-                                       key = NULL;
-                               } else {
-                                       if (!keyd_write_size(fd, 0)) {
-                                               ret = 1;
-                                       }
-                               }
-                       }
-                       break;
-               case KEYD_CMD_GET_FP:
-                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
-                               ret = 1;
-                       }
-                       if (ret == 0) {
-                               if ((read(fd, &bytes, 1) != 1) ||
-                                               (bytes > MAX_FINGERPRINT_LEN)) {
-                                       ret = 1;
-                               } else {
-                                       fingerprint.length = bytes;
-                                       bytes = read(fd, fingerprint.fp,
-                                               fingerprint.length);
-                                       if (bytes != fingerprint.length) {
-                                               ret = 1;
-                                       }
-                               }
-                       }
-                       if (ret == 0) {
-                               logthing(LOGTHING_INFO,
-                                               "Fetching by fingerprint"
-                                               ", result: %d",
-                                               dbctx->fetch_key_fp(dbctx,
-                                                       &fingerprint,
-                                                       &key, false));
-                               if (key != NULL) {
-                                       keyd_write_key(fd, key);
-                                       free_publickey(key);
-                                       key = NULL;
-                               } else {
-                                       if (!keyd_write_size(fd, 0)) {
-                                               ret = 1;
-                                       }
-                               }
-                       }
-                       break;
-
-               case KEYD_CMD_GET_TEXT:
-                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
-                               ret = 1;
-                       }
-                       if (ret == 0) {
-                               bytes = read(fd, &count, sizeof(count));
-                               if (bytes != sizeof(count)) {
-                                       ret = 1;
-                               }
-                       }
-                       if (ret == 0) {
-                               search = malloc(count+1);
-                               bytes = read(fd, search, count);
-                               if (bytes != count) {
-                                       ret = 1;
-                                       free(search);
-                                       break;
-                               }
-                               search[count] = 0;
-                               logthing(LOGTHING_INFO,
-                                               "Fetching %s, result: %d",
-                                               search,
-                                               dbctx->fetch_key_text(dbctx,
-                                                       search, &key));
-                               if (key != NULL) {
-                                       keyd_write_key(fd, key);
-                                       free_publickey(key);
-                                       key = NULL;
-                               } else {
-                                       if (!keyd_write_size(fd, 0)) {
-                                               ret = 1;
-                                       }
-                               }
-                               free(search);
-                       }
-                       break;
-               case KEYD_CMD_STORE:
-               case KEYD_CMD_UPDATE:
-                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
-                               ret = 1;
-                       }
-                       if (ret == 0) {
-                               bytes = read(fd, &storebuf.size,
-                                       sizeof(storebuf.size));
-                               logthing(LOGTHING_TRACE, "Reading %d bytes.",
-                                       storebuf.size);
-                               if (bytes != sizeof(storebuf.size)) {
-                                       ret = 1;
-                               }
-                       }
-                       if (ret == 0 && storebuf.size > 0) {
-                               storebuf.buffer = malloc(storebuf.size);
-                               storebuf.offset = 0;
-                               bytes = count = 0;
-                               while (bytes >= 0 && count < storebuf.size) {
-                                       bytes = read(fd,
-                                               &storebuf.buffer[count],
-                                               storebuf.size - count);
-                                       logthing(LOGTHING_TRACE,
-                                                       "Read %d bytes.",
-                                                       bytes);
-                                       count += bytes;
-                               }
-                               read_openpgp_stream(buffer_fetchchar,
-                                               &storebuf,
-                                               &packets,
-                                               0);
-                               parse_keys(packets, &key);
-                               dbctx->store_key(dbctx, key, false,
-                                       (cmd == KEYD_CMD_UPDATE));
-                               free_packet_list(packets);
-                               packets = NULL;
-                               free_publickey(key);
-                               key = NULL;
-                               free(storebuf.buffer);
-                               storebuf.buffer = NULL;
-                               storebuf.size = storebuf.offset = 0;
-                       }
-                       break;
-               case KEYD_CMD_DELETE:
-                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
-                               ret = 1;
-                       }
-                       if (ret == 0) {
-                               bytes = read(fd, &fingerprint,
-                                               sizeof(fingerprint));
-                               if (bytes != sizeof(fingerprint)) {
-                                       ret = 1;
-                               }
-                       }
-                       if (ret == 0) {
-                               logthing(LOGTHING_INFO,
-                                               "Deleting 0x%" PRIX64
-                                               ", result: %d",
-                                               keyid,
-                                               dbctx->delete_key(dbctx,
-                                                       &fingerprint, false));
-                       }
-                       break;
-               case KEYD_CMD_KEYITER:
-                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
-                               ret = 1;
-                       }
-                       if (ret == 0) {
-                               dbctx->iterate_keys(dbctx, iteratefunc,
-                                       &fd);
-                               if (!keyd_write_size(fd, 0)) {
-                                       ret = 1;
-                               }
-                       }
-                       break;
-               case KEYD_CMD_CLOSE:
-                       /* We're going to close the FD even if this fails */
-                       (void) keyd_write_reply(fd, KEYD_REPLY_OK);
-                       ret = 1;
-                       break;
-               case KEYD_CMD_QUIT:
-                       /* We're going to quit even if this fails */
-                       (void) keyd_write_reply(fd, KEYD_REPLY_OK);
-                       logthing(LOGTHING_NOTICE,
-                               "Exiting due to quit request.");
-                       ret = 1;
-                       trytocleanup();
-                       break;
-               case KEYD_CMD_STATS:
-                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
-                               ret = 1;
-                       }
-                       if (ret == 0) {
-                               cmd = sizeof(*stats);
-                               bytes = write(fd, &cmd, sizeof(cmd));
-                               if (bytes != sizeof(cmd)) {
-                                       ret = 1;
-                               }
-                       }
-                       if (ret == 0) {
-                               bytes = write(fd, stats, sizeof(*stats));
-                               if (bytes != sizeof(*stats)) {
-                                       ret = 1;
-                               }
-                       }
-                       break;
-               case KEYD_CMD_GET_SKSHASH:
-                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
-                               ret = 1;
-                       }
-                       if (ret == 0) {
-                               bytes = read(fd, hash.hash, sizeof(hash.hash));
-                               if (bytes != sizeof(hash.hash)) {
-                                       ret = 1;
-                               }
-                       }
-                       if (ret == 0) {
-                               logthing(LOGTHING_INFO,
-                                               "Fetching by hash"
-                                               ", result: %d",
-                                               dbctx->fetch_key_skshash(dbctx,
-                                                       &hash, &key));
-                               if (key != NULL) {
-                                       keyd_write_key(fd, key);
-                                       free_publickey(key);
-                                       key = NULL;
-                               } else {
-                                       if (!keyd_write_size(fd, 0)) {
-                                               ret = 1;
-                                       }
-                               }
-                       }
-                       break;
-
-               default:
-                       logthing(LOGTHING_ERROR, "Got unknown command: %d",
-                                       cmd);
-                       if (!keyd_write_reply(fd, KEYD_REPLY_UNKNOWN_CMD)) {
-                               ret = 1;
-                       }
-               }
-       }
-
-       return(ret);
-}
-
-static int sock_close(int fd)
-{
-       shutdown(fd, SHUT_RDWR);
-       return close(fd);
-}
-
-static int sock_accept(int fd)
-{
-       struct sockaddr_un sock;
-       socklen_t          socklen;
-       int    srv = -1;
-       int    ret = -1;
-
-       socklen = sizeof(sock);
-       srv = accept(fd, (struct sockaddr *) &sock, &socklen);
-       if (srv != -1) {
-               ret = fcntl(srv, F_SETFD, FD_CLOEXEC);
-       }
-
-       if (ret != -1) {
-               stats->connects++;
-       }
-
-       return (srv);
-}
-
-static void usage(void)
-{
-       puts("keyd " ONAK_VERSION " - backend key serving daemon for the "
-               "onak PGP keyserver.\n");
-       puts("Usage:\n");
-       puts("\tkeyd [options]\n");
-       puts("\tOptions:\n:");
-       puts("-c <file> - use <file> as the config file");
-       puts("-f        - run in the foreground");
-       puts("-h        - show this help text");
-       exit(EXIT_FAILURE);
-}
-
-int main(int argc, char *argv[])
-{
-       int fd = -1, maxfd, i, clients[MAX_CLIENTS];
-       fd_set rfds = { 0 }; /* Avoid scan-build false report for FD_SET */
-       char sockname[100];
-       char *configfile = NULL;
-       bool foreground = false;
-       int optchar;
-       struct onak_dbctx *dbctx;
-
-       while ((optchar = getopt(argc, argv, "c:fh")) != -1 ) {
-               switch (optchar) {
-               case 'c':
-                       if (configfile != NULL) {
-                               free(configfile);
-                       }
-                       configfile = strdup(optarg);
-                       break;
-               case 'f':
-                       foreground = true;
-                       break;
-               case 'h':
-               default:
-                       usage();
-                       break;
-               }
-       }
-
-       readconfig(configfile);
-       free(configfile);
-       configfile = NULL;
-       initlogthing("keyd", config.logfile);
-       config.use_keyd = false;
-
-       if (!foreground) {
-               daemonize();
-       }
-
-       catchsignals();
-       signal(SIGPIPE, SIG_IGN);
-
-
-       stats = calloc(1, sizeof(*stats));
-       if (!stats) {
-               logthing(LOGTHING_ERROR,
-                       "Couldn't allocate memory for stats structure.");
-               exit(EXIT_FAILURE);
-       }
-       stats->started = time(NULL);
-
-       snprintf(sockname, sizeof(sockname) - 1, "%s/%s",
-                       config.sock_dir, KEYD_SOCKET);
-       fd = sock_init(sockname);
-
-       if (fd != -1) {
-               FD_ZERO(&rfds);
-               FD_SET(fd, &rfds);
-               maxfd = fd;
-               memset(clients, -1, sizeof (clients));
-
-               dbctx = config.dbinit(config.backend, false);
-
-               logthing(LOGTHING_NOTICE, "Accepting connections.");
-               while (!cleanup() && select(maxfd + 1, &rfds, NULL, NULL, NULL) != -1) {
-                       /*
-                        * Deal with existing clients first; if we're at our
-                        * connection limit then processing them might free
-                        * things up and let us accept the next client below.
-                        */
-                       for (i = 0; i < MAX_CLIENTS; i++) {
-                               if (clients[i] != -1 &&
-                                               FD_ISSET(clients[i], &rfds)) {
-                                       logthing(LOGTHING_DEBUG,
-                                               "Handling connection for client %d.", i);
-                                       if (sock_do(dbctx, clients[i])) {
-                                               sock_close(clients[i]);
-                                               clients[i] = -1;
-                                               logthing(LOGTHING_DEBUG,
-                                                       "Closed connection for client %d.", i);
-                                       }
-                               }
-                       }
-                       /*
-                        * Check if we have a new incoming connection to accept.
-                        */
-                       if (FD_ISSET(fd, &rfds)) {
-                               for (i = 0; i < MAX_CLIENTS; i++) {
-                                       if (clients[i] == -1) {
-                                               break;
-                                       }
-                               }
-                               if (i < MAX_CLIENTS) {
-                                       logthing(LOGTHING_INFO,
-                                               "Accepted connection %d.", i);
-                                       clients[i] = sock_accept(fd);
-                               }
-                       }
-                       FD_ZERO(&rfds);
-                       FD_SET(fd, &rfds);
-                       maxfd = fd;
-                       for (i = 0; i < MAX_CLIENTS; i++) {
-                               if (clients[i] != -1) {
-                                       FD_SET(clients[i], &rfds);
-                                       maxfd = (maxfd > clients[i]) ?
-                                                       maxfd : clients[i];
-                               }
-                       }
-               }
-               dbctx->cleanupdb(dbctx);
-#ifdef HAVE_SYSTEMD
-               if (!using_socket_activation) {
-#endif
-                       sock_close(fd);
-                       unlink(sockname);
-#ifdef HAVE_SYSTEMD
-               }
-#endif
-       }
-
-       free(stats);
-
-       cleanuplogthing();
-       cleanupconfig();
-
-       return(EXIT_SUCCESS);
-}
diff --git a/keyd.h b/keyd.h
deleted file mode 100644 (file)
index 1ea601c..0000000
--- a/keyd.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/**
- * @file keyd.h
- * @brief Public API for keyd.
- *
- * Copyright 2004,2011 Jonathan McDowell <noodles@earth.li>
- *
- * 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/>.
- */
-
-#ifndef __KEYD_H__
-#define __KEYD_H__
-
-#include <stdint.h>
-
-/**
- * @brief The name of the keyd Unix domain socket
- */
-#define KEYD_SOCKET "keyd.sock"
-
-/**
- * @brief keyd commands
- */
-enum keyd_ops {
-       KEYD_CMD_UNKNOWN = 0,
-       KEYD_CMD_VERSION = 1,
-       KEYD_CMD_GET_ID,
-       KEYD_CMD_STORE,
-       KEYD_CMD_DELETE,
-       KEYD_CMD_GET_TEXT,
-       KEYD_CMD_GETFULLKEYID,
-       KEYD_CMD_KEYITER,
-       KEYD_CMD_CLOSE,
-       KEYD_CMD_QUIT,
-       KEYD_CMD_STATS,
-       KEYD_CMD_GET_SKSHASH,
-       KEYD_CMD_GET_FP,
-       KEYD_CMD_UPDATE,
-       KEYD_CMD_LAST                   /* Placeholder */
-};
-
-/**
- * @brief Reply codes for keyd commands
- */
-enum keyd_reply {
-       KEYD_REPLY_OK = 0,
-       KEYD_REPLY_UNKNOWN_CMD = 1
-};
-
-/**
- * @brief Version of the keyd protocol currently supported
- */
-static const uint32_t keyd_version = 5;
-
-/**
- * @brief Response structure for the @a KEYD_CMD_STATS response
- */
-struct keyd_stats {
-       /** Unix time of when the keyd daemon was started */
-       time_t started;
-       /** Number of connects we've seen to keyd */
-       uint32_t connects;
-       /** Count of the number of times each command has been used */
-       uint32_t command_stats[KEYD_CMD_LAST];
-};
-
-#endif /* __KEYD_H__ */
diff --git a/keydb.c b/keydb.c
deleted file mode 100644 (file)
index f6b9682..0000000
--- a/keydb.c
+++ /dev/null
@@ -1,285 +0,0 @@
-/*
- * keydb.c - Routines for DB access that just use store/fetch.
- *
- * Copyright 2002-2004 Jonathan McDowell <noodles@earth.li>
- *
- * 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/>.
- */
-
-/**
- *     The routines in this file are meant to be used as an initial step when
- *     adding a new db access module. They provide various functions required
- *     of the db access module using only the store and fetch functions. As
- *     they need to parse the actual OpenPGP data to work they are a lot
- *     slower than custom functions however.
- */
-
-#include <stdbool.h>
-#include <stdio.h>
-
-#include "decodekey.h"
-#include "hash.h"
-#include "keydb.h"
-#include "keyid.h"
-#include "keystructs.h"
-#include "ll.h"
-#include "mem.h"
-#include "merge.h"
-#include "openpgp.h"
-#include "sendsync.h"
-#include "stats.h"
-
-#ifdef NEED_KEYID2UID
-/**
- *     keyid2uid - Takes a keyid and returns the primary UID for it.
- *     @keyid: The keyid to lookup.
- */
-char *generic_keyid2uid(struct onak_dbctx *dbctx, uint64_t keyid)
-{
-       struct openpgp_publickey *publickey = NULL;
-       struct openpgp_signedpacket_list *curuid = NULL;
-       char buf[1024];
-
-       buf[0]=0;
-       if (dbctx->fetch_key_id(dbctx, keyid, &publickey, false) &&
-                       publickey != NULL) {
-               curuid = publickey->uids;
-               while (curuid != NULL && buf[0] == 0) {
-                       if (curuid->packet->tag == OPENPGP_PACKET_UID) {
-                               snprintf(buf, 1023, "%.*s",
-                                               (int) curuid->packet->length,
-                                               curuid->packet->data);
-                       }
-                       curuid = curuid -> next;
-               }
-               free_publickey(publickey);
-       }
-
-       if (buf[0] == 0) {
-               return NULL;
-       } else {
-               return strdup(buf);
-       }
-}
-#endif
-
-#ifdef NEED_GETKEYSIGS
-/**
- *     getkeysigs - Gets a linked list of the signatures on a key.
- *     @keyid: The keyid to get the sigs for.
- *     @revoked: Is the key revoked?
- *
- *     This function gets the list of signatures on a key. Used for key 
- *     indexing and doing stats bits. If revoked is non-NULL then if the key
- *     is revoked it's set to true.
- */
-struct ll *generic_getkeysigs(struct onak_dbctx *dbctx,
-               uint64_t keyid, bool *revoked)
-{
-       struct ll *sigs = NULL;
-       struct openpgp_signedpacket_list *uids = NULL;
-       struct openpgp_packet_list *cursig;
-       struct openpgp_publickey *publickey = NULL;
-
-       dbctx->fetch_key_id(dbctx, keyid, &publickey, false);
-       
-       if (publickey != NULL) {
-               for (uids = publickey->uids; uids != NULL; uids = uids->next) {
-                       for (cursig = uids->sigs; cursig != NULL;
-                                       cursig = cursig->next) {
-                               sigs = lladd(sigs,
-                                               createandaddtohash(sig_keyid(
-                                                       cursig->packet)));
-                       }
-               }
-               if (revoked != NULL) {
-                       *revoked = publickey->revoked;
-               }
-               free_publickey(publickey);
-       }
-
-       return sigs;
-}
-#endif
-
-/**
- *     cached_getkeysigs - Gets the signatures on a key.
- *     @keyid: The key we want the signatures for.
- *     
- *     This function gets the signatures on a key. It's the same as the
- *     getkeysigs function above except we use the hash module to cache the
- *     data so if we need it again it's already loaded.
- */
-struct ll *generic_cached_getkeysigs(struct onak_dbctx *dbctx, uint64_t keyid)
-{
-       struct stats_key *key = NULL;
-       struct stats_key *signedkey = NULL;
-       struct ll        *cursig = NULL;
-       struct ll        *sigs = NULL;
-       bool              revoked = false;
-
-       if (keyid == 0)  {
-               return NULL;
-       }
-
-       key = findinhash(keyid);
-
-       if (key == NULL || key->gotsigs == false) {
-               sigs = dbctx->getkeysigs(dbctx, keyid, &revoked);
-               if (sigs == NULL) {
-                       return NULL;
-               }
-               if (key == NULL) {
-                       key = createandaddtohash(keyid);
-               }
-               key->sigs = sigs;
-               key->revoked = revoked;
-               for (cursig = key->sigs; cursig != NULL;
-                               cursig = cursig->next) {
-                       signedkey = (struct stats_key *) cursig->object;
-                       signedkey->signs = lladd(signedkey->signs, key);
-               }
-               key->gotsigs = true;
-       }
-
-       return key->sigs;
-}
-
-#ifdef NEED_UPDATEKEYS
-/**
- *     update_keys - Takes a list of public keys and updates them in the DB.
- *     @keys: The keys to update in the DB.
- *     @blacklist: A keyarray of key fingerprints not to accept.
- *     @updateonly: Only update existing keys, don't add new ones.
- *     @sendsync: Should we send a sync mail to our peers.
- *
- *     Takes a list of keys and adds them to the database, merging them with
- *     the key in the database if it's already present there. The key list is
- *     update to contain the minimum set of updates required to get from what
- *     we had before to what we have now (ie the set of data that was added to
- *     the DB). Returns the number of entirely new keys added.
- */
-int generic_update_keys(struct onak_dbctx *dbctx,
-               struct openpgp_publickey **keys,
-               struct keyarray *blacklist,
-               bool updateonly,
-               bool sendsync)
-{
-       struct openpgp_publickey **curkey, *tmp = NULL;
-       struct openpgp_publickey *oldkey = NULL;
-       struct openpgp_fingerprint fp;
-       int newkeys = 0, ret;
-       bool intrans;
-
-       curkey = keys;
-       while (*curkey != NULL) {
-               get_fingerprint((*curkey)->publickey, &fp);
-               if (blacklist && array_find(blacklist, &fp)) {
-                       logthing(LOGTHING_INFO, "Ignoring blacklisted key.");
-                       tmp = *curkey;
-                       *curkey = (*curkey)->next;
-                       tmp->next = NULL;
-                       free_publickey(tmp);
-                       continue;
-               }
-
-               intrans = dbctx->starttrans(dbctx);
-
-               ret = dbctx->fetch_key_fp(dbctx, &fp, &oldkey, intrans);
-               if (ret == 0 && updateonly) {
-                       logthing(LOGTHING_INFO,
-                               "Skipping new key as update only set.");
-                       curkey = &(*curkey)->next;
-                       goto next;
-               }
-
-               /*
-                * If we already have the key stored in the DB then merge it
-                * with the new one that's been supplied. Otherwise the key
-                * we've just got is the one that goes in the DB and also the
-                * one that we send out.
-                */
-               if (oldkey != NULL) {
-                       merge_keys(oldkey, *curkey);
-                       if ((*curkey)->sigs == NULL &&
-                                       (*curkey)->uids == NULL &&
-                                       (*curkey)->subkeys == NULL) {
-                               tmp = *curkey;
-                               *curkey = (*curkey)->next;
-                               tmp->next = NULL;
-                               free_publickey(tmp);
-                       } else {
-                               logthing(LOGTHING_INFO,
-                                       "Merged key; storing updated key.");
-                               dbctx->store_key(dbctx, oldkey, intrans,
-                                       true);
-                               curkey = &(*curkey)->next;
-                       }
-                       free_publickey(oldkey);
-                       oldkey = NULL;
-               } else {
-                       logthing(LOGTHING_INFO,
-                               "Storing completely new key.");
-                       dbctx->store_key(dbctx, *curkey, intrans, false);
-                       newkeys++;
-                       curkey = &(*curkey)->next;
-               }
-next:
-               dbctx->endtrans(dbctx);
-       }
-
-       if (sendsync && keys != NULL && *keys != NULL) {
-               sendkeysync(*keys);
-       }
-
-       return newkeys;
-}
-#endif /* NEED_UPDATEKEYS */
-
-#ifdef NEED_GET_FP
-static int generic_fetch_key_fp(struct onak_dbctx *dbctx,
-               struct openpgp_fingerprint *fingerprint,
-               struct openpgp_publickey **publickey, bool intrans)
-{
-       uint64_t keyid;
-       int i;
-
-       if (fingerprint->length > MAX_FINGERPRINT_LEN) {
-               return 0;
-       }
-
-       /*
-        * We assume if the backend is using this function it's not storing
-        * anything bigger than the 64 bit key ID and just truncate the
-        * fingerprint to get that value. v4 keys want the lowest 64 bits, v5
-        * keys need the top 64 bits.  This doesn't work for v3 keys,
-        * but there's no way to map from v3 fingerprint to v3 key ID so
-        * if the backend can't do it we're going to fail anyway.
-        */
-       keyid = 0;
-       if (fingerprint->length == 20) {
-               /* v4 */
-               for (i = (fingerprint->length - 8); i < fingerprint->length;
-                               i++) {
-                       keyid = (keyid << 8) + fingerprint->fp[i];
-               }
-       } else {
-               /* v5 */
-               for (i = 0; i < 8; i++) {
-                       keyid = (keyid << 8) + fingerprint->fp[i];
-               }
-       }
-
-       return dbctx->fetch_key_id(dbctx, keyid, publickey, intrans);
-}
-#endif
diff --git a/keydb/CMakeLists.txt b/keydb/CMakeLists.txt
new file mode 100644 (file)
index 0000000..35911f6
--- /dev/null
@@ -0,0 +1,61 @@
+# Key database backends
+
+# These have no dependencies and can always be compiled
+set(BACKENDS "file" "fs" "keyring" "stacked")
+
+# DB4 backend (add check for existence)
+LIST(APPEND BACKENDS db4)
+set(BACKEND_db4_LIBS db-5.3)
+
+# HKP backend - needs libcurl
+pkg_check_modules(CURL libcurl)
+if (CURL_FOUND)
+       LIST(APPEND BACKENDS hkp)
+       set(BACKEND_hkp_INC ${CURL_INCLUDE_DIRS})
+       set(BACKEND_hkp_LIBS ${CURL_LIBRARIES})
+endif()
+
+# PostgreSQL backend - needs libpq
+pkg_check_modules(POSTGRESQL libpq)
+if (POSTGRESQL_FOUND)
+       LIST(APPEND BACKENDS pg)
+       set(BACKEND_pg_INC ${POSTGRESQL_INCLUDE_DIRS})
+       set(BACKEND_pg_LIBS ${POSTGRESQL_LIBRARIES})
+endif()
+
+# keyd backend - can be disabled entirely
+if (KEYD STREQUAL "ON")
+       LIST(APPEND BACKENDS keyd)
+
+       add_executable(keyd keyd.c)
+       target_link_libraries(keyd libonak)
+       add_executable(keydctl keydctl.c ../onak-conf.c)
+       target_link_libraries(keydctl libonak)
+       target_compile_definitions(keydctl PRIVATE
+               CONFIGDIR="${CMAKE_INSTALL_FULL_SYSCONFDIR}")
+
+       pkg_check_modules(SYSTEMD libsystemd)
+       if (SYSTEMD_FOUND)
+               set(HAVE_SYSTEMD true)
+               target_include_directories(keyd SYSTEM PUBLIC
+                       ${SYSTEMD_INCLUDE_DIRS})
+               target_link_libraries(keyd ${SYSTEMD_LIBRARIES})
+       endif()
+
+       install(TARGETS keydctl RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
+       install(TARGETS keyd RUNTIME DESTINATION ${CMAKE_INSTALL_SBINDIR})
+       install(FILES keyd.8 keydctl.8
+               DESTINATION ${CMAKE_INSTALL_MANDIR}/man8/)
+endif()
+
+if (DBTYPE STREQUAL "dynamic")
+       foreach(BACKEND IN LISTS BACKENDS)
+               add_library(keydb_${BACKEND} SHARED keydb_${BACKEND}.c)
+               target_include_directories(keydb_${BACKEND} SYSTEM PUBLIC
+                       ${BACKEND_${BACKEND}_INC})
+               target_link_libraries(keydb_${BACKEND} libonak
+                       ${BACKEND_${BACKEND}_LIBS})
+               install(TARGETS keydb_${BACKEND} LIBRARY DESTINATION
+                       ${CMAKE_INSTALL_LIBDIR}/onak/backends/)
+       endforeach(BACKEND)
+endif()
diff --git a/keydb/keyd.8 b/keydb/keyd.8
new file mode 100644 (file)
index 0000000..93cb31f
--- /dev/null
@@ -0,0 +1,42 @@
+.TH KEYD 8
+.SH NAME
+keyd \- Backend key serving daemon for the onak PGP keyserver 
+.SH SYNOPSIS
+.PP
+.B keyd
+[
+.B options
+]
+.SH DESCRIPTION
+.PP
+keyd is a backend daemon for the onak PGP keyserver. It listens on a Unix
+socket for connections from the various CGI and mail front ends and
+handles talking to the backend database. It is intended to be of use in
+high load keyservers to reduce the time spent in connecting and
+disconnecting from the key database.
+.PP
+keyd is currently fairly alpha code; it is only recommended that you use
+it if you know what you are doing.
+.SS "Options"
+.TP
+\fB\-c \fIFILE\fR\fR
+Use \fIFILE\fR as the config file instead of the default.
+.TP
+\fB\-f\fR
+Run in the foreground rather than forking and daemonising.
+.TP
+\fB\-h\fR
+Display help text.
+.SH FILES
+.br
+.nf
+.\" set tabstop to longest possible filename, plus a wee bit
+.ta \w'/usr/lib/perl/getopts.pl   'u
+\fI/etc/onak.ini\fR    default configuration file
+.SH NOTES
+This man page could probably do with some more details.
+.SH "SEE ALSO"
+.BR onak (1)
+.SH AUTHOR
+onak was written by Jonathan McDowell <noodles@earth.li>. It can be found at
+http://www.earth.li/projectpurple/progs/onak.html
diff --git a/keydb/keyd.c b/keydb/keyd.c
new file mode 100644 (file)
index 0000000..ddd1366
--- /dev/null
@@ -0,0 +1,709 @@
+/*
+ * keyd.c - key retrieval daemon
+ *
+ * Copyright 2004,2011 Jonathan McDowell <noodles@earth.li>
+ *
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "build-config.h"
+
+#ifdef HAVE_SYSTEMD
+#include <systemd/sd-daemon.h>
+#endif
+
+#include "charfuncs.h"
+#include "cleanup.h"
+#include "keyd.h"
+#include "keydb.h"
+#include "keyid.h"
+#include "keystructs.h"
+#include "log.h"
+#include "mem.h"
+#include "onak-conf.h"
+#include "parsekey.h"
+
+/* Maximum number of clients we're prepared to accept at once */
+#define MAX_CLIENTS 16
+
+#ifdef HAVE_SYSTEMD
+static bool using_socket_activation = false;
+#endif
+
+static struct keyd_stats *stats;
+
+static void daemonize(void)
+{
+       pid_t pid;
+
+       pid = fork();
+
+       if (pid < 0) {
+               logthing(LOGTHING_CRITICAL,
+                       "Failed to fork into background: %d (%s)",
+                       errno,
+                       strerror(errno));
+               exit(EXIT_FAILURE);
+       } else if (pid > 0) {
+               logthing(LOGTHING_INFO, "Backgrounded as pid %d.", pid);
+               exit(EXIT_SUCCESS);
+       }
+
+       if (setsid() == -1) {
+               logthing(LOGTHING_CRITICAL,
+                       "Couldn't set process group leader: %d (%s)",
+                       errno,
+                       strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+
+       if (!freopen("/dev/null", "r", stdin)) {
+               logthing(LOGTHING_CRITICAL,
+                       "Couldn't reopen stdin to NULL: %d (%s)",
+                       errno,
+                       strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+       if (!freopen("/dev/null", "w", stdout)) {
+               logthing(LOGTHING_CRITICAL,
+                       "Couldn't reopen stdout to NULL: %d (%s)",
+                       errno,
+                       strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+       if (!freopen("/dev/null", "w", stderr)) {
+               logthing(LOGTHING_CRITICAL,
+                       "Couldn't reopen stderr to NULL: %d (%s)",
+                       errno,
+                       strerror(errno));
+               exit(EXIT_FAILURE);
+       }
+
+       return;
+}
+
+static bool keyd_write_key(int fd, struct openpgp_publickey *key)
+{
+       struct openpgp_packet_list *packets = NULL;
+       struct openpgp_packet_list *list_end = NULL;
+       struct buffer_ctx           storebuf;
+       ssize_t written;
+       bool    ok = true;
+
+       storebuf.offset = 0;
+       storebuf.size = 8192;
+       storebuf.buffer = malloc(8192);
+
+       flatten_publickey(key,
+                               &packets,
+                               &list_end);
+       write_openpgp_stream(buffer_putchar,
+                               &storebuf,
+                               packets);
+       logthing(LOGTHING_TRACE,
+                               "Sending %d bytes.",
+                               storebuf.offset);
+       written = write(fd, &storebuf.offset,
+                       sizeof(storebuf.offset));
+       if (written == 0) {
+               ok = false;
+       } else {
+               written = write(fd, storebuf.buffer,
+                       storebuf.offset);
+               if (written != storebuf.offset) {
+                       ok = false;
+               }
+       }
+
+       free(storebuf.buffer);
+       storebuf.buffer = NULL;
+       storebuf.size = storebuf.offset = 0;
+       free_packet_list(packets);
+       packets = list_end = NULL;
+
+       return (ok);
+}
+
+static bool keyd_write_reply(int fd, enum keyd_reply _reply)
+{
+       uint32_t reply = _reply;
+       ssize_t written;
+       bool ok = true;
+
+       written = write(fd, &reply, sizeof(reply));
+       if (written != sizeof(reply)) {
+               ok = false;
+       }
+
+       return (ok);
+}
+
+static bool keyd_write_size(int fd, size_t size)
+{
+       ssize_t written;
+       bool ok = true;
+
+       written = write(fd, &size, sizeof(size));
+       if (written != sizeof(size)) {
+               ok = false;
+       }
+
+       return (ok);
+}
+
+static void iteratefunc(void *ctx, struct openpgp_publickey *key)
+{
+       int      *fd = (int *) ctx;
+       uint64_t  keyid;
+
+       if (key != NULL) {
+               get_keyid(key, &keyid);
+               logthing(LOGTHING_TRACE,
+                               "Iterating over 0x%016" PRIX64 ".",
+                               keyid);
+
+               keyd_write_key(*fd, key);
+       }
+
+       return;
+}
+
+static int sock_init(const char *sockname)
+{
+       struct sockaddr_un sock;
+       int                fd = -1;
+       int                ret = -1;
+#ifdef HAVE_SYSTEMD
+       int                n;
+
+       n = sd_listen_fds(0);
+       if (n > 1) {
+               logthing(LOGTHING_ERROR,
+                       "Too many file descriptors received from systemd.");
+       } else if (n == 1) {
+               fd = SD_LISTEN_FDS_START + 0;
+               if (sd_is_socket_unix(fd, SOCK_STREAM, 1, NULL, 0) <= 0) {
+                       logthing(LOGTHING_ERROR,
+                               "systemd passed an invalid socket.");
+                       fd = -1;
+               }
+               using_socket_activation = true;
+       } else {
+#endif
+               fd = socket(PF_UNIX, SOCK_STREAM, 0);
+               if (fd != -1) {
+                       ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
+               }
+
+               if (ret != -1) {
+                       sock.sun_family = AF_UNIX;
+                       strncpy(sock.sun_path, sockname,
+                                       sizeof(sock.sun_path) - 1);
+                       unlink(sockname);
+                       ret = bind(fd, (struct sockaddr *) &sock,
+                                       sizeof(sock));
+               }
+
+               if (ret != -1) {
+                       ret = listen(fd, 5);
+                       if (ret == -1) {
+                               close(fd);
+                               fd = -1;
+                       }
+               }
+#ifdef HAVE_SYSTEMD
+       }
+#endif
+
+       return fd;
+}
+
+static int sock_do(struct onak_dbctx *dbctx, int fd)
+{
+       uint32_t cmd = KEYD_CMD_UNKNOWN;
+       ssize_t  bytes = 0;
+       ssize_t  count = 0;
+       int      ret = 0;
+       uint64_t keyid = 0;
+       char     *search = NULL;
+       struct openpgp_publickey *key = NULL;
+       struct openpgp_packet_list *packets = NULL;
+       struct buffer_ctx storebuf;
+       struct skshash hash;
+       struct openpgp_fingerprint fingerprint;
+
+       /*
+        * Get the command from the client.
+        */
+       bytes = read(fd, &cmd, sizeof(cmd));
+
+       logthing(LOGTHING_DEBUG, "Read %d bytes, command: %d", bytes, cmd);
+
+       if (bytes != sizeof(cmd)) {
+               ret = 1;
+       }
+       
+       if (ret == 0) {
+               if (cmd < KEYD_CMD_LAST) {
+                       stats->command_stats[cmd]++;
+               } else {
+                       stats->command_stats[KEYD_CMD_UNKNOWN]++;
+               }
+               switch (cmd) {
+               case KEYD_CMD_VERSION:
+                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
+                               ret = 1;
+                       }
+                       if (ret == 0) {
+                               cmd = sizeof(keyd_version);
+                               bytes = write(fd, &cmd, sizeof(cmd));
+                               if (bytes != sizeof(cmd)) {
+                                       ret = 1;
+                               }
+                       }
+                       if (ret == 0) {
+                               bytes = write(fd, &keyd_version,
+                                       sizeof(keyd_version));
+                               if (bytes != sizeof(keyd_version)) {
+                                       ret = 1;
+                               }
+                       }
+                       break;
+               case KEYD_CMD_GET_ID:
+                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
+                               ret = 1;
+                       }
+                       if (ret == 0) {
+                               bytes = read(fd, &keyid, sizeof(keyid));
+                               if (bytes != sizeof(keyid)) {
+                                       ret = 1;
+                               }
+                       }
+                       if (ret == 0) {
+                               logthing(LOGTHING_INFO,
+                                               "Fetching 0x%" PRIX64
+                                               ", result: %d",
+                                               keyid,
+                                               dbctx->fetch_key_id(dbctx,
+                                                       keyid,
+                                                       &key, false));
+                               if (key != NULL) {
+                                       keyd_write_key(fd, key);
+                                       free_publickey(key);
+                                       key = NULL;
+                               } else {
+                                       if (!keyd_write_size(fd, 0)) {
+                                               ret = 1;
+                                       }
+                               }
+                       }
+                       break;
+               case KEYD_CMD_GET_FP:
+                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
+                               ret = 1;
+                       }
+                       if (ret == 0) {
+                               if ((read(fd, &bytes, 1) != 1) ||
+                                               (bytes > MAX_FINGERPRINT_LEN)) {
+                                       ret = 1;
+                               } else {
+                                       fingerprint.length = bytes;
+                                       bytes = read(fd, fingerprint.fp,
+                                               fingerprint.length);
+                                       if (bytes != fingerprint.length) {
+                                               ret = 1;
+                                       }
+                               }
+                       }
+                       if (ret == 0) {
+                               logthing(LOGTHING_INFO,
+                                               "Fetching by fingerprint"
+                                               ", result: %d",
+                                               dbctx->fetch_key_fp(dbctx,
+                                                       &fingerprint,
+                                                       &key, false));
+                               if (key != NULL) {
+                                       keyd_write_key(fd, key);
+                                       free_publickey(key);
+                                       key = NULL;
+                               } else {
+                                       if (!keyd_write_size(fd, 0)) {
+                                               ret = 1;
+                                       }
+                               }
+                       }
+                       break;
+
+               case KEYD_CMD_GET_TEXT:
+                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
+                               ret = 1;
+                       }
+                       if (ret == 0) {
+                               bytes = read(fd, &count, sizeof(count));
+                               if (bytes != sizeof(count)) {
+                                       ret = 1;
+                               }
+                       }
+                       if (ret == 0) {
+                               search = malloc(count+1);
+                               bytes = read(fd, search, count);
+                               if (bytes != count) {
+                                       ret = 1;
+                                       free(search);
+                                       break;
+                               }
+                               search[count] = 0;
+                               logthing(LOGTHING_INFO,
+                                               "Fetching %s, result: %d",
+                                               search,
+                                               dbctx->fetch_key_text(dbctx,
+                                                       search, &key));
+                               if (key != NULL) {
+                                       keyd_write_key(fd, key);
+                                       free_publickey(key);
+                                       key = NULL;
+                               } else {
+                                       if (!keyd_write_size(fd, 0)) {
+                                               ret = 1;
+                                       }
+                               }
+                               free(search);
+                       }
+                       break;
+               case KEYD_CMD_STORE:
+               case KEYD_CMD_UPDATE:
+                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
+                               ret = 1;
+                       }
+                       if (ret == 0) {
+                               bytes = read(fd, &storebuf.size,
+                                       sizeof(storebuf.size));
+                               logthing(LOGTHING_TRACE, "Reading %d bytes.",
+                                       storebuf.size);
+                               if (bytes != sizeof(storebuf.size)) {
+                                       ret = 1;
+                               }
+                       }
+                       if (ret == 0 && storebuf.size > 0) {
+                               storebuf.buffer = malloc(storebuf.size);
+                               storebuf.offset = 0;
+                               bytes = count = 0;
+                               while (bytes >= 0 && count < storebuf.size) {
+                                       bytes = read(fd,
+                                               &storebuf.buffer[count],
+                                               storebuf.size - count);
+                                       logthing(LOGTHING_TRACE,
+                                                       "Read %d bytes.",
+                                                       bytes);
+                                       count += bytes;
+                               }
+                               read_openpgp_stream(buffer_fetchchar,
+                                               &storebuf,
+                                               &packets,
+                                               0);
+                               parse_keys(packets, &key);
+                               dbctx->store_key(dbctx, key, false,
+                                       (cmd == KEYD_CMD_UPDATE));
+                               free_packet_list(packets);
+                               packets = NULL;
+                               free_publickey(key);
+                               key = NULL;
+                               free(storebuf.buffer);
+                               storebuf.buffer = NULL;
+                               storebuf.size = storebuf.offset = 0;
+                       }
+                       break;
+               case KEYD_CMD_DELETE:
+                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
+                               ret = 1;
+                       }
+                       if (ret == 0) {
+                               bytes = read(fd, &fingerprint,
+                                               sizeof(fingerprint));
+                               if (bytes != sizeof(fingerprint)) {
+                                       ret = 1;
+                               }
+                       }
+                       if (ret == 0) {
+                               logthing(LOGTHING_INFO,
+                                               "Deleting 0x%" PRIX64
+                                               ", result: %d",
+                                               keyid,
+                                               dbctx->delete_key(dbctx,
+                                                       &fingerprint, false));
+                       }
+                       break;
+               case KEYD_CMD_KEYITER:
+                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
+                               ret = 1;
+                       }
+                       if (ret == 0) {
+                               dbctx->iterate_keys(dbctx, iteratefunc,
+                                       &fd);
+                               if (!keyd_write_size(fd, 0)) {
+                                       ret = 1;
+                               }
+                       }
+                       break;
+               case KEYD_CMD_CLOSE:
+                       /* We're going to close the FD even if this fails */
+                       (void) keyd_write_reply(fd, KEYD_REPLY_OK);
+                       ret = 1;
+                       break;
+               case KEYD_CMD_QUIT:
+                       /* We're going to quit even if this fails */
+                       (void) keyd_write_reply(fd, KEYD_REPLY_OK);
+                       logthing(LOGTHING_NOTICE,
+                               "Exiting due to quit request.");
+                       ret = 1;
+                       trytocleanup();
+                       break;
+               case KEYD_CMD_STATS:
+                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
+                               ret = 1;
+                       }
+                       if (ret == 0) {
+                               cmd = sizeof(*stats);
+                               bytes = write(fd, &cmd, sizeof(cmd));
+                               if (bytes != sizeof(cmd)) {
+                                       ret = 1;
+                               }
+                       }
+                       if (ret == 0) {
+                               bytes = write(fd, stats, sizeof(*stats));
+                               if (bytes != sizeof(*stats)) {
+                                       ret = 1;
+                               }
+                       }
+                       break;
+               case KEYD_CMD_GET_SKSHASH:
+                       if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
+                               ret = 1;
+                       }
+                       if (ret == 0) {
+                               bytes = read(fd, hash.hash, sizeof(hash.hash));
+                               if (bytes != sizeof(hash.hash)) {
+                                       ret = 1;
+                               }
+                       }
+                       if (ret == 0) {
+                               logthing(LOGTHING_INFO,
+                                               "Fetching by hash"
+                                               ", result: %d",
+                                               dbctx->fetch_key_skshash(dbctx,
+                                                       &hash, &key));
+                               if (key != NULL) {
+                                       keyd_write_key(fd, key);
+                                       free_publickey(key);
+                                       key = NULL;
+                               } else {
+                                       if (!keyd_write_size(fd, 0)) {
+                                               ret = 1;
+                                       }
+                               }
+                       }
+                       break;
+
+               default:
+                       logthing(LOGTHING_ERROR, "Got unknown command: %d",
+                                       cmd);
+                       if (!keyd_write_reply(fd, KEYD_REPLY_UNKNOWN_CMD)) {
+                               ret = 1;
+                       }
+               }
+       }
+
+       return(ret);
+}
+
+static int sock_close(int fd)
+{
+       shutdown(fd, SHUT_RDWR);
+       return close(fd);
+}
+
+static int sock_accept(int fd)
+{
+       struct sockaddr_un sock;
+       socklen_t          socklen;
+       int    srv = -1;
+       int    ret = -1;
+
+       socklen = sizeof(sock);
+       srv = accept(fd, (struct sockaddr *) &sock, &socklen);
+       if (srv != -1) {
+               ret = fcntl(srv, F_SETFD, FD_CLOEXEC);
+       }
+
+       if (ret != -1) {
+               stats->connects++;
+       }
+
+       return (srv);
+}
+
+static void usage(void)
+{
+       puts("keyd " ONAK_VERSION " - backend key serving daemon for the "
+               "onak PGP keyserver.\n");
+       puts("Usage:\n");
+       puts("\tkeyd [options]\n");
+       puts("\tOptions:\n:");
+       puts("-c <file> - use <file> as the config file");
+       puts("-f        - run in the foreground");
+       puts("-h        - show this help text");
+       exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+       int fd = -1, maxfd, i, clients[MAX_CLIENTS];
+       fd_set rfds = { 0 }; /* Avoid scan-build false report for FD_SET */
+       char sockname[100];
+       char *configfile = NULL;
+       bool foreground = false;
+       int optchar;
+       struct onak_dbctx *dbctx;
+
+       while ((optchar = getopt(argc, argv, "c:fh")) != -1 ) {
+               switch (optchar) {
+               case 'c':
+                       if (configfile != NULL) {
+                               free(configfile);
+                       }
+                       configfile = strdup(optarg);
+                       break;
+               case 'f':
+                       foreground = true;
+                       break;
+               case 'h':
+               default:
+                       usage();
+                       break;
+               }
+       }
+
+       readconfig(configfile);
+       free(configfile);
+       configfile = NULL;
+       initlogthing("keyd", config.logfile);
+       config.use_keyd = false;
+
+       if (!foreground) {
+               daemonize();
+       }
+
+       catchsignals();
+       signal(SIGPIPE, SIG_IGN);
+
+
+       stats = calloc(1, sizeof(*stats));
+       if (!stats) {
+               logthing(LOGTHING_ERROR,
+                       "Couldn't allocate memory for stats structure.");
+               exit(EXIT_FAILURE);
+       }
+       stats->started = time(NULL);
+
+       snprintf(sockname, sizeof(sockname) - 1, "%s/%s",
+                       config.sock_dir, KEYD_SOCKET);
+       fd = sock_init(sockname);
+
+       if (fd != -1) {
+               FD_ZERO(&rfds);
+               FD_SET(fd, &rfds);
+               maxfd = fd;
+               memset(clients, -1, sizeof (clients));
+
+               dbctx = config.dbinit(config.backend, false);
+
+               logthing(LOGTHING_NOTICE, "Accepting connections.");
+               while (!cleanup() && select(maxfd + 1, &rfds, NULL, NULL, NULL) != -1) {
+                       /*
+                        * Deal with existing clients first; if we're at our
+                        * connection limit then processing them might free
+                        * things up and let us accept the next client below.
+                        */
+                       for (i = 0; i < MAX_CLIENTS; i++) {
+                               if (clients[i] != -1 &&
+                                               FD_ISSET(clients[i], &rfds)) {
+                                       logthing(LOGTHING_DEBUG,
+                                               "Handling connection for client %d.", i);
+                                       if (sock_do(dbctx, clients[i])) {
+                                               sock_close(clients[i]);
+                                               clients[i] = -1;
+                                               logthing(LOGTHING_DEBUG,
+                                                       "Closed connection for client %d.", i);
+                                       }
+                               }
+                       }
+                       /*
+                        * Check if we have a new incoming connection to accept.
+                        */
+                       if (FD_ISSET(fd, &rfds)) {
+                               for (i = 0; i < MAX_CLIENTS; i++) {
+                                       if (clients[i] == -1) {
+                                               break;
+                                       }
+                               }
+                               if (i < MAX_CLIENTS) {
+                                       logthing(LOGTHING_INFO,
+                                               "Accepted connection %d.", i);
+                                       clients[i] = sock_accept(fd);
+                               }
+                       }
+                       FD_ZERO(&rfds);
+                       FD_SET(fd, &rfds);
+                       maxfd = fd;
+                       for (i = 0; i < MAX_CLIENTS; i++) {
+                               if (clients[i] != -1) {
+                                       FD_SET(clients[i], &rfds);
+                                       maxfd = (maxfd > clients[i]) ?
+                                                       maxfd : clients[i];
+                               }
+                       }
+               }
+               dbctx->cleanupdb(dbctx);
+#ifdef HAVE_SYSTEMD
+               if (!using_socket_activation) {
+#endif
+                       sock_close(fd);
+                       unlink(sockname);
+#ifdef HAVE_SYSTEMD
+               }
+#endif
+       }
+
+       free(stats);
+
+       cleanuplogthing();
+       cleanupconfig();
+
+       return(EXIT_SUCCESS);
+}
diff --git a/keydb/keyd.h b/keydb/keyd.h
new file mode 100644 (file)
index 0000000..1ea601c
--- /dev/null
@@ -0,0 +1,76 @@
+/**
+ * @file keyd.h
+ * @brief Public API for keyd.
+ *
+ * Copyright 2004,2011 Jonathan McDowell <noodles@earth.li>
+ *
+ * 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/>.
+ */
+
+#ifndef __KEYD_H__
+#define __KEYD_H__
+
+#include <stdint.h>
+
+/**
+ * @brief The name of the keyd Unix domain socket
+ */
+#define KEYD_SOCKET "keyd.sock"
+
+/**
+ * @brief keyd commands
+ */
+enum keyd_ops {
+       KEYD_CMD_UNKNOWN = 0,
+       KEYD_CMD_VERSION = 1,
+       KEYD_CMD_GET_ID,
+       KEYD_CMD_STORE,
+       KEYD_CMD_DELETE,
+       KEYD_CMD_GET_TEXT,
+       KEYD_CMD_GETFULLKEYID,
+       KEYD_CMD_KEYITER,
+       KEYD_CMD_CLOSE,
+       KEYD_CMD_QUIT,
+       KEYD_CMD_STATS,
+       KEYD_CMD_GET_SKSHASH,
+       KEYD_CMD_GET_FP,
+       KEYD_CMD_UPDATE,
+       KEYD_CMD_LAST                   /* Placeholder */
+};
+
+/**
+ * @brief Reply codes for keyd commands
+ */
+enum keyd_reply {
+       KEYD_REPLY_OK = 0,
+       KEYD_REPLY_UNKNOWN_CMD = 1
+};
+
+/**
+ * @brief Version of the keyd protocol currently supported
+ */
+static const uint32_t keyd_version = 5;
+
+/**
+ * @brief Response structure for the @a KEYD_CMD_STATS response
+ */
+struct keyd_stats {
+       /** Unix time of when the keyd daemon was started */
+       time_t started;
+       /** Number of connects we've seen to keyd */
+       uint32_t connects;
+       /** Count of the number of times each command has been used */
+       uint32_t command_stats[KEYD_CMD_LAST];
+};
+
+#endif /* __KEYD_H__ */
diff --git a/keydb/keydb.c b/keydb/keydb.c
new file mode 100644 (file)
index 0000000..f6b9682
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ * keydb.c - Routines for DB access that just use store/fetch.
+ *
+ * Copyright 2002-2004 Jonathan McDowell <noodles@earth.li>
+ *
+ * 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/>.
+ */
+
+/**
+ *     The routines in this file are meant to be used as an initial step when
+ *     adding a new db access module. They provide various functions required
+ *     of the db access module using only the store and fetch functions. As
+ *     they need to parse the actual OpenPGP data to work they are a lot
+ *     slower than custom functions however.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+
+#include "decodekey.h"
+#include "hash.h"
+#include "keydb.h"
+#include "keyid.h"
+#include "keystructs.h"
+#include "ll.h"
+#include "mem.h"
+#include "merge.h"
+#include "openpgp.h"
+#include "sendsync.h"
+#include "stats.h"
+
+#ifdef NEED_KEYID2UID
+/**
+ *     keyid2uid - Takes a keyid and returns the primary UID for it.
+ *     @keyid: The keyid to lookup.
+ */
+char *generic_keyid2uid(struct onak_dbctx *dbctx, uint64_t keyid)
+{
+       struct openpgp_publickey *publickey = NULL;
+       struct openpgp_signedpacket_list *curuid = NULL;
+       char buf[1024];
+
+       buf[0]=0;
+       if (dbctx->fetch_key_id(dbctx, keyid, &publickey, false) &&
+                       publickey != NULL) {
+               curuid = publickey->uids;
+               while (curuid != NULL && buf[0] == 0) {
+                       if (curuid->packet->tag == OPENPGP_PACKET_UID) {
+                               snprintf(buf, 1023, "%.*s",
+                                               (int) curuid->packet->length,
+                                               curuid->packet->data);
+                       }
+                       curuid = curuid -> next;
+               }
+               free_publickey(publickey);
+       }
+
+       if (buf[0] == 0) {
+               return NULL;
+       } else {
+               return strdup(buf);
+       }
+}
+#endif
+
+#ifdef NEED_GETKEYSIGS
+/**
+ *     getkeysigs - Gets a linked list of the signatures on a key.
+ *     @keyid: The keyid to get the sigs for.
+ *     @revoked: Is the key revoked?
+ *
+ *     This function gets the list of signatures on a key. Used for key 
+ *     indexing and doing stats bits. If revoked is non-NULL then if the key
+ *     is revoked it's set to true.
+ */
+struct ll *generic_getkeysigs(struct onak_dbctx *dbctx,
+               uint64_t keyid, bool *revoked)
+{
+       struct ll *sigs = NULL;
+       struct openpgp_signedpacket_list *uids = NULL;
+       struct openpgp_packet_list *cursig;
+       struct openpgp_publickey *publickey = NULL;
+
+       dbctx->fetch_key_id(dbctx, keyid, &publickey, false);
+       
+       if (publickey != NULL) {
+               for (uids = publickey->uids; uids != NULL; uids = uids->next) {
+                       for (cursig = uids->sigs; cursig != NULL;
+                                       cursig = cursig->next) {
+                               sigs = lladd(sigs,
+                                               createandaddtohash(sig_keyid(
+                                                       cursig->packet)));
+                       }
+               }
+               if (revoked != NULL) {
+                       *revoked = publickey->revoked;
+               }
+               free_publickey(publickey);
+       }
+
+       return sigs;
+}
+#endif
+
+/**
+ *     cached_getkeysigs - Gets the signatures on a key.
+ *     @keyid: The key we want the signatures for.
+ *     
+ *     This function gets the signatures on a key. It's the same as the
+ *     getkeysigs function above except we use the hash module to cache the
+ *     data so if we need it again it's already loaded.
+ */
+struct ll *generic_cached_getkeysigs(struct onak_dbctx *dbctx, uint64_t keyid)
+{
+       struct stats_key *key = NULL;
+       struct stats_key *signedkey = NULL;
+       struct ll        *cursig = NULL;
+       struct ll        *sigs = NULL;
+       bool              revoked = false;
+
+       if (keyid == 0)  {
+               return NULL;
+       }
+
+       key = findinhash(keyid);
+
+       if (key == NULL || key->gotsigs == false) {
+               sigs = dbctx->getkeysigs(dbctx, keyid, &revoked);
+               if (sigs == NULL) {
+                       return NULL;
+               }
+               if (key == NULL) {
+                       key = createandaddtohash(keyid);
+               }
+               key->sigs = sigs;
+               key->revoked = revoked;
+               for (cursig = key->sigs; cursig != NULL;
+                               cursig = cursig->next) {
+                       signedkey = (struct stats_key *) cursig->object;
+                       signedkey->signs = lladd(signedkey->signs, key);
+               }
+               key->gotsigs = true;
+       }
+
+       return key->sigs;
+}
+
+#ifdef NEED_UPDATEKEYS
+/**
+ *     update_keys - Takes a list of public keys and updates them in the DB.
+ *     @keys: The keys to update in the DB.
+ *     @blacklist: A keyarray of key fingerprints not to accept.
+ *     @updateonly: Only update existing keys, don't add new ones.
+ *     @sendsync: Should we send a sync mail to our peers.
+ *
+ *     Takes a list of keys and adds them to the database, merging them with
+ *     the key in the database if it's already present there. The key list is
+ *     update to contain the minimum set of updates required to get from what
+ *     we had before to what we have now (ie the set of data that was added to
+ *     the DB). Returns the number of entirely new keys added.
+ */
+int generic_update_keys(struct onak_dbctx *dbctx,
+               struct openpgp_publickey **keys,
+               struct keyarray *blacklist,
+               bool updateonly,
+               bool sendsync)
+{
+       struct openpgp_publickey **curkey, *tmp = NULL;
+       struct openpgp_publickey *oldkey = NULL;
+       struct openpgp_fingerprint fp;
+       int newkeys = 0, ret;
+       bool intrans;
+
+       curkey = keys;
+       while (*curkey != NULL) {
+               get_fingerprint((*curkey)->publickey, &fp);
+               if (blacklist && array_find(blacklist, &fp)) {
+                       logthing(LOGTHING_INFO, "Ignoring blacklisted key.");
+                       tmp = *curkey;
+                       *curkey = (*curkey)->next;
+                       tmp->next = NULL;
+                       free_publickey(tmp);
+                       continue;
+               }
+
+               intrans = dbctx->starttrans(dbctx);
+
+               ret = dbctx->fetch_key_fp(dbctx, &fp, &oldkey, intrans);
+               if (ret == 0 && updateonly) {
+                       logthing(LOGTHING_INFO,
+                               "Skipping new key as update only set.");
+                       curkey = &(*curkey)->next;
+                       goto next;
+               }
+
+               /*
+                * If we already have the key stored in the DB then merge it
+                * with the new one that's been supplied. Otherwise the key
+                * we've just got is the one that goes in the DB and also the
+                * one that we send out.
+                */
+               if (oldkey != NULL) {
+                       merge_keys(oldkey, *curkey);
+                       if ((*curkey)->sigs == NULL &&
+                                       (*curkey)->uids == NULL &&
+                                       (*curkey)->subkeys == NULL) {
+                               tmp = *curkey;
+                               *curkey = (*curkey)->next;
+                               tmp->next = NULL;
+                               free_publickey(tmp);
+                       } else {
+                               logthing(LOGTHING_INFO,
+                                       "Merged key; storing updated key.");
+                               dbctx->store_key(dbctx, oldkey, intrans,
+                                       true);
+                               curkey = &(*curkey)->next;
+                       }
+                       free_publickey(oldkey);
+                       oldkey = NULL;
+               } else {
+                       logthing(LOGTHING_INFO,
+                               "Storing completely new key.");
+                       dbctx->store_key(dbctx, *curkey, intrans, false);
+                       newkeys++;
+                       curkey = &(*curkey)->next;
+               }
+next:
+               dbctx->endtrans(dbctx);
+       }
+
+       if (sendsync && keys != NULL && *keys != NULL) {
+               sendkeysync(*keys);
+       }
+
+       return newkeys;
+}
+#endif /* NEED_UPDATEKEYS */
+
+#ifdef NEED_GET_FP
+static int generic_fetch_key_fp(struct onak_dbctx *dbctx,
+               struct openpgp_fingerprint *fingerprint,
+               struct openpgp_publickey **publickey, bool intrans)
+{
+       uint64_t keyid;
+       int i;
+
+       if (fingerprint->length > MAX_FINGERPRINT_LEN) {
+               return 0;
+       }
+
+       /*
+        * We assume if the backend is using this function it's not storing
+        * anything bigger than the 64 bit key ID and just truncate the
+        * fingerprint to get that value. v4 keys want the lowest 64 bits, v5
+        * keys need the top 64 bits.  This doesn't work for v3 keys,
+        * but there's no way to map from v3 fingerprint to v3 key ID so
+        * if the backend can't do it we're going to fail anyway.
+        */
+       keyid = 0;
+       if (fingerprint->length == 20) {
+               /* v4 */
+               for (i = (fingerprint->length - 8); i < fingerprint->length;
+                               i++) {
+                       keyid = (keyid << 8) + fingerprint->fp[i];
+               }
+       } else {
+               /* v5 */
+               for (i = 0; i < 8; i++) {
+                       keyid = (keyid << 8) + fingerprint->fp[i];
+               }
+       }
+
+       return dbctx->fetch_key_id(dbctx, keyid, publickey, intrans);
+}
+#endif
diff --git a/keydb/keydb_db4.c b/keydb/keydb_db4.c
new file mode 100644 (file)
index 0000000..1f306e2
--- /dev/null
@@ -0,0 +1,1760 @@
+/*
+ * keydb_db4.c - Routines to store and fetch keys in a DB4 database.
+ *
+ * Copyright 2002-2008 Jonathan McDowell <noodles@earth.li>
+ *
+ * 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 <sys/types.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <db.h>
+
+#include "charfuncs.h"
+#include "keyarray.h"
+#include "keydb.h"
+#include "keyid.h"
+#include "decodekey.h"
+#include "keystructs.h"
+#include "mem.h"
+#include "ll.h"
+#include "log.h"
+#include "onak.h"
+#include "onak-conf.h"
+#include "parsekey.h"
+#include "wordlist.h"
+
+#define DB4_UPGRADE_FILE "db_upgrade.lck"
+
+struct onak_db4_dbctx {
+       DB_ENV *dbenv;  /* The database environment context */
+       int numdbs;     /* Number of data databases in use */
+       DB **dbconns;   /* Connections to the key data databases */
+       DB *worddb;     /* Connection to the word lookup database */
+       DB *id32db;     /* Connection to the 32 bit ID lookup database */
+       DB *id64db;     /* Connection to the 64 bit ID lookup database */
+       DB *skshashdb;  /* Connection to the SKS hash database */
+       DB *subkeydb;   /* Connection to the subkey ID lookup database */
+       DB_TXN *txn;    /* Our current transaction ID */
+};
+
+DB *keydb_id(struct onak_db4_dbctx *privctx, uint64_t keyid)
+{
+       uint64_t keytrun;
+
+       keytrun = keyid >> 8;
+
+       return(privctx->dbconns[keytrun % privctx->numdbs]);
+}
+
+DB *keydb_fp(struct onak_db4_dbctx *privctx, struct openpgp_fingerprint *fp)
+{
+       uint64_t keytrun;
+
+       keytrun = fp->fp[4];
+       keytrun <<= 8;
+       keytrun |= fp->fp[5];
+       keytrun <<= 8;
+       keytrun |= fp->fp[6];
+       keytrun <<= 8;
+       keytrun |= fp->fp[7];
+
+       return(privctx->dbconns[keytrun % privctx->numdbs]);
+}
+
+/**
+ *     db4_errfunc - Direct DB errors to logfile
+ *
+ *     Basic function to take errors from the DB library and output them to
+ *     the logfile rather than stderr.
+ */
+#if (DB_VERSION_MAJOR == 4) && (DB_VERSION_MINOR < 3)
+static void db4_errfunc(const char *errpfx, const char *errmsg)
+#else
+static void db4_errfunc(const DB_ENV *edbenv, const char *errpfx,
+               const char *errmsg)
+#endif
+{
+       if (errpfx) {
+               logthing(LOGTHING_DEBUG, "db4 error: %s:%s", errpfx, errmsg);
+       } else {
+               logthing(LOGTHING_DEBUG, "db4 error: %s", errmsg);
+       }
+
+       return;
+}
+
+/**
+ *     starttrans - Start a transaction.
+ *
+ *     Start a transaction. Intended to be used if we're about to perform many
+ *     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 db4_starttrans(struct onak_dbctx *dbctx)
+{
+       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
+       int ret;
+
+       log_assert(privctx->dbenv != NULL);
+       log_assert(privctx->txn == NULL);
+
+       ret = privctx->dbenv->txn_begin(privctx->dbenv,
+               NULL, /* No parent transaction */
+               &privctx->txn,
+               0);
+       if (ret != 0) {
+               logthing(LOGTHING_CRITICAL,
+                               "Error starting transaction: %s",
+                               db_strerror(ret));
+               exit(1);
+       }
+
+       return true;
+}
+
+/**
+ *     endtrans - End a transaction.
+ *
+ *     Ends a transaction.
+ */
+static void db4_endtrans(struct onak_dbctx *dbctx)
+{
+       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
+       int ret;
+
+       log_assert(privctx->dbenv != NULL);
+       log_assert(privctx->txn != NULL);
+
+       ret = privctx->txn->commit(privctx->txn,
+               0);
+       if (ret != 0) {
+               logthing(LOGTHING_CRITICAL,
+                               "Error ending transaction: %s",
+                               db_strerror(ret));
+               exit(1);
+       }
+       privctx->txn = NULL;
+
+       return;
+}
+
+/**
+ *     db4_upgradedb - Upgrade a DB4 database
+ *
+ *     Called if we discover we need to upgrade our DB4 database; ie if
+ *     we're running with a newer version of db4 than the database was
+ *     created with.
+ */
+static int db4_upgradedb(struct onak_dbctx *dbctx)
+{
+       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
+       DB *curdb = NULL;
+       int ret;
+       int i;
+       char buf[1024];
+       int lockfile_fd;
+       struct stat statbuf;
+       ssize_t written;
+
+       snprintf(buf, sizeof(buf) - 1, "%s/%s", dbctx->config->location,
+                       DB4_UPGRADE_FILE);
+       lockfile_fd = open(buf, O_RDWR | O_CREAT | O_EXCL, 0600);
+       if (lockfile_fd < 0) {
+               if (errno == EEXIST) {
+                       while (stat(buf, &statbuf) == 0) ;
+                       return 0;
+               } else {
+                       logthing(LOGTHING_CRITICAL, "Couldn't open database "
+                               "update lock file: %s", strerror(errno));
+                       return -1;
+               }
+       }
+       snprintf(buf, sizeof(buf) - 1, "%d", getpid());
+       written = write(lockfile_fd, buf, strlen(buf));
+       close(lockfile_fd);
+       if (written != strlen(buf)) {
+               logthing(LOGTHING_CRITICAL, "Couldn't write PID to lockfile: "
+                               "%s", strerror(errno));
+               snprintf(buf, sizeof(buf) - 1, "%s/%s", dbctx->config->location,
+                               DB4_UPGRADE_FILE);
+               unlink(buf);
+               return -1;
+       }
+
+       logthing(LOGTHING_NOTICE, "Upgrading DB4 database");
+       ret = db_env_create(&privctx->dbenv, 0);
+       if (ret == 0) {
+               privctx->dbenv->set_errcall(privctx->dbenv, &db4_errfunc);
+               privctx->dbenv->remove(privctx->dbenv, dbctx->config->location, 0);
+               privctx->dbenv = NULL;
+       }
+       for (i = 0; i < privctx->numdbs; i++) {
+               ret = db_create(&curdb, NULL, 0);
+               if (ret == 0) {
+                       snprintf(buf, sizeof(buf) - 1, "%s/keydb.%d.db",
+                               dbctx->config->location, i);
+                       logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
+                       curdb->upgrade(curdb, buf, 0);
+                       curdb->close(curdb, 0);
+               } else {
+                       logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
+                               buf,
+                               db_strerror(ret));
+               }
+       }
+
+       ret = db_create(&curdb, NULL, 0);
+       if (ret == 0) {
+               snprintf(buf, sizeof(buf) - 1, "%s/worddb", dbctx->config->location);
+               logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
+               curdb->upgrade(curdb, buf, 0);
+               curdb->close(curdb, 0);
+       } else {
+               logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
+                       buf,
+                       db_strerror(ret));
+       }
+
+       ret = db_create(&curdb, NULL, 0);
+       if (ret == 0) {
+               snprintf(buf, sizeof(buf) - 1, "%s/id32db", dbctx->config->location);
+               logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
+               curdb->upgrade(curdb, buf, 0);
+               curdb->close(curdb, 0);
+       } else {
+               logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
+                       buf,
+                       db_strerror(ret));
+       }
+
+       ret = db_create(&curdb, NULL, 0);
+       if (ret == 0) {
+               snprintf(buf, sizeof(buf) - 1, "%s/id64db", dbctx->config->location);
+               logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
+               curdb->upgrade(curdb, buf, 0);
+               curdb->close(curdb, 0);
+       } else {
+               logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
+                       buf,
+                       db_strerror(ret));
+       }
+
+       ret = db_create(&curdb, NULL, 0);
+       if (ret == 0) {
+               snprintf(buf, sizeof(buf) - 1, "%s/skshashdb", dbctx->config->location);
+               logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
+               curdb->upgrade(curdb, buf, 0);
+               curdb->close(curdb, 0);
+       } else {
+               logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
+                       buf,
+                       db_strerror(ret));
+       }
+
+       ret = db_create(&curdb, NULL, 0);
+       if (ret == 0) {
+               snprintf(buf, sizeof(buf) - 1, "%s/subkeydb", dbctx->config->location);
+               logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
+               curdb->upgrade(curdb, buf, 0);
+               curdb->close(curdb, 0);
+       } else {
+               logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
+                       buf,
+                       db_strerror(ret));
+       }
+
+       snprintf(buf, sizeof(buf) - 1, "%s/%s", dbctx->config->location,
+                       DB4_UPGRADE_FILE);
+       unlink(buf);
+
+       return ret;
+}
+
+/**
+ *     fetch_key_fp - Given a fingerprint fetch the key from storage.
+ */
+static int db4_fetch_key_fp(struct onak_dbctx *dbctx,
+               struct openpgp_fingerprint *fingerprint,
+               struct openpgp_publickey **publickey,
+               bool intrans)
+{
+       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
+       struct openpgp_packet_list *packets = NULL;
+       DBT key, data;
+       int ret = 0;
+       int numkeys = 0;
+       struct buffer_ctx fetchbuf;
+       struct openpgp_fingerprint subfp;
+
+       memset(&key, 0, sizeof(key));
+       memset(&data, 0, sizeof(data));
+
+       data.size = 0;
+       data.data = NULL;
+
+       key.size = fingerprint->length;
+       key.data = fingerprint->fp;
+
+       if (!intrans) {
+               db4_starttrans(dbctx);
+       }
+
+       ret = keydb_fp(privctx, fingerprint)->get(keydb_fp(privctx,
+                                                       fingerprint),
+                       privctx->txn,
+                       &key,
+                       &data,
+                       0); /* flags*/
+
+       if (ret == DB_NOTFOUND) {
+               /* If we didn't find the key ID see if it's a subkey ID */
+               memset(&key, 0, sizeof(key));
+               memset(&data, 0, sizeof(data));
+               data.data = subfp.fp;
+               data.ulen = MAX_FINGERPRINT_LEN;
+               data.flags = DB_DBT_USERMEM;
+               key.data = fingerprint->fp;
+               key.size = fingerprint->length;
+
+               ret = privctx->subkeydb->get(privctx->subkeydb,
+                       privctx->txn,
+                       &key,
+                       &data,
+                       0); /* flags*/
+
+               if (ret == 0) {
+                       /* We got a subkey match; retrieve the actual key */
+                       memset(&key, 0, sizeof(key));
+                       key.size = subfp.length = data.size;
+                       key.data = subfp.fp;
+
+                       memset(&data, 0, sizeof(data));
+                       data.size = 0;
+                       data.data = NULL;
+
+                       ret = keydb_fp(privctx, &subfp)->get(
+                               keydb_fp(privctx, &subfp),
+                               privctx->txn,
+                               &key,
+                               &data,
+                               0); /* flags*/
+               }
+       }
+
+       if (ret == 0) {
+               fetchbuf.buffer = data.data;
+               fetchbuf.offset = 0;
+               fetchbuf.size = data.size;
+               read_openpgp_stream(buffer_fetchchar, &fetchbuf,
+                               &packets, 0);
+               parse_keys(packets, publickey);
+               free_packet_list(packets);
+               packets = NULL;
+               numkeys++;
+       } else if (ret != DB_NOTFOUND) {
+               logthing(LOGTHING_ERROR,
+                               "Problem retrieving key: %s",
+                               db_strerror(ret));
+       }
+
+       if (!intrans) {
+               db4_endtrans(dbctx);
+       }
+
+       return (numkeys);
+}
+
+/**
+ *     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.
+ *
+ *     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 db4_fetch_key_id(struct onak_dbctx *dbctx, uint64_t keyid,
+               struct openpgp_publickey **publickey,
+               bool intrans)
+{
+       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
+       DBT key, data;
+       DBC *cursor = NULL;
+       int ret = 0;
+       int numkeys = 0;
+       uint32_t  shortkeyid = 0;
+       struct openpgp_fingerprint fingerprint;
+       bool first;
+
+       if (!intrans) {
+               db4_starttrans(dbctx);
+       }
+
+       /* If the key ID fits in 32 bits assume it's a short key id */
+       if (keyid < 0x100000000LL) {
+               ret = privctx->id32db->cursor(privctx->id32db,
+                               privctx->txn,
+                               &cursor,
+                               0);   /* flags */
+
+               shortkeyid = keyid & 0xFFFFFFFF;
+               memset(&key, 0, sizeof(key));
+               memset(&data, 0, sizeof(data));
+               key.data = &shortkeyid;
+               key.size = sizeof(shortkeyid);
+       } else {
+               ret = privctx->id64db->cursor(privctx->id64db,
+                               privctx->txn,
+                               &cursor,
+                               0); /* flags*/
+
+               memset(&key, 0, sizeof(key));
+               memset(&data, 0, sizeof(data));
+               key.data = &keyid;
+               key.size = sizeof(keyid);
+       }
+
+       if (ret != 0) {
+               return 0;
+       }
+
+       memset(&data, 0, sizeof(data));
+       data.ulen = MAX_FINGERPRINT_LEN;
+       data.data = fingerprint.fp;
+       data.flags = DB_DBT_USERMEM;
+
+       first = true;
+       while (cursor->c_get(cursor, &key, &data,
+                               first ? DB_SET : DB_NEXT_DUP) == 0) {
+               /* We got a match; retrieve the actual key */
+               fingerprint.length = data.size;
+
+               if (db4_fetch_key_fp(dbctx, &fingerprint,
+                                       publickey, true))
+                       numkeys++;
+
+               memset(&data, 0, sizeof(data));
+               data.ulen = MAX_FINGERPRINT_LEN;
+               data.data = fingerprint.fp;
+               data.flags = DB_DBT_USERMEM;
+               first = false;
+       }
+       cursor->c_close(cursor);
+       cursor = NULL;
+
+       if (!intrans) {
+               db4_endtrans(dbctx);
+       }
+
+       return (numkeys);
+}
+
+/**
+ *     fetch_key_text - Trys 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.
+ */
+static int db4_fetch_key_text(struct onak_dbctx *dbctx, const char *search,
+               struct openpgp_publickey **publickey)
+{
+       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
+       DBC *cursor = NULL;
+       DBT key, data;
+       int ret;
+       int i;
+       int numkeys;
+       char *searchtext = NULL;
+       struct ll *wordlist = NULL;
+       struct ll *curword = NULL;
+       struct keyarray keylist = { NULL, 0, 0 };
+       struct keyarray newkeylist = { NULL, 0, 0 };
+       int firstpass = 1;
+       struct openpgp_fingerprint fingerprint;
+
+       numkeys = 0;
+       searchtext = strdup(search);
+       wordlist = makewordlist(wordlist, searchtext);
+
+       for (curword = wordlist; curword != NULL; curword = curword->next) {
+               db4_starttrans(dbctx);
+
+               ret = privctx->worddb->cursor(privctx->worddb,
+                               privctx->txn,
+                               &cursor,
+                               0);   /* flags */
+
+               if (ret != 0) {
+                       db4_endtrans(dbctx);
+                       break;
+               }
+
+               memset(&key, 0, sizeof(key));
+               memset(&data, 0, sizeof(data));
+               key.data = curword->object;
+               key.size = strlen(curword->object);
+               data.flags = DB_DBT_MALLOC;
+               ret = cursor->c_get(cursor,
+                               &key,
+                               &data,
+                               DB_SET);
+               while (ret == 0 && strncmp(key.data, curword->object,
+                                       key.size) == 0 &&
+                               ((char *) curword->object)[key.size] == 0) {
+
+                       fingerprint.length = data.size;
+                       memcpy(fingerprint.fp, data.data, data.size);
+
+                       /*
+                        * Only add the keys containing this word if this is
+                        * our first pass (ie we have no existing key list),
+                        * or the key contained a previous word.
+                        */
+                       if (firstpass || array_find(&keylist, &fingerprint)) {
+                               array_add(&newkeylist, &fingerprint);
+                       }
+
+                       free(data.data);
+                       data.data = NULL;
+
+                       ret = cursor->c_get(cursor,
+                                       &key,
+                                       &data,
+                                       DB_NEXT);
+               }
+               array_free(&keylist);
+               keylist.keys = newkeylist.keys;
+               keylist.count = newkeylist.count;
+               keylist.size = newkeylist.size;
+               newkeylist.keys = NULL;
+               newkeylist.count = newkeylist.size = 0;
+               if (data.data != NULL) {
+                       free(data.data);
+                       data.data = NULL;
+               }
+               cursor->c_close(cursor);
+               cursor = NULL;
+               firstpass = 0;
+               db4_endtrans(dbctx);
+       }
+       llfree(wordlist, NULL);
+       wordlist = NULL;
+
+       if (keylist.count > config.maxkeys) {
+               keylist.count = config.maxkeys;
+       }
+
+       db4_starttrans(dbctx);
+       for (i = 0; i < keylist.count; i++) {
+               numkeys += db4_fetch_key_fp(dbctx, &keylist.keys[i],
+                       publickey,
+                       true);
+       }
+       array_free(&keylist);
+       free(searchtext);
+       searchtext = NULL;
+
+       db4_endtrans(dbctx);
+
+       return (numkeys);
+}
+
+static int db4_fetch_key_skshash(struct onak_dbctx *dbctx,
+               const struct skshash *hash,
+               struct openpgp_publickey **publickey)
+{
+       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
+       DBT       key, data;
+       DBC      *cursor = NULL;
+       int       ret;
+       int       count = 0;
+       struct openpgp_fingerprint fingerprint;
+
+       ret = privctx->skshashdb->cursor(privctx->skshashdb,
+                       privctx->txn,
+                       &cursor,
+                       0);   /* flags */
+
+       if (ret != 0) {
+               return 0;
+       }
+
+       memset(&key, 0, sizeof(key));
+       memset(&data, 0, sizeof(data));
+       key.data = (void *) hash->hash;
+       key.size = sizeof(hash->hash);
+       data.ulen = MAX_FINGERPRINT_LEN;
+       data.data = fingerprint.fp;
+       data.flags = DB_DBT_USERMEM;
+
+       ret = cursor->c_get(cursor,
+               &key,
+               &data,
+               DB_SET);
+
+       if (ret == 0) {
+               fingerprint.length = data.size;
+               count = db4_fetch_key_fp(dbctx, &fingerprint,
+                       publickey, false);
+       }
+
+       cursor->c_close(cursor);
+       cursor = NULL;
+
+       return count;
+}
+
+/**
+ *     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 db4_delete_key(struct onak_dbctx *dbctx,
+               struct openpgp_fingerprint *fp,
+               bool intrans)
+{
+       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
+       struct openpgp_publickey *publickey = NULL;
+       DBT key, data;
+       DBC *cursor = NULL;
+       DBC *cursor64 = NULL;
+       uint32_t shortkeyid = 0;
+       uint64_t subkeyid = 0;
+       struct openpgp_fingerprint *subkeyids = NULL;
+       int ret = 0;
+       int i;
+       char **uids = NULL;
+       char *primary = NULL;
+       struct ll *wordlist = NULL;
+       struct ll *curword  = NULL;
+       bool deadlock = false;
+       struct skshash hash;
+       uint64_t keyid;
+
+       if (!intrans) {
+               db4_starttrans(dbctx);
+       }
+
+       if (db4_fetch_key_fp(dbctx, fp, &publickey, true) == 0) {
+               if (!intrans) {
+                       db4_endtrans(dbctx);
+               }
+               return 1;
+       }
+
+       if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
+               return 1;
+       }
+
+       /*
+        * Walk through the uids removing the words from the worddb.
+        */
+       if (publickey != NULL) {
+               uids = keyuids(publickey, &primary);
+       }
+       if (uids != NULL) {
+               for (i = 0; ret == 0 && uids[i] != NULL; i++) {
+                       wordlist = makewordlist(wordlist, uids[i]);
+               }
+
+               privctx->worddb->cursor(privctx->worddb,
+                       privctx->txn,
+                       &cursor,
+                       0);   /* flags */
+
+               for (curword = wordlist; curword != NULL && !deadlock;
+                               curword = curword->next) {
+                       /*
+                        * New style uses the fingerprint as the data
+                        * Old (unsupported) style was the 64 bit keyid
+                        */
+                       memset(&key, 0, sizeof(key));
+                       memset(&data, 0, sizeof(data));
+                       key.data = curword->object;
+                       key.size = strlen(key.data);
+                       data.data = fp->fp;
+                       data.size = fp->length;
+
+                       ret = cursor->c_get(cursor,
+                               &key,
+                               &data,
+                               DB_GET_BOTH);
+
+                       if (ret == 0) {
+                               ret = cursor->c_del(cursor, 0);
+                       }
+
+                       if (ret != 0 && ret != DB_NOTFOUND) {
+                               logthing(LOGTHING_ERROR,
+                                       "Problem deleting word: %s "
+                                       "(0x%016" PRIX64 ")",
+                                       db_strerror(ret),
+                                       keyid);
+                               if (ret == DB_LOCK_DEADLOCK) {
+                                       deadlock = true;
+                               }
+                       }
+               }
+               cursor->c_close(cursor);
+               cursor = NULL;
+
+               /*
+                * Free our UID and word lists.
+                */
+               llfree(wordlist, NULL);
+               for (i = 0; uids[i] != NULL; i++) {
+                       free(uids[i]);
+                       uids[i] = NULL;
+               }
+               free(uids);
+               uids = NULL;
+       }
+
+       if (!deadlock) {
+               privctx->id32db->cursor(privctx->id32db,
+                       privctx->txn,
+                       &cursor,
+                       0);   /* flags */
+               privctx->id64db->cursor(privctx->id64db,
+                       privctx->txn,
+                       &cursor64,
+                       0);   /* flags */
+
+               /* 32 bit short key mapping to fingerprint */
+               shortkeyid = keyid & 0xFFFFFFFF;
+
+               memset(&key, 0, sizeof(key));
+               memset(&data, 0, sizeof(data));
+               key.data = &shortkeyid;
+               key.size = sizeof(shortkeyid);
+               data.data = fp->fp;
+               data.size = fp->length;
+
+               ret = cursor->c_get(cursor,
+                       &key,
+                       &data,
+                       DB_GET_BOTH);
+
+               if (ret == 0) {
+                       ret = cursor->c_del(cursor, 0);
+               }
+
+               if (ret != 0 && ret != DB_NOTFOUND) {
+                       logthing(LOGTHING_ERROR,
+                               "Problem deleting short keyid: %s "
+                               "(0x%016" PRIX64 ")",
+                               db_strerror(ret),
+                               keyid);
+                       if (ret == DB_LOCK_DEADLOCK) {
+                               deadlock = true;
+                       }
+               }
+
+               /* 64 bit key mapping to fingerprint */
+               memset(&key, 0, sizeof(key));
+               memset(&data, 0, sizeof(data));
+               key.data = &keyid;
+               key.size = sizeof(keyid);
+               data.data = fp->fp;
+               data.size = fp->length;
+
+               ret = cursor64->c_get(cursor64,
+                       &key,
+                       &data,
+                       DB_GET_BOTH);
+
+               if (ret == 0) {
+                       ret = cursor64->c_del(cursor64, 0);
+               }
+
+               if (ret != 0 && ret != DB_NOTFOUND) {
+                       logthing(LOGTHING_ERROR,
+                               "Problem deleting keyid: %s "
+                               "(0x%016" PRIX64 ")",
+                               db_strerror(ret),
+                               keyid);
+                       if (ret == DB_LOCK_DEADLOCK) {
+                               deadlock = true;
+                       }
+               }
+
+               subkeyids = keysubkeys(publickey);
+               i = 0;
+               while (subkeyids != NULL && subkeyids[i].length != 0) {
+                       subkeyid = fingerprint2keyid(&subkeyids[i]);
+                       memset(&key, 0, sizeof(key));
+                       key.data = subkeyids[i].fp;
+                       key.size = subkeyids[i].length;
+                       ret = privctx->subkeydb->del(privctx->subkeydb,
+                                       privctx->txn, &key, 0);
+                       if (ret != 0 && ret != DB_NOTFOUND) {
+                               logthing(LOGTHING_ERROR,
+                                       "Problem deleting subkey id: %s "
+                                       "(0x%016" PRIX64 ")",
+                                       db_strerror(ret),
+                                       keyid);
+                               if (ret == DB_LOCK_DEADLOCK) {
+                                       deadlock = true;
+                               }
+                       }
+
+                       shortkeyid = subkeyid & 0xFFFFFFFF;
+
+                       /* Remove 32 bit keyid -> fingerprint mapping */
+                       memset(&key, 0, sizeof(key));
+                       memset(&data, 0, sizeof(data));
+                       key.data = &shortkeyid;
+                       key.size = sizeof(shortkeyid);
+                       data.data = fp->fp;
+                       data.size = fp->length;
+
+                       ret = cursor->c_get(cursor,
+                               &key,
+                               &data,
+                               DB_GET_BOTH);
+
+                       if (ret == 0) {
+                               ret = cursor->c_del(cursor, 0);
+                       }
+
+                       if (ret != 0 && ret != DB_NOTFOUND) {
+                               logthing(LOGTHING_ERROR,
+                                       "Problem deleting short keyid: %s "
+                                       "(0x%016" PRIX64 ")",
+                                       db_strerror(ret),
+                                       keyid);
+                               if (ret == DB_LOCK_DEADLOCK) {
+                                       deadlock = true;
+                               }
+                       }
+
+                       /* Remove 64 bit keyid -> fingerprint mapping */
+                       memset(&key, 0, sizeof(key));
+                       memset(&data, 0, sizeof(data));
+                       key.data = &subkeyid;
+                       key.size = sizeof(subkeyid);
+                       data.data = fp->fp;
+                       data.size = fp->length;
+
+                       ret = cursor64->c_get(cursor64,
+                               &key,
+                               &data,
+                               DB_GET_BOTH);
+
+                       if (ret == 0) {
+                               ret = cursor64->c_del(cursor64, 0);
+                       }
+
+                       if (ret != 0 && ret != DB_NOTFOUND) {
+                               logthing(LOGTHING_ERROR,
+                                       "Problem deleting keyid: %s "
+                                       "(0x%016" PRIX64 ")",
+                                       db_strerror(ret),
+                                       keyid);
+                               if (ret == DB_LOCK_DEADLOCK) {
+                                       deadlock = true;
+                               }
+                       }
+                       i++;
+               }
+               if (subkeyids != NULL) {
+                       free(subkeyids);
+                       subkeyids = NULL;
+               }
+               cursor64->c_close(cursor64);
+               cursor64 = NULL;
+               cursor->c_close(cursor);
+               cursor = NULL;
+       }
+
+       if (!deadlock) {
+               ret = privctx->skshashdb->cursor(privctx->skshashdb,
+                       privctx->txn,
+                       &cursor,
+                       0);   /* flags */
+               if (ret == 0) {
+                       get_skshash(publickey, &hash);
+
+                       /* Remove SKS hash -> fingerprint mapping */
+                       memset(&key, 0, sizeof(key));
+                       memset(&data, 0, sizeof(data));
+                       key.data = hash.hash;
+                       key.size = sizeof(hash.hash);
+                       data.data = fp->fp;
+                       data.size = fp->length;
+
+                       ret = cursor->c_get(cursor,
+                               &key,
+                               &data,
+                               DB_GET_BOTH);
+
+                       if (ret == 0) {
+                               ret = cursor->c_del(cursor, 0);
+                       }
+
+                       if (ret != 0 && ret != DB_NOTFOUND) {
+                               logthing(LOGTHING_ERROR,
+                                       "Problem deleting skshash: %s "
+                                       "(0x%016" PRIX64 ")",
+                                       db_strerror(ret),
+                                       keyid);
+                               if (ret == DB_LOCK_DEADLOCK) {
+                                       deadlock = true;
+                               }
+                       }
+
+                       cursor->c_close(cursor);
+                       cursor = NULL;
+               }
+       }
+       free_publickey(publickey);
+       publickey = NULL;
+
+       if (!deadlock) {
+               key.data = fp->fp;
+               key.size = fp->length;
+
+               keydb_fp(privctx, fp)->del(keydb_fp(privctx, fp),
+                               privctx->txn,
+                               &key,
+                               0); /* flags */
+       }
+
+       if (!intrans) {
+               db4_endtrans(dbctx);
+       }
+
+       return deadlock ? (-1) : (ret == DB_NOTFOUND);
+}
+
+/**
+ *     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.
+ *
+ *     Again we just use the hex representation of the keyid as the filename
+ *     to store the key to. We flatten the public key to a list of OpenPGP
+ *     packets and then use write_openpgp_stream() to write the stream out to
+ *     the file. If update is true then we delete the old key first, otherwise
+ *     we trust that it doesn't exist.
+ */
+static int db4_store_key(struct onak_dbctx *dbctx,
+               struct openpgp_publickey *publickey, bool intrans,
+               bool update)
+{
+       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
+       struct     openpgp_packet_list *packets = NULL;
+       struct     openpgp_packet_list *list_end = NULL;
+       struct     openpgp_publickey *next = NULL;
+       int        ret = 0;
+       int        i = 0;
+       struct     buffer_ctx storebuf;
+       DBT        key;
+       DBT        data;
+       uint64_t   keyid = 0;
+       uint32_t   shortkeyid = 0;
+       struct openpgp_fingerprint *subkeyids = NULL;
+       char     **uids = NULL;
+       char      *primary = NULL;
+       struct ll *wordlist = NULL;
+       struct ll *curword  = NULL;
+       bool       deadlock = false;
+       struct skshash hash;
+       struct openpgp_fingerprint fingerprint;
+
+       if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
+               logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
+               return 0;
+       }
+
+       if (get_fingerprint(publickey->publickey, &fingerprint) != ONAK_E_OK) {
+               logthing(LOGTHING_ERROR, "Couldn't find fingerprint for key.");
+               return 0;
+       }
+
+       if (!intrans) {
+               db4_starttrans(dbctx);
+       }
+
+       /*
+        * Delete the key if we already have it.
+        *
+        * TODO: Can we optimize this perhaps? Possibly when other data is
+        * involved as well? I suspect this is easiest and doesn't make a lot
+        * of difference though - the largest chunk of data is the keydata and
+        * it definitely needs updated.
+        */
+       if (update) {
+               deadlock = (db4_delete_key(dbctx, &fingerprint, true) == -1);
+       }
+
+       /*
+        * Convert the key to a flat set of binary data.
+        */
+       if (!deadlock) {
+               next = publickey->next;
+               publickey->next = NULL;
+               flatten_publickey(publickey, &packets, &list_end);
+               publickey->next = next;
+
+               storebuf.offset = 0;
+               storebuf.size = 8192;
+               storebuf.buffer = malloc(8192);
+
+               write_openpgp_stream(buffer_putchar, &storebuf, packets);
+
+               /*
+                * Now we have the key data store it in the DB; the fingerprint
+                * is the key.
+                */
+               memset(&key, 0, sizeof(key));
+               memset(&data, 0, sizeof(data));
+               key.data = fingerprint.fp;
+               key.size = fingerprint.length;
+               data.size = storebuf.offset;
+               data.data = storebuf.buffer;
+
+               ret = keydb_fp(privctx, &fingerprint)->put(
+                               keydb_fp(privctx, &fingerprint),
+                               privctx->txn,
+                               &key,
+                               &data,
+                               0); /* flags*/
+               if (ret != 0) {
+                       logthing(LOGTHING_ERROR,
+                                       "Problem storing key: %s",
+                                       db_strerror(ret));
+                       if (ret == DB_LOCK_DEADLOCK) {
+                               deadlock = true;
+                       }
+               }
+
+               free(storebuf.buffer);
+               storebuf.buffer = NULL;
+               storebuf.size = 0;
+               storebuf.offset = 0;
+
+               free_packet_list(packets);
+               packets = NULL;
+       }
+
+       /*
+        * Walk through our uids storing the words into the db with the
+        * fingerprint.
+        */
+       if (!deadlock) {
+               uids = keyuids(publickey, &primary);
+       }
+       if (uids != NULL) {
+               for (i = 0; ret == 0 && uids[i] != NULL; i++) {
+                       wordlist = makewordlist(wordlist, uids[i]);
+               }
+
+               for (curword = wordlist; curword != NULL && !deadlock;
+                               curword = curword->next) {
+                       memset(&key, 0, sizeof(key));
+                       memset(&data, 0, sizeof(data));
+                       key.data = curword->object;
+                       key.size = strlen(key.data);
+                       data.data = fingerprint.fp;
+                       data.size = fingerprint.length;
+
+                       ret = privctx->worddb->put(privctx->worddb,
+                               privctx->txn,
+                               &key,
+                               &data,
+                               0);
+                       if (ret != 0) {
+                               logthing(LOGTHING_ERROR,
+                                       "Problem storing word: %s",
+                                       db_strerror(ret));
+                               if (ret == DB_LOCK_DEADLOCK) {
+                                       deadlock = true;
+                               }
+                       }
+               }
+
+               /*
+                * Free our UID and word lists.
+                */
+               llfree(wordlist, NULL);
+               for (i = 0; uids[i] != NULL; i++) {
+                       free(uids[i]);
+                       uids[i] = NULL;
+               }
+               free(uids);
+               uids = NULL;
+       }
+
+       /*
+        * Write the truncated 32 bit keyid so we can lookup the fingerprint
+        * for queries.
+        */
+       if (!deadlock) {
+               shortkeyid = keyid & 0xFFFFFFFF;
+
+               memset(&key, 0, sizeof(key));
+               memset(&data, 0, sizeof(data));
+               key.data = &shortkeyid;
+               key.size = sizeof(shortkeyid);
+               data.data = fingerprint.fp;
+               data.size = fingerprint.length;
+
+               ret = privctx->id32db->put(privctx->id32db,
+                       privctx->txn,
+                       &key,
+                       &data,
+                       0);
+               if (ret != 0) {
+                       logthing(LOGTHING_ERROR,
+                               "Problem storing short keyid: %s",
+                               db_strerror(ret));
+                       if (ret == DB_LOCK_DEADLOCK) {
+                               deadlock = true;
+                       }
+               }
+       }
+
+       /*
+        * Write the 64 bit keyid so we can lookup the fingerprint for
+        * queries.
+        */
+       if (!deadlock) {
+               memset(&key, 0, sizeof(key));
+               memset(&data, 0, sizeof(data));
+               key.data = &keyid;
+               key.size = sizeof(keyid);
+               data.data = fingerprint.fp;
+               data.size = fingerprint.length;
+
+               ret = privctx->id64db->put(privctx->id64db,
+                       privctx->txn,
+                       &key,
+                       &data,
+                       0);
+               if (ret != 0) {
+                       logthing(LOGTHING_ERROR,
+                               "Problem storing keyid: %s",
+                               db_strerror(ret));
+                       if (ret == DB_LOCK_DEADLOCK) {
+                               deadlock = true;
+                       }
+               }
+       }
+
+       if (!deadlock) {
+               subkeyids = keysubkeys(publickey);
+               i = 0;
+               while (subkeyids != NULL && subkeyids[i].length != 0) {
+                       /* Store the subkey ID -> main key fp mapping */
+                       memset(&key, 0, sizeof(key));
+                       memset(&data, 0, sizeof(data));
+                       key.data = subkeyids[i].fp;
+                       key.size = subkeyids[i].length;
+                       data.data = fingerprint.fp;
+                       data.size = fingerprint.length;
+
+                       ret = privctx->subkeydb->put(privctx->subkeydb,
+                               privctx->txn,
+                               &key,
+                               &data,
+                               0);
+                       if (ret != 0) {
+                               logthing(LOGTHING_ERROR,
+                                       "Problem storing subkey keyid: %s",
+                                       db_strerror(ret));
+                               if (ret == DB_LOCK_DEADLOCK) {
+                                       deadlock = true;
+                               }
+                       }
+
+                       /* Store the 64 bit subkey ID -> main key fp mapping */
+                       memset(&key, 0, sizeof(key));
+                       memset(&data, 0, sizeof(data));
+
+                       keyid = fingerprint2keyid(&subkeyids[i]);
+                       key.data = &keyid;
+                       key.size = sizeof(keyid);
+                       data.data = fingerprint.fp;
+                       data.size = fingerprint.length;
+
+                       ret = privctx->id64db->put(privctx->id64db,
+                               privctx->txn,
+                               &key,
+                               &data,
+                               0);
+                       if (ret != 0) {
+                               logthing(LOGTHING_ERROR,
+                                       "Problem storing keyid: %s",
+                                       db_strerror(ret));
+                               if (ret == DB_LOCK_DEADLOCK) {
+                                       deadlock = true;
+                               }
+                       }
+
+                       /* Store the short subkey ID -> main key fp mapping */
+                       shortkeyid = keyid & 0xFFFFFFFF;
+
+                       memset(&key, 0, sizeof(key));
+                       memset(&data, 0, sizeof(data));
+                       key.data = &shortkeyid;
+                       key.size = sizeof(shortkeyid);
+                       data.data = fingerprint.fp;
+                       data.size = fingerprint.length;
+
+                       ret = privctx->id32db->put(privctx->id32db,
+                               privctx->txn,
+                               &key,
+                               &data,
+                               0);
+                       if (ret != 0) {
+                               logthing(LOGTHING_ERROR,
+                                       "Problem storing short keyid: %s",
+                                       db_strerror(ret));
+                               if (ret == DB_LOCK_DEADLOCK) {
+                                       deadlock = true;
+                               }
+                       }
+                       i++;
+               }
+               if (subkeyids != NULL) {
+                       free(subkeyids);
+                       subkeyids = NULL;
+               }
+       }
+
+       if (!deadlock) {
+               get_skshash(publickey, &hash);
+               memset(&key, 0, sizeof(key));
+               memset(&data, 0, sizeof(data));
+               key.data = hash.hash;
+               key.size = sizeof(hash.hash);
+               data.data = fingerprint.fp;
+               data.size = fingerprint.length;
+
+               ret = privctx->skshashdb->put(privctx->skshashdb,
+                       privctx->txn,
+                       &key,
+                       &data,
+                       0);
+               if (ret != 0) {
+                       logthing(LOGTHING_ERROR,
+                               "Problem storing SKS hash: %s",
+                               db_strerror(ret));
+                       if (ret == DB_LOCK_DEADLOCK) {
+                               deadlock = true;
+                       }
+               }
+       }
+
+       if (!intrans) {
+               db4_endtrans(dbctx);
+       }
+
+       return deadlock ? -1 : 0 ;
+}
+
+/**
+ *     iterate_keys - call a function once for each key in the db.
+ *     @iterfunc: The function to call.
+ *     @ctx: A context pointer
+ *
+ *     Calls iterfunc once for each key in the database. ctx is passed
+ *     unaltered to iterfunc. This function is intended to aid database dumps
+ *     and statistic calculations.
+ *
+ *     Returns the number of keys we iterated over.
+ */
+static int db4_iterate_keys(struct onak_dbctx *dbctx,
+               void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
+               void *ctx)
+{
+       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
+       DBT                         dbkey, data;
+       DBC                        *cursor = NULL;
+       int                         ret = 0;
+       int                         i = 0;
+       int                         numkeys = 0;
+       struct buffer_ctx           fetchbuf;
+       struct openpgp_packet_list *packets = NULL;
+       struct openpgp_publickey   *key = NULL;
+
+       for (i = 0; i < privctx->numdbs; i++) {
+               ret = privctx->dbconns[i]->cursor(privctx->dbconns[i],
+                       NULL,
+                       &cursor,
+                       0);   /* flags */
+
+               if (ret != 0) {
+                       continue;
+               }
+
+               memset(&dbkey, 0, sizeof(dbkey));
+               memset(&data, 0, sizeof(data));
+               ret = cursor->c_get(cursor, &dbkey, &data, DB_NEXT);
+               while (ret == 0) {
+                       fetchbuf.buffer = data.data;
+                       fetchbuf.offset = 0;
+                       fetchbuf.size = data.size;
+                       read_openpgp_stream(buffer_fetchchar, &fetchbuf,
+                               &packets, 0);
+                       parse_keys(packets, &key);
+
+                       iterfunc(ctx, key);
+
+                       free_publickey(key);
+                       key = NULL;
+                       free_packet_list(packets);
+                       packets = NULL;
+
+                       memset(&dbkey, 0, sizeof(dbkey));
+                       memset(&data, 0, sizeof(data));
+                       ret = cursor->c_get(cursor, &dbkey, &data,
+                                       DB_NEXT);
+                       numkeys++;
+               }
+               if (ret != DB_NOTFOUND) {
+                       logthing(LOGTHING_ERROR,
+                               "Problem reading key: %s",
+                               db_strerror(ret));
+               }
+
+               cursor->c_close(cursor);
+               cursor = NULL;
+       }
+
+       return numkeys;
+}
+
+/*
+ * Include the basic keydb routines.
+ */
+#define NEED_GETKEYSIGS 1
+#define NEED_KEYID2UID 1
+#define NEED_UPDATEKEYS 1
+#include "keydb.c"
+
+/**
+ *     cleanupdb - De-initialize the key database.
+ *
+ *     This function should be called upon program exit to allow the DB to
+ *     cleanup after itself.
+ */
+static void db4_cleanupdb(struct onak_dbctx *dbctx)
+{
+       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
+       int i = 0;
+
+       if (privctx->dbenv != NULL) {
+               privctx->dbenv->txn_checkpoint(privctx->dbenv, 0, 0, 0);
+               if (privctx->subkeydb != NULL) {
+                       privctx->subkeydb->close(privctx->subkeydb, 0);
+                       privctx->subkeydb = NULL;
+               }
+               if (privctx->skshashdb != NULL) {
+                       privctx->skshashdb->close(privctx->skshashdb, 0);
+                       privctx->skshashdb = NULL;
+               }
+               if (privctx->id64db != NULL) {
+                       privctx->id64db->close(privctx->id64db, 0);
+                       privctx->id64db = NULL;
+               }
+               if (privctx->id32db != NULL) {
+                       privctx->id32db->close(privctx->id32db, 0);
+                       privctx->id32db = NULL;
+               }
+               if (privctx->worddb != NULL) {
+                       privctx->worddb->close(privctx->worddb, 0);
+                       privctx->worddb = NULL;
+               }
+               for (i = 0; i < privctx->numdbs; i++) {
+                       if (privctx->dbconns[i] != NULL) {
+                               privctx->dbconns[i]->close(privctx->dbconns[i],
+                                               0);
+                               privctx->dbconns[i] = NULL;
+                       }
+               }
+               free(privctx->dbconns);
+               privctx->dbconns = NULL;
+               privctx->dbenv->close(privctx->dbenv, 0);
+               privctx->dbenv = NULL;
+       }
+
+       free(privctx);
+       dbctx->priv = 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_db4_init(struct onak_db_config *dbcfg, bool readonly)
+{
+       char       buf[1024];
+       FILE      *numdb = NULL;
+       int        ret = 0;
+       int        i = 0;
+       uint32_t   flags = 0;
+       struct stat statbuf;
+       int        maxlocks;
+       struct onak_dbctx *dbctx;
+       struct onak_db4_dbctx *privctx;
+
+       dbctx = malloc(sizeof(*dbctx));
+       if (dbctx == NULL) {
+               return NULL;
+       }
+       dbctx->config = dbcfg;
+       dbctx->priv = privctx = calloc(1, sizeof(*privctx));
+       if (privctx == NULL) {
+               free(dbctx);
+               return NULL;
+       }
+
+       /* Default to 16 key data DBs */
+       privctx->numdbs = 16;
+
+       snprintf(buf, sizeof(buf) - 1, "%s/%s", dbcfg->location,
+                       DB4_UPGRADE_FILE);
+       ret = stat(buf, &statbuf);
+       while ((ret == 0) || (errno != ENOENT)) {
+               if (ret != 0) {
+                       logthing(LOGTHING_CRITICAL, "Couldn't stat upgrade "
+                               "lock file: %s (%d)", strerror(errno), ret);
+                       exit(1);
+               }
+               logthing(LOGTHING_DEBUG, "DB4 upgrade in progress; waiting.");
+               sleep(5);
+               ret = stat(buf, &statbuf);
+       }
+       ret = 0;
+
+       snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", dbcfg->location);
+       numdb = fopen(buf, "r");
+       if (numdb != NULL) {
+               if (fgets(buf, sizeof(buf), numdb) != NULL) {
+                       privctx->numdbs = atoi(buf);
+               }
+               fclose(numdb);
+       } else if (!readonly) {
+               logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s",
+                               strerror(errno));
+               numdb = fopen(buf, "w");
+               if (numdb != NULL) {
+                       fprintf(numdb, "%d", privctx->numdbs);
+                       fclose(numdb);
+               } else {
+                       logthing(LOGTHING_ERROR,
+                               "Couldn't write num_keydb: %s",
+                               strerror(errno));
+               }
+       }
+
+       privctx->dbconns = calloc(privctx->numdbs, sizeof (DB *));
+       if (privctx->dbconns == NULL) {
+               logthing(LOGTHING_CRITICAL,
+                               "Couldn't allocate memory for dbconns");
+               ret = 1;
+       }
+
+       if (ret == 0) {
+               ret = db_env_create(&privctx->dbenv, 0);
+               if (ret != 0) {
+                       logthing(LOGTHING_CRITICAL,
+                               "db_env_create: %s", db_strerror(ret));
+               }
+       }
+
+       /*
+        * Up the number of locks we're allowed at once. We base this on
+        * the maximum number of keys we're going to return.
+        */
+       if (ret == 0) {
+               maxlocks = config.maxkeys * 16;
+               if (maxlocks < 1000) {
+                       maxlocks = 1000;
+               }
+               privctx->dbenv->set_lk_max_locks(privctx->dbenv, maxlocks);
+               privctx->dbenv->set_lk_max_objects(privctx->dbenv, maxlocks);
+       }
+
+       /*
+        * Enable deadlock detection so that we don't block indefinitely on
+        * anything. What we really want is simple 2 state locks, but I'm not
+        * sure how to make the standard DB functions do that yet.
+        */
+       if (ret == 0) {
+               privctx->dbenv->set_errcall(privctx->dbenv, &db4_errfunc);
+               ret = privctx->dbenv->set_lk_detect(privctx->dbenv, DB_LOCK_DEFAULT);
+               if (ret != 0) {
+                       logthing(LOGTHING_CRITICAL,
+                               "db_env_create: %s", db_strerror(ret));
+               }
+       }
+
+       if (ret == 0) {
+               ret = privctx->dbenv->open(privctx->dbenv, dbcfg->location,
+                               DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
+                               DB_INIT_TXN |
+                               DB_CREATE,
+                               0);
+#ifdef DB_VERSION_MISMATCH
+               if (ret == DB_VERSION_MISMATCH) {
+                       privctx->dbenv->close(privctx->dbenv, 0);
+                       privctx->dbenv = NULL;
+                       ret = db4_upgradedb(dbctx);
+                       if (ret == 0) {
+                               ret = db_env_create(&privctx->dbenv, 0);
+                       }
+                       if (ret == 0) {
+                               privctx->dbenv->set_errcall(privctx->dbenv,
+                                       &db4_errfunc);
+                               privctx->dbenv->set_lk_detect(privctx->dbenv,
+                                       DB_LOCK_DEFAULT);
+                               ret = privctx->dbenv->open(privctx->dbenv,
+                                       dbcfg->location,
+                                       DB_INIT_LOG | DB_INIT_MPOOL |
+                                       DB_INIT_LOCK | DB_INIT_TXN |
+                                       DB_CREATE | DB_RECOVER,
+                                       0);
+
+                               if (ret == 0) {
+                                       privctx->dbenv->txn_checkpoint(
+                                                       privctx->dbenv,
+                                                       0,
+                                                       0,
+                                                       DB_FORCE);
+                               }
+                       }
+               }
+#endif
+               if (ret != 0) {
+                       logthing(LOGTHING_CRITICAL,
+                                       "Error opening db environment: %s (%s)",
+                                       dbcfg->location,
+                                       db_strerror(ret));
+                       if (privctx->dbenv != NULL) {
+                               privctx->dbenv->close(privctx->dbenv, 0);
+                               privctx->dbenv = NULL;
+                       }
+               }
+       }
+
+       if (ret == 0) {
+               db4_starttrans(dbctx);
+
+               for (i = 0; !ret && i < privctx->numdbs; i++) {
+                       ret = db_create(&privctx->dbconns[i],
+                                       privctx->dbenv, 0);
+                       if (ret != 0) {
+                               logthing(LOGTHING_CRITICAL,
+                                       "db_create: %s", db_strerror(ret));
+                       }
+
+                       if (ret == 0) {
+                               snprintf(buf, 1023, "keydb.%d.db", i);
+                               flags = DB_CREATE;
+                               if (readonly) {
+                                       flags = DB_RDONLY;
+                               }
+                               ret = privctx->dbconns[i]->open(
+                                               privctx->dbconns[i],
+                                               privctx->txn,
+                                               buf,
+                                               "keydb",
+                                               DB_HASH,
+                                               flags,
+                                               0664);
+                               if (ret != 0) {
+                                       logthing(LOGTHING_CRITICAL,
+                                               "Error opening key database:"
+                                               " %s (%s)",
+                                               buf,
+                                               db_strerror(ret));
+                               }
+                       }
+               }
+       }
+
+       if (ret == 0) {
+               ret = db_create(&privctx->worddb, privctx->dbenv, 0);
+               if (ret != 0) {
+                       logthing(LOGTHING_CRITICAL, "db_create: %s",
+                                       db_strerror(ret));
+               }
+       }
+
+       if (ret == 0) {
+               ret = privctx->worddb->set_flags(privctx->worddb, DB_DUP);
+       }
+
+       if (ret == 0) {
+               ret = privctx->worddb->open(privctx->worddb, privctx->txn,
+                               "worddb", "worddb", DB_BTREE,
+                               flags,
+                               0664);
+               if (ret != 0) {
+                       logthing(LOGTHING_CRITICAL,
+                                       "Error opening word database: %s (%s)",
+                                       "worddb",
+                                       db_strerror(ret));
+               }
+       }
+
+       if (ret == 0) {
+               ret = db_create(&privctx->id32db, privctx->dbenv, 0);
+               if (ret != 0) {
+                       logthing(LOGTHING_CRITICAL, "db_create: %s",
+                                       db_strerror(ret));
+               }
+       }
+
+       if (ret == 0) {
+               ret = privctx->id32db->set_flags(privctx->id32db, DB_DUP);
+       }
+
+       if (ret == 0) {
+               ret = privctx->id32db->open(privctx->id32db, privctx->txn,
+                               "id32db", "id32db", DB_HASH,
+                               flags,
+                               0664);
+               if (ret != 0) {
+                       logthing(LOGTHING_CRITICAL,
+                                       "Error opening id32 database: %s (%s)",
+                                       "id32db",
+                                       db_strerror(ret));
+               }
+       }
+
+       if (ret == 0) {
+               ret = db_create(&privctx->id64db, privctx->dbenv, 0);
+               if (ret != 0) {
+                       logthing(LOGTHING_CRITICAL, "db_create: %s",
+                                       db_strerror(ret));
+               }
+       }
+
+       if (ret == 0) {
+               ret = privctx->id64db->set_flags(privctx->id64db, DB_DUP);
+       }
+
+       if (ret == 0) {
+               ret = privctx->id64db->open(privctx->id64db, privctx->txn,
+                               "id64db", "id64db", DB_HASH,
+                               flags,
+                               0664);
+               if (ret != 0) {
+                       logthing(LOGTHING_CRITICAL,
+                                       "Error opening id64 database: %s (%s)",
+                                       "id64db",
+                                       db_strerror(ret));
+               }
+       }
+
+       if (ret == 0) {
+               ret = db_create(&privctx->skshashdb, privctx->dbenv, 0);
+               if (ret != 0) {
+                       logthing(LOGTHING_CRITICAL, "db_create: %s",
+                                       db_strerror(ret));
+               }
+       }
+
+       if (ret == 0) {
+               ret = privctx->skshashdb->open(privctx->skshashdb, privctx->txn,
+                               "skshashdb",
+                               "skshashdb", DB_HASH,
+                               flags,
+                               0664);
+               if (ret != 0) {
+                       logthing(LOGTHING_CRITICAL,
+                               "Error opening skshash database: %s (%s)",
+                               "skshashdb",
+                               db_strerror(ret));
+               }
+       }
+
+       if (ret == 0) {
+               ret = db_create(&privctx->subkeydb, privctx->dbenv, 0);
+               if (ret != 0) {
+                       logthing(LOGTHING_CRITICAL, "db_create: %s",
+                                       db_strerror(ret));
+               }
+       }
+
+       if (ret == 0) {
+               ret = privctx->subkeydb->open(privctx->subkeydb, privctx->txn,
+                               "subkeydb", "subkeydb",
+                               DB_HASH,
+                               flags,
+                               0664);
+               if (ret != 0) {
+                       logthing(LOGTHING_CRITICAL,
+                               "Error opening subkey database: %s (%s)",
+                               "subkeydb",
+                               db_strerror(ret));
+               }
+       }
+
+       if (privctx->txn != NULL) {
+               db4_endtrans(dbctx);
+       }
+
+       if (ret != 0) {
+               db4_cleanupdb(dbctx);
+               logthing(LOGTHING_CRITICAL,
+                               "Error opening database; exiting");
+               exit(EXIT_FAILURE);
+       }
+
+       dbctx->cleanupdb                = db4_cleanupdb;
+       dbctx->starttrans               = db4_starttrans;
+       dbctx->endtrans                 = db4_endtrans;
+       dbctx->fetch_key_id             = db4_fetch_key_id;
+       dbctx->fetch_key_fp             = db4_fetch_key_fp;
+       dbctx->fetch_key_text           = db4_fetch_key_text;
+       dbctx->fetch_key_skshash        = db4_fetch_key_skshash;
+       dbctx->store_key                = db4_store_key;
+       dbctx->update_keys              = generic_update_keys;
+       dbctx->delete_key               = db4_delete_key;
+       dbctx->getkeysigs               = generic_getkeysigs;
+       dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
+       dbctx->keyid2uid                = generic_keyid2uid;
+       dbctx->iterate_keys             = db4_iterate_keys;
+
+       return dbctx;
+}
diff --git a/keydb/keydb_dynamic.c b/keydb/keydb_dynamic.c
new file mode 100644 (file)
index 0000000..385c09c
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * keydb_dynamic.c - backend that can load the other backends
+ *
+ * Copyright 2005 Brett Parker <iDunno@sommitrealweird.co.uk>
+ *
+ * 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 <dlfcn.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "decodekey.h"
+#include "hash.h"
+#include "keydb.h"
+#include "keyid.h"
+#include "keystructs.h"
+#include "log.h"
+#include "mem.h"
+#include "merge.h"
+#include "onak-conf.h"
+#include "openpgp.h"
+#include "parsekey.h"
+#include "sendsync.h"
+
+struct onak_dynamic_dbctx {
+       struct onak_dbctx *loadeddbctx;
+       void              *backend_handle;
+};
+
+static bool dynamic_starttrans(struct onak_dbctx *dbctx)
+{
+       struct onak_dynamic_dbctx *privctx =
+                       (struct onak_dynamic_dbctx *) dbctx->priv;
+
+       return privctx->loadeddbctx->starttrans(privctx->loadeddbctx);
+}
+
+static void dynamic_endtrans(struct onak_dbctx *dbctx)
+{
+       struct onak_dynamic_dbctx *privctx =
+                       (struct onak_dynamic_dbctx *) dbctx->priv;
+
+       privctx->loadeddbctx->endtrans(privctx->loadeddbctx);
+}
+
+static int dynamic_fetch_key_id(struct onak_dbctx *dbctx, uint64_t keyid,
+               struct openpgp_publickey **publickey, bool intrans)
+{
+       struct onak_dynamic_dbctx *privctx =
+                       (struct onak_dynamic_dbctx *) dbctx->priv;
+
+       return privctx->loadeddbctx->fetch_key_id(privctx->loadeddbctx, keyid,
+                       publickey, intrans);
+}
+
+static int dynamic_fetch_key_fp(struct onak_dbctx *dbctx,
+               struct openpgp_fingerprint *fingerprint,
+               struct openpgp_publickey **publickey, bool intrans)
+{
+       struct onak_dynamic_dbctx *privctx =
+                       (struct onak_dynamic_dbctx *) dbctx->priv;
+
+       return privctx->loadeddbctx->fetch_key_fp(privctx->loadeddbctx,
+                       fingerprint, publickey, intrans);
+}
+
+static int dynamic_fetch_key_text(struct onak_dbctx *dbctx,
+               const char *search,
+               struct openpgp_publickey **publickey)
+{
+       struct onak_dynamic_dbctx *privctx =
+                       (struct onak_dynamic_dbctx *) dbctx->priv;
+
+       return privctx->loadeddbctx->fetch_key_text(privctx->loadeddbctx,
+                       search, publickey);
+}
+
+static int dynamic_fetch_key_skshash(struct onak_dbctx *dbctx,
+               const struct skshash *hash,
+               struct openpgp_publickey **publickey)
+{
+       struct onak_dynamic_dbctx *privctx =
+                       (struct onak_dynamic_dbctx *) dbctx->priv;
+
+       return privctx->loadeddbctx->fetch_key_skshash(privctx->loadeddbctx,
+                       hash, publickey);
+}
+
+static int dynamic_store_key(struct onak_dbctx *dbctx,
+               struct openpgp_publickey *publickey, bool intrans,
+               bool update)
+{
+       struct onak_dynamic_dbctx *privctx =
+                       (struct onak_dynamic_dbctx *) dbctx->priv;
+
+       return privctx->loadeddbctx->store_key(privctx->loadeddbctx,
+                       publickey, intrans, update);
+}
+
+static int dynamic_delete_key(struct onak_dbctx *dbctx,
+               struct openpgp_fingerprint *fp,
+               bool intrans)
+{
+       struct onak_dynamic_dbctx *privctx =
+                       (struct onak_dynamic_dbctx *) dbctx->priv;
+
+       return privctx->loadeddbctx->delete_key(privctx->loadeddbctx,
+                       fp, intrans);
+}
+
+static int dynamic_update_keys(struct onak_dbctx *dbctx,
+               struct openpgp_publickey **keys,
+               struct keyarray *blacklist,
+               bool updateonly,
+               bool sendsync)
+{
+       struct onak_dynamic_dbctx *privctx =
+                       (struct onak_dynamic_dbctx *) dbctx->priv;
+
+       return privctx->loadeddbctx->update_keys(privctx->loadeddbctx,
+                       keys, blacklist, updateonly, sendsync);
+}
+
+static struct ll *dynamic_getkeysigs(struct onak_dbctx *dbctx,
+               uint64_t keyid, bool *revoked)
+{
+       struct onak_dynamic_dbctx *privctx =
+                       (struct onak_dynamic_dbctx *) dbctx->priv;
+
+       return privctx->loadeddbctx->getkeysigs(privctx->loadeddbctx,
+                       keyid, revoked);
+}
+
+static struct ll *dynamic_cached_getkeysigs(struct onak_dbctx *dbctx,
+               uint64_t keyid)
+{
+       struct onak_dynamic_dbctx *privctx =
+                       (struct onak_dynamic_dbctx *) dbctx->priv;
+
+       return privctx->loadeddbctx->cached_getkeysigs(privctx->loadeddbctx,
+                       keyid);
+}
+
+static char *dynamic_keyid2uid(struct onak_dbctx *dbctx,
+                       uint64_t keyid)
+{
+       struct onak_dynamic_dbctx *privctx =
+                       (struct onak_dynamic_dbctx *) dbctx->priv;
+
+       return privctx->loadeddbctx->keyid2uid(privctx->loadeddbctx,
+                       keyid);
+}
+
+static int dynamic_iterate_keys(struct onak_dbctx *dbctx,
+               void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
+               void *ctx)
+{
+       struct onak_dynamic_dbctx *privctx =
+                       (struct onak_dynamic_dbctx *) dbctx->priv;
+
+       return privctx->loadeddbctx->iterate_keys(privctx->loadeddbctx,
+                       iterfunc, ctx);
+}
+
+static void dynamic_cleanupdb(struct onak_dbctx *dbctx)
+{
+       struct onak_dynamic_dbctx *privctx =
+                       (struct onak_dynamic_dbctx *) dbctx->priv;
+
+       if (privctx->loadeddbctx != NULL) {
+               if (privctx->loadeddbctx->cleanupdb != NULL) {
+                       privctx->loadeddbctx->cleanupdb(privctx->loadeddbctx);
+                       privctx->loadeddbctx = NULL;
+               }
+       }
+
+       if (privctx->backend_handle != NULL) {
+               dlclose(privctx->backend_handle);
+               privctx->backend_handle = NULL;
+       }
+
+       if (dbctx->priv != NULL) {
+               free(dbctx->priv);
+               dbctx->priv = NULL;
+       }
+
+       if (dbctx != NULL) {
+               free(dbctx);
+       }
+}
+
+struct onak_dbctx *keydb_dynamic_init(struct onak_db_config *dbcfg,
+               bool readonly)
+{
+       struct onak_dbctx *dbctx;
+       char *soname;
+       char *initname;
+       struct onak_dbctx *(*backend_init)(struct onak_db_config *, bool);
+       struct onak_dynamic_dbctx *privctx;
+       char *type;
+
+       if (dbcfg == NULL) {
+               logthing(LOGTHING_CRITICAL,
+                       "No backend database configuration supplied.");
+               return NULL;
+       }
+
+       dbctx = malloc(sizeof(struct onak_dbctx));
+
+       if (dbctx == NULL) {
+               return NULL;
+       }
+
+       dbctx->config = dbcfg;
+       dbctx->priv = privctx = malloc(sizeof(struct onak_dynamic_dbctx));
+       if (dbctx->priv == NULL) {
+               free(dbctx);
+               return (NULL);
+       }
+
+       type = dbcfg->type;
+       if (config.use_keyd) {
+               type = "keyd";
+       }
+
+       if (!config.db_backend) {
+               logthing(LOGTHING_CRITICAL, "No database backend defined.");
+               exit(EXIT_FAILURE);
+       }
+
+       if (config.backends_dir == NULL) {
+               soname = malloc(strlen(type)
+                       + strlen("./libkeydb_")
+                       + strlen(".so")
+                       + 1);
+
+               sprintf(soname, "./libkeydb_%s.so", type);
+       } else {
+               soname = malloc(strlen(type)
+                       + strlen("/libkeydb_")
+                       + strlen(".so")
+                       + strlen(config.backends_dir)
+                       + 1);
+
+               sprintf(soname, "%s/libkeydb_%s.so", config.backends_dir,
+                       type);
+       }
+
+       logthing(LOGTHING_INFO, "Loading dynamic backend: %s", soname);
+
+       privctx->backend_handle = dlopen(soname, RTLD_LAZY);
+       if (privctx->backend_handle == NULL) {
+               logthing(LOGTHING_CRITICAL,
+                               "Failed to open handle to library '%s': %s",
+                               soname, dlerror());
+               free(soname);
+               soname = NULL;
+               exit(EXIT_FAILURE);
+       }
+
+       initname = malloc(strlen(config.db_backend)
+                       + strlen("keydb_")
+                       + strlen("_init")
+                       + 1);
+       sprintf(initname, "keydb_%s_init", type);
+
+       *(void **) (&backend_init) = dlsym(privctx->backend_handle, initname);
+       free(initname);
+
+       if (backend_init == NULL) {
+               logthing(LOGTHING_CRITICAL,
+                               "Failed to find dbfuncs structure in library "
+                               "'%s' : %s", soname, dlerror());
+               free(soname);
+               soname = NULL;
+               exit(EXIT_FAILURE);
+       }
+
+       privctx->loadeddbctx = backend_init(dbcfg, readonly);
+
+       if (privctx->loadeddbctx == NULL) {
+               logthing(LOGTHING_CRITICAL,
+                               "Failed to initialise dynamic backend: %s",
+                               soname);
+               free(soname);
+               soname = NULL;
+               exit(EXIT_FAILURE);
+       }
+       free(soname);
+       soname = NULL;
+
+       if (privctx->loadeddbctx != NULL) {
+               dbctx->cleanupdb = dynamic_cleanupdb;
+               dbctx->starttrans = dynamic_starttrans;
+               dbctx->endtrans = dynamic_endtrans;
+               dbctx->fetch_key_id = dynamic_fetch_key_id;
+               dbctx->fetch_key_fp = dynamic_fetch_key_fp;
+               dbctx->fetch_key_text = dynamic_fetch_key_text;
+               dbctx->fetch_key_skshash = dynamic_fetch_key_skshash;
+               dbctx->store_key = dynamic_store_key;
+               dbctx->update_keys = dynamic_update_keys;
+               dbctx->delete_key = dynamic_delete_key;
+               dbctx->getkeysigs = dynamic_getkeysigs;
+               dbctx->cached_getkeysigs = dynamic_cached_getkeysigs;
+               dbctx->keyid2uid = dynamic_keyid2uid;
+               dbctx->iterate_keys = dynamic_iterate_keys;
+       }
+
+       return dbctx;
+}
diff --git a/keydb/keydb_file.c b/keydb/keydb_file.c
new file mode 100644 (file)
index 0000000..ded6a9e
--- /dev/null
@@ -0,0 +1,300 @@
+/*
+ * keydb.c - Routines to store and fetch keys.
+ *
+ * Copyright 2002-2004 Jonathan McDowell <noodles@earth.li>
+ *
+ * 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 <sys/types.h>
+#include <sys/uio.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "charfuncs.h"
+#include "keydb.h"
+#include "keyid.h"
+#include "keystructs.h"
+#include "log.h"
+#include "mem.h"
+#include "onak.h"
+#include "onak-conf.h"
+#include "parsekey.h"
+
+/**
+ *     starttrans - Start a transaction.
+ *
+ *     This is just a no-op for flat file access.
+ */
+static bool file_starttrans(struct onak_dbctx *dbctx)
+{
+       return true;
+}
+
+/**
+ *     endtrans - End a transaction.
+ *
+ *     This is just a no-op for flat file access.
+ */
+static void file_endtrans(struct onak_dbctx *dbctx)
+{
+       return;
+}
+
+/**
+ *     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.
+ *
+ *     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 file_fetch_key_id(struct onak_dbctx *dbctx,
+               uint64_t keyid,
+               struct openpgp_publickey **publickey,
+               bool intrans)
+{
+       char *db_dir = (char *) dbctx->priv;
+       struct openpgp_packet_list *packets = NULL;
+       char keyfile[1024];
+       int fd = -1;
+
+       snprintf(keyfile, 1023, "%s/0x%" PRIX64, db_dir,
+                       keyid & 0xFFFFFFFF);
+       fd = open(keyfile, O_RDONLY); // | O_SHLOCK);
+
+       if (fd > -1) {
+               read_openpgp_stream(file_fetchchar, &fd, &packets, 0);
+               parse_keys(packets, publickey);
+               free_packet_list(packets);
+               packets = NULL;
+               close(fd);
+       }
+
+       return (fd > -1);
+}
+
+/**
+ *     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.
+ *
+ *     Again we just use the hex representation of the keyid as the filename
+ *     to store the key to. We flatten the public key to a list of OpenPGP
+ *     packets and then use write_openpgp_stream() to write the stream out to
+ *     the file.
+ */
+static int file_store_key(struct onak_dbctx *dbctx,
+               struct openpgp_publickey *publickey, bool intrans,
+               bool update)
+{
+       char *db_dir = (char *) dbctx->priv;
+       struct openpgp_packet_list *packets = NULL;
+       struct openpgp_packet_list *list_end = NULL;
+       struct openpgp_publickey *next = NULL;
+       char keyfile[1024];
+       int fd = -1;
+       uint64_t keyid;
+
+       if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
+               logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
+               return 0;
+       }
+       snprintf(keyfile, 1023, "%s/0x%" PRIX64, db_dir,
+                       keyid & 0xFFFFFFFF);
+       fd = open(keyfile, O_WRONLY | O_CREAT, 0664); // | O_EXLOCK);
+
+       if (fd > -1) {
+               next = publickey -> next;
+               publickey -> next = NULL;
+               flatten_publickey(publickey, &packets, &list_end);
+               publickey -> next = next;
+               
+               write_openpgp_stream(file_putchar, &fd, packets);
+               close(fd);
+               free_packet_list(packets);
+               packets = NULL;
+       }
+
+       return (fd > -1);
+}
+
+/**
+ *     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 file_delete_key(struct onak_dbctx *dbctx,
+               struct openpgp_fingerprint *fp, bool intrans)
+{
+       char *db_dir = (char *) dbctx->priv;
+       char keyfile[1024];
+
+       snprintf(keyfile, 1023, "%s/0x%" PRIX64, db_dir,
+                       fingerprint2keyid(fp) & 0xFFFFFFFF);
+
+       return unlink(keyfile);
+}
+
+/**
+ *     fetch_key_text - Trys 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 file_fetch_key_text(struct onak_dbctx *dbctx,
+               const char *search,
+               struct openpgp_publickey **publickey)
+{
+       return 0;
+}
+
+/**
+ *     iterate_keys - call a function once for each key in the db.
+ *     @iterfunc: The function to call.
+ *     @ctx: A context pointer
+ *
+ *     Calls iterfunc once for each key in the database. ctx is passed
+ *     unaltered to iterfunc. This function is intended to aid database dumps
+ *     and statistic calculations.
+ *
+ *     Returns the number of keys we iterated over.
+ */
+static int file_iterate_keys(struct onak_dbctx *dbctx,
+               void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
+               void *ctx)
+{
+       char *db_dir = (char *) dbctx->priv;
+       int                         numkeys = 0;
+       struct openpgp_packet_list *packets = NULL;
+       struct openpgp_publickey   *key = NULL;
+       DIR                        *dir;
+       char                        keyfile[1024];
+       int                         fd = -1;
+       struct dirent              *curfile = NULL;
+
+       dir = opendir(db_dir);
+
+       if (dir != NULL) {
+               while ((curfile = readdir(dir)) != NULL) {
+                       if (curfile->d_name[0] == '0' &&
+                                       curfile->d_name[1] == 'x') {
+                               snprintf(keyfile, 1023, "%s/%s",
+                                               db_dir,
+                                               curfile->d_name);
+                               fd = open(keyfile, O_RDONLY);
+
+                               if (fd > -1) {
+                                       read_openpgp_stream(file_fetchchar,
+                                                       &fd,
+                                                       &packets,
+                                                       0);
+                                       parse_keys(packets, &key);
+
+                                       iterfunc(ctx, key);
+
+                                       free_publickey(key);
+                                       key = NULL;
+                                       free_packet_list(packets);
+                                       packets = NULL;
+                                       close(fd);
+                               }
+                               numkeys++;
+                       }
+               }
+               
+               closedir(dir);
+               dir = NULL;
+       }
+
+       return numkeys;
+}
+
+/*
+ * Include the basic keydb routines.
+ */
+#define NEED_KEYID2UID 1
+#define NEED_GETKEYSIGS 1
+#define NEED_UPDATEKEYS 1
+#define NEED_GET_FP 1
+#include "keydb.c"
+
+/**
+ *     cleanupdb - De-initialize the key database.
+ *
+ *     This is just a no-op for flat file access.
+ */
+static void file_cleanupdb(struct onak_dbctx *dbctx)
+{
+       if (dbctx->priv != NULL) {
+               free(dbctx->priv);
+               dbctx->priv = NULL;
+       }
+
+       if (dbctx != NULL) {
+               free(dbctx);
+       }
+}
+
+/**
+ *     initdb - Initialize the key database.
+ *
+ *     This is just a no-op for flat file access.
+ */
+struct onak_dbctx *keydb_file_init(struct onak_db_config *dbcfg, bool readonly)
+{
+       struct onak_dbctx *dbctx;
+
+       dbctx = malloc(sizeof(struct onak_dbctx));
+       if (dbctx == NULL) {
+               return NULL;
+       }
+
+       dbctx->config = dbcfg;
+       dbctx->priv = strdup(dbcfg->location);
+
+       dbctx->cleanupdb                = file_cleanupdb;
+       dbctx->starttrans               = file_starttrans;
+       dbctx->endtrans                 = file_endtrans;
+       dbctx->fetch_key_id             = file_fetch_key_id;
+       dbctx->fetch_key_fp             = generic_fetch_key_fp;
+       dbctx->fetch_key_text           = file_fetch_key_text;
+       dbctx->store_key                = file_store_key;
+       dbctx->update_keys              = generic_update_keys;
+       dbctx->delete_key               = file_delete_key;
+       dbctx->getkeysigs               = generic_getkeysigs;
+       dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
+       dbctx->keyid2uid                = generic_keyid2uid;
+       dbctx->iterate_keys             = file_iterate_keys;
+
+       return dbctx;
+}
diff --git a/keydb/keydb_fs.c b/keydb/keydb_fs.c
new file mode 100644 (file)
index 0000000..f8d55dc
--- /dev/null
@@ -0,0 +1,725 @@
+/*
+ * keydb_fs.c - Routines to store and fetch keys in a filesystem hierarchy.
+ *
+ * Copyright 2004 Daniel Silverstone <dsilvers@digital-scurf.org>
+ *
+ * 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 <sys/types.h>
+#include <sys/uio.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <dirent.h>
+
+#include "charfuncs.h"
+#include "decodekey.h"
+#include "keydb.h"
+#include "keyid.h"
+#include "keystructs.h"
+#include "ll.h"
+#include "mem.h"
+#include "onak.h"
+#include "onak-conf.h"
+#include "parsekey.h"
+#include "log.h"
+#include "wordlist.h"
+
+/* Hack: We should really dynamically allocate our path buffers */
+#ifndef PATH_MAX
+#define PATH_MAX 1024
+#endif
+
+struct onak_fs_dbctx {
+       int lockfile_fd;
+       bool lockfile_readonly;
+};
+
+/*****************************************************************************/
+
+/* Helper functions */
+
+#define FNV_offset_basis 2166136261ul
+#define FNV_mixing_prime 16777619ul
+
+static uint32_t calchash(uint8_t * ptr)
+{
+       register uint32_t h = FNV_offset_basis;
+       register uint32_t p = FNV_mixing_prime;
+       register uint32_t n = strlen((char *) ptr);
+       register uint8_t *c = ptr;
+       while (n--) {
+               h *= p;
+               h ^= *c++;
+       }
+       return h ? h : 1;       /* prevent a hash of zero happening */
+}
+
+
+static void keypath(char *buffer, size_t length, uint64_t _keyid,
+               char *basepath)
+{
+       uint64_t keyid = _keyid << 32;
+       snprintf(buffer, length, "%s/key/%02X/%02X/%08X/%016" PRIX64,
+                basepath, (uint8_t) ((keyid >> 56) & 0xFF),
+                (uint8_t) ((keyid >> 48) & 0xFF),
+                (uint32_t) (keyid >> 32), _keyid);
+}
+
+static void keydir(char *buffer, size_t length, uint64_t _keyid,
+               char *basepath)
+{
+       uint64_t keyid = _keyid << 32;
+       snprintf(buffer, length, "%s/key/%02X/%02X/%08X", basepath,
+                (uint8_t) ((keyid >> 56) & 0xFF),
+                (uint8_t) ((keyid >> 48) & 0xFF),
+                (uint32_t) (keyid >> 32));
+}
+
+static void prove_path_to(uint64_t keyid, char *what, char *basepath)
+{
+       static char buffer[PATH_MAX];
+       snprintf(buffer, sizeof(buffer), "%s/%s", basepath, what);
+       mkdir(buffer, 0777);
+
+       snprintf(buffer, sizeof(buffer), "%s/%s/%02X", basepath, what,
+                (uint8_t) ((keyid >> 24) & 0xFF));
+       mkdir(buffer, 0777);
+
+       snprintf(buffer, sizeof(buffer), "%s/%s/%02X/%02X", basepath,
+                what,
+                (uint8_t) ((keyid >> 24) & 0xFF),
+                (uint8_t) ((keyid >> 16) & 0xFF));
+       mkdir(buffer, 0777);
+
+       snprintf(buffer, sizeof(buffer), "%s/%s/%02X/%02X/%08X", basepath,
+                what,
+                (uint8_t) ((keyid >> 24) & 0xFF),
+                (uint8_t) ((keyid >> 16) & 0xFF), (uint32_t) (keyid));
+       mkdir(buffer, 0777);
+}
+
+static void wordpath(char *buffer, size_t length, char *word, uint32_t hash,
+               uint64_t keyid, char *basepath)
+{
+       snprintf(buffer, length, "%s/words/%02X/%02X/%08X/%s/%016" PRIX64,
+                basepath, (uint8_t) ((hash >> 24) & 0xFF),
+                (uint8_t) ((hash >> 16) & 0xFF), hash, word, keyid);
+}
+
+static void worddir(char *buffer, size_t length, char *word, uint32_t hash,
+               char *basepath)
+{
+       snprintf(buffer, length, "%s/words/%02X/%02X/%08X/%s", basepath,
+                (uint8_t) ((hash >> 24) & 0xFF),
+                (uint8_t) ((hash >> 16) & 0xFF), hash, word);
+}
+
+static void subkeypath(char *buffer, size_t length, uint64_t subkey,
+               char *basepath)
+{
+       snprintf(buffer, length, "%s/subkeys/%02X/%02X/%08X/%016" PRIX64,
+                basepath,
+                (uint8_t) ((subkey >> 24) & 0xFF),
+                (uint8_t) ((subkey >> 16) & 0xFF),
+                (uint32_t) (subkey & 0xFFFFFFFF),
+                subkey);
+}
+
+static void subkeydir(char *buffer, size_t length, uint64_t subkey,
+               char *basepath)
+{
+       snprintf(buffer, length, "%s/subkeys/%02X/%02X/%08X",
+                basepath,
+                (uint8_t) ((subkey >> 24) & 0xFF),
+                (uint8_t) ((subkey >> 16) & 0xFF),
+                (uint32_t) (subkey & 0xFFFFFFFF));
+}
+
+static void skshashpath(char *buffer, size_t length,
+               const struct skshash *hash, char *basepath)
+{
+       snprintf(buffer, length, "%s/skshash/%02X/%02X/%02X%02X%02X%02X/"
+               "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
+                basepath,
+                hash->hash[0], hash->hash[1],
+                hash->hash[0], hash->hash[1], hash->hash[2], hash->hash[3],
+                hash->hash[4], hash->hash[5], hash->hash[6], hash->hash[7],
+                hash->hash[8], hash->hash[9], hash->hash[10], hash->hash[11],
+                hash->hash[12], hash->hash[13], hash->hash[14],
+                hash->hash[15]);
+}
+
+/*****************************************************************************/
+
+/**
+ *     starttrans - Start a transaction.
+ */
+static bool fs_starttrans(struct onak_dbctx *dbctx)
+{
+       struct onak_fs_dbctx *privctx = (struct onak_fs_dbctx *) dbctx->priv;
+       struct flock lockstruct;
+       int remaining = 20;
+       lockstruct.l_type =
+           F_RDLCK | ((privctx->lockfile_readonly) ? 0 : F_WRLCK);
+       lockstruct.l_whence = SEEK_SET;
+       lockstruct.l_start = 0;
+       lockstruct.l_len = 1;
+
+       while (fcntl(privctx->lockfile_fd, F_SETLK, &lockstruct) == -1) {
+               if (remaining-- == 0)
+                       return false;   /* Hope to hell that noodles DTRT */
+               usleep(100);
+       }
+       return true;
+}
+
+/**
+ *     endtrans - End a transaction.
+ */
+static void fs_endtrans(struct onak_dbctx *dbctx)
+{
+       struct onak_fs_dbctx *privctx = (struct onak_fs_dbctx *) dbctx->priv;
+       struct flock lockstruct;
+
+       lockstruct.l_type = F_UNLCK;
+       lockstruct.l_whence = SEEK_SET;
+       lockstruct.l_start = 0;
+       lockstruct.l_len = 1;
+       fcntl(privctx->lockfile_fd, F_SETLK, &lockstruct);
+}
+
+static uint64_t fs_getfullkeyid(struct onak_dbctx *dbctx, uint64_t keyid)
+{
+       static char buffer[PATH_MAX];
+       DIR *d = NULL;
+       struct dirent *de = NULL;
+       uint64_t ret = 0;
+
+       keydir(buffer, sizeof(buffer), keyid, dbctx->config->location);
+
+       d = opendir(buffer);
+       if (d) {
+               do {
+                       de = readdir(d);
+                       if (de && de->d_name[0] != '.') {
+                               ret = strtoull(de->d_name, NULL, 16);
+                       }
+               } while (de && de->d_name[0] == '.');
+               closedir(d);    
+       }
+
+       if (ret == 0) {
+               subkeydir(buffer, sizeof(buffer), keyid,
+                       dbctx->config->location);
+
+               d = opendir(buffer);
+               if (d) {
+                       do {
+                               de = readdir(d);
+                               if (de && de->d_name[0] != '.') {
+                                       ret = strtoull(de->d_name, NULL, 16);
+                               }
+                       } while (de && de->d_name[0] == '.');
+                       closedir(d);
+               }
+       }
+
+       return ret;
+}
+
+/**
+ *     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.
+ */
+static int fs_fetch_key_id(struct onak_dbctx *dbctx,
+             uint64_t keyid,
+             struct openpgp_publickey **publickey,
+             bool intrans)
+{
+       static char buffer[PATH_MAX];
+       int ret = 0, fd;
+       struct openpgp_packet_list *packets = NULL;
+
+       if (!intrans)
+               fs_starttrans(dbctx);
+
+       if ((keyid >> 32) == 0)
+               keyid = fs_getfullkeyid(dbctx, keyid);
+
+       keypath(buffer, sizeof(buffer), keyid, dbctx->config->location);
+       fd = open(buffer, O_RDONLY);
+       if (fd == -1 && errno == ENOENT) {
+               subkeypath(buffer, sizeof(buffer), keyid,
+                       dbctx->config->location);
+               fd = open(buffer, O_RDONLY);
+       }
+
+       if (fd != -1) {
+               /* File is present, load it in... */
+               read_openpgp_stream(file_fetchchar, &fd, &packets, 0);
+               parse_keys(packets, publickey);
+               free_packet_list(packets);
+               packets = NULL;
+               close(fd);
+               ret = 1;
+       }
+
+       if (!intrans)
+               fs_endtrans(dbctx);
+       return ret;
+}
+
+/**
+ *     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 fs_store_key(struct onak_dbctx *dbctx,
+             struct openpgp_publickey *publickey, bool intrans,
+             bool update)
+{
+       static char buffer[PATH_MAX];
+       static char wbuffer[PATH_MAX];
+       int ret = 0, fd;
+       struct openpgp_packet_list *packets = NULL;
+       struct openpgp_packet_list *list_end = NULL;
+       struct openpgp_publickey *next = NULL;
+       uint64_t keyid;
+       struct ll *wordlist = NULL, *wl = NULL;
+       struct skshash hash;
+       struct openpgp_fingerprint *subkeyids = NULL;
+       uint32_t hashid;
+       int i = 0;
+
+       if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
+               logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
+               return 0;
+       }
+
+       if (!intrans)
+               fs_starttrans(dbctx);
+
+       prove_path_to(keyid, "key", dbctx->config->location);
+       keypath(buffer, sizeof(buffer), keyid, dbctx->config->location);
+
+       if ((fd =
+            open(buffer, O_WRONLY | (update ? O_TRUNC : O_CREAT),
+                 0644)) != -1) {
+               next = publickey->next;
+               publickey->next = NULL;
+               flatten_publickey(publickey, &packets, &list_end);
+               publickey->next = next;
+
+               write_openpgp_stream(file_putchar, &fd, packets);
+               close(fd);
+               free_packet_list(packets);
+               packets = NULL;
+               ret = 1;
+       }
+
+       if (ret) {
+               wl = wordlist = makewordlistfromkey(wordlist, publickey);
+               while (wl) {
+                       uint32_t hash = calchash((uint8_t *) (wl->object));
+                       prove_path_to(hash, "words", dbctx->config->location);
+
+                       worddir(wbuffer, sizeof(wbuffer), wl->object, hash,
+                               dbctx->config->location);
+                       mkdir(wbuffer, 0777);
+                       wordpath(wbuffer, sizeof(wbuffer), wl->object, hash,
+                               keyid, dbctx->config->location);
+                       link(buffer, wbuffer);
+
+                       wl = wl->next;
+               }
+               llfree(wordlist, free);
+               
+               subkeyids = keysubkeys(publickey);
+               i = 0;
+               while (subkeyids != NULL && subkeyids[i].length != 0) {
+                       keyid = fingerprint2keyid(&subkeyids[i]);
+
+                       prove_path_to(keyid, "subkeys",
+                               dbctx->config->location);
+
+                       subkeydir(wbuffer, sizeof(wbuffer), keyid,
+                               dbctx->config->location);
+                       mkdir(wbuffer, 0777);
+                       subkeypath(wbuffer, sizeof(wbuffer), keyid,
+                               dbctx->config->location);
+                       link(buffer, wbuffer);
+
+                       i++;
+               }
+               if (subkeyids != NULL) {
+                       free(subkeyids);
+                       subkeyids = NULL;
+               }
+
+               get_skshash(publickey, &hash);
+               hashid = hash.hash[0];
+               hashid <<= 8;
+               hashid |= hash.hash[1];
+               hashid <<= 8;
+               hashid |= hash.hash[2];
+               hashid <<= 8;
+               hashid |= hash.hash[3];
+               prove_path_to(hashid, "skshash", dbctx->config->location);
+               skshashpath(wbuffer, sizeof(wbuffer), &hash,
+                       dbctx->config->location);
+               link(buffer, wbuffer);
+       }
+
+       if (!intrans)
+               fs_endtrans(dbctx);
+       return ret;
+}
+
+/**
+ *     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.
+ */
+static int fs_delete_key(struct onak_dbctx *dbctx,
+               struct openpgp_fingerprint *fp, bool intrans)
+{
+       static char buffer[PATH_MAX];
+       int ret;
+       struct openpgp_publickey *pk = NULL;
+       struct skshash hash;
+       struct ll *wordlist = NULL, *wl = NULL;
+       struct openpgp_fingerprint *subkeyids = NULL;
+       uint64_t subkeyid;
+       int i = 0;
+       uint64_t keyid;
+
+       keyid = fingerprint2keyid(fp);
+       if (keyid == 0)
+               return 1;
+
+       if (!intrans)
+               fs_starttrans(dbctx);
+
+       ret = fs_fetch_key_id(dbctx, keyid, &pk, true);
+
+       if (ret) {
+               logthing(LOGTHING_DEBUG, "Wordlist for key %016" PRIX64,
+                        keyid);
+               wl = wordlist = makewordlistfromkey(wordlist, pk);
+               logthing(LOGTHING_DEBUG,
+                        "Wordlist for key %016" PRIX64 " done", keyid);
+               while (wl) {
+                       uint32_t hash = calchash((uint8_t *) (wl->object));
+                       prove_path_to(hash, "words", dbctx->config->location);
+
+                       wordpath(buffer, sizeof(buffer), wl->object, hash,
+                               keyid, dbctx->config->location);
+                       unlink(buffer);
+
+                       wl = wl->next;
+               }
+               llfree(wordlist, free);
+               wordlist = NULL;
+
+               subkeyids = keysubkeys(pk);
+               i = 0;
+               while (subkeyids != NULL && subkeyids[i].length != 0) {
+                       subkeyid = fingerprint2keyid(&subkeyids[i]);
+                       prove_path_to(subkeyid, "subkeys",
+                               dbctx->config->location);
+
+                       subkeypath(buffer, sizeof(buffer), subkeyid,
+                               dbctx->config->location);
+                       unlink(buffer);
+
+                       i++;
+               }
+               if (subkeyids != NULL) {
+                       free(subkeyids);
+                       subkeyids = NULL;
+               }
+
+               get_skshash(pk, &hash);
+               skshashpath(buffer, sizeof(buffer), &hash,
+                       dbctx->config->location);
+               unlink(buffer);
+       }
+
+       keypath(buffer, sizeof(buffer), keyid, dbctx->config->location);
+       unlink(buffer);
+
+       free_publickey(pk);
+
+       if (!intrans)
+               fs_endtrans(dbctx);
+       return 1;
+}
+
+static struct ll *internal_get_key_by_word(char *word, struct ll *mct,
+               char *basepath)
+{
+       struct ll *keys = NULL;
+       DIR *d = NULL;
+       char buffer[PATH_MAX];
+       uint32_t hash = calchash((uint8_t *) (word));
+       struct dirent *de;
+
+       worddir(buffer, sizeof(buffer), word, hash, basepath);
+       d = opendir(buffer);
+       logthing(LOGTHING_DEBUG, "Scanning for word %s in dir %s", word,
+                buffer);
+       if (d) {
+               do {
+                       de = readdir(d);
+                       if (de && de->d_name[0] != '.') {
+                               if ((!mct)
+                                   || (llfind(mct, de->d_name,
+                                       (int (*)(const void *, const void *))
+                                                   strcmp) !=
+                                       NULL)) {
+                                       logthing(LOGTHING_DEBUG,
+                                                "Found %s // %s", word,
+                                                de->d_name);
+                                       keys =
+                                           lladd(keys,
+                                                 strdup(de->d_name));
+                               }
+                       }
+               } while (de);
+               closedir(d);
+       }
+
+       return keys;
+}
+
+/*
+ *     fetch_key_text - Trys 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.
+ */
+static int fs_fetch_key_text(struct onak_dbctx *dbctx,
+                  const char *search,
+                  struct openpgp_publickey **publickey)
+{
+       struct ll *wordlist = NULL, *wl = NULL;
+       struct ll *keylist = NULL;
+       char      *searchtext = NULL;
+       int addedkeys = 0;
+
+       logthing(LOGTHING_DEBUG, "Search was '%s'", search);
+
+       searchtext = strdup(search);
+       wl = wordlist = makewordlist(wordlist, searchtext);
+
+       keylist = internal_get_key_by_word(wordlist->object, NULL,
+               dbctx->config->location);
+
+       if (!keylist) {
+               llfree(wordlist, NULL);
+               free(searchtext);
+               searchtext = NULL;
+               return 0;
+       }
+
+       wl = wl->next;
+       while (wl) {
+               struct ll *nkl =
+                   internal_get_key_by_word(wl->object, keylist,
+                       dbctx->config->location);
+               if (!nkl) {
+                       llfree(wordlist, NULL);
+                       llfree(keylist, free);
+                       free(searchtext);
+                       searchtext = NULL;
+                       return 0;
+               }
+               llfree(keylist, free);
+               keylist = nkl;
+               wl = wl->next;
+       }
+
+       llfree(wordlist, NULL);
+
+       /* Now add the keys... */
+       wl = keylist;
+       while (wl) {
+               logthing(LOGTHING_DEBUG, "Adding key: %s", wl->object);
+               addedkeys +=
+                   fs_fetch_key_id(dbctx,
+                             strtoull(wl->object, NULL, 16), publickey,
+                             false);
+               if (addedkeys >= config.maxkeys)
+                       break;
+               wl = wl->next;
+       }
+
+       llfree(keylist, free);
+       free(searchtext);
+       searchtext = NULL;
+
+       return addedkeys;
+}
+
+/**
+ *     fetch_key_skshash - Given an SKS hash fetch the key from storage.
+ *     @hash: The hash to fetch.
+ *     @publickey: A pointer to a structure to return the key in.
+ *     @intrans: If we're already in a transaction.
+ */
+static int fs_fetch_key_skshash(struct onak_dbctx *dbctx,
+             const struct skshash *hash,
+             struct openpgp_publickey **publickey)
+{
+       static char buffer[PATH_MAX];
+       int ret = 0, fd;
+       struct openpgp_packet_list *packets = NULL;
+
+       skshashpath(buffer, sizeof(buffer), hash, dbctx->config->location);
+       if ((fd = open(buffer, O_RDONLY)) != -1) {
+               read_openpgp_stream(file_fetchchar, &fd, &packets, 0);
+               parse_keys(packets, publickey);
+               free_packet_list(packets);
+               packets = NULL;
+               close(fd);
+               ret = 1;
+       }
+
+       return ret;
+}
+
+/**
+ *     iterate_keys - call a function once for each key in the db.
+ *     @iterfunc: The function to call.
+ *     @ctx: A context pointer
+ *
+ *     Calls iterfunc once for each key in the database. ctx is passed
+ *     unaltered to iterfunc. This function is intended to aid database dumps
+ *     and statistic calculations.
+ *
+ *     Returns the number of keys we iterated over.
+ */
+static int fs_iterate_keys(struct onak_dbctx *dbctx,
+               void (*iterfunc)(void *ctx,
+               struct openpgp_publickey *key), void *ctx)
+{
+       return 0;
+}
+
+/*
+ * Include the basic keydb routines.
+ */
+#define NEED_KEYID2UID 1
+#define NEED_GETKEYSIGS 1
+#define NEED_UPDATEKEYS 1
+#define NEED_GET_FP 1
+#include "keydb.c"
+
+/**
+ *     cleanupdb - De-initialize the key database.
+ */
+static void fs_cleanupdb(struct onak_dbctx *dbctx)
+{
+       struct onak_fs_dbctx *privctx = (struct onak_fs_dbctx *) dbctx->priv;
+
+       /* Mmmm nothing to do here? */
+       close(privctx->lockfile_fd);
+
+       free(privctx);
+       dbctx->priv = NULL;
+       free(dbctx);
+}
+
+/**
+ *     initdb - Initialize the key database.
+ */
+struct onak_dbctx *keydb_fs_init(struct onak_db_config *dbcfg, bool readonly)
+{
+       char buffer[PATH_MAX];
+       struct onak_dbctx *dbctx;
+       struct onak_fs_dbctx *privctx;
+
+       dbctx = malloc(sizeof(struct onak_dbctx));
+       if (dbctx == NULL) {
+               return NULL;
+       }
+       dbctx->config = dbcfg;
+       dbctx->priv = privctx = malloc(sizeof(*privctx));
+       if (privctx == NULL) {
+               free(dbctx);
+               return NULL;
+       }
+
+       privctx->lockfile_readonly = readonly;
+
+       snprintf(buffer, sizeof(buffer), "%s/.lock", dbcfg->location);
+
+       if (access(dbcfg->location, R_OK | W_OK | X_OK) == -1) {
+               if (errno != ENOENT) {
+                       logthing(LOGTHING_CRITICAL,
+                                "Unable to access keydb_fs root of '%s'. (%s)",
+                                dbcfg->location, strerror(errno));
+                       exit(1);        /* Lacking rwx on the key dir */
+               }
+               mkdir(dbcfg->location, 0777);
+               privctx->lockfile_fd = open(buffer, O_RDWR | O_CREAT, 0600);
+       }
+       if (chdir(dbcfg->location) == -1) {
+               /* Shouldn't happen after the above */
+               logthing(LOGTHING_CRITICAL,
+                       "Couldn't change to database directory: %s",
+                       strerror(errno));
+               free(dbctx->priv);
+               free(dbctx);
+               return NULL;
+       }
+       privctx->lockfile_fd = open(buffer,
+                                (privctx->lockfile_readonly) ?
+                                O_RDONLY : O_RDWR);
+       if (privctx->lockfile_fd == -1)
+               privctx->lockfile_fd = open(buffer, O_RDWR | O_CREAT, 0600);
+       if (privctx->lockfile_fd == -1) {
+               logthing(LOGTHING_CRITICAL,
+                        "Unable to open lockfile '%s'. (%s)",
+                        buffer, strerror(errno));
+               exit(1);        /* Lacking rwx on the key dir */
+       }
+
+       dbctx->cleanupdb                = fs_cleanupdb;
+       dbctx->starttrans               = fs_starttrans;
+       dbctx->endtrans                 = fs_endtrans;
+       dbctx->fetch_key_id             = fs_fetch_key_id;
+       dbctx->fetch_key_fp             = generic_fetch_key_fp;
+       dbctx->fetch_key_text           = fs_fetch_key_text;
+       dbctx->fetch_key_skshash        = fs_fetch_key_skshash;
+       dbctx->store_key                = fs_store_key;
+       dbctx->update_keys              = generic_update_keys;
+       dbctx->delete_key               = fs_delete_key;
+       dbctx->getkeysigs               = generic_getkeysigs;
+       dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
+       dbctx->keyid2uid                = generic_keyid2uid;
+       dbctx->iterate_keys             = fs_iterate_keys;
+
+       return dbctx;
+}
diff --git a/keydb/keydb_hkp.c b/keydb/keydb_hkp.c
new file mode 100644 (file)
index 0000000..79d5c4b
--- /dev/null
@@ -0,0 +1,406 @@
+/*
+ * keydb_hkp.c - Routines to store and fetch keys from another keyserver.
+ *
+ * Copyright 2013 Jonathan McDowell <noodles@earth.li>
+ *
+ * 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 <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <curl/curl.h>
+
+#include "build-config.h"
+
+#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"
+
+struct onak_hkp_dbctx {
+       struct onak_db_config *config; /* Our DB config info */
+       CURL *curl;
+       char hkpbase[512];
+};
+
+static int hkp_parse_url(struct onak_hkp_dbctx *privctx, const char *url)
+{
+       char proto[6], host[256];
+       unsigned int port;
+       int matched;
+       int ret = 1;
+
+       proto[0] = host[0] = 0;
+       port = 0;
+
+       matched = sscanf(url, "%5[a-z]://%256[a-zA-Z0-9.-]:%u", proto, host,
+                       &port);
+       if (matched < 2) {
+               proto[0] = 0;
+               sscanf(url, "%256[a-zA-Z0-9.-]:%u", host, &port);
+       }
+
+       if (host[0] == 0) {
+               logthing(LOGTHING_CRITICAL, "Couldn't parse HKP host: %s",
+                       url);
+               ret = 0;
+               goto out;
+       }
+
+       if (proto[0] == 0 || !strcmp(proto, "hkp")) {
+               if (port == 0) {
+                       port = 11371;
+               }
+               snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
+                       "http://%s:%u/pks", host, port);
+       } else if (!strcmp(proto, "hkps")) {
+               if (port == 0) {
+                       port = 11372;
+               }
+               snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
+                       "https://%s:%u/pks", host, port);
+       } else if (strcmp(proto, "http") && strcmp(proto, "https")) {
+               logthing(LOGTHING_CRITICAL, "Unknown HKP protocol: %s",
+                       proto);
+               ret = 0;
+               goto out;
+       } else if (port == 0) {
+               snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
+                       "%s://%s/pks", proto, host);
+       } else {
+               snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
+                       "%s://%s:%u/pks", proto, host, port);
+       }
+
+out:
+       return ret;
+}
+
+/**
+ *     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);
+}
+
+static int hkp_fetch_key_url(struct onak_dbctx *dbctx,
+               char *url,
+               struct openpgp_publickey **publickey,
+               bool intrans)
+{
+       struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
+       struct openpgp_packet_list *packets = NULL;
+       CURLcode res;
+       struct buffer_ctx buf;
+       int count = 0;
+
+       buf.offset = 0;
+       buf.size = 8192;
+       buf.buffer = malloc(8192);
+
+       curl_easy_setopt(privctx->curl, CURLOPT_URL, url);
+       curl_easy_setopt(privctx->curl, CURLOPT_WRITEFUNCTION,
+                       hkp_curl_recv_data);
+       curl_easy_setopt(privctx->curl, CURLOPT_WRITEDATA, &buf);
+       res = curl_easy_perform(privctx->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;
+}
+
+/**
+ *     hkp_fetch_key_id - Given a keyid fetch the key from HKP server.
+ */
+static int hkp_fetch_key_id(struct onak_dbctx *dbctx,
+               uint64_t keyid,
+               struct openpgp_publickey **publickey,
+               bool intrans)
+{
+       struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
+       char keyurl[1024];
+
+       snprintf(keyurl, sizeof(keyurl),
+                       "%s/lookup?op=get&search=0x%08" PRIX64,
+                       privctx->hkpbase, keyid);
+
+       return (hkp_fetch_key_url(dbctx, keyurl, publickey, intrans));
+}
+
+/**
+ *     hkp_fetch_key_fp - Given a fingerprint fetch the key from HKP server.
+ */
+static int hkp_fetch_key_fp(struct onak_dbctx *dbctx,
+               struct openpgp_fingerprint *fingerprint,
+               struct openpgp_publickey **publickey,
+               bool intrans)
+{
+       struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
+       char keyurl[1024];
+       int i, ofs;
+
+       if (fingerprint->length > MAX_FINGERPRINT_LEN) {
+               return 0;
+       }
+
+       ofs = snprintf(keyurl, sizeof(keyurl),
+                       "%s/lookup?op=get&search=0x", privctx->hkpbase);
+
+       if ((ofs + fingerprint->length * 2 + 1)> sizeof(keyurl)) {
+               return 0;
+       }
+
+       for (i = 0; i < fingerprint->length; i++) {
+               ofs += sprintf(&keyurl[ofs], "%02X", fingerprint->fp[i]);
+       }
+
+       return (hkp_fetch_key_url(dbctx, keyurl, publickey, intrans));
+}
+
+/**
+ *     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(struct onak_dbctx *dbctx,
+               const char *search,
+               struct openpgp_publickey **publickey)
+{
+       struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
+       char keyurl[1024];
+
+       snprintf(keyurl, sizeof(keyurl),
+                       "%s/lookup?op=get&search=%s",
+                       privctx->hkpbase, search);
+
+       return (hkp_fetch_key_url(dbctx, keyurl, publickey, false));
+}
+
+/**
+ *     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 onak_dbctx *dbctx,
+               struct openpgp_publickey *publickey, bool intrans,
+               bool update)
+{
+       struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
+       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(privctx->curl, buf.buffer, buf.offset);
+       addform[7] = '=';
+
+       snprintf(keyurl, sizeof(keyurl), "%s/add", privctx->hkpbase);
+
+       curl_easy_setopt(privctx->curl, CURLOPT_URL, keyurl);
+       curl_easy_setopt(privctx->curl, CURLOPT_POSTFIELDS, addform);
+       curl_easy_setopt(privctx->curl, CURLOPT_WRITEFUNCTION,
+                       hkp_curl_recv_data);
+       buf.offset = 0;
+       curl_easy_setopt(privctx->curl, CURLOPT_WRITEDATA, &buf);
+       res = curl_easy_perform(privctx->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.
+ *     @fp: The fingerprint of the key to delete.
+ *     @intrans: If we're already in a transaction.
+ *
+ *     No op for HKP.
+ */
+static int hkp_delete_key(struct onak_dbctx *dbctx,
+               struct openpgp_fingerprint *fp, 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(struct onak_dbctx *dbctx,
+               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(struct onak_dbctx *dbctx)
+{
+       return true;
+}
+
+/**
+ *     endtrans - End a transaction.
+ *
+ *     This is just a no-op for HKP access.
+ */
+static void hkp_endtrans(struct onak_dbctx *dbctx)
+{
+       return;
+}
+
+/*
+ * Include the basic keydb routines.
+ */
+#define NEED_KEYID2UID 1
+#define NEED_GETKEYSIGS 1
+#define NEED_UPDATEKEYS 1
+#include "keydb.c"
+
+/**
+ *     cleanupdb - De-initialize the key database.
+ *
+ *     We cleanup CURL here.
+ */
+static void hkp_cleanupdb(struct onak_dbctx *dbctx)
+{
+       struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
+
+       if (privctx->curl) {
+               curl_easy_cleanup(privctx->curl);
+               privctx->curl = NULL;
+       }
+       curl_global_cleanup();
+       free(privctx);
+       free(dbctx);
+}
+
+/**
+ *     initdb - Initialize the key database.
+ *
+ *     We initialize CURL here.
+ */
+struct onak_dbctx *keydb_hkp_init(struct onak_db_config *dbcfg, bool readonly)
+{
+       struct onak_dbctx *dbctx;
+       struct onak_hkp_dbctx *privctx;
+       curl_version_info_data *curl_info;
+
+       dbctx = malloc(sizeof(struct onak_dbctx));
+       if (dbctx == NULL) {
+               return NULL;
+       }
+
+       dbctx->config = dbcfg;
+       dbctx->priv = privctx = malloc(sizeof(*privctx));
+       dbctx->cleanupdb                = hkp_cleanupdb;
+       dbctx->starttrans               = hkp_starttrans;
+       dbctx->endtrans                 = hkp_endtrans;
+       dbctx->fetch_key_id             = hkp_fetch_key_id;
+       dbctx->fetch_key_fp             = hkp_fetch_key_fp;
+       dbctx->fetch_key_text           = hkp_fetch_key_text;
+       dbctx->store_key                = hkp_store_key;
+       dbctx->update_keys              = generic_update_keys;
+       dbctx->delete_key               = hkp_delete_key;
+       dbctx->getkeysigs               = generic_getkeysigs;
+       dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
+       dbctx->keyid2uid                = generic_keyid2uid;
+       dbctx->iterate_keys             = hkp_iterate_keys;
+
+       if (!hkp_parse_url(privctx, dbcfg->location)) {
+               exit(EXIT_FAILURE);
+       }
+       logthing(LOGTHING_INFO, "Using %s as HKP forwarding URL.",
+               privctx->hkpbase);
+       curl_global_init(CURL_GLOBAL_DEFAULT);
+       privctx->curl = curl_easy_init();
+       if (privctx->curl == NULL) {
+               logthing(LOGTHING_CRITICAL, "Could not initialize CURL.");
+               hkp_cleanupdb(dbctx);
+               dbctx = NULL;
+               exit(EXIT_FAILURE);
+       }
+       curl_easy_setopt(privctx->curl, CURLOPT_USERAGENT,
+               "onak/" ONAK_VERSION);
+
+       if (strncmp(privctx->hkpbase, "https://", 8) == 0) {
+               curl_info = curl_version_info(CURLVERSION_NOW);
+               if (! (curl_info->features & CURL_VERSION_SSL)) {
+                       logthing(LOGTHING_CRITICAL,
+                               "CURL lacks SSL support; cannot use HKP url: %s",
+                               privctx->hkpbase);
+                       hkp_cleanupdb(dbctx);
+                       dbctx = NULL;
+                       exit(EXIT_FAILURE);
+               }
+       }
+
+       return dbctx;
+}
diff --git a/keydb/keydb_keyd.c b/keydb/keydb_keyd.c
new file mode 100644 (file)
index 0000000..e8f9961
--- /dev/null
@@ -0,0 +1,575 @@
+/*
+ * keydb_keyd.c - Routines to talk to keyd backend.
+ *
+ * Copyright 2002-2004,2011 Jonathan McDowell <noodles@earth.li>
+ *
+ * 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 <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "charfuncs.h"
+#include "keyd.h"
+#include "keydb.h"
+#include "keyid.h"
+#include "keystructs.h"
+#include "log.h"
+#include "mem.h"
+#include "onak.h"
+#include "onak-conf.h"
+#include "parsekey.h"
+
+/**
+ *     starttrans - Start a transaction.
+ *
+ *     Start a transaction. Intended to be used if we're about to perform many
+ *     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 keyd_starttrans(struct onak_dbctx *dbctx)
+{
+       return true;
+}
+
+/**
+ *     endtrans - End a transaction.
+ *
+ *     Ends a transaction.
+ */
+static void keyd_endtrans(struct onak_dbctx *dbctx)
+{
+       return;
+}
+
+static bool keyd_send_cmd(int fd, enum keyd_ops _cmd)
+{
+       uint32_t cmd = _cmd;
+       ssize_t bytes;
+
+       bytes = write(fd, &cmd, sizeof(cmd));
+       if (bytes != sizeof(cmd)) {
+               return false;
+       }
+
+       bytes = read(fd, &cmd, sizeof(cmd));
+       if (bytes != sizeof(cmd)) {
+               return false;
+       }
+
+       if (cmd != KEYD_REPLY_OK) {
+               return false;
+       }
+
+       return true;
+}
+
+/**
+ *     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.
+ *
+ *     This function returns a public key from whatever storage mechanism we
+ *     are using.
+ *
+ *      TODO: What about keyid collisions? Should we use fingerprint instead?
+ */
+static int keyd_fetch_key_id(struct onak_dbctx *dbctx,
+               uint64_t keyid,
+               struct openpgp_publickey **publickey,
+               bool intrans)
+{
+       int keyd_fd = (intptr_t) dbctx->priv;
+       struct buffer_ctx           keybuf;
+       struct openpgp_packet_list *packets = NULL;
+       ssize_t                     bytes = 0;
+       ssize_t                     count = 0;
+
+       if (keyd_send_cmd(keyd_fd, KEYD_CMD_GET_ID)) {
+               write(keyd_fd, &keyid, sizeof(keyid));
+               keybuf.offset = 0;
+               read(keyd_fd, &keybuf.size, sizeof(keybuf.size));
+               if (keybuf.size > 0) {
+                       keybuf.buffer = malloc(keybuf.size);
+                       bytes = count = 0;
+                       logthing(LOGTHING_TRACE,
+                                       "Getting %d bytes of key data.",
+                                       keybuf.size);
+                       while (bytes >= 0 && count < keybuf.size) {
+                               bytes = read(keyd_fd, &keybuf.buffer[count],
+                                               keybuf.size - count);
+                               logthing(LOGTHING_TRACE,
+                                               "Read %d bytes.", bytes);
+                               count += bytes;
+                       }
+                       read_openpgp_stream(buffer_fetchchar, &keybuf,
+                                       &packets, 0);
+                       parse_keys(packets, publickey);
+                       free_packet_list(packets);
+                       packets = NULL;
+                       free(keybuf.buffer);
+                       keybuf.buffer = NULL;
+                       keybuf.size = 0;
+               }
+       }
+
+       return (count > 0) ? 1 : 0;
+}
+
+static int keyd_fetch_key_fp(struct onak_dbctx *dbctx,
+               struct openpgp_fingerprint *fingerprint,
+               struct openpgp_publickey **publickey,
+               bool intrans)
+{
+       int keyd_fd = (intptr_t) dbctx->priv;
+       struct buffer_ctx           keybuf;
+       struct openpgp_packet_list *packets = NULL;
+       ssize_t                     bytes = 0;
+       ssize_t                     count = 0;
+       uint8_t                     size;
+
+       if (fingerprint->length > MAX_FINGERPRINT_LEN) {
+               return 0;
+       }
+
+       if (keyd_send_cmd(keyd_fd, KEYD_CMD_GET_FP)) {
+               size = fingerprint->length;
+               write(keyd_fd, &size, sizeof(size));
+               write(keyd_fd, fingerprint->fp, size);
+               keybuf.offset = 0;
+               read(keyd_fd, &keybuf.size, sizeof(keybuf.size));
+               if (keybuf.size > 0) {
+                       keybuf.buffer = malloc(keybuf.size);
+                       bytes = count = 0;
+                       logthing(LOGTHING_TRACE,
+                                       "Getting %d bytes of key data.",
+                                       keybuf.size);
+                       while (bytes >= 0 && count < keybuf.size) {
+                               bytes = read(keyd_fd, &keybuf.buffer[count],
+                                               keybuf.size - count);
+                               logthing(LOGTHING_TRACE,
+                                               "Read %d bytes.", bytes);
+                               count += bytes;
+                       }
+                       read_openpgp_stream(buffer_fetchchar, &keybuf,
+                                       &packets, 0);
+                       parse_keys(packets, publickey);
+                       free_packet_list(packets);
+                       packets = NULL;
+                       free(keybuf.buffer);
+                       keybuf.buffer = NULL;
+                       keybuf.size = 0;
+               }
+       }
+
+       return (count > 0) ? 1 : 0;
+}
+
+/**
+*      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 keyd_delete_key(struct onak_dbctx *dbctx,
+               struct openpgp_fingerprint *fp, bool intrans)
+{
+       int keyd_fd = (intptr_t) dbctx->priv;
+
+       if (keyd_send_cmd(keyd_fd, KEYD_CMD_DELETE)) {
+               write(keyd_fd, fp, sizeof(*fp));
+       }
+
+       return 0;
+}
+
+/**
+ *     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.
+ *
+ *     This function stores a public key in whatever storage mechanism we are
+ *     using. intrans indicates if we're already in a transaction so don't
+ *     need to start one. update indicates if the key already exists and is
+ *     just being updated.
+ *
+ *     TODO: Do we store multiple keys of the same id? Or only one and replace
+ *     it?
+ */
+static int keyd_store_key(struct onak_dbctx *dbctx,
+               struct openpgp_publickey *publickey, bool intrans,
+               bool update)
+{
+       int keyd_fd = (intptr_t) dbctx->priv;
+       struct buffer_ctx           keybuf;
+       struct openpgp_packet_list *packets = NULL;
+       struct openpgp_packet_list *list_end = NULL;
+       struct openpgp_publickey   *next = NULL;
+       uint64_t                    keyid;
+       enum keyd_ops               cmd = KEYD_CMD_STORE;
+
+       if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
+               logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
+               return 0;
+       }
+
+       if (update) {
+               cmd = KEYD_CMD_UPDATE;
+       }
+
+       if (keyd_send_cmd(keyd_fd, cmd)) {
+               keybuf.offset = 0;
+               keybuf.size = 8192;
+               keybuf.buffer = malloc(keybuf.size);
+
+               next = publickey->next;
+               publickey->next = NULL;
+               flatten_publickey(publickey,
+                               &packets,
+                               &list_end);
+               publickey->next = next;
+
+               write_openpgp_stream(buffer_putchar, &keybuf, packets);
+               logthing(LOGTHING_TRACE, "Sending %d bytes.", keybuf.offset);
+               write(keyd_fd, &keybuf.offset, sizeof(keybuf.offset));
+               write(keyd_fd, keybuf.buffer, keybuf.offset);
+
+               free_packet_list(packets);
+               packets = list_end = NULL;
+               free(keybuf.buffer);
+               keybuf.buffer = NULL;
+               keybuf.size = keybuf.offset = 0;
+       }
+
+       return 0;
+}
+
+/**
+ *     fetch_key_text - Trys 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.
+ */
+static int keyd_fetch_key_text(struct onak_dbctx *dbctx,
+               const char *search,
+               struct openpgp_publickey **publickey)
+{
+       int keyd_fd = (intptr_t) dbctx->priv;
+       struct buffer_ctx           keybuf;
+       struct openpgp_packet_list *packets = NULL;
+       ssize_t                     bytes = 0;
+       ssize_t                     count = 0;
+
+       if (keyd_send_cmd(keyd_fd, KEYD_CMD_GET_TEXT)) {
+               bytes = strlen(search);
+               write(keyd_fd, &bytes, sizeof(bytes));
+               write(keyd_fd, search, bytes);
+               keybuf.offset = 0;
+               read(keyd_fd, &keybuf.size, sizeof(keybuf.size));
+               if (keybuf.size > 0) {
+                       keybuf.buffer = malloc(keybuf.size);
+                       bytes = count = 0;
+                       logthing(LOGTHING_TRACE,
+                                       "Getting %d bytes of key data.",
+                                       keybuf.size);
+                       while (bytes >= 0 && count < keybuf.size) {
+                               bytes = read(keyd_fd, &keybuf.buffer[count],
+                                               keybuf.size - count);
+                               logthing(LOGTHING_TRACE,
+                                               "Read %d bytes.", bytes);
+                               count += bytes;
+                       }
+                       read_openpgp_stream(buffer_fetchchar, &keybuf,
+                                       &packets, 0);
+                       parse_keys(packets, publickey);
+                       free_packet_list(packets);
+                       packets = NULL;
+                       free(keybuf.buffer);
+                       keybuf.buffer = NULL;
+                       keybuf.size = 0;
+               }
+       }
+
+       return (count > 0) ? 1 : 0;
+
+       return 0;
+}
+
+static int keyd_fetch_key_skshash(struct onak_dbctx *dbctx,
+               const struct skshash *hash,
+               struct openpgp_publickey **publickey)
+{
+       int keyd_fd = (intptr_t) dbctx->priv;
+       struct buffer_ctx           keybuf;
+       struct openpgp_packet_list *packets = NULL;
+       ssize_t                     bytes = 0;
+       ssize_t                     count = 0;
+
+       if (keyd_send_cmd(keyd_fd, KEYD_CMD_GET_SKSHASH)) {
+               write(keyd_fd, hash->hash, sizeof(hash->hash));
+               keybuf.offset = 0;
+               read(keyd_fd, &keybuf.size, sizeof(keybuf.size));
+               if (keybuf.size > 0) {
+                       keybuf.buffer = malloc(keybuf.size);
+                       bytes = count = 0;
+                       logthing(LOGTHING_TRACE,
+                                       "Getting %d bytes of key data.",
+                                       keybuf.size);
+                       while (bytes >= 0 && count < keybuf.size) {
+                               bytes = read(keyd_fd, &keybuf.buffer[count],
+                                               keybuf.size - count);
+                               logthing(LOGTHING_TRACE,
+                                               "Read %d bytes.", bytes);
+                               count += bytes;
+                       }
+                       read_openpgp_stream(buffer_fetchchar, &keybuf,
+                                       &packets, 0);
+                       parse_keys(packets, publickey);
+                       free_packet_list(packets);
+                       packets = NULL;
+                       free(keybuf.buffer);
+                       keybuf.buffer = NULL;
+                       keybuf.size = 0;
+               }
+       }
+
+       return (count > 0) ? 1 : 0;
+}
+
+/**
+ *     iterate_keys - call a function once for each key in the db.
+ *     @iterfunc: The function to call.
+ *     @ctx: A context pointer
+ *
+ *     Calls iterfunc once for each key in the database. ctx is passed
+ *     unaltered to iterfunc. This function is intended to aid database dumps
+ *     and statistic calculations.
+ *
+ *     Returns the number of keys we iterated over.
+ */
+static int keyd_iterate_keys(struct onak_dbctx *dbctx,
+               void (*iterfunc)(void *ctx,
+               struct openpgp_publickey *key), void *ctx)
+{
+       int keyd_fd = (intptr_t) dbctx->priv;
+       struct buffer_ctx           keybuf;
+       struct openpgp_packet_list *packets = NULL;
+       struct openpgp_publickey   *key = NULL;
+       ssize_t                     bytes = 0;
+       ssize_t                     count = 0;
+       int                         numkeys = 0;
+
+       if (keyd_send_cmd(keyd_fd, KEYD_CMD_KEYITER)) {
+               keybuf.offset = 0;
+               read(keyd_fd, &keybuf.size, sizeof(keybuf.size));
+               while (keybuf.size > 0) {
+                       keybuf.buffer = malloc(keybuf.size);
+                       bytes = count = 0;
+                       logthing(LOGTHING_TRACE,
+                                       "Getting %d bytes of key data.",
+                                       keybuf.size);
+                       while (bytes >= 0 && count < keybuf.size) {
+                               bytes = read(keyd_fd, &keybuf.buffer[count],
+                                               keybuf.size - count);
+                               logthing(LOGTHING_TRACE,
+                                               "Read %d bytes.", bytes);
+                               count += bytes;
+                       }
+                       read_openpgp_stream(buffer_fetchchar, &keybuf,
+                                       &packets, 0);
+                       parse_keys(packets, &key);
+
+                       if (iterfunc != NULL && key != NULL) {
+                               iterfunc(ctx, key);
+                       }
+
+                       free_publickey(key);
+                       key = NULL;
+                       free_packet_list(packets);
+                       packets = NULL;
+                       free(keybuf.buffer);
+                       keybuf.buffer = NULL;
+                       keybuf.size = keybuf.offset = 0;
+
+                       numkeys++;
+
+                       read(keyd_fd, &keybuf.size, sizeof(keybuf.size));
+               }
+       }
+
+       return numkeys;
+}
+
+#define NEED_KEYID2UID 1
+#define NEED_GETKEYSIGS 1
+#define NEED_UPDATEKEYS 1
+#include "keydb.c"
+
+/**
+ *     cleanupdb - De-initialize the key database.
+ *
+ *     This function should be called upon program exit to allow the DB to
+ *     cleanup after itself.
+ */
+static void keyd_cleanupdb(struct onak_dbctx *dbctx)
+{
+       int keyd_fd = (intptr_t) dbctx->priv;
+       uint32_t cmd = KEYD_CMD_CLOSE;
+
+       if (write(keyd_fd, &cmd, sizeof(cmd)) != sizeof(cmd)) {
+               logthing(LOGTHING_CRITICAL,
+                               "Couldn't send close cmd: %s (%d)",
+                               strerror(errno),
+                               errno);
+       }
+
+       if (read(keyd_fd, &cmd, sizeof(cmd)) != sizeof(cmd)) {
+               logthing(LOGTHING_CRITICAL,
+                       "Couldn't read close cmd reply: %s (%d)",
+                       strerror(errno),
+                       errno);
+       } else if (cmd != KEYD_REPLY_OK) {
+               logthing(LOGTHING_CRITICAL,
+                       "Got bad reply to KEYD_CMD_CLOSE: %d", cmd);
+       }
+
+       if (shutdown(keyd_fd, SHUT_RDWR) < 0) {
+               logthing(LOGTHING_NOTICE, "Error shutting down socket: %d",
+                               errno);
+       }
+       if (close(keyd_fd) < 0) {
+               logthing(LOGTHING_NOTICE, "Error closing down socket: %d",
+                               errno);
+       }
+
+       free(dbctx);
+
+       return;
+}
+
+/**
+ *     initdb - Initialize the key database.
+ *     @readonly: If we'll only be reading the DB, not writing to it.
+ *
+ *     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_keyd_init(struct onak_db_config *dbcfg, bool readonly)
+{
+       struct sockaddr_un sock;
+       uint32_t           cmd = KEYD_CMD_UNKNOWN;
+       uint32_t           reply = KEYD_REPLY_UNKNOWN_CMD;
+       ssize_t            count;
+       int keyd_fd;
+       struct onak_dbctx *dbctx;
+
+       dbctx = malloc(sizeof(*dbctx));
+       if (dbctx == NULL) {
+               return NULL;
+       }
+       dbctx->config = dbcfg;
+
+       keyd_fd = socket(PF_UNIX, SOCK_STREAM, 0);
+       if (keyd_fd < 0) {
+               logthing(LOGTHING_CRITICAL,
+                               "Couldn't open socket: %s (%d)",
+                               strerror(errno),
+                               errno);
+               exit(EXIT_FAILURE);
+       }
+
+       sock.sun_family = AF_UNIX;
+       snprintf(sock.sun_path, sizeof(sock.sun_path) - 1, "%s/%s",
+                       config.sock_dir,
+                       KEYD_SOCKET);
+       if (connect(keyd_fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) {
+               logthing(LOGTHING_CRITICAL,
+                               "Couldn't connect to socket %s: %s (%d)",
+                               sock.sun_path,
+                               strerror(errno),
+                               errno);
+               exit(EXIT_FAILURE);
+       }
+
+       cmd = KEYD_CMD_VERSION;
+       if (write(keyd_fd, &cmd, sizeof(cmd)) != sizeof(cmd)) {
+               logthing(LOGTHING_CRITICAL,
+                               "Couldn't write version cmd: %s (%d)",
+                               strerror(errno),
+                               errno);
+       } else {
+               count = read(keyd_fd, &reply, sizeof(reply));
+               if (count == sizeof(reply) && reply == KEYD_REPLY_OK) {
+                       count = read(keyd_fd, &reply, sizeof(reply));
+                       if (count != sizeof(reply) || reply != sizeof(reply)) {
+                               logthing(LOGTHING_CRITICAL,
+                                       "Error! Unexpected keyd version "
+                                       "length: %d != %d",
+                                       reply, sizeof(reply));
+                               exit(EXIT_FAILURE);
+                       }
+
+                       count = read(keyd_fd, &reply, sizeof(reply));
+                       if (count != sizeof(reply)) {
+                               logthing(LOGTHING_CRITICAL,
+                                       "Error! Unexpected keyd version "
+                                       "length: %d != %d",
+                                       count, sizeof(reply));
+                               exit(EXIT_FAILURE);
+                       }
+                       logthing(LOGTHING_DEBUG,
+                                       "keyd protocol version %d",
+                                       reply);
+                       if (reply != keyd_version) {
+                               logthing(LOGTHING_CRITICAL,
+                                       "Error! keyd protocol version "
+                                       "mismatch. (us = %d, it = %d)",
+                                               keyd_version, reply);
+                       }
+               }
+       }
+
+       dbctx->priv                     = (void *) (intptr_t) keyd_fd;
+       dbctx->cleanupdb                = keyd_cleanupdb;
+       dbctx->starttrans               = keyd_starttrans;
+       dbctx->endtrans                 = keyd_endtrans;
+       dbctx->fetch_key_id             = keyd_fetch_key_id;
+       dbctx->fetch_key_fp             = keyd_fetch_key_fp;
+       dbctx->fetch_key_text           = keyd_fetch_key_text;
+       dbctx->fetch_key_skshash        = keyd_fetch_key_skshash;
+       dbctx->store_key                = keyd_store_key;
+       dbctx->update_keys              = generic_update_keys;
+       dbctx->delete_key               = keyd_delete_key;
+       dbctx->getkeysigs               = generic_getkeysigs;
+       dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
+       dbctx->keyid2uid                = generic_keyid2uid;
+       dbctx->iterate_keys             = keyd_iterate_keys;
+
+       return dbctx;
+}
diff --git a/keydb/keydb_keyring.c b/keydb/keydb_keyring.c
new file mode 100644 (file)
index 0000000..9550434
--- /dev/null
@@ -0,0 +1,455 @@
+/*
+ * keydb_keyring.c - Routines to fetch keys from a PGP keyring file.
+ *
+ * Copyright 2019 Jonathan McDowell <noodles@earth.li>
+ *
+ * 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 <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "charfuncs.h"
+#include "keyarray.h"
+#include "keydb.h"
+#include "keyid.h"
+#include "keystructs.h"
+#include "log.h"
+#include "mem.h"
+#include "onak.h"
+#include "onak-conf.h"
+#include "parsekey.h"
+
+struct onak_keyring_dbctx {
+       uint8_t *file;
+       size_t   length;
+       unsigned int space;
+       unsigned int count;
+       struct {
+               struct openpgp_fingerprint fp;
+               uint8_t *start;
+               size_t len;
+       } *keys;
+};
+
+/**
+ *     starttrans - Start a transaction.
+ *
+ *     This is just a no-op for keyring file access.
+ */
+static bool keyring_starttrans(struct onak_dbctx *dbctx)
+{
+       return true;
+}
+
+/**
+ *     endtrans - End a transaction.
+ *
+ *     This is just a no-op for keyring file access.
+ */
+static void keyring_endtrans(struct onak_dbctx *dbctx)
+{
+       return;
+}
+
+/**
+ * keyring_fetch_key - fetch a key given its index
+ */
+static int keyring_fetch_key(struct onak_keyring_dbctx *privctx,
+               unsigned int index,
+               struct openpgp_publickey **publickey)
+{
+       struct openpgp_packet_list *packets = NULL;
+       struct buffer_ctx buf;
+
+       if (index > privctx->count)
+               return 0;
+
+       buf.buffer = (char *) privctx->keys[index].start;
+       buf.size = privctx->keys[index].len;
+       buf.offset = 0;
+
+       read_openpgp_stream(buffer_fetchchar, &buf, &packets, 0);
+       parse_keys(packets, publickey);
+       free_packet_list(packets);
+       packets = NULL;
+
+       return 1;
+}
+
+static int keyring_fetch_key_fp(struct onak_dbctx *dbctx,
+                       struct openpgp_fingerprint *fingerprint,
+                       struct openpgp_publickey **publickey,
+                       bool intrans)
+{
+       struct onak_keyring_dbctx *privctx =
+               (struct onak_keyring_dbctx *) dbctx->priv;
+       int i;
+
+       for (i = 0; i < privctx->count; i++) {
+               if (fingerprint_cmp(fingerprint, &privctx->keys[i].fp) == 0)
+                       break;
+       }
+
+       if (i < privctx->count) {
+               return keyring_fetch_key(privctx, i, publickey);
+       }
+
+       return 0;
+}
+
+/**
+ *     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.
+ */
+static int keyring_fetch_key_id(struct onak_dbctx *dbctx,
+               uint64_t keyid,
+               struct openpgp_publickey **publickey,
+               bool intrans)
+{
+       struct onak_keyring_dbctx *privctx =
+               (struct onak_keyring_dbctx *) dbctx->priv;
+       int count, i;
+
+       count = 0;
+       for (i = 0; i < privctx->count; i++) {
+               if (fingerprint2keyid(&privctx->keys[i].fp) == keyid) {
+                       if (keyring_fetch_key(privctx, i, publickey))
+                               count++;
+               }
+       }
+
+       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.
+ *
+ *     We don't support storing keys into a keyring file.
+ */
+static int keyring_store_key(struct onak_dbctx *dbctx,
+               struct openpgp_publickey *publickey, bool intrans,
+               bool update)
+{
+       return 0;
+}
+
+/**
+ *     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.
+ *
+ *     We don't support removing keys from a keyring file.
+ */
+static int keyring_delete_key(struct onak_dbctx *dbctx,
+               struct openpgp_fingerprint *fp, bool intrans)
+{
+       return 1;
+}
+
+/**
+ *     fetch_key_text - Trys 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 keyring_fetch_key_text(struct onak_dbctx *dbctx,
+               const char *search,
+               struct openpgp_publickey **publickey)
+{
+       return 0;
+}
+
+/**
+ *     iterate_keys - call a function once for each key in the db.
+ *     @iterfunc: The function to call.
+ *     @ctx: A context pointer
+ *
+ *     Calls iterfunc once for each key in the database. ctx is passed
+ *     unaltered to iterfunc. This function is intended to aid database dumps
+ *     and statistic calculations.
+ *
+ *     Returns the number of keys we iterated over.
+ */
+static int keyring_iterate_keys(struct onak_dbctx *dbctx,
+               void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
+               void *ctx)
+{
+       struct onak_keyring_dbctx *privctx =
+               (struct onak_keyring_dbctx *) dbctx->priv;
+       struct openpgp_publickey  *key = NULL;
+       int count, i;
+
+       count = 0;
+       for (i = 0; i < privctx->count; i++) {
+               if (keyring_fetch_key(privctx, i, &key)) {
+                       iterfunc(ctx, key);
+                       free_publickey(key);
+                       key = NULL;
+               }
+       }
+
+       return count;
+}
+
+static int keyring_update_keys(struct onak_dbctx *dbctx,
+               struct openpgp_publickey **keys,
+               struct keyarray *blacklist,
+               bool updateonly,
+               bool sendsync)
+{
+       return 0;
+}
+
+/*
+ * Include the basic keydb routines.
+ */
+#define NEED_KEYID2UID 1
+#define NEED_GETKEYSIGS 1
+#include "keydb.c"
+
+static int keyring_parse_keys(struct onak_keyring_dbctx *privctx)
+{
+       size_t len, pos, start, totlen;
+       struct openpgp_publickey *key;
+       uint8_t tag;
+
+       if (privctx == NULL) {
+               return 0;
+       }
+
+       if (privctx->file == NULL) {
+               return 0;
+       }
+
+       /*
+        * Walk the keyring file, noting the start of each public key and the
+        * total length of packets associated with it.
+        */
+       len = pos = start = totlen = 0;
+       while (((privctx->length - pos) > 5) && (privctx->file[pos] & 0x80)) {
+               if (privctx->file[pos] & 0x40) {
+                       tag = privctx->file[pos] & 0x3F;
+                       len = privctx->file[pos + 1];
+                       if (len > 191 && len < 224) {
+                               len -= 192;
+                               len <<= 8;
+                               len += privctx->file[pos + 2];
+                               len += 192;
+                               len += 1; /* Header */
+                       } else if (len > 223 && len < 255) {
+                               // Unsupported
+                       } else if (len == 255) {
+                               len = privctx->file[pos + 2];
+                               len <<= 8;
+                               len += privctx->file[pos + 3];
+                               len <<= 8;
+                               len += privctx->file[pos + 4];
+                               len <<= 8;
+                               len += privctx->file[pos + 5];
+                               len += 4; /* Header */
+                       }
+                       len += 2; /* Header */
+               } else {
+                       tag = (privctx->file[pos] & 0x3C) >> 2;
+                       switch (privctx->file[pos] & 3) {
+                       case 0:
+                               len = privctx->file[pos + 1];
+                               len += 2; /* Header */
+                               break;
+                       case 1:
+                               len = privctx->file[pos + 1];
+                               len <<= 8;
+                               len += privctx->file[pos + 2];
+                               len += 3; /* Header */
+                               break;
+                       case 2:
+                               len = privctx->file[pos + 1];
+                               len <<= 8;
+                               len += privctx->file[pos + 2];
+                               len <<= 8;
+                               len += privctx->file[pos + 3];
+                               len <<= 8;
+                               len += privctx->file[pos + 4];
+                               len += 5; /* Header */
+                               break;
+                       case 3:
+                               // Unsupported
+                               break;
+                       }
+               }
+               if (tag == OPENPGP_PACKET_PUBLICKEY) {
+                       if (totlen > 0) {
+                               /* Expand the array of keys if necessary */
+                               if (privctx->count == privctx->space) {
+                                       privctx->space *= 2;
+                                       privctx->keys = realloc(privctx->keys,
+                                               privctx->space *
+                                               sizeof(*privctx->keys));
+                               }
+
+                               /* TODO: Sort by fingerprint? */
+                               privctx->keys[privctx->count].start =
+                                       &privctx->file[start];
+                               privctx->keys[privctx->count].len = totlen;
+                               privctx->count++;
+
+                               /*
+                                * We need to fetch the key to calculate the
+                                * fingerprint.
+                                */
+                               keyring_fetch_key(privctx, privctx->count - 1,
+                                               &key);
+                               get_fingerprint(key->publickey,
+                                       &privctx->keys[privctx->count - 1].fp);
+                               free_publickey(key);
+                               key = NULL;
+                       }
+                       start = pos;
+                       totlen = 0;
+               }
+               totlen += len;
+               pos += len;
+       }
+
+       return privctx->count;
+}
+
+/**
+ *     cleanupdb - De-initialize the key database.
+ *
+ *     This is just a no-op for flat file access.
+ */
+static void keyring_cleanupdb(struct onak_dbctx *dbctx)
+{
+       struct onak_keyring_dbctx *privctx =
+               (struct onak_keyring_dbctx *) dbctx->priv;
+
+       if (dbctx->priv != NULL) {
+               if (privctx->file != NULL) {
+                       munmap(privctx->file, privctx->length);
+               }
+               free(privctx->keys);
+               free(dbctx->priv);
+               dbctx->priv = NULL;
+       }
+
+       if (dbctx != NULL) {
+               free(dbctx);
+       }
+};
+
+/**
+ *     initdb - Initialize the key database.
+ *
+ *     This is just a no-op for flat file access.
+ */
+struct onak_dbctx *keydb_keyring_init(struct onak_db_config *dbcfg,
+               bool readonly)
+{
+       struct onak_keyring_dbctx *privctx;
+       struct onak_dbctx *dbctx;
+       struct stat sb;
+       int fd;
+
+       dbctx = malloc(sizeof(struct onak_dbctx));
+       if (dbctx == NULL) {
+               return NULL;
+       }
+       dbctx->config = dbcfg;
+       dbctx->priv = privctx = calloc(1, sizeof(*privctx));
+       if (privctx == NULL) {
+               free(dbctx);
+               return NULL;
+       }
+       privctx->space = 16;
+       privctx->keys = calloc(privctx->space, sizeof(*privctx->keys));
+
+       fd = open(dbcfg->location, O_RDONLY);
+       if (fd < 0) {
+               logthing(LOGTHING_CRITICAL,
+                               "Couldn't open keyring file %s: %s (%d)",
+                               dbcfg->location,
+                               strerror(errno),
+                               errno);
+               keyring_cleanupdb(dbctx);
+               return NULL;
+       }
+       if (fstat(fd, &sb) < 0) {
+               logthing(LOGTHING_CRITICAL,
+                               "Couldn't stat keyring file %s: %s (%d)",
+                               dbcfg->location,
+                               strerror(errno),
+                               errno);
+               close(fd);
+               keyring_cleanupdb(dbctx);
+               return NULL;
+       }
+       privctx->file = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+       if (privctx->file == MAP_FAILED) {
+               logthing(LOGTHING_CRITICAL,
+                               "Couldn't mmap keyring file %s: %s (%d)",
+                               dbcfg->location,
+                               strerror(errno),
+                               errno);
+               privctx->file = NULL;
+               close(fd);
+               keyring_cleanupdb(dbctx);
+               return NULL;
+       }
+       privctx->length = sb.st_size;
+       close(fd);
+
+       if (keyring_parse_keys(privctx) == 0) {
+               logthing(LOGTHING_CRITICAL,
+                               "Failed to load any keys from keyring file %s",
+                               dbcfg->location);
+               keyring_cleanupdb(dbctx);
+               return NULL;
+       }
+
+       dbctx->cleanupdb                = keyring_cleanupdb;
+       dbctx->starttrans               = keyring_starttrans;
+       dbctx->endtrans                 = keyring_endtrans;
+       dbctx->fetch_key_id             = keyring_fetch_key_id;
+       dbctx->fetch_key_fp             = keyring_fetch_key_fp;
+       dbctx->fetch_key_text           = keyring_fetch_key_text;
+       dbctx->store_key                = keyring_store_key;
+       dbctx->update_keys              = keyring_update_keys;
+       dbctx->delete_key               = keyring_delete_key;
+       dbctx->getkeysigs               = generic_getkeysigs;
+       dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
+       dbctx->keyid2uid                = generic_keyid2uid;
+       dbctx->iterate_keys             = keyring_iterate_keys;
+
+       return dbctx;
+}
diff --git a/keydb/keydb_pg.c b/keydb/keydb_pg.c
new file mode 100644 (file)
index 0000000..7d59640
--- /dev/null
@@ -0,0 +1,717 @@
+/*
+ * keydb_pg.c - Routines to store and fetch keys in a PostGres database.
+ *
+ * Copyright 2002-2004 Jonathan McDowell <noodles@earth.li>
+ *
+ * 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 <postgresql/libpq/libpq-fs.h>
+
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "hash.h"
+#include "keydb.h"
+#include "keyid.h"
+#include "decodekey.h"
+#include "keystructs.h"
+#include "log.h"
+#include "mem.h"
+#include "onak-conf.h"
+#include "parsekey.h"
+
+struct pg_fc_ctx {
+       PGconn *dbconn;
+       int fd;
+};
+
+/**
+ *     keydb_fetchchar - Fetches a char from a file.
+ */
+static int keydb_fetchchar(void *_ctx, size_t count, void *c)
+{
+       struct pg_fc_ctx *ctx = (struct pg_fc_ctx *) _ctx;
+
+       return (!lo_read(ctx->dbconn, ctx->fd, (char *) c, count));
+}
+
+/**
+ *     keydb_putchar - Puts a char to a file.
+ */
+static int keydb_putchar(void *_ctx, size_t count, void *c)
+{
+       struct pg_fc_ctx *ctx = (struct pg_fc_ctx *) _ctx;
+
+       return !(lo_write(ctx->dbconn, ctx->fd, (char *) c, count));
+}
+
+/**
+ *     starttrans - Start a transaction.
+ *
+ *     Start a transaction. Intended to be used if we're about to perform many
+ *     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(struct onak_dbctx *dbctx)
+{
+       PGconn *dbconn = (PGconn *) dbctx->priv;
+       PGresult *result = NULL;
+       
+       result = PQexec(dbconn, "BEGIN");
+       PQclear(result);
+
+       return true;
+}
+
+/**
+ *     endtrans - End a transaction.
+ *
+ *     Ends a transaction.
+ */
+static void pg_endtrans(struct onak_dbctx *dbctx)
+{
+       PGconn *dbconn = (PGconn *) dbctx->priv;
+       PGresult *result = NULL;
+
+       result = PQexec(dbconn, "COMMIT");
+       PQclear(result);
+
+       return;
+}
+
+/**
+ *     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.
+ *
+ *     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 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 i = 0;
+       int numkeys = 0;
+       Oid key_oid;
+       struct pg_fc_ctx fcctx;
+
+       if (!intrans) {
+               result = PQexec(dbconn, "BEGIN");
+               PQclear(result);
+       }
+       
+       if (keyid > 0xFFFFFFFF) {
+               snprintf(statement, 1023,
+                       "SELECT keydata FROM onak_keys WHERE keyid = '%"
+                       PRIX64 "'",
+                       keyid);
+       } else {
+               snprintf(statement, 1023,
+                       "SELECT keydata FROM onak_keys WHERE keyid "
+                       "LIKE '%%%" PRIX64 "'",
+                       keyid);
+       }
+       result = PQexec(dbconn, statement);
+
+       if (PQresultStatus(result) == PGRES_TUPLES_OK) {
+               numkeys = PQntuples(result);
+               for (i = 0; i < numkeys && numkeys <= config.maxkeys; i++) {
+                       oids = PQgetvalue(result, i, 0);
+                       key_oid = (Oid) atoi(oids);
+
+                       fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
+                       if (fcctx.fd < 0) {
+                               logthing(LOGTHING_ERROR,
+                                               "Can't open large object.");
+                       } else {
+                               fcctx.dbconn = dbconn;
+                               read_openpgp_stream(keydb_fetchchar, &fcctx,
+                                               &packets, 0);
+                               parse_keys(packets, publickey);
+                               lo_close(dbconn, fcctx.fd);
+                               free_packet_list(packets);
+                               packets = NULL;
+                       }
+               }
+       } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
+               logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
+       }
+
+       PQclear(result);
+
+       if (!intrans) {
+               result = PQexec(dbconn, "COMMIT");
+               PQclear(result);
+       }
+       return (numkeys);
+}
+
+/**
+ *     fetch_key_text - Trys 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.
+ */
+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 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);
+       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 "
+                       "AND onak_uids.uid LIKE '%%%s%%'",
+                       newsearch);
+       result = PQexec(dbconn, statement);
+       free(newsearch);
+       newsearch = NULL;
+
+       if (PQresultStatus(result) == PGRES_TUPLES_OK) {
+               numkeys = PQntuples(result);
+               for (i = 0; i < numkeys && numkeys <= config.maxkeys; i++) {
+                       oids = PQgetvalue(result, i, 0);
+                       key_oid = (Oid) atoi(oids);
+
+                       fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
+                       if (fcctx.fd < 0) {
+                               logthing(LOGTHING_ERROR,
+                                               "Can't open large object.");
+                       } else {
+                               fcctx.dbconn = dbconn;
+                               read_openpgp_stream(keydb_fetchchar, &fcctx,
+                                               &packets,
+                                               0);
+                               parse_keys(packets, publickey);
+                               lo_close(dbconn, fcctx.fd);
+                               free_packet_list(packets);
+                               packets = NULL;
+                       }
+               }
+       } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
+               logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
+       }
+
+       PQclear(result);
+
+       result = PQexec(dbconn, "COMMIT");
+       PQclear(result);
+       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.
+ *     @intrans: If we're already in a transaction.
+ *     @update: If true the key exists and should be updated.
+ *
+ *     Again we just use the hex representation of the keyid as the filename
+ *     to store the key to. We flatten the public key to a list of OpenPGP
+ *     packets and then use write_openpgp_stream() to write the stream out to
+ *     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 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;
+       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.
+        *
+        * TODO: Can we optimize this perhaps? Possibly when other data is
+        * involved as well? I suspect this is easiest and doesn't make a lot
+        * of difference though - the largest chunk of data is the keydata and
+        * it definitely needs updated.
+        */
+       if (update) {
+               get_fingerprint(publickey->publickey, &fp);
+               pg_delete_key(dbctx, &fp, true);
+       }
+
+       next = publickey->next;
+       publickey->next = NULL;
+       flatten_publickey(publickey, &packets, &list_end);
+       publickey->next = next;
+               
+       key_oid = lo_creat(dbconn, INV_READ | INV_WRITE);
+       if (key_oid == 0) {
+               logthing(LOGTHING_ERROR, "Can't create key OID");
+       } else {
+               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 "
+                       "('%" PRIX64 "', '%d')", 
+                       keyid,
+                       key_oid);
+       result = PQexec(dbconn, statement);
+
+       if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+               logthing(LOGTHING_ERROR, "Problem storing key in DB.");
+               logthing(LOGTHING_ERROR, "%s", PQresultErrorMessage(result));
+       }
+       PQclear(result);
+
+       uids = keyuids(publickey, &primary);
+       if (uids != NULL) {
+               for (i = 0; uids[i] != NULL; i++) {
+                       safeuid = malloc(strlen(uids[i]) * 2 + 1);
+                       if (safeuid != NULL) {
+                               memset(safeuid, 0, strlen(uids[i]) * 2 + 1);
+                               PQescapeStringConn(dbconn, safeuid, uids[i],
+                                               strlen(uids[i]), NULL);
+
+                               snprintf(statement, 1023,
+                                       "INSERT INTO onak_uids "
+                                       "(keyid, uid, pri) "
+                                       "VALUES ('%" PRIX64 "', '%s', '%c')",
+                                       keyid,
+                                       safeuid,
+                                       (uids[i] == primary) ? 't' : 'f');
+                               result = PQexec(dbconn, statement);
+
+                               free(safeuid);
+                               safeuid = NULL;
+                       }
+                       if (uids[i] != NULL) {
+                               free(uids[i]);
+                               uids[i] = NULL;
+                       }
+
+                       if (PQresultStatus(result) != PGRES_COMMAND_OK) {
+                               logthing(LOGTHING_ERROR,
+                                               "Problem storing key in DB.");
+                               logthing(LOGTHING_ERROR, "%s",
+                                               PQresultErrorMessage(result));
+                       }
+                       /*
+                        * TODO: Check result.
+                        */
+                       PQclear(result);
+               }
+               free(uids);
+               uids = NULL;
+       }
+
+       for (curuid = publickey->uids; curuid != NULL; curuid = curuid->next) {
+               for (packets = curuid->sigs; packets != NULL; 
+                               packets = packets->next) {
+                       snprintf(statement, 1023,
+                               "INSERT INTO onak_sigs (signer, signee) "
+                               "VALUES ('%" PRIX64 "', '%" PRIX64 "')",
+                               sig_keyid(packets->packet),
+                               keyid);
+                       result = PQexec(dbconn, statement);
+                       PQclear(result);
+               }
+       }
+
+       if (!intrans) {
+               result = PQexec(dbconn, "COMMIT");
+               PQclear(result);
+       }
+       
+       return 0;
+}
+
+/**
+ *     keyid2uid - Takes a keyid and returns the primary UID for it.
+ *     @keyid: The keyid to lookup.
+ */
+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 = '%" PRIX64
+               "' AND pri = 't'",
+               keyid);
+       result = PQexec(dbconn, statement);
+
+       /*
+        * Technically we only expect one response to the query; a key only has
+        * one primary ID. Better to return something than nothing though.
+        *
+        * TODO: Log if we get more than one response? Needs logging framework
+        * first though.
+        */
+       if (PQresultStatus(result) == PGRES_TUPLES_OK &&
+                       PQntuples(result) >= 1) {
+               uid = strdup(PQgetvalue(result, 0, 0));
+       } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
+               logthing(LOGTHING_ERROR,
+                               "Problem retrieving key (%" PRIX64
+                               ") from DB.",
+                               keyid);
+       }
+
+       PQclear(result);
+
+       return uid;
+}
+
+/**
+ *     getkeysigs - Gets a linked list of the signatures on a key.
+ *     @keyid: The keyid to get the sigs for.
+ *     @revoked: If the key is revoked.
+ *
+ *     This function gets the list of signatures on a key. Used for key 
+ *     indexing and doing stats bits.
+ */
+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];
+       int i, j;
+       int numsigs = 0;
+       bool intrans = false;
+       char *str;
+
+       if (!intrans) {
+               result = PQexec(dbconn, "BEGIN");
+               PQclear(result);
+       }
+
+       snprintf(statement, 1023,
+               "SELECT DISTINCT signer FROM onak_sigs WHERE signee = '%"
+               PRIX64 "'",
+               keyid);
+       result = PQexec(dbconn, statement);
+
+       if (PQresultStatus(result) == PGRES_TUPLES_OK) {
+               numsigs = PQntuples(result);
+               for (i = 0; i < numsigs;  i++) {
+                       j = 0;
+                       signer = 0;
+                       str = PQgetvalue(result, i, 0);
+                       while (str[j] != 0) {
+                               signer <<= 4;
+                               if (str[j] >= '0' && str[j] <= '9') {
+                                       signer += str[j] - '0';
+                               } else {
+                                       signer += str[j] - 'A' + 10;
+                               }
+                               j++;
+                       }
+                       sigs = lladd(sigs, createandaddtohash(signer));
+               }
+       } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
+               logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
+       }
+
+       PQclear(result);
+
+       if (!intrans) {
+               result = PQexec(dbconn, "COMMIT");
+               PQclear(result);
+       }
+
+       /*
+        * TODO: What do we do about revocations? We don't have the details
+        * stored in a separate table, so we'd have to grab the key and decode
+        * it, which we're trying to avoid by having a signers table.
+        */
+       if (revoked != NULL) {
+               *revoked = false;
+       }
+       
+       return sigs;
+}
+
+/**
+ *     iterate_keys - call a function once for each key in the db.
+ *     @iterfunc: The function to call.
+ *     @ctx: A context pointer
+ *
+ *     Calls iterfunc once for each key in the database. ctx is passed
+ *     unaltered to iterfunc. This function is intended to aid database dumps
+ *     and statistic calculations.
+ *
+ *     Returns the number of keys we iterated over.
+ */
+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;
+       int i = 0;
+       int numkeys = 0;
+       Oid key_oid;
+       struct pg_fc_ctx fcctx;
+
+       result = PQexec(dbconn, "SELECT keydata FROM onak_keys;");
+
+       if (PQresultStatus(result) == PGRES_TUPLES_OK) {
+               numkeys = PQntuples(result);
+               for (i = 0; i < numkeys; i++) {
+                       oids = PQgetvalue(result, i, 0);
+                       key_oid = (Oid) atoi(oids);
+
+                       fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
+                       if (fcctx.fd < 0) {
+                               logthing(LOGTHING_ERROR,
+                                               "Can't open large object.");
+                       } else {
+                               fcctx.dbconn = dbconn;
+                               read_openpgp_stream(keydb_fetchchar, &fcctx,
+                                               &packets, 0);
+                               parse_keys(packets, &key);
+                               lo_close(dbconn, fcctx.fd);
+
+                               iterfunc(ctx, key);
+                                       
+                               free_publickey(key);
+                               key = NULL;
+                               free_packet_list(packets);
+                               packets = NULL;
+                       }
+               }
+       } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
+               logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
+       }
+
+       PQclear(result);
+
+       return (numkeys);
+}
+
+/*
+ * Include the basic keydb routines.
+ */
+#define NEED_UPDATEKEYS 1
+#define NEED_GET_FP 1
+#include "keydb.c"
+
+/**
+ *     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;
+}
diff --git a/keydb/keydb_stacked.c b/keydb/keydb_stacked.c
new file mode 100644 (file)
index 0000000..7e997d9
--- /dev/null
@@ -0,0 +1,386 @@
+/*
+ * keydb_stacked.c - backend that stacks other backends together
+ *
+ * Copyright 2016 Jonathan McDowell <noodles@earth.li>
+ *
+ * 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 <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "cleankey.h"
+#include "keydb.h"
+#include "keystructs.h"
+#include "ll.h"
+#include "log.h"
+#include "onak-conf.h"
+
+struct onak_stacked_dbctx {
+       struct ll *backends;
+       bool store_on_fallback;
+};
+
+/*
+ * The following functions only apply to the first backend.
+ */
+
+static bool stacked_starttrans(struct onak_dbctx *dbctx)
+{
+       struct onak_stacked_dbctx *privctx =
+                       (struct onak_stacked_dbctx *) dbctx->priv;
+       struct onak_dbctx *backend =
+                       (struct onak_dbctx *) privctx->backends->object;
+
+       return backend->starttrans(backend);
+}
+
+static void stacked_endtrans(struct onak_dbctx *dbctx)
+{
+       struct onak_stacked_dbctx *privctx =
+                       (struct onak_stacked_dbctx *) dbctx->priv;
+       struct onak_dbctx *backend =
+                       (struct onak_dbctx *) privctx->backends->object;
+
+       backend->starttrans(backend);
+}
+
+static int stacked_store_key(struct onak_dbctx *dbctx,
+               struct openpgp_publickey *publickey, bool intrans,
+               bool update)
+{
+       struct onak_stacked_dbctx *privctx =
+                       (struct onak_stacked_dbctx *) dbctx->priv;
+       struct onak_dbctx *backend =
+                       (struct onak_dbctx *) privctx->backends->object;
+
+       return backend->store_key(backend,
+                       publickey, intrans, update);
+}
+
+static int stacked_delete_key(struct onak_dbctx *dbctx,
+               struct openpgp_fingerprint *fp,
+               bool intrans)
+{
+       struct onak_stacked_dbctx *privctx =
+                       (struct onak_stacked_dbctx *) dbctx->priv;
+       struct onak_dbctx *backend =
+                       (struct onak_dbctx *) privctx->backends->object;
+
+       return backend->delete_key(backend,
+                       fp, intrans);
+}
+
+static int stacked_update_keys(struct onak_dbctx *dbctx,
+               struct openpgp_publickey **keys,
+               struct keyarray *blacklist,
+               bool updateonly,
+               bool sendsync)
+{
+       struct onak_stacked_dbctx *privctx =
+                       (struct onak_stacked_dbctx *) dbctx->priv;
+       struct onak_dbctx *backend =
+                       (struct onak_dbctx *) privctx->backends->object;
+
+       return backend->update_keys(backend, keys, blacklist, updateonly,
+                       sendsync);
+}
+
+static int stacked_iterate_keys(struct onak_dbctx *dbctx,
+               void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
+               void *ctx)
+{
+       struct onak_stacked_dbctx *privctx =
+                       (struct onak_stacked_dbctx *) dbctx->priv;
+       struct onak_dbctx *backend =
+                       (struct onak_dbctx *) privctx->backends->object;
+
+       return backend->iterate_keys(backend, iterfunc, ctx);
+}
+
+static void store_on_fallback(struct onak_stacked_dbctx *privctx,
+               struct openpgp_publickey *publickey, bool intrans)
+{
+       struct onak_dbctx *backend =
+                       (struct onak_dbctx *) privctx->backends->object;
+       struct openpgp_publickey *curkey;
+
+       cleankeys(&publickey, config.clean_policies);
+       /*
+        * If we walked the stack at all, store the key in the first
+        * backend if configured to do so. It's not an update as we
+        * know it's not there or we wouldn't have fallen back.
+        */
+       for (curkey = publickey; curkey != NULL; curkey = curkey->next) {
+               backend->store_key(backend, curkey, intrans, false);
+       }
+}
+
+/*
+ * The functions below will walk along the backend stack until they
+ * reach the end or get a successful result.
+ */
+
+static int stacked_fetch_key_id(struct onak_dbctx *dbctx, uint64_t keyid,
+               struct openpgp_publickey **publickey, bool intrans)
+{
+       struct onak_stacked_dbctx *privctx =
+                       (struct onak_stacked_dbctx *) dbctx->priv;
+       struct onak_dbctx *backend;
+       struct ll *cur;
+       int res = 0;
+
+       for (cur = privctx->backends; cur != NULL && res == 0;
+                       cur = cur->next) {
+               backend = (struct onak_dbctx *) cur->object;
+               res = backend->fetch_key_id(backend, keyid, publickey,
+                               intrans);
+       }
+
+       if (privctx->store_on_fallback && cur != privctx->backends) {
+               store_on_fallback(privctx, *publickey, intrans);
+       }
+
+       return res;
+}
+
+static int stacked_fetch_key_fp(struct onak_dbctx *dbctx,
+               struct openpgp_fingerprint *fingerprint,
+               struct openpgp_publickey **publickey, bool intrans)
+{
+       struct onak_stacked_dbctx *privctx =
+                       (struct onak_stacked_dbctx *) dbctx->priv;
+       struct onak_dbctx *backend;
+       struct ll *cur;
+       int res = 0;
+
+       for (cur = privctx->backends; cur != NULL && res == 0;
+                       cur = cur->next) {
+               backend = (struct onak_dbctx *) cur->object;
+               res = backend->fetch_key_fp(backend, fingerprint, publickey,
+                               intrans);
+       }
+
+       if (privctx->store_on_fallback && cur != privctx->backends) {
+               store_on_fallback(privctx, *publickey, intrans);
+       }
+
+       return res;
+}
+
+static int stacked_fetch_key_text(struct onak_dbctx *dbctx,
+               const char *search,
+               struct openpgp_publickey **publickey)
+{
+       struct onak_stacked_dbctx *privctx =
+                       (struct onak_stacked_dbctx *) dbctx->priv;
+       struct onak_dbctx *backend;
+       struct ll *cur;
+       int res = 0;
+
+       for (cur = privctx->backends; cur != NULL && res == 0;
+                       cur = cur->next) {
+               backend = (struct onak_dbctx *) cur->object;
+               res = backend->fetch_key_text(backend, search, publickey);
+       }
+
+       if (privctx->store_on_fallback && cur != privctx->backends) {
+               store_on_fallback(privctx, *publickey, false);
+       }
+
+       return res;
+}
+
+static int stacked_fetch_key_skshash(struct onak_dbctx *dbctx,
+               const struct skshash *hash,
+               struct openpgp_publickey **publickey)
+{
+       struct onak_stacked_dbctx *privctx =
+                       (struct onak_stacked_dbctx *) dbctx->priv;
+       struct onak_dbctx *backend;
+       struct ll *cur;
+       int res = 0;
+
+       for (cur = privctx->backends; cur != NULL && res == 0;
+                       cur = cur->next) {
+               backend = (struct onak_dbctx *) cur->object;
+               res = backend->fetch_key_skshash(backend, hash, publickey);
+       }
+
+       if (privctx->store_on_fallback && cur != privctx->backends) {
+               store_on_fallback(privctx, *publickey, false);
+       }
+
+       return res;
+}
+
+/*
+ * Include the basic keydb routines so we can use them for fall back.
+ * For all of the following we try the top keydb backend and if that doesn't
+ * have answer fall back to the generics, which will do a retrieve from a
+ * backend further down the stack, then a fallback store.
+ */
+#define NEED_KEYID2UID 1
+#define NEED_GETKEYSIGS 1
+#define NEED_UPDATEKEYS 1
+#include "keydb.c"
+
+static struct ll *stacked_getkeysigs(struct onak_dbctx *dbctx,
+               uint64_t keyid, bool *revoked)
+{
+       struct onak_stacked_dbctx *privctx =
+                       (struct onak_stacked_dbctx *) dbctx->priv;
+       struct onak_dbctx *backend =
+                       (struct onak_dbctx *) privctx->backends->object;
+       struct ll *res;
+
+       res = backend->getkeysigs(backend, keyid, revoked);
+       if (res == NULL) {
+               res = generic_getkeysigs(dbctx, keyid, revoked);
+       }
+
+       return res;
+}
+
+static struct ll *stacked_cached_getkeysigs(struct onak_dbctx *dbctx,
+               uint64_t keyid)
+{
+       struct onak_stacked_dbctx *privctx =
+                       (struct onak_stacked_dbctx *) dbctx->priv;
+       struct onak_dbctx *backend =
+                       (struct onak_dbctx *) privctx->backends->object;
+       struct ll *res;
+
+       res = backend->cached_getkeysigs(backend, keyid);
+       if (res == NULL) {
+               res = generic_cached_getkeysigs(dbctx, keyid);
+       }
+
+       return res;
+}
+
+static char *stacked_keyid2uid(struct onak_dbctx *dbctx,
+                       uint64_t keyid)
+{
+       struct onak_stacked_dbctx *privctx =
+                       (struct onak_stacked_dbctx *) dbctx->priv;
+       struct onak_dbctx *backend =
+                       (struct onak_dbctx *) privctx->backends->object;
+       char *res = NULL;
+
+       res = backend->keyid2uid(backend, keyid);
+       if (!res) {
+               res = generic_keyid2uid(dbctx, keyid);
+       }
+
+       return res;
+}
+
+static void stacked_cleanupdb(struct onak_dbctx *dbctx)
+{
+       struct onak_stacked_dbctx *privctx =
+                       (struct onak_stacked_dbctx *) dbctx->priv;
+       struct onak_dbctx *backend;
+       struct ll *cur;
+       int res = 0;
+
+       for (cur = privctx->backends; cur != NULL && res == 0;
+                       cur = cur->next) {
+               backend = (struct onak_dbctx *) cur->object;
+               backend->cleanupdb(backend);
+       }
+
+       if (dbctx->priv != NULL) {
+               free(dbctx->priv);
+               dbctx->priv = NULL;
+       }
+
+       if (dbctx != NULL) {
+               free(dbctx);
+       }
+}
+
+struct onak_dbctx *keydb_stacked_init(struct onak_db_config *dbcfg,
+               bool readonly)
+{
+       struct onak_dbctx *dbctx;
+       struct onak_stacked_dbctx *privctx;
+       struct onak_dbctx *backend;
+       struct onak_db_config *backend_cfg;
+       char *backend_name, *saveptr = NULL;
+
+       if (dbcfg == NULL) {
+               logthing(LOGTHING_CRITICAL,
+                       "No backend database configuration supplied.");
+               return NULL;
+       }
+
+       dbctx = malloc(sizeof(struct onak_dbctx));
+
+       if (dbctx == NULL) {
+               return NULL;
+       }
+
+       dbctx->config = dbcfg;
+       dbctx->priv = privctx = malloc(sizeof(struct onak_stacked_dbctx));
+       if (dbctx->priv == NULL) {
+               free(dbctx);
+               return (NULL);
+       }
+
+       /* TODO: Make configurable? */
+       privctx->store_on_fallback = true;
+       privctx->backends = NULL;
+
+       backend_name = strtok_r(dbcfg->location, ":", &saveptr);
+       while (backend_name != NULL) {
+               backend_cfg = find_db_backend_config(config.backends,
+                               backend_name);
+               if (backend_cfg == NULL) {
+                       logthing(LOGTHING_CRITICAL,
+                               "Couldn't find configuration for %s backend",
+                               backend_name);
+                       stacked_cleanupdb(dbctx);
+                       return NULL;
+               }
+               logthing(LOGTHING_INFO, "Loading stacked backend: %s",
+                               backend_cfg->name);
+
+               backend = config.dbinit(backend_cfg, readonly);
+               privctx->backends = lladdend(privctx->backends, backend);
+
+               backend_name = strtok_r(NULL, ":", &saveptr);
+       }
+
+       if (privctx->backends != NULL) {
+               dbctx->cleanupdb = stacked_cleanupdb;
+               dbctx->starttrans = stacked_starttrans;
+               dbctx->endtrans = stacked_endtrans;
+               dbctx->fetch_key_id = stacked_fetch_key_id;
+               dbctx->fetch_key_fp = stacked_fetch_key_fp;
+               dbctx->fetch_key_text = stacked_fetch_key_text;
+               dbctx->fetch_key_skshash = stacked_fetch_key_skshash;
+               dbctx->store_key = stacked_store_key;
+               dbctx->update_keys = stacked_update_keys;
+               dbctx->delete_key = stacked_delete_key;
+               dbctx->getkeysigs = stacked_getkeysigs;
+               dbctx->cached_getkeysigs = stacked_cached_getkeysigs;
+               dbctx->keyid2uid = stacked_keyid2uid;
+               dbctx->iterate_keys = stacked_iterate_keys;
+       }
+
+       return dbctx;
+}
diff --git a/keydb/keydctl.8 b/keydb/keydctl.8
new file mode 100644 (file)
index 0000000..430da37
--- /dev/null
@@ -0,0 +1,51 @@
+.TH KEYD 8
+.SH NAME
+keydctl \- control an onak keyd instance
+.SH SYNOPSIS
+.PP
+.B keydctl
+[
+.B options
+]
+.B command
+.SH DESCRIPTION
+.PP
+keydctl is a command line client for interacting with a backend keyd
+daemon. It's intended to perform functions that are specifically related
+to keyd rather than being generic keyserver functions. See
+.BR onak(1)
+for details about how to perform key related operations.
+.SS "Options"
+.TP
+\fB\-c \fIFILE\fR\fR
+Use \fIFILE\fR as the config file instead of the default.
+.TP
+\fB\-h\fR
+Display help text.
+.SS "Commands"
+.TP
+.B check
+Query if keyd is running and accepting commands. Returns 0 if it is, 1
+otherwise. Outputs nothing to stdout/stderr.
+.TP
+.B quit
+Request that keyd exits cleanly
+.TP
+.B status
+Display the status of a running keyd. Currently the protocol version in use,
+when keyd was started, the number of client connections seen and a breakdown
+of the commands seen.
+.SH FILES
+.br
+.nf
+.\" set tabstop to longest possible filename, plus a wee bit
+.ta \w'/usr/lib/perl/getopts.pl   'u
+\fI/etc/onak.ini\fR    default configuration file
+.SH NOTES
+This man page could probably do with some more details.
+.SH "SEE ALSO"
+.BR onak (1)
+.BR keyd (8)
+.SH AUTHOR
+onak was written by Jonathan McDowell <noodles@earth.li>. It can be found at
+http://www.earth.li/projectpurple/progs/onak.html
diff --git a/keydb/keydctl.c b/keydb/keydctl.c
new file mode 100644 (file)
index 0000000..8712f63
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * keydctl.c - A simple program to control a running keyd instance
+ *
+ * Copyright 2011 Jonathan McDowell <noodles@earth.li>
+ *
+ * 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 <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include "build-config.h"
+
+#include "keyd.h"
+#include "onak-conf.h"
+
+static int keyd_fd = -1;
+static int verbose = 0;
+
+static int keyd_do_command(enum keyd_ops cmd, void *buf, size_t len)
+{
+       uint32_t tmp;
+
+       if (keyd_fd < 0) {
+               return -1;
+       }
+
+       tmp = cmd;
+       if (write(keyd_fd, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+               if (verbose >= 0) {
+                       fprintf(stderr,
+                               "Couldn't write keyd command %d: %s (%d)\n",
+                               cmd, strerror(errno), errno);
+               }
+               exit(EXIT_FAILURE);
+       } else if (read(keyd_fd, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+               if (verbose >= 0) {
+                       fprintf(stderr,
+                               "Couldn't read keyd command %d reply: "
+                               "%s (%d)\n",
+                               cmd, strerror(errno), errno);
+                       }
+               exit(EXIT_FAILURE);
+       } else if (tmp != KEYD_REPLY_OK) {
+               return -1;
+       } else if (buf == NULL) {
+               return 0;
+       } else if (read(keyd_fd, &tmp, sizeof(tmp)) != sizeof(tmp)) {
+               if (verbose >= 0) {
+                       fprintf(stderr,
+                               "Couldn't read keyd command %d reply length: "
+                               "%s (%d)\n",
+                               cmd, strerror(errno), errno);
+               }
+               exit(EXIT_FAILURE);
+       } else if (tmp > len) {
+               /* TODO: Read what we can into buf and skip the rest */
+               return -1;
+       } else {
+               return read(keyd_fd, buf, tmp);
+       }
+}
+
+static void keyd_connect(void)
+{
+       struct sockaddr_un sock;
+       uint32_t           reply = KEYD_REPLY_UNKNOWN_CMD;
+
+       keyd_fd = socket(PF_UNIX, SOCK_STREAM, 0);
+       if (keyd_fd < 0) {
+               if (verbose >= 0) {
+                       fprintf(stderr,
+                               "Couldn't open socket: %s (%d)\n",
+                               strerror(errno),
+                               errno);
+               }
+               exit(EXIT_FAILURE);
+       }
+
+       sock.sun_family = AF_UNIX;
+       snprintf(sock.sun_path, sizeof(sock.sun_path) - 1, "%s/%s",
+                       config.sock_dir,
+                       KEYD_SOCKET);
+       if (connect(keyd_fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) {
+               if (verbose >= 0) {
+                       fprintf(stderr,
+                               "Couldn't connect to socket %s: %s (%d)\n",
+                               sock.sun_path,
+                               strerror(errno),
+                               errno);
+               }
+               exit(EXIT_FAILURE);
+       }
+
+       keyd_do_command(KEYD_CMD_VERSION, &reply, sizeof(reply));
+       if (reply != keyd_version) {
+               if (verbose >= 0) {
+                       fprintf(stderr, "Error! keyd protocol version "
+                               "mismatch. (us = %d, it = %d)\n",
+                               keyd_version, reply);
+               }
+               exit(EXIT_FAILURE);
+       }
+
+       return;
+}
+
+static void keyd_close(void)
+{
+       uint32_t cmd = KEYD_CMD_CLOSE;
+
+       if (write(keyd_fd, &cmd, sizeof(cmd)) != sizeof(cmd) && verbose >= 0) {
+               fprintf(stderr, "Couldn't send close cmd: %s (%d)\n",
+                               strerror(errno),
+                               errno);
+       }
+
+       if (shutdown(keyd_fd, SHUT_RDWR) < 0 && verbose >= 0) {
+               fprintf(stderr, "Error shutting down socket: %d\n",
+                               errno);
+       }
+       if (close(keyd_fd) < 0 && verbose >= 0) {
+               fprintf(stderr, "Error closing down socket: %d\n",
+                               errno);
+       }
+       keyd_fd = -1;
+
+       return;
+
+}
+
+static void keyd_status(void)
+{
+       uint32_t reply;
+       struct keyd_stats stats;
+
+       if (keyd_do_command(KEYD_CMD_VERSION, &reply, sizeof(reply)) == -1) {
+               printf("Got failure asking for keyd version.\n");
+               return;
+       }
+       printf("Using keyd protocol version %d.\n", reply);
+
+       if (keyd_do_command(KEYD_CMD_STATS, &stats, sizeof(stats)) == -1) {
+               printf("Got failure asking for keyd statistics.\n");
+               return;
+       }
+
+       printf("keyd running since %s", ctime(&stats.started));
+       printf("%d client connections received\n", stats.connects);
+
+       printf("Command statistics:\n");
+       printf("  Version:          %d\n",
+               stats.command_stats[KEYD_CMD_VERSION]);
+       printf("  Get key by ID:    %d\n",
+               stats.command_stats[KEYD_CMD_GET_ID]);
+       printf("  Get key by FP:    %d\n",
+               stats.command_stats[KEYD_CMD_GET_FP]);
+       printf("  Get key by hash:  %d\n",
+               stats.command_stats[KEYD_CMD_GET_SKSHASH]);
+       printf("  Store key:        %d\n",
+               stats.command_stats[KEYD_CMD_STORE]);
+       printf("  Delete key:       %d\n",
+               stats.command_stats[KEYD_CMD_DELETE]);
+       printf("  Update key:       %d\n",
+               stats.command_stats[KEYD_CMD_UPDATE]);
+       printf("  Search key:       %d\n",
+               stats.command_stats[KEYD_CMD_GET_TEXT]);
+       printf("  Get full keyid:   %d\n",
+               stats.command_stats[KEYD_CMD_GETFULLKEYID]);
+       printf("  Iterate all keys: %d\n",
+               stats.command_stats[KEYD_CMD_KEYITER]);
+       printf("  Close:            %d\n",
+               stats.command_stats[KEYD_CMD_CLOSE]);
+       printf("  Quit:             %d\n", stats.command_stats[KEYD_CMD_QUIT]);
+       printf("  Get statistics:   %d\n",
+               stats.command_stats[KEYD_CMD_STATS]);
+       printf("  Unknown:          %d\n",
+               stats.command_stats[KEYD_CMD_UNKNOWN]);
+
+       return;
+}
+
+static void usage(void)
+{
+       puts("keydctl " ONAK_VERSION " - control an onak keyd instance.\n");
+       puts("Usage:\n");
+       puts("\tkeydctl [options] <command> <parameters>\n");
+       puts("\tCommands:\n");
+       puts("\tcheck    - check if keyd is running");
+       puts("\tquit     - request that keyd cleanly shuts down");
+       puts("\tstatus   - display running keyd status");
+       exit(EXIT_FAILURE);
+}
+
+int main(int argc, char *argv[])
+{
+       int      optchar;
+       char    *configfile = NULL;
+
+       while ((optchar = getopt(argc, argv, "c:h")) != -1 ) {
+               switch (optchar) {
+               case 'c':
+                       configfile = strdup(optarg);
+                       break;
+               case 'h':
+               default:
+                       usage();
+                       break;
+               }
+       }
+
+       readconfig(configfile);
+       free(configfile);
+       configfile = NULL;
+
+       if ((argc - optind) < 1) {
+               cleanupconfig();
+               usage();
+       } else if (!strcmp("check", argv[optind])) {
+               /* Just do the connect and close quietly */
+               verbose = -1;
+               keyd_connect();
+               keyd_close();
+       } else if (!strcmp("status", argv[optind])) {
+               keyd_connect();
+               keyd_status();
+               keyd_close();
+       } else if (!strcmp("quit", argv[optind])) {
+               keyd_connect();
+               keyd_do_command(KEYD_CMD_QUIT, NULL, 0);
+               keyd_close();
+       } else {
+               cleanupconfig();
+               usage();
+       }
+
+       cleanupconfig();
+
+       exit(EXIT_SUCCESS);
+}
diff --git a/keydb_db4.c b/keydb_db4.c
deleted file mode 100644 (file)
index 1f306e2..0000000
+++ /dev/null
@@ -1,1760 +0,0 @@
-/*
- * keydb_db4.c - Routines to store and fetch keys in a DB4 database.
- *
- * Copyright 2002-2008 Jonathan McDowell <noodles@earth.li>
- *
- * 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 <sys/types.h>
-#include <sys/stat.h>
-#include <sys/uio.h>
-#include <ctype.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <db.h>
-
-#include "charfuncs.h"
-#include "keyarray.h"
-#include "keydb.h"
-#include "keyid.h"
-#include "decodekey.h"
-#include "keystructs.h"
-#include "mem.h"
-#include "ll.h"
-#include "log.h"
-#include "onak.h"
-#include "onak-conf.h"
-#include "parsekey.h"
-#include "wordlist.h"
-
-#define DB4_UPGRADE_FILE "db_upgrade.lck"
-
-struct onak_db4_dbctx {
-       DB_ENV *dbenv;  /* The database environment context */
-       int numdbs;     /* Number of data databases in use */
-       DB **dbconns;   /* Connections to the key data databases */
-       DB *worddb;     /* Connection to the word lookup database */
-       DB *id32db;     /* Connection to the 32 bit ID lookup database */
-       DB *id64db;     /* Connection to the 64 bit ID lookup database */
-       DB *skshashdb;  /* Connection to the SKS hash database */
-       DB *subkeydb;   /* Connection to the subkey ID lookup database */
-       DB_TXN *txn;    /* Our current transaction ID */
-};
-
-DB *keydb_id(struct onak_db4_dbctx *privctx, uint64_t keyid)
-{
-       uint64_t keytrun;
-
-       keytrun = keyid >> 8;
-
-       return(privctx->dbconns[keytrun % privctx->numdbs]);
-}
-
-DB *keydb_fp(struct onak_db4_dbctx *privctx, struct openpgp_fingerprint *fp)
-{
-       uint64_t keytrun;
-
-       keytrun = fp->fp[4];
-       keytrun <<= 8;
-       keytrun |= fp->fp[5];
-       keytrun <<= 8;
-       keytrun |= fp->fp[6];
-       keytrun <<= 8;
-       keytrun |= fp->fp[7];
-
-       return(privctx->dbconns[keytrun % privctx->numdbs]);
-}
-
-/**
- *     db4_errfunc - Direct DB errors to logfile
- *
- *     Basic function to take errors from the DB library and output them to
- *     the logfile rather than stderr.
- */
-#if (DB_VERSION_MAJOR == 4) && (DB_VERSION_MINOR < 3)
-static void db4_errfunc(const char *errpfx, const char *errmsg)
-#else
-static void db4_errfunc(const DB_ENV *edbenv, const char *errpfx,
-               const char *errmsg)
-#endif
-{
-       if (errpfx) {
-               logthing(LOGTHING_DEBUG, "db4 error: %s:%s", errpfx, errmsg);
-       } else {
-               logthing(LOGTHING_DEBUG, "db4 error: %s", errmsg);
-       }
-
-       return;
-}
-
-/**
- *     starttrans - Start a transaction.
- *
- *     Start a transaction. Intended to be used if we're about to perform many
- *     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 db4_starttrans(struct onak_dbctx *dbctx)
-{
-       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
-       int ret;
-
-       log_assert(privctx->dbenv != NULL);
-       log_assert(privctx->txn == NULL);
-
-       ret = privctx->dbenv->txn_begin(privctx->dbenv,
-               NULL, /* No parent transaction */
-               &privctx->txn,
-               0);
-       if (ret != 0) {
-               logthing(LOGTHING_CRITICAL,
-                               "Error starting transaction: %s",
-                               db_strerror(ret));
-               exit(1);
-       }
-
-       return true;
-}
-
-/**
- *     endtrans - End a transaction.
- *
- *     Ends a transaction.
- */
-static void db4_endtrans(struct onak_dbctx *dbctx)
-{
-       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
-       int ret;
-
-       log_assert(privctx->dbenv != NULL);
-       log_assert(privctx->txn != NULL);
-
-       ret = privctx->txn->commit(privctx->txn,
-               0);
-       if (ret != 0) {
-               logthing(LOGTHING_CRITICAL,
-                               "Error ending transaction: %s",
-                               db_strerror(ret));
-               exit(1);
-       }
-       privctx->txn = NULL;
-
-       return;
-}
-
-/**
- *     db4_upgradedb - Upgrade a DB4 database
- *
- *     Called if we discover we need to upgrade our DB4 database; ie if
- *     we're running with a newer version of db4 than the database was
- *     created with.
- */
-static int db4_upgradedb(struct onak_dbctx *dbctx)
-{
-       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
-       DB *curdb = NULL;
-       int ret;
-       int i;
-       char buf[1024];
-       int lockfile_fd;
-       struct stat statbuf;
-       ssize_t written;
-
-       snprintf(buf, sizeof(buf) - 1, "%s/%s", dbctx->config->location,
-                       DB4_UPGRADE_FILE);
-       lockfile_fd = open(buf, O_RDWR | O_CREAT | O_EXCL, 0600);
-       if (lockfile_fd < 0) {
-               if (errno == EEXIST) {
-                       while (stat(buf, &statbuf) == 0) ;
-                       return 0;
-               } else {
-                       logthing(LOGTHING_CRITICAL, "Couldn't open database "
-                               "update lock file: %s", strerror(errno));
-                       return -1;
-               }
-       }
-       snprintf(buf, sizeof(buf) - 1, "%d", getpid());
-       written = write(lockfile_fd, buf, strlen(buf));
-       close(lockfile_fd);
-       if (written != strlen(buf)) {
-               logthing(LOGTHING_CRITICAL, "Couldn't write PID to lockfile: "
-                               "%s", strerror(errno));
-               snprintf(buf, sizeof(buf) - 1, "%s/%s", dbctx->config->location,
-                               DB4_UPGRADE_FILE);
-               unlink(buf);
-               return -1;
-       }
-
-       logthing(LOGTHING_NOTICE, "Upgrading DB4 database");
-       ret = db_env_create(&privctx->dbenv, 0);
-       if (ret == 0) {
-               privctx->dbenv->set_errcall(privctx->dbenv, &db4_errfunc);
-               privctx->dbenv->remove(privctx->dbenv, dbctx->config->location, 0);
-               privctx->dbenv = NULL;
-       }
-       for (i = 0; i < privctx->numdbs; i++) {
-               ret = db_create(&curdb, NULL, 0);
-               if (ret == 0) {
-                       snprintf(buf, sizeof(buf) - 1, "%s/keydb.%d.db",
-                               dbctx->config->location, i);
-                       logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
-                       curdb->upgrade(curdb, buf, 0);
-                       curdb->close(curdb, 0);
-               } else {
-                       logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
-                               buf,
-                               db_strerror(ret));
-               }
-       }
-
-       ret = db_create(&curdb, NULL, 0);
-       if (ret == 0) {
-               snprintf(buf, sizeof(buf) - 1, "%s/worddb", dbctx->config->location);
-               logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
-               curdb->upgrade(curdb, buf, 0);
-               curdb->close(curdb, 0);
-       } else {
-               logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
-                       buf,
-                       db_strerror(ret));
-       }
-
-       ret = db_create(&curdb, NULL, 0);
-       if (ret == 0) {
-               snprintf(buf, sizeof(buf) - 1, "%s/id32db", dbctx->config->location);
-               logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
-               curdb->upgrade(curdb, buf, 0);
-               curdb->close(curdb, 0);
-       } else {
-               logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
-                       buf,
-                       db_strerror(ret));
-       }
-
-       ret = db_create(&curdb, NULL, 0);
-       if (ret == 0) {
-               snprintf(buf, sizeof(buf) - 1, "%s/id64db", dbctx->config->location);
-               logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
-               curdb->upgrade(curdb, buf, 0);
-               curdb->close(curdb, 0);
-       } else {
-               logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
-                       buf,
-                       db_strerror(ret));
-       }
-
-       ret = db_create(&curdb, NULL, 0);
-       if (ret == 0) {
-               snprintf(buf, sizeof(buf) - 1, "%s/skshashdb", dbctx->config->location);
-               logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
-               curdb->upgrade(curdb, buf, 0);
-               curdb->close(curdb, 0);
-       } else {
-               logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
-                       buf,
-                       db_strerror(ret));
-       }
-
-       ret = db_create(&curdb, NULL, 0);
-       if (ret == 0) {
-               snprintf(buf, sizeof(buf) - 1, "%s/subkeydb", dbctx->config->location);
-               logthing(LOGTHING_DEBUG, "Upgrading %s", buf);
-               curdb->upgrade(curdb, buf, 0);
-               curdb->close(curdb, 0);
-       } else {
-               logthing(LOGTHING_ERROR, "Error upgrading DB %s : %s",
-                       buf,
-                       db_strerror(ret));
-       }
-
-       snprintf(buf, sizeof(buf) - 1, "%s/%s", dbctx->config->location,
-                       DB4_UPGRADE_FILE);
-       unlink(buf);
-
-       return ret;
-}
-
-/**
- *     fetch_key_fp - Given a fingerprint fetch the key from storage.
- */
-static int db4_fetch_key_fp(struct onak_dbctx *dbctx,
-               struct openpgp_fingerprint *fingerprint,
-               struct openpgp_publickey **publickey,
-               bool intrans)
-{
-       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
-       struct openpgp_packet_list *packets = NULL;
-       DBT key, data;
-       int ret = 0;
-       int numkeys = 0;
-       struct buffer_ctx fetchbuf;
-       struct openpgp_fingerprint subfp;
-
-       memset(&key, 0, sizeof(key));
-       memset(&data, 0, sizeof(data));
-
-       data.size = 0;
-       data.data = NULL;
-
-       key.size = fingerprint->length;
-       key.data = fingerprint->fp;
-
-       if (!intrans) {
-               db4_starttrans(dbctx);
-       }
-
-       ret = keydb_fp(privctx, fingerprint)->get(keydb_fp(privctx,
-                                                       fingerprint),
-                       privctx->txn,
-                       &key,
-                       &data,
-                       0); /* flags*/
-
-       if (ret == DB_NOTFOUND) {
-               /* If we didn't find the key ID see if it's a subkey ID */
-               memset(&key, 0, sizeof(key));
-               memset(&data, 0, sizeof(data));
-               data.data = subfp.fp;
-               data.ulen = MAX_FINGERPRINT_LEN;
-               data.flags = DB_DBT_USERMEM;
-               key.data = fingerprint->fp;
-               key.size = fingerprint->length;
-
-               ret = privctx->subkeydb->get(privctx->subkeydb,
-                       privctx->txn,
-                       &key,
-                       &data,
-                       0); /* flags*/
-
-               if (ret == 0) {
-                       /* We got a subkey match; retrieve the actual key */
-                       memset(&key, 0, sizeof(key));
-                       key.size = subfp.length = data.size;
-                       key.data = subfp.fp;
-
-                       memset(&data, 0, sizeof(data));
-                       data.size = 0;
-                       data.data = NULL;
-
-                       ret = keydb_fp(privctx, &subfp)->get(
-                               keydb_fp(privctx, &subfp),
-                               privctx->txn,
-                               &key,
-                               &data,
-                               0); /* flags*/
-               }
-       }
-
-       if (ret == 0) {
-               fetchbuf.buffer = data.data;
-               fetchbuf.offset = 0;
-               fetchbuf.size = data.size;
-               read_openpgp_stream(buffer_fetchchar, &fetchbuf,
-                               &packets, 0);
-               parse_keys(packets, publickey);
-               free_packet_list(packets);
-               packets = NULL;
-               numkeys++;
-       } else if (ret != DB_NOTFOUND) {
-               logthing(LOGTHING_ERROR,
-                               "Problem retrieving key: %s",
-                               db_strerror(ret));
-       }
-
-       if (!intrans) {
-               db4_endtrans(dbctx);
-       }
-
-       return (numkeys);
-}
-
-/**
- *     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.
- *
- *     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 db4_fetch_key_id(struct onak_dbctx *dbctx, uint64_t keyid,
-               struct openpgp_publickey **publickey,
-               bool intrans)
-{
-       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
-       DBT key, data;
-       DBC *cursor = NULL;
-       int ret = 0;
-       int numkeys = 0;
-       uint32_t  shortkeyid = 0;
-       struct openpgp_fingerprint fingerprint;
-       bool first;
-
-       if (!intrans) {
-               db4_starttrans(dbctx);
-       }
-
-       /* If the key ID fits in 32 bits assume it's a short key id */
-       if (keyid < 0x100000000LL) {
-               ret = privctx->id32db->cursor(privctx->id32db,
-                               privctx->txn,
-                               &cursor,
-                               0);   /* flags */
-
-               shortkeyid = keyid & 0xFFFFFFFF;
-               memset(&key, 0, sizeof(key));
-               memset(&data, 0, sizeof(data));
-               key.data = &shortkeyid;
-               key.size = sizeof(shortkeyid);
-       } else {
-               ret = privctx->id64db->cursor(privctx->id64db,
-                               privctx->txn,
-                               &cursor,
-                               0); /* flags*/
-
-               memset(&key, 0, sizeof(key));
-               memset(&data, 0, sizeof(data));
-               key.data = &keyid;
-               key.size = sizeof(keyid);
-       }
-
-       if (ret != 0) {
-               return 0;
-       }
-
-       memset(&data, 0, sizeof(data));
-       data.ulen = MAX_FINGERPRINT_LEN;
-       data.data = fingerprint.fp;
-       data.flags = DB_DBT_USERMEM;
-
-       first = true;
-       while (cursor->c_get(cursor, &key, &data,
-                               first ? DB_SET : DB_NEXT_DUP) == 0) {
-               /* We got a match; retrieve the actual key */
-               fingerprint.length = data.size;
-
-               if (db4_fetch_key_fp(dbctx, &fingerprint,
-                                       publickey, true))
-                       numkeys++;
-
-               memset(&data, 0, sizeof(data));
-               data.ulen = MAX_FINGERPRINT_LEN;
-               data.data = fingerprint.fp;
-               data.flags = DB_DBT_USERMEM;
-               first = false;
-       }
-       cursor->c_close(cursor);
-       cursor = NULL;
-
-       if (!intrans) {
-               db4_endtrans(dbctx);
-       }
-
-       return (numkeys);
-}
-
-/**
- *     fetch_key_text - Trys 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.
- */
-static int db4_fetch_key_text(struct onak_dbctx *dbctx, const char *search,
-               struct openpgp_publickey **publickey)
-{
-       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
-       DBC *cursor = NULL;
-       DBT key, data;
-       int ret;
-       int i;
-       int numkeys;
-       char *searchtext = NULL;
-       struct ll *wordlist = NULL;
-       struct ll *curword = NULL;
-       struct keyarray keylist = { NULL, 0, 0 };
-       struct keyarray newkeylist = { NULL, 0, 0 };
-       int firstpass = 1;
-       struct openpgp_fingerprint fingerprint;
-
-       numkeys = 0;
-       searchtext = strdup(search);
-       wordlist = makewordlist(wordlist, searchtext);
-
-       for (curword = wordlist; curword != NULL; curword = curword->next) {
-               db4_starttrans(dbctx);
-
-               ret = privctx->worddb->cursor(privctx->worddb,
-                               privctx->txn,
-                               &cursor,
-                               0);   /* flags */
-
-               if (ret != 0) {
-                       db4_endtrans(dbctx);
-                       break;
-               }
-
-               memset(&key, 0, sizeof(key));
-               memset(&data, 0, sizeof(data));
-               key.data = curword->object;
-               key.size = strlen(curword->object);
-               data.flags = DB_DBT_MALLOC;
-               ret = cursor->c_get(cursor,
-                               &key,
-                               &data,
-                               DB_SET);
-               while (ret == 0 && strncmp(key.data, curword->object,
-                                       key.size) == 0 &&
-                               ((char *) curword->object)[key.size] == 0) {
-
-                       fingerprint.length = data.size;
-                       memcpy(fingerprint.fp, data.data, data.size);
-
-                       /*
-                        * Only add the keys containing this word if this is
-                        * our first pass (ie we have no existing key list),
-                        * or the key contained a previous word.
-                        */
-                       if (firstpass || array_find(&keylist, &fingerprint)) {
-                               array_add(&newkeylist, &fingerprint);
-                       }
-
-                       free(data.data);
-                       data.data = NULL;
-
-                       ret = cursor->c_get(cursor,
-                                       &key,
-                                       &data,
-                                       DB_NEXT);
-               }
-               array_free(&keylist);
-               keylist.keys = newkeylist.keys;
-               keylist.count = newkeylist.count;
-               keylist.size = newkeylist.size;
-               newkeylist.keys = NULL;
-               newkeylist.count = newkeylist.size = 0;
-               if (data.data != NULL) {
-                       free(data.data);
-                       data.data = NULL;
-               }
-               cursor->c_close(cursor);
-               cursor = NULL;
-               firstpass = 0;
-               db4_endtrans(dbctx);
-       }
-       llfree(wordlist, NULL);
-       wordlist = NULL;
-
-       if (keylist.count > config.maxkeys) {
-               keylist.count = config.maxkeys;
-       }
-
-       db4_starttrans(dbctx);
-       for (i = 0; i < keylist.count; i++) {
-               numkeys += db4_fetch_key_fp(dbctx, &keylist.keys[i],
-                       publickey,
-                       true);
-       }
-       array_free(&keylist);
-       free(searchtext);
-       searchtext = NULL;
-
-       db4_endtrans(dbctx);
-
-       return (numkeys);
-}
-
-static int db4_fetch_key_skshash(struct onak_dbctx *dbctx,
-               const struct skshash *hash,
-               struct openpgp_publickey **publickey)
-{
-       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
-       DBT       key, data;
-       DBC      *cursor = NULL;
-       int       ret;
-       int       count = 0;
-       struct openpgp_fingerprint fingerprint;
-
-       ret = privctx->skshashdb->cursor(privctx->skshashdb,
-                       privctx->txn,
-                       &cursor,
-                       0);   /* flags */
-
-       if (ret != 0) {
-               return 0;
-       }
-
-       memset(&key, 0, sizeof(key));
-       memset(&data, 0, sizeof(data));
-       key.data = (void *) hash->hash;
-       key.size = sizeof(hash->hash);
-       data.ulen = MAX_FINGERPRINT_LEN;
-       data.data = fingerprint.fp;
-       data.flags = DB_DBT_USERMEM;
-
-       ret = cursor->c_get(cursor,
-               &key,
-               &data,
-               DB_SET);
-
-       if (ret == 0) {
-               fingerprint.length = data.size;
-               count = db4_fetch_key_fp(dbctx, &fingerprint,
-                       publickey, false);
-       }
-
-       cursor->c_close(cursor);
-       cursor = NULL;
-
-       return count;
-}
-
-/**
- *     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 db4_delete_key(struct onak_dbctx *dbctx,
-               struct openpgp_fingerprint *fp,
-               bool intrans)
-{
-       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
-       struct openpgp_publickey *publickey = NULL;
-       DBT key, data;
-       DBC *cursor = NULL;
-       DBC *cursor64 = NULL;
-       uint32_t shortkeyid = 0;
-       uint64_t subkeyid = 0;
-       struct openpgp_fingerprint *subkeyids = NULL;
-       int ret = 0;
-       int i;
-       char **uids = NULL;
-       char *primary = NULL;
-       struct ll *wordlist = NULL;
-       struct ll *curword  = NULL;
-       bool deadlock = false;
-       struct skshash hash;
-       uint64_t keyid;
-
-       if (!intrans) {
-               db4_starttrans(dbctx);
-       }
-
-       if (db4_fetch_key_fp(dbctx, fp, &publickey, true) == 0) {
-               if (!intrans) {
-                       db4_endtrans(dbctx);
-               }
-               return 1;
-       }
-
-       if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
-               return 1;
-       }
-
-       /*
-        * Walk through the uids removing the words from the worddb.
-        */
-       if (publickey != NULL) {
-               uids = keyuids(publickey, &primary);
-       }
-       if (uids != NULL) {
-               for (i = 0; ret == 0 && uids[i] != NULL; i++) {
-                       wordlist = makewordlist(wordlist, uids[i]);
-               }
-
-               privctx->worddb->cursor(privctx->worddb,
-                       privctx->txn,
-                       &cursor,
-                       0);   /* flags */
-
-               for (curword = wordlist; curword != NULL && !deadlock;
-                               curword = curword->next) {
-                       /*
-                        * New style uses the fingerprint as the data
-                        * Old (unsupported) style was the 64 bit keyid
-                        */
-                       memset(&key, 0, sizeof(key));
-                       memset(&data, 0, sizeof(data));
-                       key.data = curword->object;
-                       key.size = strlen(key.data);
-                       data.data = fp->fp;
-                       data.size = fp->length;
-
-                       ret = cursor->c_get(cursor,
-                               &key,
-                               &data,
-                               DB_GET_BOTH);
-
-                       if (ret == 0) {
-                               ret = cursor->c_del(cursor, 0);
-                       }
-
-                       if (ret != 0 && ret != DB_NOTFOUND) {
-                               logthing(LOGTHING_ERROR,
-                                       "Problem deleting word: %s "
-                                       "(0x%016" PRIX64 ")",
-                                       db_strerror(ret),
-                                       keyid);
-                               if (ret == DB_LOCK_DEADLOCK) {
-                                       deadlock = true;
-                               }
-                       }
-               }
-               cursor->c_close(cursor);
-               cursor = NULL;
-
-               /*
-                * Free our UID and word lists.
-                */
-               llfree(wordlist, NULL);
-               for (i = 0; uids[i] != NULL; i++) {
-                       free(uids[i]);
-                       uids[i] = NULL;
-               }
-               free(uids);
-               uids = NULL;
-       }
-
-       if (!deadlock) {
-               privctx->id32db->cursor(privctx->id32db,
-                       privctx->txn,
-                       &cursor,
-                       0);   /* flags */
-               privctx->id64db->cursor(privctx->id64db,
-                       privctx->txn,
-                       &cursor64,
-                       0);   /* flags */
-
-               /* 32 bit short key mapping to fingerprint */
-               shortkeyid = keyid & 0xFFFFFFFF;
-
-               memset(&key, 0, sizeof(key));
-               memset(&data, 0, sizeof(data));
-               key.data = &shortkeyid;
-               key.size = sizeof(shortkeyid);
-               data.data = fp->fp;
-               data.size = fp->length;
-
-               ret = cursor->c_get(cursor,
-                       &key,
-                       &data,
-                       DB_GET_BOTH);
-
-               if (ret == 0) {
-                       ret = cursor->c_del(cursor, 0);
-               }
-
-               if (ret != 0 && ret != DB_NOTFOUND) {
-                       logthing(LOGTHING_ERROR,
-                               "Problem deleting short keyid: %s "
-                               "(0x%016" PRIX64 ")",
-                               db_strerror(ret),
-                               keyid);
-                       if (ret == DB_LOCK_DEADLOCK) {
-                               deadlock = true;
-                       }
-               }
-
-               /* 64 bit key mapping to fingerprint */
-               memset(&key, 0, sizeof(key));
-               memset(&data, 0, sizeof(data));
-               key.data = &keyid;
-               key.size = sizeof(keyid);
-               data.data = fp->fp;
-               data.size = fp->length;
-
-               ret = cursor64->c_get(cursor64,
-                       &key,
-                       &data,
-                       DB_GET_BOTH);
-
-               if (ret == 0) {
-                       ret = cursor64->c_del(cursor64, 0);
-               }
-
-               if (ret != 0 && ret != DB_NOTFOUND) {
-                       logthing(LOGTHING_ERROR,
-                               "Problem deleting keyid: %s "
-                               "(0x%016" PRIX64 ")",
-                               db_strerror(ret),
-                               keyid);
-                       if (ret == DB_LOCK_DEADLOCK) {
-                               deadlock = true;
-                       }
-               }
-
-               subkeyids = keysubkeys(publickey);
-               i = 0;
-               while (subkeyids != NULL && subkeyids[i].length != 0) {
-                       subkeyid = fingerprint2keyid(&subkeyids[i]);
-                       memset(&key, 0, sizeof(key));
-                       key.data = subkeyids[i].fp;
-                       key.size = subkeyids[i].length;
-                       ret = privctx->subkeydb->del(privctx->subkeydb,
-                                       privctx->txn, &key, 0);
-                       if (ret != 0 && ret != DB_NOTFOUND) {
-                               logthing(LOGTHING_ERROR,
-                                       "Problem deleting subkey id: %s "
-                                       "(0x%016" PRIX64 ")",
-                                       db_strerror(ret),
-                                       keyid);
-                               if (ret == DB_LOCK_DEADLOCK) {
-                                       deadlock = true;
-                               }
-                       }
-
-                       shortkeyid = subkeyid & 0xFFFFFFFF;
-
-                       /* Remove 32 bit keyid -> fingerprint mapping */
-                       memset(&key, 0, sizeof(key));
-                       memset(&data, 0, sizeof(data));
-                       key.data = &shortkeyid;
-                       key.size = sizeof(shortkeyid);
-                       data.data = fp->fp;
-                       data.size = fp->length;
-
-                       ret = cursor->c_get(cursor,
-                               &key,
-                               &data,
-                               DB_GET_BOTH);
-
-                       if (ret == 0) {
-                               ret = cursor->c_del(cursor, 0);
-                       }
-
-                       if (ret != 0 && ret != DB_NOTFOUND) {
-                               logthing(LOGTHING_ERROR,
-                                       "Problem deleting short keyid: %s "
-                                       "(0x%016" PRIX64 ")",
-                                       db_strerror(ret),
-                                       keyid);
-                               if (ret == DB_LOCK_DEADLOCK) {
-                                       deadlock = true;
-                               }
-                       }
-
-                       /* Remove 64 bit keyid -> fingerprint mapping */
-                       memset(&key, 0, sizeof(key));
-                       memset(&data, 0, sizeof(data));
-                       key.data = &subkeyid;
-                       key.size = sizeof(subkeyid);
-                       data.data = fp->fp;
-                       data.size = fp->length;
-
-                       ret = cursor64->c_get(cursor64,
-                               &key,
-                               &data,
-                               DB_GET_BOTH);
-
-                       if (ret == 0) {
-                               ret = cursor64->c_del(cursor64, 0);
-                       }
-
-                       if (ret != 0 && ret != DB_NOTFOUND) {
-                               logthing(LOGTHING_ERROR,
-                                       "Problem deleting keyid: %s "
-                                       "(0x%016" PRIX64 ")",
-                                       db_strerror(ret),
-                                       keyid);
-                               if (ret == DB_LOCK_DEADLOCK) {
-                                       deadlock = true;
-                               }
-                       }
-                       i++;
-               }
-               if (subkeyids != NULL) {
-                       free(subkeyids);
-                       subkeyids = NULL;
-               }
-               cursor64->c_close(cursor64);
-               cursor64 = NULL;
-               cursor->c_close(cursor);
-               cursor = NULL;
-       }
-
-       if (!deadlock) {
-               ret = privctx->skshashdb->cursor(privctx->skshashdb,
-                       privctx->txn,
-                       &cursor,
-                       0);   /* flags */
-               if (ret == 0) {
-                       get_skshash(publickey, &hash);
-
-                       /* Remove SKS hash -> fingerprint mapping */
-                       memset(&key, 0, sizeof(key));
-                       memset(&data, 0, sizeof(data));
-                       key.data = hash.hash;
-                       key.size = sizeof(hash.hash);
-                       data.data = fp->fp;
-                       data.size = fp->length;
-
-                       ret = cursor->c_get(cursor,
-                               &key,
-                               &data,
-                               DB_GET_BOTH);
-
-                       if (ret == 0) {
-                               ret = cursor->c_del(cursor, 0);
-                       }
-
-                       if (ret != 0 && ret != DB_NOTFOUND) {
-                               logthing(LOGTHING_ERROR,
-                                       "Problem deleting skshash: %s "
-                                       "(0x%016" PRIX64 ")",
-                                       db_strerror(ret),
-                                       keyid);
-                               if (ret == DB_LOCK_DEADLOCK) {
-                                       deadlock = true;
-                               }
-                       }
-
-                       cursor->c_close(cursor);
-                       cursor = NULL;
-               }
-       }
-       free_publickey(publickey);
-       publickey = NULL;
-
-       if (!deadlock) {
-               key.data = fp->fp;
-               key.size = fp->length;
-
-               keydb_fp(privctx, fp)->del(keydb_fp(privctx, fp),
-                               privctx->txn,
-                               &key,
-                               0); /* flags */
-       }
-
-       if (!intrans) {
-               db4_endtrans(dbctx);
-       }
-
-       return deadlock ? (-1) : (ret == DB_NOTFOUND);
-}
-
-/**
- *     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.
- *
- *     Again we just use the hex representation of the keyid as the filename
- *     to store the key to. We flatten the public key to a list of OpenPGP
- *     packets and then use write_openpgp_stream() to write the stream out to
- *     the file. If update is true then we delete the old key first, otherwise
- *     we trust that it doesn't exist.
- */
-static int db4_store_key(struct onak_dbctx *dbctx,
-               struct openpgp_publickey *publickey, bool intrans,
-               bool update)
-{
-       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
-       struct     openpgp_packet_list *packets = NULL;
-       struct     openpgp_packet_list *list_end = NULL;
-       struct     openpgp_publickey *next = NULL;
-       int        ret = 0;
-       int        i = 0;
-       struct     buffer_ctx storebuf;
-       DBT        key;
-       DBT        data;
-       uint64_t   keyid = 0;
-       uint32_t   shortkeyid = 0;
-       struct openpgp_fingerprint *subkeyids = NULL;
-       char     **uids = NULL;
-       char      *primary = NULL;
-       struct ll *wordlist = NULL;
-       struct ll *curword  = NULL;
-       bool       deadlock = false;
-       struct skshash hash;
-       struct openpgp_fingerprint fingerprint;
-
-       if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
-               logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
-               return 0;
-       }
-
-       if (get_fingerprint(publickey->publickey, &fingerprint) != ONAK_E_OK) {
-               logthing(LOGTHING_ERROR, "Couldn't find fingerprint for key.");
-               return 0;
-       }
-
-       if (!intrans) {
-               db4_starttrans(dbctx);
-       }
-
-       /*
-        * Delete the key if we already have it.
-        *
-        * TODO: Can we optimize this perhaps? Possibly when other data is
-        * involved as well? I suspect this is easiest and doesn't make a lot
-        * of difference though - the largest chunk of data is the keydata and
-        * it definitely needs updated.
-        */
-       if (update) {
-               deadlock = (db4_delete_key(dbctx, &fingerprint, true) == -1);
-       }
-
-       /*
-        * Convert the key to a flat set of binary data.
-        */
-       if (!deadlock) {
-               next = publickey->next;
-               publickey->next = NULL;
-               flatten_publickey(publickey, &packets, &list_end);
-               publickey->next = next;
-
-               storebuf.offset = 0;
-               storebuf.size = 8192;
-               storebuf.buffer = malloc(8192);
-
-               write_openpgp_stream(buffer_putchar, &storebuf, packets);
-
-               /*
-                * Now we have the key data store it in the DB; the fingerprint
-                * is the key.
-                */
-               memset(&key, 0, sizeof(key));
-               memset(&data, 0, sizeof(data));
-               key.data = fingerprint.fp;
-               key.size = fingerprint.length;
-               data.size = storebuf.offset;
-               data.data = storebuf.buffer;
-
-               ret = keydb_fp(privctx, &fingerprint)->put(
-                               keydb_fp(privctx, &fingerprint),
-                               privctx->txn,
-                               &key,
-                               &data,
-                               0); /* flags*/
-               if (ret != 0) {
-                       logthing(LOGTHING_ERROR,
-                                       "Problem storing key: %s",
-                                       db_strerror(ret));
-                       if (ret == DB_LOCK_DEADLOCK) {
-                               deadlock = true;
-                       }
-               }
-
-               free(storebuf.buffer);
-               storebuf.buffer = NULL;
-               storebuf.size = 0;
-               storebuf.offset = 0;
-
-               free_packet_list(packets);
-               packets = NULL;
-       }
-
-       /*
-        * Walk through our uids storing the words into the db with the
-        * fingerprint.
-        */
-       if (!deadlock) {
-               uids = keyuids(publickey, &primary);
-       }
-       if (uids != NULL) {
-               for (i = 0; ret == 0 && uids[i] != NULL; i++) {
-                       wordlist = makewordlist(wordlist, uids[i]);
-               }
-
-               for (curword = wordlist; curword != NULL && !deadlock;
-                               curword = curword->next) {
-                       memset(&key, 0, sizeof(key));
-                       memset(&data, 0, sizeof(data));
-                       key.data = curword->object;
-                       key.size = strlen(key.data);
-                       data.data = fingerprint.fp;
-                       data.size = fingerprint.length;
-
-                       ret = privctx->worddb->put(privctx->worddb,
-                               privctx->txn,
-                               &key,
-                               &data,
-                               0);
-                       if (ret != 0) {
-                               logthing(LOGTHING_ERROR,
-                                       "Problem storing word: %s",
-                                       db_strerror(ret));
-                               if (ret == DB_LOCK_DEADLOCK) {
-                                       deadlock = true;
-                               }
-                       }
-               }
-
-               /*
-                * Free our UID and word lists.
-                */
-               llfree(wordlist, NULL);
-               for (i = 0; uids[i] != NULL; i++) {
-                       free(uids[i]);
-                       uids[i] = NULL;
-               }
-               free(uids);
-               uids = NULL;
-       }
-
-       /*
-        * Write the truncated 32 bit keyid so we can lookup the fingerprint
-        * for queries.
-        */
-       if (!deadlock) {
-               shortkeyid = keyid & 0xFFFFFFFF;
-
-               memset(&key, 0, sizeof(key));
-               memset(&data, 0, sizeof(data));
-               key.data = &shortkeyid;
-               key.size = sizeof(shortkeyid);
-               data.data = fingerprint.fp;
-               data.size = fingerprint.length;
-
-               ret = privctx->id32db->put(privctx->id32db,
-                       privctx->txn,
-                       &key,
-                       &data,
-                       0);
-               if (ret != 0) {
-                       logthing(LOGTHING_ERROR,
-                               "Problem storing short keyid: %s",
-                               db_strerror(ret));
-                       if (ret == DB_LOCK_DEADLOCK) {
-                               deadlock = true;
-                       }
-               }
-       }
-
-       /*
-        * Write the 64 bit keyid so we can lookup the fingerprint for
-        * queries.
-        */
-       if (!deadlock) {
-               memset(&key, 0, sizeof(key));
-               memset(&data, 0, sizeof(data));
-               key.data = &keyid;
-               key.size = sizeof(keyid);
-               data.data = fingerprint.fp;
-               data.size = fingerprint.length;
-
-               ret = privctx->id64db->put(privctx->id64db,
-                       privctx->txn,
-                       &key,
-                       &data,
-                       0);
-               if (ret != 0) {
-                       logthing(LOGTHING_ERROR,
-                               "Problem storing keyid: %s",
-                               db_strerror(ret));
-                       if (ret == DB_LOCK_DEADLOCK) {
-                               deadlock = true;
-                       }
-               }
-       }
-
-       if (!deadlock) {
-               subkeyids = keysubkeys(publickey);
-               i = 0;
-               while (subkeyids != NULL && subkeyids[i].length != 0) {
-                       /* Store the subkey ID -> main key fp mapping */
-                       memset(&key, 0, sizeof(key));
-                       memset(&data, 0, sizeof(data));
-                       key.data = subkeyids[i].fp;
-                       key.size = subkeyids[i].length;
-                       data.data = fingerprint.fp;
-                       data.size = fingerprint.length;
-
-                       ret = privctx->subkeydb->put(privctx->subkeydb,
-                               privctx->txn,
-                               &key,
-                               &data,
-                               0);
-                       if (ret != 0) {
-                               logthing(LOGTHING_ERROR,
-                                       "Problem storing subkey keyid: %s",
-                                       db_strerror(ret));
-                               if (ret == DB_LOCK_DEADLOCK) {
-                                       deadlock = true;
-                               }
-                       }
-
-                       /* Store the 64 bit subkey ID -> main key fp mapping */
-                       memset(&key, 0, sizeof(key));
-                       memset(&data, 0, sizeof(data));
-
-                       keyid = fingerprint2keyid(&subkeyids[i]);
-                       key.data = &keyid;
-                       key.size = sizeof(keyid);
-                       data.data = fingerprint.fp;
-                       data.size = fingerprint.length;
-
-                       ret = privctx->id64db->put(privctx->id64db,
-                               privctx->txn,
-                               &key,
-                               &data,
-                               0);
-                       if (ret != 0) {
-                               logthing(LOGTHING_ERROR,
-                                       "Problem storing keyid: %s",
-                                       db_strerror(ret));
-                               if (ret == DB_LOCK_DEADLOCK) {
-                                       deadlock = true;
-                               }
-                       }
-
-                       /* Store the short subkey ID -> main key fp mapping */
-                       shortkeyid = keyid & 0xFFFFFFFF;
-
-                       memset(&key, 0, sizeof(key));
-                       memset(&data, 0, sizeof(data));
-                       key.data = &shortkeyid;
-                       key.size = sizeof(shortkeyid);
-                       data.data = fingerprint.fp;
-                       data.size = fingerprint.length;
-
-                       ret = privctx->id32db->put(privctx->id32db,
-                               privctx->txn,
-                               &key,
-                               &data,
-                               0);
-                       if (ret != 0) {
-                               logthing(LOGTHING_ERROR,
-                                       "Problem storing short keyid: %s",
-                                       db_strerror(ret));
-                               if (ret == DB_LOCK_DEADLOCK) {
-                                       deadlock = true;
-                               }
-                       }
-                       i++;
-               }
-               if (subkeyids != NULL) {
-                       free(subkeyids);
-                       subkeyids = NULL;
-               }
-       }
-
-       if (!deadlock) {
-               get_skshash(publickey, &hash);
-               memset(&key, 0, sizeof(key));
-               memset(&data, 0, sizeof(data));
-               key.data = hash.hash;
-               key.size = sizeof(hash.hash);
-               data.data = fingerprint.fp;
-               data.size = fingerprint.length;
-
-               ret = privctx->skshashdb->put(privctx->skshashdb,
-                       privctx->txn,
-                       &key,
-                       &data,
-                       0);
-               if (ret != 0) {
-                       logthing(LOGTHING_ERROR,
-                               "Problem storing SKS hash: %s",
-                               db_strerror(ret));
-                       if (ret == DB_LOCK_DEADLOCK) {
-                               deadlock = true;
-                       }
-               }
-       }
-
-       if (!intrans) {
-               db4_endtrans(dbctx);
-       }
-
-       return deadlock ? -1 : 0 ;
-}
-
-/**
- *     iterate_keys - call a function once for each key in the db.
- *     @iterfunc: The function to call.
- *     @ctx: A context pointer
- *
- *     Calls iterfunc once for each key in the database. ctx is passed
- *     unaltered to iterfunc. This function is intended to aid database dumps
- *     and statistic calculations.
- *
- *     Returns the number of keys we iterated over.
- */
-static int db4_iterate_keys(struct onak_dbctx *dbctx,
-               void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
-               void *ctx)
-{
-       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
-       DBT                         dbkey, data;
-       DBC                        *cursor = NULL;
-       int                         ret = 0;
-       int                         i = 0;
-       int                         numkeys = 0;
-       struct buffer_ctx           fetchbuf;
-       struct openpgp_packet_list *packets = NULL;
-       struct openpgp_publickey   *key = NULL;
-
-       for (i = 0; i < privctx->numdbs; i++) {
-               ret = privctx->dbconns[i]->cursor(privctx->dbconns[i],
-                       NULL,
-                       &cursor,
-                       0);   /* flags */
-
-               if (ret != 0) {
-                       continue;
-               }
-
-               memset(&dbkey, 0, sizeof(dbkey));
-               memset(&data, 0, sizeof(data));
-               ret = cursor->c_get(cursor, &dbkey, &data, DB_NEXT);
-               while (ret == 0) {
-                       fetchbuf.buffer = data.data;
-                       fetchbuf.offset = 0;
-                       fetchbuf.size = data.size;
-                       read_openpgp_stream(buffer_fetchchar, &fetchbuf,
-                               &packets, 0);
-                       parse_keys(packets, &key);
-
-                       iterfunc(ctx, key);
-
-                       free_publickey(key);
-                       key = NULL;
-                       free_packet_list(packets);
-                       packets = NULL;
-
-                       memset(&dbkey, 0, sizeof(dbkey));
-                       memset(&data, 0, sizeof(data));
-                       ret = cursor->c_get(cursor, &dbkey, &data,
-                                       DB_NEXT);
-                       numkeys++;
-               }
-               if (ret != DB_NOTFOUND) {
-                       logthing(LOGTHING_ERROR,
-                               "Problem reading key: %s",
-                               db_strerror(ret));
-               }
-
-               cursor->c_close(cursor);
-               cursor = NULL;
-       }
-
-       return numkeys;
-}
-
-/*
- * Include the basic keydb routines.
- */
-#define NEED_GETKEYSIGS 1
-#define NEED_KEYID2UID 1
-#define NEED_UPDATEKEYS 1
-#include "keydb.c"
-
-/**
- *     cleanupdb - De-initialize the key database.
- *
- *     This function should be called upon program exit to allow the DB to
- *     cleanup after itself.
- */
-static void db4_cleanupdb(struct onak_dbctx *dbctx)
-{
-       struct onak_db4_dbctx *privctx = (struct onak_db4_dbctx *) dbctx->priv;
-       int i = 0;
-
-       if (privctx->dbenv != NULL) {
-               privctx->dbenv->txn_checkpoint(privctx->dbenv, 0, 0, 0);
-               if (privctx->subkeydb != NULL) {
-                       privctx->subkeydb->close(privctx->subkeydb, 0);
-                       privctx->subkeydb = NULL;
-               }
-               if (privctx->skshashdb != NULL) {
-                       privctx->skshashdb->close(privctx->skshashdb, 0);
-                       privctx->skshashdb = NULL;
-               }
-               if (privctx->id64db != NULL) {
-                       privctx->id64db->close(privctx->id64db, 0);
-                       privctx->id64db = NULL;
-               }
-               if (privctx->id32db != NULL) {
-                       privctx->id32db->close(privctx->id32db, 0);
-                       privctx->id32db = NULL;
-               }
-               if (privctx->worddb != NULL) {
-                       privctx->worddb->close(privctx->worddb, 0);
-                       privctx->worddb = NULL;
-               }
-               for (i = 0; i < privctx->numdbs; i++) {
-                       if (privctx->dbconns[i] != NULL) {
-                               privctx->dbconns[i]->close(privctx->dbconns[i],
-                                               0);
-                               privctx->dbconns[i] = NULL;
-                       }
-               }
-               free(privctx->dbconns);
-               privctx->dbconns = NULL;
-               privctx->dbenv->close(privctx->dbenv, 0);
-               privctx->dbenv = NULL;
-       }
-
-       free(privctx);
-       dbctx->priv = 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_db4_init(struct onak_db_config *dbcfg, bool readonly)
-{
-       char       buf[1024];
-       FILE      *numdb = NULL;
-       int        ret = 0;
-       int        i = 0;
-       uint32_t   flags = 0;
-       struct stat statbuf;
-       int        maxlocks;
-       struct onak_dbctx *dbctx;
-       struct onak_db4_dbctx *privctx;
-
-       dbctx = malloc(sizeof(*dbctx));
-       if (dbctx == NULL) {
-               return NULL;
-       }
-       dbctx->config = dbcfg;
-       dbctx->priv = privctx = calloc(1, sizeof(*privctx));
-       if (privctx == NULL) {
-               free(dbctx);
-               return NULL;
-       }
-
-       /* Default to 16 key data DBs */
-       privctx->numdbs = 16;
-
-       snprintf(buf, sizeof(buf) - 1, "%s/%s", dbcfg->location,
-                       DB4_UPGRADE_FILE);
-       ret = stat(buf, &statbuf);
-       while ((ret == 0) || (errno != ENOENT)) {
-               if (ret != 0) {
-                       logthing(LOGTHING_CRITICAL, "Couldn't stat upgrade "
-                               "lock file: %s (%d)", strerror(errno), ret);
-                       exit(1);
-               }
-               logthing(LOGTHING_DEBUG, "DB4 upgrade in progress; waiting.");
-               sleep(5);
-               ret = stat(buf, &statbuf);
-       }
-       ret = 0;
-
-       snprintf(buf, sizeof(buf) - 1, "%s/num_keydb", dbcfg->location);
-       numdb = fopen(buf, "r");
-       if (numdb != NULL) {
-               if (fgets(buf, sizeof(buf), numdb) != NULL) {
-                       privctx->numdbs = atoi(buf);
-               }
-               fclose(numdb);
-       } else if (!readonly) {
-               logthing(LOGTHING_ERROR, "Couldn't open num_keydb: %s",
-                               strerror(errno));
-               numdb = fopen(buf, "w");
-               if (numdb != NULL) {
-                       fprintf(numdb, "%d", privctx->numdbs);
-                       fclose(numdb);
-               } else {
-                       logthing(LOGTHING_ERROR,
-                               "Couldn't write num_keydb: %s",
-                               strerror(errno));
-               }
-       }
-
-       privctx->dbconns = calloc(privctx->numdbs, sizeof (DB *));
-       if (privctx->dbconns == NULL) {
-               logthing(LOGTHING_CRITICAL,
-                               "Couldn't allocate memory for dbconns");
-               ret = 1;
-       }
-
-       if (ret == 0) {
-               ret = db_env_create(&privctx->dbenv, 0);
-               if (ret != 0) {
-                       logthing(LOGTHING_CRITICAL,
-                               "db_env_create: %s", db_strerror(ret));
-               }
-       }
-
-       /*
-        * Up the number of locks we're allowed at once. We base this on
-        * the maximum number of keys we're going to return.
-        */
-       if (ret == 0) {
-               maxlocks = config.maxkeys * 16;
-               if (maxlocks < 1000) {
-                       maxlocks = 1000;
-               }
-               privctx->dbenv->set_lk_max_locks(privctx->dbenv, maxlocks);
-               privctx->dbenv->set_lk_max_objects(privctx->dbenv, maxlocks);
-       }
-
-       /*
-        * Enable deadlock detection so that we don't block indefinitely on
-        * anything. What we really want is simple 2 state locks, but I'm not
-        * sure how to make the standard DB functions do that yet.
-        */
-       if (ret == 0) {
-               privctx->dbenv->set_errcall(privctx->dbenv, &db4_errfunc);
-               ret = privctx->dbenv->set_lk_detect(privctx->dbenv, DB_LOCK_DEFAULT);
-               if (ret != 0) {
-                       logthing(LOGTHING_CRITICAL,
-                               "db_env_create: %s", db_strerror(ret));
-               }
-       }
-
-       if (ret == 0) {
-               ret = privctx->dbenv->open(privctx->dbenv, dbcfg->location,
-                               DB_INIT_LOG | DB_INIT_MPOOL | DB_INIT_LOCK |
-                               DB_INIT_TXN |
-                               DB_CREATE,
-                               0);
-#ifdef DB_VERSION_MISMATCH
-               if (ret == DB_VERSION_MISMATCH) {
-                       privctx->dbenv->close(privctx->dbenv, 0);
-                       privctx->dbenv = NULL;
-                       ret = db4_upgradedb(dbctx);
-                       if (ret == 0) {
-                               ret = db_env_create(&privctx->dbenv, 0);
-                       }
-                       if (ret == 0) {
-                               privctx->dbenv->set_errcall(privctx->dbenv,
-                                       &db4_errfunc);
-                               privctx->dbenv->set_lk_detect(privctx->dbenv,
-                                       DB_LOCK_DEFAULT);
-                               ret = privctx->dbenv->open(privctx->dbenv,
-                                       dbcfg->location,
-                                       DB_INIT_LOG | DB_INIT_MPOOL |
-                                       DB_INIT_LOCK | DB_INIT_TXN |
-                                       DB_CREATE | DB_RECOVER,
-                                       0);
-
-                               if (ret == 0) {
-                                       privctx->dbenv->txn_checkpoint(
-                                                       privctx->dbenv,
-                                                       0,
-                                                       0,
-                                                       DB_FORCE);
-                               }
-                       }
-               }
-#endif
-               if (ret != 0) {
-                       logthing(LOGTHING_CRITICAL,
-                                       "Error opening db environment: %s (%s)",
-                                       dbcfg->location,
-                                       db_strerror(ret));
-                       if (privctx->dbenv != NULL) {
-                               privctx->dbenv->close(privctx->dbenv, 0);
-                               privctx->dbenv = NULL;
-                       }
-               }
-       }
-
-       if (ret == 0) {
-               db4_starttrans(dbctx);
-
-               for (i = 0; !ret && i < privctx->numdbs; i++) {
-                       ret = db_create(&privctx->dbconns[i],
-                                       privctx->dbenv, 0);
-                       if (ret != 0) {
-                               logthing(LOGTHING_CRITICAL,
-                                       "db_create: %s", db_strerror(ret));
-                       }
-
-                       if (ret == 0) {
-                               snprintf(buf, 1023, "keydb.%d.db", i);
-                               flags = DB_CREATE;
-                               if (readonly) {
-                                       flags = DB_RDONLY;
-                               }
-                               ret = privctx->dbconns[i]->open(
-                                               privctx->dbconns[i],
-                                               privctx->txn,
-                                               buf,
-                                               "keydb",
-                                               DB_HASH,
-                                               flags,
-                                               0664);
-                               if (ret != 0) {
-                                       logthing(LOGTHING_CRITICAL,
-                                               "Error opening key database:"
-                                               " %s (%s)",
-                                               buf,
-                                               db_strerror(ret));
-                               }
-                       }
-               }
-       }
-
-       if (ret == 0) {
-               ret = db_create(&privctx->worddb, privctx->dbenv, 0);
-               if (ret != 0) {
-                       logthing(LOGTHING_CRITICAL, "db_create: %s",
-                                       db_strerror(ret));
-               }
-       }
-
-       if (ret == 0) {
-               ret = privctx->worddb->set_flags(privctx->worddb, DB_DUP);
-       }
-
-       if (ret == 0) {
-               ret = privctx->worddb->open(privctx->worddb, privctx->txn,
-                               "worddb", "worddb", DB_BTREE,
-                               flags,
-                               0664);
-               if (ret != 0) {
-                       logthing(LOGTHING_CRITICAL,
-                                       "Error opening word database: %s (%s)",
-                                       "worddb",
-                                       db_strerror(ret));
-               }
-       }
-
-       if (ret == 0) {
-               ret = db_create(&privctx->id32db, privctx->dbenv, 0);
-               if (ret != 0) {
-                       logthing(LOGTHING_CRITICAL, "db_create: %s",
-                                       db_strerror(ret));
-               }
-       }
-
-       if (ret == 0) {
-               ret = privctx->id32db->set_flags(privctx->id32db, DB_DUP);
-       }
-
-       if (ret == 0) {
-               ret = privctx->id32db->open(privctx->id32db, privctx->txn,
-                               "id32db", "id32db", DB_HASH,
-                               flags,
-                               0664);
-               if (ret != 0) {
-                       logthing(LOGTHING_CRITICAL,
-                                       "Error opening id32 database: %s (%s)",
-                                       "id32db",
-                                       db_strerror(ret));
-               }
-       }
-
-       if (ret == 0) {
-               ret = db_create(&privctx->id64db, privctx->dbenv, 0);
-               if (ret != 0) {
-                       logthing(LOGTHING_CRITICAL, "db_create: %s",
-                                       db_strerror(ret));
-               }
-       }
-
-       if (ret == 0) {
-               ret = privctx->id64db->set_flags(privctx->id64db, DB_DUP);
-       }
-
-       if (ret == 0) {
-               ret = privctx->id64db->open(privctx->id64db, privctx->txn,
-                               "id64db", "id64db", DB_HASH,
-                               flags,
-                               0664);
-               if (ret != 0) {
-                       logthing(LOGTHING_CRITICAL,
-                                       "Error opening id64 database: %s (%s)",
-                                       "id64db",
-                                       db_strerror(ret));
-               }
-       }
-
-       if (ret == 0) {
-               ret = db_create(&privctx->skshashdb, privctx->dbenv, 0);
-               if (ret != 0) {
-                       logthing(LOGTHING_CRITICAL, "db_create: %s",
-                                       db_strerror(ret));
-               }
-       }
-
-       if (ret == 0) {
-               ret = privctx->skshashdb->open(privctx->skshashdb, privctx->txn,
-                               "skshashdb",
-                               "skshashdb", DB_HASH,
-                               flags,
-                               0664);
-               if (ret != 0) {
-                       logthing(LOGTHING_CRITICAL,
-                               "Error opening skshash database: %s (%s)",
-                               "skshashdb",
-                               db_strerror(ret));
-               }
-       }
-
-       if (ret == 0) {
-               ret = db_create(&privctx->subkeydb, privctx->dbenv, 0);
-               if (ret != 0) {
-                       logthing(LOGTHING_CRITICAL, "db_create: %s",
-                                       db_strerror(ret));
-               }
-       }
-
-       if (ret == 0) {
-               ret = privctx->subkeydb->open(privctx->subkeydb, privctx->txn,
-                               "subkeydb", "subkeydb",
-                               DB_HASH,
-                               flags,
-                               0664);
-               if (ret != 0) {
-                       logthing(LOGTHING_CRITICAL,
-                               "Error opening subkey database: %s (%s)",
-                               "subkeydb",
-                               db_strerror(ret));
-               }
-       }
-
-       if (privctx->txn != NULL) {
-               db4_endtrans(dbctx);
-       }
-
-       if (ret != 0) {
-               db4_cleanupdb(dbctx);
-               logthing(LOGTHING_CRITICAL,
-                               "Error opening database; exiting");
-               exit(EXIT_FAILURE);
-       }
-
-       dbctx->cleanupdb                = db4_cleanupdb;
-       dbctx->starttrans               = db4_starttrans;
-       dbctx->endtrans                 = db4_endtrans;
-       dbctx->fetch_key_id             = db4_fetch_key_id;
-       dbctx->fetch_key_fp             = db4_fetch_key_fp;
-       dbctx->fetch_key_text           = db4_fetch_key_text;
-       dbctx->fetch_key_skshash        = db4_fetch_key_skshash;
-       dbctx->store_key                = db4_store_key;
-       dbctx->update_keys              = generic_update_keys;
-       dbctx->delete_key               = db4_delete_key;
-       dbctx->getkeysigs               = generic_getkeysigs;
-       dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
-       dbctx->keyid2uid                = generic_keyid2uid;
-       dbctx->iterate_keys             = db4_iterate_keys;
-
-       return dbctx;
-}
diff --git a/keydb_dynamic.c b/keydb_dynamic.c
deleted file mode 100644 (file)
index 385c09c..0000000
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * keydb_dynamic.c - backend that can load the other backends
- *
- * Copyright 2005 Brett Parker <iDunno@sommitrealweird.co.uk>
- *
- * 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 <dlfcn.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "decodekey.h"
-#include "hash.h"
-#include "keydb.h"
-#include "keyid.h"
-#include "keystructs.h"
-#include "log.h"
-#include "mem.h"
-#include "merge.h"
-#include "onak-conf.h"
-#include "openpgp.h"
-#include "parsekey.h"
-#include "sendsync.h"
-
-struct onak_dynamic_dbctx {
-       struct onak_dbctx *loadeddbctx;
-       void              *backend_handle;
-};
-
-static bool dynamic_starttrans(struct onak_dbctx *dbctx)
-{
-       struct onak_dynamic_dbctx *privctx =
-                       (struct onak_dynamic_dbctx *) dbctx->priv;
-
-       return privctx->loadeddbctx->starttrans(privctx->loadeddbctx);
-}
-
-static void dynamic_endtrans(struct onak_dbctx *dbctx)
-{
-       struct onak_dynamic_dbctx *privctx =
-                       (struct onak_dynamic_dbctx *) dbctx->priv;
-
-       privctx->loadeddbctx->endtrans(privctx->loadeddbctx);
-}
-
-static int dynamic_fetch_key_id(struct onak_dbctx *dbctx, uint64_t keyid,
-               struct openpgp_publickey **publickey, bool intrans)
-{
-       struct onak_dynamic_dbctx *privctx =
-                       (struct onak_dynamic_dbctx *) dbctx->priv;
-
-       return privctx->loadeddbctx->fetch_key_id(privctx->loadeddbctx, keyid,
-                       publickey, intrans);
-}
-
-static int dynamic_fetch_key_fp(struct onak_dbctx *dbctx,
-               struct openpgp_fingerprint *fingerprint,
-               struct openpgp_publickey **publickey, bool intrans)
-{
-       struct onak_dynamic_dbctx *privctx =
-                       (struct onak_dynamic_dbctx *) dbctx->priv;
-
-       return privctx->loadeddbctx->fetch_key_fp(privctx->loadeddbctx,
-                       fingerprint, publickey, intrans);
-}
-
-static int dynamic_fetch_key_text(struct onak_dbctx *dbctx,
-               const char *search,
-               struct openpgp_publickey **publickey)
-{
-       struct onak_dynamic_dbctx *privctx =
-                       (struct onak_dynamic_dbctx *) dbctx->priv;
-
-       return privctx->loadeddbctx->fetch_key_text(privctx->loadeddbctx,
-                       search, publickey);
-}
-
-static int dynamic_fetch_key_skshash(struct onak_dbctx *dbctx,
-               const struct skshash *hash,
-               struct openpgp_publickey **publickey)
-{
-       struct onak_dynamic_dbctx *privctx =
-                       (struct onak_dynamic_dbctx *) dbctx->priv;
-
-       return privctx->loadeddbctx->fetch_key_skshash(privctx->loadeddbctx,
-                       hash, publickey);
-}
-
-static int dynamic_store_key(struct onak_dbctx *dbctx,
-               struct openpgp_publickey *publickey, bool intrans,
-               bool update)
-{
-       struct onak_dynamic_dbctx *privctx =
-                       (struct onak_dynamic_dbctx *) dbctx->priv;
-
-       return privctx->loadeddbctx->store_key(privctx->loadeddbctx,
-                       publickey, intrans, update);
-}
-
-static int dynamic_delete_key(struct onak_dbctx *dbctx,
-               struct openpgp_fingerprint *fp,
-               bool intrans)
-{
-       struct onak_dynamic_dbctx *privctx =
-                       (struct onak_dynamic_dbctx *) dbctx->priv;
-
-       return privctx->loadeddbctx->delete_key(privctx->loadeddbctx,
-                       fp, intrans);
-}
-
-static int dynamic_update_keys(struct onak_dbctx *dbctx,
-               struct openpgp_publickey **keys,
-               struct keyarray *blacklist,
-               bool updateonly,
-               bool sendsync)
-{
-       struct onak_dynamic_dbctx *privctx =
-                       (struct onak_dynamic_dbctx *) dbctx->priv;
-
-       return privctx->loadeddbctx->update_keys(privctx->loadeddbctx,
-                       keys, blacklist, updateonly, sendsync);
-}
-
-static struct ll *dynamic_getkeysigs(struct onak_dbctx *dbctx,
-               uint64_t keyid, bool *revoked)
-{
-       struct onak_dynamic_dbctx *privctx =
-                       (struct onak_dynamic_dbctx *) dbctx->priv;
-
-       return privctx->loadeddbctx->getkeysigs(privctx->loadeddbctx,
-                       keyid, revoked);
-}
-
-static struct ll *dynamic_cached_getkeysigs(struct onak_dbctx *dbctx,
-               uint64_t keyid)
-{
-       struct onak_dynamic_dbctx *privctx =
-                       (struct onak_dynamic_dbctx *) dbctx->priv;
-
-       return privctx->loadeddbctx->cached_getkeysigs(privctx->loadeddbctx,
-                       keyid);
-}
-
-static char *dynamic_keyid2uid(struct onak_dbctx *dbctx,
-                       uint64_t keyid)
-{
-       struct onak_dynamic_dbctx *privctx =
-                       (struct onak_dynamic_dbctx *) dbctx->priv;
-
-       return privctx->loadeddbctx->keyid2uid(privctx->loadeddbctx,
-                       keyid);
-}
-
-static int dynamic_iterate_keys(struct onak_dbctx *dbctx,
-               void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
-               void *ctx)
-{
-       struct onak_dynamic_dbctx *privctx =
-                       (struct onak_dynamic_dbctx *) dbctx->priv;
-
-       return privctx->loadeddbctx->iterate_keys(privctx->loadeddbctx,
-                       iterfunc, ctx);
-}
-
-static void dynamic_cleanupdb(struct onak_dbctx *dbctx)
-{
-       struct onak_dynamic_dbctx *privctx =
-                       (struct onak_dynamic_dbctx *) dbctx->priv;
-
-       if (privctx->loadeddbctx != NULL) {
-               if (privctx->loadeddbctx->cleanupdb != NULL) {
-                       privctx->loadeddbctx->cleanupdb(privctx->loadeddbctx);
-                       privctx->loadeddbctx = NULL;
-               }
-       }
-
-       if (privctx->backend_handle != NULL) {
-               dlclose(privctx->backend_handle);
-               privctx->backend_handle = NULL;
-       }
-
-       if (dbctx->priv != NULL) {
-               free(dbctx->priv);
-               dbctx->priv = NULL;
-       }
-
-       if (dbctx != NULL) {
-               free(dbctx);
-       }
-}
-
-struct onak_dbctx *keydb_dynamic_init(struct onak_db_config *dbcfg,
-               bool readonly)
-{
-       struct onak_dbctx *dbctx;
-       char *soname;
-       char *initname;
-       struct onak_dbctx *(*backend_init)(struct onak_db_config *, bool);
-       struct onak_dynamic_dbctx *privctx;
-       char *type;
-
-       if (dbcfg == NULL) {
-               logthing(LOGTHING_CRITICAL,
-                       "No backend database configuration supplied.");
-               return NULL;
-       }
-
-       dbctx = malloc(sizeof(struct onak_dbctx));
-
-       if (dbctx == NULL) {
-               return NULL;
-       }
-
-       dbctx->config = dbcfg;
-       dbctx->priv = privctx = malloc(sizeof(struct onak_dynamic_dbctx));
-       if (dbctx->priv == NULL) {
-               free(dbctx);
-               return (NULL);
-       }
-
-       type = dbcfg->type;
-       if (config.use_keyd) {
-               type = "keyd";
-       }
-
-       if (!config.db_backend) {
-               logthing(LOGTHING_CRITICAL, "No database backend defined.");
-               exit(EXIT_FAILURE);
-       }
-
-       if (config.backends_dir == NULL) {
-               soname = malloc(strlen(type)
-                       + strlen("./libkeydb_")
-                       + strlen(".so")
-                       + 1);
-
-               sprintf(soname, "./libkeydb_%s.so", type);
-       } else {
-               soname = malloc(strlen(type)
-                       + strlen("/libkeydb_")
-                       + strlen(".so")
-                       + strlen(config.backends_dir)
-                       + 1);
-
-               sprintf(soname, "%s/libkeydb_%s.so", config.backends_dir,
-                       type);
-       }
-
-       logthing(LOGTHING_INFO, "Loading dynamic backend: %s", soname);
-
-       privctx->backend_handle = dlopen(soname, RTLD_LAZY);
-       if (privctx->backend_handle == NULL) {
-               logthing(LOGTHING_CRITICAL,
-                               "Failed to open handle to library '%s': %s",
-                               soname, dlerror());
-               free(soname);
-               soname = NULL;
-               exit(EXIT_FAILURE);
-       }
-
-       initname = malloc(strlen(config.db_backend)
-                       + strlen("keydb_")
-                       + strlen("_init")
-                       + 1);
-       sprintf(initname, "keydb_%s_init", type);
-
-       *(void **) (&backend_init) = dlsym(privctx->backend_handle, initname);
-       free(initname);
-
-       if (backend_init == NULL) {
-               logthing(LOGTHING_CRITICAL,
-                               "Failed to find dbfuncs structure in library "
-                               "'%s' : %s", soname, dlerror());
-               free(soname);
-               soname = NULL;
-               exit(EXIT_FAILURE);
-       }
-
-       privctx->loadeddbctx = backend_init(dbcfg, readonly);
-
-       if (privctx->loadeddbctx == NULL) {
-               logthing(LOGTHING_CRITICAL,
-                               "Failed to initialise dynamic backend: %s",
-                               soname);
-               free(soname);
-               soname = NULL;
-               exit(EXIT_FAILURE);
-       }
-       free(soname);
-       soname = NULL;
-
-       if (privctx->loadeddbctx != NULL) {
-               dbctx->cleanupdb = dynamic_cleanupdb;
-               dbctx->starttrans = dynamic_starttrans;
-               dbctx->endtrans = dynamic_endtrans;
-               dbctx->fetch_key_id = dynamic_fetch_key_id;
-               dbctx->fetch_key_fp = dynamic_fetch_key_fp;
-               dbctx->fetch_key_text = dynamic_fetch_key_text;
-               dbctx->fetch_key_skshash = dynamic_fetch_key_skshash;
-               dbctx->store_key = dynamic_store_key;
-               dbctx->update_keys = dynamic_update_keys;
-               dbctx->delete_key = dynamic_delete_key;
-               dbctx->getkeysigs = dynamic_getkeysigs;
-               dbctx->cached_getkeysigs = dynamic_cached_getkeysigs;
-               dbctx->keyid2uid = dynamic_keyid2uid;
-               dbctx->iterate_keys = dynamic_iterate_keys;
-       }
-
-       return dbctx;
-}
diff --git a/keydb_file.c b/keydb_file.c
deleted file mode 100644 (file)
index ded6a9e..0000000
+++ /dev/null
@@ -1,300 +0,0 @@
-/*
- * keydb.c - Routines to store and fetch keys.
- *
- * Copyright 2002-2004 Jonathan McDowell <noodles@earth.li>
- *
- * 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 <sys/types.h>
-#include <sys/uio.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "charfuncs.h"
-#include "keydb.h"
-#include "keyid.h"
-#include "keystructs.h"
-#include "log.h"
-#include "mem.h"
-#include "onak.h"
-#include "onak-conf.h"
-#include "parsekey.h"
-
-/**
- *     starttrans - Start a transaction.
- *
- *     This is just a no-op for flat file access.
- */
-static bool file_starttrans(struct onak_dbctx *dbctx)
-{
-       return true;
-}
-
-/**
- *     endtrans - End a transaction.
- *
- *     This is just a no-op for flat file access.
- */
-static void file_endtrans(struct onak_dbctx *dbctx)
-{
-       return;
-}
-
-/**
- *     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.
- *
- *     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 file_fetch_key_id(struct onak_dbctx *dbctx,
-               uint64_t keyid,
-               struct openpgp_publickey **publickey,
-               bool intrans)
-{
-       char *db_dir = (char *) dbctx->priv;
-       struct openpgp_packet_list *packets = NULL;
-       char keyfile[1024];
-       int fd = -1;
-
-       snprintf(keyfile, 1023, "%s/0x%" PRIX64, db_dir,
-                       keyid & 0xFFFFFFFF);
-       fd = open(keyfile, O_RDONLY); // | O_SHLOCK);
-
-       if (fd > -1) {
-               read_openpgp_stream(file_fetchchar, &fd, &packets, 0);
-               parse_keys(packets, publickey);
-               free_packet_list(packets);
-               packets = NULL;
-               close(fd);
-       }
-
-       return (fd > -1);
-}
-
-/**
- *     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.
- *
- *     Again we just use the hex representation of the keyid as the filename
- *     to store the key to. We flatten the public key to a list of OpenPGP
- *     packets and then use write_openpgp_stream() to write the stream out to
- *     the file.
- */
-static int file_store_key(struct onak_dbctx *dbctx,
-               struct openpgp_publickey *publickey, bool intrans,
-               bool update)
-{
-       char *db_dir = (char *) dbctx->priv;
-       struct openpgp_packet_list *packets = NULL;
-       struct openpgp_packet_list *list_end = NULL;
-       struct openpgp_publickey *next = NULL;
-       char keyfile[1024];
-       int fd = -1;
-       uint64_t keyid;
-
-       if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
-               logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
-               return 0;
-       }
-       snprintf(keyfile, 1023, "%s/0x%" PRIX64, db_dir,
-                       keyid & 0xFFFFFFFF);
-       fd = open(keyfile, O_WRONLY | O_CREAT, 0664); // | O_EXLOCK);
-
-       if (fd > -1) {
-               next = publickey -> next;
-               publickey -> next = NULL;
-               flatten_publickey(publickey, &packets, &list_end);
-               publickey -> next = next;
-               
-               write_openpgp_stream(file_putchar, &fd, packets);
-               close(fd);
-               free_packet_list(packets);
-               packets = NULL;
-       }
-
-       return (fd > -1);
-}
-
-/**
- *     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 file_delete_key(struct onak_dbctx *dbctx,
-               struct openpgp_fingerprint *fp, bool intrans)
-{
-       char *db_dir = (char *) dbctx->priv;
-       char keyfile[1024];
-
-       snprintf(keyfile, 1023, "%s/0x%" PRIX64, db_dir,
-                       fingerprint2keyid(fp) & 0xFFFFFFFF);
-
-       return unlink(keyfile);
-}
-
-/**
- *     fetch_key_text - Trys 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 file_fetch_key_text(struct onak_dbctx *dbctx,
-               const char *search,
-               struct openpgp_publickey **publickey)
-{
-       return 0;
-}
-
-/**
- *     iterate_keys - call a function once for each key in the db.
- *     @iterfunc: The function to call.
- *     @ctx: A context pointer
- *
- *     Calls iterfunc once for each key in the database. ctx is passed
- *     unaltered to iterfunc. This function is intended to aid database dumps
- *     and statistic calculations.
- *
- *     Returns the number of keys we iterated over.
- */
-static int file_iterate_keys(struct onak_dbctx *dbctx,
-               void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
-               void *ctx)
-{
-       char *db_dir = (char *) dbctx->priv;
-       int                         numkeys = 0;
-       struct openpgp_packet_list *packets = NULL;
-       struct openpgp_publickey   *key = NULL;
-       DIR                        *dir;
-       char                        keyfile[1024];
-       int                         fd = -1;
-       struct dirent              *curfile = NULL;
-
-       dir = opendir(db_dir);
-
-       if (dir != NULL) {
-               while ((curfile = readdir(dir)) != NULL) {
-                       if (curfile->d_name[0] == '0' &&
-                                       curfile->d_name[1] == 'x') {
-                               snprintf(keyfile, 1023, "%s/%s",
-                                               db_dir,
-                                               curfile->d_name);
-                               fd = open(keyfile, O_RDONLY);
-
-                               if (fd > -1) {
-                                       read_openpgp_stream(file_fetchchar,
-                                                       &fd,
-                                                       &packets,
-                                                       0);
-                                       parse_keys(packets, &key);
-
-                                       iterfunc(ctx, key);
-
-                                       free_publickey(key);
-                                       key = NULL;
-                                       free_packet_list(packets);
-                                       packets = NULL;
-                                       close(fd);
-                               }
-                               numkeys++;
-                       }
-               }
-               
-               closedir(dir);
-               dir = NULL;
-       }
-
-       return numkeys;
-}
-
-/*
- * Include the basic keydb routines.
- */
-#define NEED_KEYID2UID 1
-#define NEED_GETKEYSIGS 1
-#define NEED_UPDATEKEYS 1
-#define NEED_GET_FP 1
-#include "keydb.c"
-
-/**
- *     cleanupdb - De-initialize the key database.
- *
- *     This is just a no-op for flat file access.
- */
-static void file_cleanupdb(struct onak_dbctx *dbctx)
-{
-       if (dbctx->priv != NULL) {
-               free(dbctx->priv);
-               dbctx->priv = NULL;
-       }
-
-       if (dbctx != NULL) {
-               free(dbctx);
-       }
-}
-
-/**
- *     initdb - Initialize the key database.
- *
- *     This is just a no-op for flat file access.
- */
-struct onak_dbctx *keydb_file_init(struct onak_db_config *dbcfg, bool readonly)
-{
-       struct onak_dbctx *dbctx;
-
-       dbctx = malloc(sizeof(struct onak_dbctx));
-       if (dbctx == NULL) {
-               return NULL;
-       }
-
-       dbctx->config = dbcfg;
-       dbctx->priv = strdup(dbcfg->location);
-
-       dbctx->cleanupdb                = file_cleanupdb;
-       dbctx->starttrans               = file_starttrans;
-       dbctx->endtrans                 = file_endtrans;
-       dbctx->fetch_key_id             = file_fetch_key_id;
-       dbctx->fetch_key_fp             = generic_fetch_key_fp;
-       dbctx->fetch_key_text           = file_fetch_key_text;
-       dbctx->store_key                = file_store_key;
-       dbctx->update_keys              = generic_update_keys;
-       dbctx->delete_key               = file_delete_key;
-       dbctx->getkeysigs               = generic_getkeysigs;
-       dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
-       dbctx->keyid2uid                = generic_keyid2uid;
-       dbctx->iterate_keys             = file_iterate_keys;
-
-       return dbctx;
-}
diff --git a/keydb_fs.c b/keydb_fs.c
deleted file mode 100644 (file)
index f8d55dc..0000000
+++ /dev/null
@@ -1,725 +0,0 @@
-/*
- * keydb_fs.c - Routines to store and fetch keys in a filesystem hierarchy.
- *
- * Copyright 2004 Daniel Silverstone <dsilvers@digital-scurf.org>
- *
- * 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 <sys/types.h>
-#include <sys/uio.h>
-#include <sys/stat.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-#include <limits.h>
-#include <dirent.h>
-
-#include "charfuncs.h"
-#include "decodekey.h"
-#include "keydb.h"
-#include "keyid.h"
-#include "keystructs.h"
-#include "ll.h"
-#include "mem.h"
-#include "onak.h"
-#include "onak-conf.h"
-#include "parsekey.h"
-#include "log.h"
-#include "wordlist.h"
-
-/* Hack: We should really dynamically allocate our path buffers */
-#ifndef PATH_MAX
-#define PATH_MAX 1024
-#endif
-
-struct onak_fs_dbctx {
-       int lockfile_fd;
-       bool lockfile_readonly;
-};
-
-/*****************************************************************************/
-
-/* Helper functions */
-
-#define FNV_offset_basis 2166136261ul
-#define FNV_mixing_prime 16777619ul
-
-static uint32_t calchash(uint8_t * ptr)
-{
-       register uint32_t h = FNV_offset_basis;
-       register uint32_t p = FNV_mixing_prime;
-       register uint32_t n = strlen((char *) ptr);
-       register uint8_t *c = ptr;
-       while (n--) {
-               h *= p;
-               h ^= *c++;
-       }
-       return h ? h : 1;       /* prevent a hash of zero happening */
-}
-
-
-static void keypath(char *buffer, size_t length, uint64_t _keyid,
-               char *basepath)
-{
-       uint64_t keyid = _keyid << 32;
-       snprintf(buffer, length, "%s/key/%02X/%02X/%08X/%016" PRIX64,
-                basepath, (uint8_t) ((keyid >> 56) & 0xFF),
-                (uint8_t) ((keyid >> 48) & 0xFF),
-                (uint32_t) (keyid >> 32), _keyid);
-}
-
-static void keydir(char *buffer, size_t length, uint64_t _keyid,
-               char *basepath)
-{
-       uint64_t keyid = _keyid << 32;
-       snprintf(buffer, length, "%s/key/%02X/%02X/%08X", basepath,
-                (uint8_t) ((keyid >> 56) & 0xFF),
-                (uint8_t) ((keyid >> 48) & 0xFF),
-                (uint32_t) (keyid >> 32));
-}
-
-static void prove_path_to(uint64_t keyid, char *what, char *basepath)
-{
-       static char buffer[PATH_MAX];
-       snprintf(buffer, sizeof(buffer), "%s/%s", basepath, what);
-       mkdir(buffer, 0777);
-
-       snprintf(buffer, sizeof(buffer), "%s/%s/%02X", basepath, what,
-                (uint8_t) ((keyid >> 24) & 0xFF));
-       mkdir(buffer, 0777);
-
-       snprintf(buffer, sizeof(buffer), "%s/%s/%02X/%02X", basepath,
-                what,
-                (uint8_t) ((keyid >> 24) & 0xFF),
-                (uint8_t) ((keyid >> 16) & 0xFF));
-       mkdir(buffer, 0777);
-
-       snprintf(buffer, sizeof(buffer), "%s/%s/%02X/%02X/%08X", basepath,
-                what,
-                (uint8_t) ((keyid >> 24) & 0xFF),
-                (uint8_t) ((keyid >> 16) & 0xFF), (uint32_t) (keyid));
-       mkdir(buffer, 0777);
-}
-
-static void wordpath(char *buffer, size_t length, char *word, uint32_t hash,
-               uint64_t keyid, char *basepath)
-{
-       snprintf(buffer, length, "%s/words/%02X/%02X/%08X/%s/%016" PRIX64,
-                basepath, (uint8_t) ((hash >> 24) & 0xFF),
-                (uint8_t) ((hash >> 16) & 0xFF), hash, word, keyid);
-}
-
-static void worddir(char *buffer, size_t length, char *word, uint32_t hash,
-               char *basepath)
-{
-       snprintf(buffer, length, "%s/words/%02X/%02X/%08X/%s", basepath,
-                (uint8_t) ((hash >> 24) & 0xFF),
-                (uint8_t) ((hash >> 16) & 0xFF), hash, word);
-}
-
-static void subkeypath(char *buffer, size_t length, uint64_t subkey,
-               char *basepath)
-{
-       snprintf(buffer, length, "%s/subkeys/%02X/%02X/%08X/%016" PRIX64,
-                basepath,
-                (uint8_t) ((subkey >> 24) & 0xFF),
-                (uint8_t) ((subkey >> 16) & 0xFF),
-                (uint32_t) (subkey & 0xFFFFFFFF),
-                subkey);
-}
-
-static void subkeydir(char *buffer, size_t length, uint64_t subkey,
-               char *basepath)
-{
-       snprintf(buffer, length, "%s/subkeys/%02X/%02X/%08X",
-                basepath,
-                (uint8_t) ((subkey >> 24) & 0xFF),
-                (uint8_t) ((subkey >> 16) & 0xFF),
-                (uint32_t) (subkey & 0xFFFFFFFF));
-}
-
-static void skshashpath(char *buffer, size_t length,
-               const struct skshash *hash, char *basepath)
-{
-       snprintf(buffer, length, "%s/skshash/%02X/%02X/%02X%02X%02X%02X/"
-               "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X",
-                basepath,
-                hash->hash[0], hash->hash[1],
-                hash->hash[0], hash->hash[1], hash->hash[2], hash->hash[3],
-                hash->hash[4], hash->hash[5], hash->hash[6], hash->hash[7],
-                hash->hash[8], hash->hash[9], hash->hash[10], hash->hash[11],
-                hash->hash[12], hash->hash[13], hash->hash[14],
-                hash->hash[15]);
-}
-
-/*****************************************************************************/
-
-/**
- *     starttrans - Start a transaction.
- */
-static bool fs_starttrans(struct onak_dbctx *dbctx)
-{
-       struct onak_fs_dbctx *privctx = (struct onak_fs_dbctx *) dbctx->priv;
-       struct flock lockstruct;
-       int remaining = 20;
-       lockstruct.l_type =
-           F_RDLCK | ((privctx->lockfile_readonly) ? 0 : F_WRLCK);
-       lockstruct.l_whence = SEEK_SET;
-       lockstruct.l_start = 0;
-       lockstruct.l_len = 1;
-
-       while (fcntl(privctx->lockfile_fd, F_SETLK, &lockstruct) == -1) {
-               if (remaining-- == 0)
-                       return false;   /* Hope to hell that noodles DTRT */
-               usleep(100);
-       }
-       return true;
-}
-
-/**
- *     endtrans - End a transaction.
- */
-static void fs_endtrans(struct onak_dbctx *dbctx)
-{
-       struct onak_fs_dbctx *privctx = (struct onak_fs_dbctx *) dbctx->priv;
-       struct flock lockstruct;
-
-       lockstruct.l_type = F_UNLCK;
-       lockstruct.l_whence = SEEK_SET;
-       lockstruct.l_start = 0;
-       lockstruct.l_len = 1;
-       fcntl(privctx->lockfile_fd, F_SETLK, &lockstruct);
-}
-
-static uint64_t fs_getfullkeyid(struct onak_dbctx *dbctx, uint64_t keyid)
-{
-       static char buffer[PATH_MAX];
-       DIR *d = NULL;
-       struct dirent *de = NULL;
-       uint64_t ret = 0;
-
-       keydir(buffer, sizeof(buffer), keyid, dbctx->config->location);
-
-       d = opendir(buffer);
-       if (d) {
-               do {
-                       de = readdir(d);
-                       if (de && de->d_name[0] != '.') {
-                               ret = strtoull(de->d_name, NULL, 16);
-                       }
-               } while (de && de->d_name[0] == '.');
-               closedir(d);    
-       }
-
-       if (ret == 0) {
-               subkeydir(buffer, sizeof(buffer), keyid,
-                       dbctx->config->location);
-
-               d = opendir(buffer);
-               if (d) {
-                       do {
-                               de = readdir(d);
-                               if (de && de->d_name[0] != '.') {
-                                       ret = strtoull(de->d_name, NULL, 16);
-                               }
-                       } while (de && de->d_name[0] == '.');
-                       closedir(d);
-               }
-       }
-
-       return ret;
-}
-
-/**
- *     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.
- */
-static int fs_fetch_key_id(struct onak_dbctx *dbctx,
-             uint64_t keyid,
-             struct openpgp_publickey **publickey,
-             bool intrans)
-{
-       static char buffer[PATH_MAX];
-       int ret = 0, fd;
-       struct openpgp_packet_list *packets = NULL;
-
-       if (!intrans)
-               fs_starttrans(dbctx);
-
-       if ((keyid >> 32) == 0)
-               keyid = fs_getfullkeyid(dbctx, keyid);
-
-       keypath(buffer, sizeof(buffer), keyid, dbctx->config->location);
-       fd = open(buffer, O_RDONLY);
-       if (fd == -1 && errno == ENOENT) {
-               subkeypath(buffer, sizeof(buffer), keyid,
-                       dbctx->config->location);
-               fd = open(buffer, O_RDONLY);
-       }
-
-       if (fd != -1) {
-               /* File is present, load it in... */
-               read_openpgp_stream(file_fetchchar, &fd, &packets, 0);
-               parse_keys(packets, publickey);
-               free_packet_list(packets);
-               packets = NULL;
-               close(fd);
-               ret = 1;
-       }
-
-       if (!intrans)
-               fs_endtrans(dbctx);
-       return ret;
-}
-
-/**
- *     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 fs_store_key(struct onak_dbctx *dbctx,
-             struct openpgp_publickey *publickey, bool intrans,
-             bool update)
-{
-       static char buffer[PATH_MAX];
-       static char wbuffer[PATH_MAX];
-       int ret = 0, fd;
-       struct openpgp_packet_list *packets = NULL;
-       struct openpgp_packet_list *list_end = NULL;
-       struct openpgp_publickey *next = NULL;
-       uint64_t keyid;
-       struct ll *wordlist = NULL, *wl = NULL;
-       struct skshash hash;
-       struct openpgp_fingerprint *subkeyids = NULL;
-       uint32_t hashid;
-       int i = 0;
-
-       if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
-               logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
-               return 0;
-       }
-
-       if (!intrans)
-               fs_starttrans(dbctx);
-
-       prove_path_to(keyid, "key", dbctx->config->location);
-       keypath(buffer, sizeof(buffer), keyid, dbctx->config->location);
-
-       if ((fd =
-            open(buffer, O_WRONLY | (update ? O_TRUNC : O_CREAT),
-                 0644)) != -1) {
-               next = publickey->next;
-               publickey->next = NULL;
-               flatten_publickey(publickey, &packets, &list_end);
-               publickey->next = next;
-
-               write_openpgp_stream(file_putchar, &fd, packets);
-               close(fd);
-               free_packet_list(packets);
-               packets = NULL;
-               ret = 1;
-       }
-
-       if (ret) {
-               wl = wordlist = makewordlistfromkey(wordlist, publickey);
-               while (wl) {
-                       uint32_t hash = calchash((uint8_t *) (wl->object));
-                       prove_path_to(hash, "words", dbctx->config->location);
-
-                       worddir(wbuffer, sizeof(wbuffer), wl->object, hash,
-                               dbctx->config->location);
-                       mkdir(wbuffer, 0777);
-                       wordpath(wbuffer, sizeof(wbuffer), wl->object, hash,
-                               keyid, dbctx->config->location);
-                       link(buffer, wbuffer);
-
-                       wl = wl->next;
-               }
-               llfree(wordlist, free);
-               
-               subkeyids = keysubkeys(publickey);
-               i = 0;
-               while (subkeyids != NULL && subkeyids[i].length != 0) {
-                       keyid = fingerprint2keyid(&subkeyids[i]);
-
-                       prove_path_to(keyid, "subkeys",
-                               dbctx->config->location);
-
-                       subkeydir(wbuffer, sizeof(wbuffer), keyid,
-                               dbctx->config->location);
-                       mkdir(wbuffer, 0777);
-                       subkeypath(wbuffer, sizeof(wbuffer), keyid,
-                               dbctx->config->location);
-                       link(buffer, wbuffer);
-
-                       i++;
-               }
-               if (subkeyids != NULL) {
-                       free(subkeyids);
-                       subkeyids = NULL;
-               }
-
-               get_skshash(publickey, &hash);
-               hashid = hash.hash[0];
-               hashid <<= 8;
-               hashid |= hash.hash[1];
-               hashid <<= 8;
-               hashid |= hash.hash[2];
-               hashid <<= 8;
-               hashid |= hash.hash[3];
-               prove_path_to(hashid, "skshash", dbctx->config->location);
-               skshashpath(wbuffer, sizeof(wbuffer), &hash,
-                       dbctx->config->location);
-               link(buffer, wbuffer);
-       }
-
-       if (!intrans)
-               fs_endtrans(dbctx);
-       return ret;
-}
-
-/**
- *     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.
- */
-static int fs_delete_key(struct onak_dbctx *dbctx,
-               struct openpgp_fingerprint *fp, bool intrans)
-{
-       static char buffer[PATH_MAX];
-       int ret;
-       struct openpgp_publickey *pk = NULL;
-       struct skshash hash;
-       struct ll *wordlist = NULL, *wl = NULL;
-       struct openpgp_fingerprint *subkeyids = NULL;
-       uint64_t subkeyid;
-       int i = 0;
-       uint64_t keyid;
-
-       keyid = fingerprint2keyid(fp);
-       if (keyid == 0)
-               return 1;
-
-       if (!intrans)
-               fs_starttrans(dbctx);
-
-       ret = fs_fetch_key_id(dbctx, keyid, &pk, true);
-
-       if (ret) {
-               logthing(LOGTHING_DEBUG, "Wordlist for key %016" PRIX64,
-                        keyid);
-               wl = wordlist = makewordlistfromkey(wordlist, pk);
-               logthing(LOGTHING_DEBUG,
-                        "Wordlist for key %016" PRIX64 " done", keyid);
-               while (wl) {
-                       uint32_t hash = calchash((uint8_t *) (wl->object));
-                       prove_path_to(hash, "words", dbctx->config->location);
-
-                       wordpath(buffer, sizeof(buffer), wl->object, hash,
-                               keyid, dbctx->config->location);
-                       unlink(buffer);
-
-                       wl = wl->next;
-               }
-               llfree(wordlist, free);
-               wordlist = NULL;
-
-               subkeyids = keysubkeys(pk);
-               i = 0;
-               while (subkeyids != NULL && subkeyids[i].length != 0) {
-                       subkeyid = fingerprint2keyid(&subkeyids[i]);
-                       prove_path_to(subkeyid, "subkeys",
-                               dbctx->config->location);
-
-                       subkeypath(buffer, sizeof(buffer), subkeyid,
-                               dbctx->config->location);
-                       unlink(buffer);
-
-                       i++;
-               }
-               if (subkeyids != NULL) {
-                       free(subkeyids);
-                       subkeyids = NULL;
-               }
-
-               get_skshash(pk, &hash);
-               skshashpath(buffer, sizeof(buffer), &hash,
-                       dbctx->config->location);
-               unlink(buffer);
-       }
-
-       keypath(buffer, sizeof(buffer), keyid, dbctx->config->location);
-       unlink(buffer);
-
-       free_publickey(pk);
-
-       if (!intrans)
-               fs_endtrans(dbctx);
-       return 1;
-}
-
-static struct ll *internal_get_key_by_word(char *word, struct ll *mct,
-               char *basepath)
-{
-       struct ll *keys = NULL;
-       DIR *d = NULL;
-       char buffer[PATH_MAX];
-       uint32_t hash = calchash((uint8_t *) (word));
-       struct dirent *de;
-
-       worddir(buffer, sizeof(buffer), word, hash, basepath);
-       d = opendir(buffer);
-       logthing(LOGTHING_DEBUG, "Scanning for word %s in dir %s", word,
-                buffer);
-       if (d) {
-               do {
-                       de = readdir(d);
-                       if (de && de->d_name[0] != '.') {
-                               if ((!mct)
-                                   || (llfind(mct, de->d_name,
-                                       (int (*)(const void *, const void *))
-                                                   strcmp) !=
-                                       NULL)) {
-                                       logthing(LOGTHING_DEBUG,
-                                                "Found %s // %s", word,
-                                                de->d_name);
-                                       keys =
-                                           lladd(keys,
-                                                 strdup(de->d_name));
-                               }
-                       }
-               } while (de);
-               closedir(d);
-       }
-
-       return keys;
-}
-
-/*
- *     fetch_key_text - Trys 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.
- */
-static int fs_fetch_key_text(struct onak_dbctx *dbctx,
-                  const char *search,
-                  struct openpgp_publickey **publickey)
-{
-       struct ll *wordlist = NULL, *wl = NULL;
-       struct ll *keylist = NULL;
-       char      *searchtext = NULL;
-       int addedkeys = 0;
-
-       logthing(LOGTHING_DEBUG, "Search was '%s'", search);
-
-       searchtext = strdup(search);
-       wl = wordlist = makewordlist(wordlist, searchtext);
-
-       keylist = internal_get_key_by_word(wordlist->object, NULL,
-               dbctx->config->location);
-
-       if (!keylist) {
-               llfree(wordlist, NULL);
-               free(searchtext);
-               searchtext = NULL;
-               return 0;
-       }
-
-       wl = wl->next;
-       while (wl) {
-               struct ll *nkl =
-                   internal_get_key_by_word(wl->object, keylist,
-                       dbctx->config->location);
-               if (!nkl) {
-                       llfree(wordlist, NULL);
-                       llfree(keylist, free);
-                       free(searchtext);
-                       searchtext = NULL;
-                       return 0;
-               }
-               llfree(keylist, free);
-               keylist = nkl;
-               wl = wl->next;
-       }
-
-       llfree(wordlist, NULL);
-
-       /* Now add the keys... */
-       wl = keylist;
-       while (wl) {
-               logthing(LOGTHING_DEBUG, "Adding key: %s", wl->object);
-               addedkeys +=
-                   fs_fetch_key_id(dbctx,
-                             strtoull(wl->object, NULL, 16), publickey,
-                             false);
-               if (addedkeys >= config.maxkeys)
-                       break;
-               wl = wl->next;
-       }
-
-       llfree(keylist, free);
-       free(searchtext);
-       searchtext = NULL;
-
-       return addedkeys;
-}
-
-/**
- *     fetch_key_skshash - Given an SKS hash fetch the key from storage.
- *     @hash: The hash to fetch.
- *     @publickey: A pointer to a structure to return the key in.
- *     @intrans: If we're already in a transaction.
- */
-static int fs_fetch_key_skshash(struct onak_dbctx *dbctx,
-             const struct skshash *hash,
-             struct openpgp_publickey **publickey)
-{
-       static char buffer[PATH_MAX];
-       int ret = 0, fd;
-       struct openpgp_packet_list *packets = NULL;
-
-       skshashpath(buffer, sizeof(buffer), hash, dbctx->config->location);
-       if ((fd = open(buffer, O_RDONLY)) != -1) {
-               read_openpgp_stream(file_fetchchar, &fd, &packets, 0);
-               parse_keys(packets, publickey);
-               free_packet_list(packets);
-               packets = NULL;
-               close(fd);
-               ret = 1;
-       }
-
-       return ret;
-}
-
-/**
- *     iterate_keys - call a function once for each key in the db.
- *     @iterfunc: The function to call.
- *     @ctx: A context pointer
- *
- *     Calls iterfunc once for each key in the database. ctx is passed
- *     unaltered to iterfunc. This function is intended to aid database dumps
- *     and statistic calculations.
- *
- *     Returns the number of keys we iterated over.
- */
-static int fs_iterate_keys(struct onak_dbctx *dbctx,
-               void (*iterfunc)(void *ctx,
-               struct openpgp_publickey *key), void *ctx)
-{
-       return 0;
-}
-
-/*
- * Include the basic keydb routines.
- */
-#define NEED_KEYID2UID 1
-#define NEED_GETKEYSIGS 1
-#define NEED_UPDATEKEYS 1
-#define NEED_GET_FP 1
-#include "keydb.c"
-
-/**
- *     cleanupdb - De-initialize the key database.
- */
-static void fs_cleanupdb(struct onak_dbctx *dbctx)
-{
-       struct onak_fs_dbctx *privctx = (struct onak_fs_dbctx *) dbctx->priv;
-
-       /* Mmmm nothing to do here? */
-       close(privctx->lockfile_fd);
-
-       free(privctx);
-       dbctx->priv = NULL;
-       free(dbctx);
-}
-
-/**
- *     initdb - Initialize the key database.
- */
-struct onak_dbctx *keydb_fs_init(struct onak_db_config *dbcfg, bool readonly)
-{
-       char buffer[PATH_MAX];
-       struct onak_dbctx *dbctx;
-       struct onak_fs_dbctx *privctx;
-
-       dbctx = malloc(sizeof(struct onak_dbctx));
-       if (dbctx == NULL) {
-               return NULL;
-       }
-       dbctx->config = dbcfg;
-       dbctx->priv = privctx = malloc(sizeof(*privctx));
-       if (privctx == NULL) {
-               free(dbctx);
-               return NULL;
-       }
-
-       privctx->lockfile_readonly = readonly;
-
-       snprintf(buffer, sizeof(buffer), "%s/.lock", dbcfg->location);
-
-       if (access(dbcfg->location, R_OK | W_OK | X_OK) == -1) {
-               if (errno != ENOENT) {
-                       logthing(LOGTHING_CRITICAL,
-                                "Unable to access keydb_fs root of '%s'. (%s)",
-                                dbcfg->location, strerror(errno));
-                       exit(1);        /* Lacking rwx on the key dir */
-               }
-               mkdir(dbcfg->location, 0777);
-               privctx->lockfile_fd = open(buffer, O_RDWR | O_CREAT, 0600);
-       }
-       if (chdir(dbcfg->location) == -1) {
-               /* Shouldn't happen after the above */
-               logthing(LOGTHING_CRITICAL,
-                       "Couldn't change to database directory: %s",
-                       strerror(errno));
-               free(dbctx->priv);
-               free(dbctx);
-               return NULL;
-       }
-       privctx->lockfile_fd = open(buffer,
-                                (privctx->lockfile_readonly) ?
-                                O_RDONLY : O_RDWR);
-       if (privctx->lockfile_fd == -1)
-               privctx->lockfile_fd = open(buffer, O_RDWR | O_CREAT, 0600);
-       if (privctx->lockfile_fd == -1) {
-               logthing(LOGTHING_CRITICAL,
-                        "Unable to open lockfile '%s'. (%s)",
-                        buffer, strerror(errno));
-               exit(1);        /* Lacking rwx on the key dir */
-       }
-
-       dbctx->cleanupdb                = fs_cleanupdb;
-       dbctx->starttrans               = fs_starttrans;
-       dbctx->endtrans                 = fs_endtrans;
-       dbctx->fetch_key_id             = fs_fetch_key_id;
-       dbctx->fetch_key_fp             = generic_fetch_key_fp;
-       dbctx->fetch_key_text           = fs_fetch_key_text;
-       dbctx->fetch_key_skshash        = fs_fetch_key_skshash;
-       dbctx->store_key                = fs_store_key;
-       dbctx->update_keys              = generic_update_keys;
-       dbctx->delete_key               = fs_delete_key;
-       dbctx->getkeysigs               = generic_getkeysigs;
-       dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
-       dbctx->keyid2uid                = generic_keyid2uid;
-       dbctx->iterate_keys             = fs_iterate_keys;
-
-       return dbctx;
-}
diff --git a/keydb_hkp.c b/keydb_hkp.c
deleted file mode 100644 (file)
index 79d5c4b..0000000
+++ /dev/null
@@ -1,406 +0,0 @@
-/*
- * keydb_hkp.c - Routines to store and fetch keys from another keyserver.
- *
- * Copyright 2013 Jonathan McDowell <noodles@earth.li>
- *
- * 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 <inttypes.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <curl/curl.h>
-
-#include "build-config.h"
-
-#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"
-
-struct onak_hkp_dbctx {
-       struct onak_db_config *config; /* Our DB config info */
-       CURL *curl;
-       char hkpbase[512];
-};
-
-static int hkp_parse_url(struct onak_hkp_dbctx *privctx, const char *url)
-{
-       char proto[6], host[256];
-       unsigned int port;
-       int matched;
-       int ret = 1;
-
-       proto[0] = host[0] = 0;
-       port = 0;
-
-       matched = sscanf(url, "%5[a-z]://%256[a-zA-Z0-9.-]:%u", proto, host,
-                       &port);
-       if (matched < 2) {
-               proto[0] = 0;
-               sscanf(url, "%256[a-zA-Z0-9.-]:%u", host, &port);
-       }
-
-       if (host[0] == 0) {
-               logthing(LOGTHING_CRITICAL, "Couldn't parse HKP host: %s",
-                       url);
-               ret = 0;
-               goto out;
-       }
-
-       if (proto[0] == 0 || !strcmp(proto, "hkp")) {
-               if (port == 0) {
-                       port = 11371;
-               }
-               snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
-                       "http://%s:%u/pks", host, port);
-       } else if (!strcmp(proto, "hkps")) {
-               if (port == 0) {
-                       port = 11372;
-               }
-               snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
-                       "https://%s:%u/pks", host, port);
-       } else if (strcmp(proto, "http") && strcmp(proto, "https")) {
-               logthing(LOGTHING_CRITICAL, "Unknown HKP protocol: %s",
-                       proto);
-               ret = 0;
-               goto out;
-       } else if (port == 0) {
-               snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
-                       "%s://%s/pks", proto, host);
-       } else {
-               snprintf(privctx->hkpbase, sizeof(privctx->hkpbase),
-                       "%s://%s:%u/pks", proto, host, port);
-       }
-
-out:
-       return ret;
-}
-
-/**
- *     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);
-}
-
-static int hkp_fetch_key_url(struct onak_dbctx *dbctx,
-               char *url,
-               struct openpgp_publickey **publickey,
-               bool intrans)
-{
-       struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
-       struct openpgp_packet_list *packets = NULL;
-       CURLcode res;
-       struct buffer_ctx buf;
-       int count = 0;
-
-       buf.offset = 0;
-       buf.size = 8192;
-       buf.buffer = malloc(8192);
-
-       curl_easy_setopt(privctx->curl, CURLOPT_URL, url);
-       curl_easy_setopt(privctx->curl, CURLOPT_WRITEFUNCTION,
-                       hkp_curl_recv_data);
-       curl_easy_setopt(privctx->curl, CURLOPT_WRITEDATA, &buf);
-       res = curl_easy_perform(privctx->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;
-}
-
-/**
- *     hkp_fetch_key_id - Given a keyid fetch the key from HKP server.
- */
-static int hkp_fetch_key_id(struct onak_dbctx *dbctx,
-               uint64_t keyid,
-               struct openpgp_publickey **publickey,
-               bool intrans)
-{
-       struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
-       char keyurl[1024];
-
-       snprintf(keyurl, sizeof(keyurl),
-                       "%s/lookup?op=get&search=0x%08" PRIX64,
-                       privctx->hkpbase, keyid);
-
-       return (hkp_fetch_key_url(dbctx, keyurl, publickey, intrans));
-}
-
-/**
- *     hkp_fetch_key_fp - Given a fingerprint fetch the key from HKP server.
- */
-static int hkp_fetch_key_fp(struct onak_dbctx *dbctx,
-               struct openpgp_fingerprint *fingerprint,
-               struct openpgp_publickey **publickey,
-               bool intrans)
-{
-       struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
-       char keyurl[1024];
-       int i, ofs;
-
-       if (fingerprint->length > MAX_FINGERPRINT_LEN) {
-               return 0;
-       }
-
-       ofs = snprintf(keyurl, sizeof(keyurl),
-                       "%s/lookup?op=get&search=0x", privctx->hkpbase);
-
-       if ((ofs + fingerprint->length * 2 + 1)> sizeof(keyurl)) {
-               return 0;
-       }
-
-       for (i = 0; i < fingerprint->length; i++) {
-               ofs += sprintf(&keyurl[ofs], "%02X", fingerprint->fp[i]);
-       }
-
-       return (hkp_fetch_key_url(dbctx, keyurl, publickey, intrans));
-}
-
-/**
- *     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(struct onak_dbctx *dbctx,
-               const char *search,
-               struct openpgp_publickey **publickey)
-{
-       struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
-       char keyurl[1024];
-
-       snprintf(keyurl, sizeof(keyurl),
-                       "%s/lookup?op=get&search=%s",
-                       privctx->hkpbase, search);
-
-       return (hkp_fetch_key_url(dbctx, keyurl, publickey, false));
-}
-
-/**
- *     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 onak_dbctx *dbctx,
-               struct openpgp_publickey *publickey, bool intrans,
-               bool update)
-{
-       struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
-       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(privctx->curl, buf.buffer, buf.offset);
-       addform[7] = '=';
-
-       snprintf(keyurl, sizeof(keyurl), "%s/add", privctx->hkpbase);
-
-       curl_easy_setopt(privctx->curl, CURLOPT_URL, keyurl);
-       curl_easy_setopt(privctx->curl, CURLOPT_POSTFIELDS, addform);
-       curl_easy_setopt(privctx->curl, CURLOPT_WRITEFUNCTION,
-                       hkp_curl_recv_data);
-       buf.offset = 0;
-       curl_easy_setopt(privctx->curl, CURLOPT_WRITEDATA, &buf);
-       res = curl_easy_perform(privctx->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.
- *     @fp: The fingerprint of the key to delete.
- *     @intrans: If we're already in a transaction.
- *
- *     No op for HKP.
- */
-static int hkp_delete_key(struct onak_dbctx *dbctx,
-               struct openpgp_fingerprint *fp, 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(struct onak_dbctx *dbctx,
-               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(struct onak_dbctx *dbctx)
-{
-       return true;
-}
-
-/**
- *     endtrans - End a transaction.
- *
- *     This is just a no-op for HKP access.
- */
-static void hkp_endtrans(struct onak_dbctx *dbctx)
-{
-       return;
-}
-
-/*
- * Include the basic keydb routines.
- */
-#define NEED_KEYID2UID 1
-#define NEED_GETKEYSIGS 1
-#define NEED_UPDATEKEYS 1
-#include "keydb.c"
-
-/**
- *     cleanupdb - De-initialize the key database.
- *
- *     We cleanup CURL here.
- */
-static void hkp_cleanupdb(struct onak_dbctx *dbctx)
-{
-       struct onak_hkp_dbctx *privctx = (struct onak_hkp_dbctx *) dbctx->priv;
-
-       if (privctx->curl) {
-               curl_easy_cleanup(privctx->curl);
-               privctx->curl = NULL;
-       }
-       curl_global_cleanup();
-       free(privctx);
-       free(dbctx);
-}
-
-/**
- *     initdb - Initialize the key database.
- *
- *     We initialize CURL here.
- */
-struct onak_dbctx *keydb_hkp_init(struct onak_db_config *dbcfg, bool readonly)
-{
-       struct onak_dbctx *dbctx;
-       struct onak_hkp_dbctx *privctx;
-       curl_version_info_data *curl_info;
-
-       dbctx = malloc(sizeof(struct onak_dbctx));
-       if (dbctx == NULL) {
-               return NULL;
-       }
-
-       dbctx->config = dbcfg;
-       dbctx->priv = privctx = malloc(sizeof(*privctx));
-       dbctx->cleanupdb                = hkp_cleanupdb;
-       dbctx->starttrans               = hkp_starttrans;
-       dbctx->endtrans                 = hkp_endtrans;
-       dbctx->fetch_key_id             = hkp_fetch_key_id;
-       dbctx->fetch_key_fp             = hkp_fetch_key_fp;
-       dbctx->fetch_key_text           = hkp_fetch_key_text;
-       dbctx->store_key                = hkp_store_key;
-       dbctx->update_keys              = generic_update_keys;
-       dbctx->delete_key               = hkp_delete_key;
-       dbctx->getkeysigs               = generic_getkeysigs;
-       dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
-       dbctx->keyid2uid                = generic_keyid2uid;
-       dbctx->iterate_keys             = hkp_iterate_keys;
-
-       if (!hkp_parse_url(privctx, dbcfg->location)) {
-               exit(EXIT_FAILURE);
-       }
-       logthing(LOGTHING_INFO, "Using %s as HKP forwarding URL.",
-               privctx->hkpbase);
-       curl_global_init(CURL_GLOBAL_DEFAULT);
-       privctx->curl = curl_easy_init();
-       if (privctx->curl == NULL) {
-               logthing(LOGTHING_CRITICAL, "Could not initialize CURL.");
-               hkp_cleanupdb(dbctx);
-               dbctx = NULL;
-               exit(EXIT_FAILURE);
-       }
-       curl_easy_setopt(privctx->curl, CURLOPT_USERAGENT,
-               "onak/" ONAK_VERSION);
-
-       if (strncmp(privctx->hkpbase, "https://", 8) == 0) {
-               curl_info = curl_version_info(CURLVERSION_NOW);
-               if (! (curl_info->features & CURL_VERSION_SSL)) {
-                       logthing(LOGTHING_CRITICAL,
-                               "CURL lacks SSL support; cannot use HKP url: %s",
-                               privctx->hkpbase);
-                       hkp_cleanupdb(dbctx);
-                       dbctx = NULL;
-                       exit(EXIT_FAILURE);
-               }
-       }
-
-       return dbctx;
-}
diff --git a/keydb_keyd.c b/keydb_keyd.c
deleted file mode 100644 (file)
index e8f9961..0000000
+++ /dev/null
@@ -1,575 +0,0 @@
-/*
- * keydb_keyd.c - Routines to talk to keyd backend.
- *
- * Copyright 2002-2004,2011 Jonathan McDowell <noodles@earth.li>
- *
- * 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 <errno.h>
-#include <fcntl.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include "charfuncs.h"
-#include "keyd.h"
-#include "keydb.h"
-#include "keyid.h"
-#include "keystructs.h"
-#include "log.h"
-#include "mem.h"
-#include "onak.h"
-#include "onak-conf.h"
-#include "parsekey.h"
-
-/**
- *     starttrans - Start a transaction.
- *
- *     Start a transaction. Intended to be used if we're about to perform many
- *     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 keyd_starttrans(struct onak_dbctx *dbctx)
-{
-       return true;
-}
-
-/**
- *     endtrans - End a transaction.
- *
- *     Ends a transaction.
- */
-static void keyd_endtrans(struct onak_dbctx *dbctx)
-{
-       return;
-}
-
-static bool keyd_send_cmd(int fd, enum keyd_ops _cmd)
-{
-       uint32_t cmd = _cmd;
-       ssize_t bytes;
-
-       bytes = write(fd, &cmd, sizeof(cmd));
-       if (bytes != sizeof(cmd)) {
-               return false;
-       }
-
-       bytes = read(fd, &cmd, sizeof(cmd));
-       if (bytes != sizeof(cmd)) {
-               return false;
-       }
-
-       if (cmd != KEYD_REPLY_OK) {
-               return false;
-       }
-
-       return true;
-}
-
-/**
- *     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.
- *
- *     This function returns a public key from whatever storage mechanism we
- *     are using.
- *
- *      TODO: What about keyid collisions? Should we use fingerprint instead?
- */
-static int keyd_fetch_key_id(struct onak_dbctx *dbctx,
-               uint64_t keyid,
-               struct openpgp_publickey **publickey,
-               bool intrans)
-{
-       int keyd_fd = (intptr_t) dbctx->priv;
-       struct buffer_ctx           keybuf;
-       struct openpgp_packet_list *packets = NULL;
-       ssize_t                     bytes = 0;
-       ssize_t                     count = 0;
-
-       if (keyd_send_cmd(keyd_fd, KEYD_CMD_GET_ID)) {
-               write(keyd_fd, &keyid, sizeof(keyid));
-               keybuf.offset = 0;
-               read(keyd_fd, &keybuf.size, sizeof(keybuf.size));
-               if (keybuf.size > 0) {
-                       keybuf.buffer = malloc(keybuf.size);
-                       bytes = count = 0;
-                       logthing(LOGTHING_TRACE,
-                                       "Getting %d bytes of key data.",
-                                       keybuf.size);
-                       while (bytes >= 0 && count < keybuf.size) {
-                               bytes = read(keyd_fd, &keybuf.buffer[count],
-                                               keybuf.size - count);
-                               logthing(LOGTHING_TRACE,
-                                               "Read %d bytes.", bytes);
-                               count += bytes;
-                       }
-                       read_openpgp_stream(buffer_fetchchar, &keybuf,
-                                       &packets, 0);
-                       parse_keys(packets, publickey);
-                       free_packet_list(packets);
-                       packets = NULL;
-                       free(keybuf.buffer);
-                       keybuf.buffer = NULL;
-                       keybuf.size = 0;
-               }
-       }
-
-       return (count > 0) ? 1 : 0;
-}
-
-static int keyd_fetch_key_fp(struct onak_dbctx *dbctx,
-               struct openpgp_fingerprint *fingerprint,
-               struct openpgp_publickey **publickey,
-               bool intrans)
-{
-       int keyd_fd = (intptr_t) dbctx->priv;
-       struct buffer_ctx           keybuf;
-       struct openpgp_packet_list *packets = NULL;
-       ssize_t                     bytes = 0;
-       ssize_t                     count = 0;
-       uint8_t                     size;
-
-       if (fingerprint->length > MAX_FINGERPRINT_LEN) {
-               return 0;
-       }
-
-       if (keyd_send_cmd(keyd_fd, KEYD_CMD_GET_FP)) {
-               size = fingerprint->length;
-               write(keyd_fd, &size, sizeof(size));
-               write(keyd_fd, fingerprint->fp, size);
-               keybuf.offset = 0;
-               read(keyd_fd, &keybuf.size, sizeof(keybuf.size));
-               if (keybuf.size > 0) {
-                       keybuf.buffer = malloc(keybuf.size);
-                       bytes = count = 0;
-                       logthing(LOGTHING_TRACE,
-                                       "Getting %d bytes of key data.",
-                                       keybuf.size);
-                       while (bytes >= 0 && count < keybuf.size) {
-                               bytes = read(keyd_fd, &keybuf.buffer[count],
-                                               keybuf.size - count);
-                               logthing(LOGTHING_TRACE,
-                                               "Read %d bytes.", bytes);
-                               count += bytes;
-                       }
-                       read_openpgp_stream(buffer_fetchchar, &keybuf,
-                                       &packets, 0);
-                       parse_keys(packets, publickey);
-                       free_packet_list(packets);
-                       packets = NULL;
-                       free(keybuf.buffer);
-                       keybuf.buffer = NULL;
-                       keybuf.size = 0;
-               }
-       }
-
-       return (count > 0) ? 1 : 0;
-}
-
-/**
-*      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 keyd_delete_key(struct onak_dbctx *dbctx,
-               struct openpgp_fingerprint *fp, bool intrans)
-{
-       int keyd_fd = (intptr_t) dbctx->priv;
-
-       if (keyd_send_cmd(keyd_fd, KEYD_CMD_DELETE)) {
-               write(keyd_fd, fp, sizeof(*fp));
-       }
-
-       return 0;
-}
-
-/**
- *     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.
- *
- *     This function stores a public key in whatever storage mechanism we are
- *     using. intrans indicates if we're already in a transaction so don't
- *     need to start one. update indicates if the key already exists and is
- *     just being updated.
- *
- *     TODO: Do we store multiple keys of the same id? Or only one and replace
- *     it?
- */
-static int keyd_store_key(struct onak_dbctx *dbctx,
-               struct openpgp_publickey *publickey, bool intrans,
-               bool update)
-{
-       int keyd_fd = (intptr_t) dbctx->priv;
-       struct buffer_ctx           keybuf;
-       struct openpgp_packet_list *packets = NULL;
-       struct openpgp_packet_list *list_end = NULL;
-       struct openpgp_publickey   *next = NULL;
-       uint64_t                    keyid;
-       enum keyd_ops               cmd = KEYD_CMD_STORE;
-
-       if (get_keyid(publickey, &keyid) != ONAK_E_OK) {
-               logthing(LOGTHING_ERROR, "Couldn't find key ID for key.");
-               return 0;
-       }
-
-       if (update) {
-               cmd = KEYD_CMD_UPDATE;
-       }
-
-       if (keyd_send_cmd(keyd_fd, cmd)) {
-               keybuf.offset = 0;
-               keybuf.size = 8192;
-               keybuf.buffer = malloc(keybuf.size);
-
-               next = publickey->next;
-               publickey->next = NULL;
-               flatten_publickey(publickey,
-                               &packets,
-                               &list_end);
-               publickey->next = next;
-
-               write_openpgp_stream(buffer_putchar, &keybuf, packets);
-               logthing(LOGTHING_TRACE, "Sending %d bytes.", keybuf.offset);
-               write(keyd_fd, &keybuf.offset, sizeof(keybuf.offset));
-               write(keyd_fd, keybuf.buffer, keybuf.offset);
-
-               free_packet_list(packets);
-               packets = list_end = NULL;
-               free(keybuf.buffer);
-               keybuf.buffer = NULL;
-               keybuf.size = keybuf.offset = 0;
-       }
-
-       return 0;
-}
-
-/**
- *     fetch_key_text - Trys 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.
- */
-static int keyd_fetch_key_text(struct onak_dbctx *dbctx,
-               const char *search,
-               struct openpgp_publickey **publickey)
-{
-       int keyd_fd = (intptr_t) dbctx->priv;
-       struct buffer_ctx           keybuf;
-       struct openpgp_packet_list *packets = NULL;
-       ssize_t                     bytes = 0;
-       ssize_t                     count = 0;
-
-       if (keyd_send_cmd(keyd_fd, KEYD_CMD_GET_TEXT)) {
-               bytes = strlen(search);
-               write(keyd_fd, &bytes, sizeof(bytes));
-               write(keyd_fd, search, bytes);
-               keybuf.offset = 0;
-               read(keyd_fd, &keybuf.size, sizeof(keybuf.size));
-               if (keybuf.size > 0) {
-                       keybuf.buffer = malloc(keybuf.size);
-                       bytes = count = 0;
-                       logthing(LOGTHING_TRACE,
-                                       "Getting %d bytes of key data.",
-                                       keybuf.size);
-                       while (bytes >= 0 && count < keybuf.size) {
-                               bytes = read(keyd_fd, &keybuf.buffer[count],
-                                               keybuf.size - count);
-                               logthing(LOGTHING_TRACE,
-                                               "Read %d bytes.", bytes);
-                               count += bytes;
-                       }
-                       read_openpgp_stream(buffer_fetchchar, &keybuf,
-                                       &packets, 0);
-                       parse_keys(packets, publickey);
-                       free_packet_list(packets);
-                       packets = NULL;
-                       free(keybuf.buffer);
-                       keybuf.buffer = NULL;
-                       keybuf.size = 0;
-               }
-       }
-
-       return (count > 0) ? 1 : 0;
-
-       return 0;
-}
-
-static int keyd_fetch_key_skshash(struct onak_dbctx *dbctx,
-               const struct skshash *hash,
-               struct openpgp_publickey **publickey)
-{
-       int keyd_fd = (intptr_t) dbctx->priv;
-       struct buffer_ctx           keybuf;
-       struct openpgp_packet_list *packets = NULL;
-       ssize_t                     bytes = 0;
-       ssize_t                     count = 0;
-
-       if (keyd_send_cmd(keyd_fd, KEYD_CMD_GET_SKSHASH)) {
-               write(keyd_fd, hash->hash, sizeof(hash->hash));
-               keybuf.offset = 0;
-               read(keyd_fd, &keybuf.size, sizeof(keybuf.size));
-               if (keybuf.size > 0) {
-                       keybuf.buffer = malloc(keybuf.size);
-                       bytes = count = 0;
-                       logthing(LOGTHING_TRACE,
-                                       "Getting %d bytes of key data.",
-                                       keybuf.size);
-                       while (bytes >= 0 && count < keybuf.size) {
-                               bytes = read(keyd_fd, &keybuf.buffer[count],
-                                               keybuf.size - count);
-                               logthing(LOGTHING_TRACE,
-                                               "Read %d bytes.", bytes);
-                               count += bytes;
-                       }
-                       read_openpgp_stream(buffer_fetchchar, &keybuf,
-                                       &packets, 0);
-                       parse_keys(packets, publickey);
-                       free_packet_list(packets);
-                       packets = NULL;
-                       free(keybuf.buffer);
-                       keybuf.buffer = NULL;
-                       keybuf.size = 0;
-               }
-       }
-
-       return (count > 0) ? 1 : 0;
-}
-
-/**
- *     iterate_keys - call a function once for each key in the db.
- *     @iterfunc: The function to call.
- *     @ctx: A context pointer
- *
- *     Calls iterfunc once for each key in the database. ctx is passed
- *     unaltered to iterfunc. This function is intended to aid database dumps
- *     and statistic calculations.
- *
- *     Returns the number of keys we iterated over.
- */
-static int keyd_iterate_keys(struct onak_dbctx *dbctx,
-               void (*iterfunc)(void *ctx,
-               struct openpgp_publickey *key), void *ctx)
-{
-       int keyd_fd = (intptr_t) dbctx->priv;
-       struct buffer_ctx           keybuf;
-       struct openpgp_packet_list *packets = NULL;
-       struct openpgp_publickey   *key = NULL;
-       ssize_t                     bytes = 0;
-       ssize_t                     count = 0;
-       int                         numkeys = 0;
-
-       if (keyd_send_cmd(keyd_fd, KEYD_CMD_KEYITER)) {
-               keybuf.offset = 0;
-               read(keyd_fd, &keybuf.size, sizeof(keybuf.size));
-               while (keybuf.size > 0) {
-                       keybuf.buffer = malloc(keybuf.size);
-                       bytes = count = 0;
-                       logthing(LOGTHING_TRACE,
-                                       "Getting %d bytes of key data.",
-                                       keybuf.size);
-                       while (bytes >= 0 && count < keybuf.size) {
-                               bytes = read(keyd_fd, &keybuf.buffer[count],
-                                               keybuf.size - count);
-                               logthing(LOGTHING_TRACE,
-                                               "Read %d bytes.", bytes);
-                               count += bytes;
-                       }
-                       read_openpgp_stream(buffer_fetchchar, &keybuf,
-                                       &packets, 0);
-                       parse_keys(packets, &key);
-
-                       if (iterfunc != NULL && key != NULL) {
-                               iterfunc(ctx, key);
-                       }
-
-                       free_publickey(key);
-                       key = NULL;
-                       free_packet_list(packets);
-                       packets = NULL;
-                       free(keybuf.buffer);
-                       keybuf.buffer = NULL;
-                       keybuf.size = keybuf.offset = 0;
-
-                       numkeys++;
-
-                       read(keyd_fd, &keybuf.size, sizeof(keybuf.size));
-               }
-       }
-
-       return numkeys;
-}
-
-#define NEED_KEYID2UID 1
-#define NEED_GETKEYSIGS 1
-#define NEED_UPDATEKEYS 1
-#include "keydb.c"
-
-/**
- *     cleanupdb - De-initialize the key database.
- *
- *     This function should be called upon program exit to allow the DB to
- *     cleanup after itself.
- */
-static void keyd_cleanupdb(struct onak_dbctx *dbctx)
-{
-       int keyd_fd = (intptr_t) dbctx->priv;
-       uint32_t cmd = KEYD_CMD_CLOSE;
-
-       if (write(keyd_fd, &cmd, sizeof(cmd)) != sizeof(cmd)) {
-               logthing(LOGTHING_CRITICAL,
-                               "Couldn't send close cmd: %s (%d)",
-                               strerror(errno),
-                               errno);
-       }
-
-       if (read(keyd_fd, &cmd, sizeof(cmd)) != sizeof(cmd)) {
-               logthing(LOGTHING_CRITICAL,
-                       "Couldn't read close cmd reply: %s (%d)",
-                       strerror(errno),
-                       errno);
-       } else if (cmd != KEYD_REPLY_OK) {
-               logthing(LOGTHING_CRITICAL,
-                       "Got bad reply to KEYD_CMD_CLOSE: %d", cmd);
-       }
-
-       if (shutdown(keyd_fd, SHUT_RDWR) < 0) {
-               logthing(LOGTHING_NOTICE, "Error shutting down socket: %d",
-                               errno);
-       }
-       if (close(keyd_fd) < 0) {
-               logthing(LOGTHING_NOTICE, "Error closing down socket: %d",
-                               errno);
-       }
-
-       free(dbctx);
-
-       return;
-}
-
-/**
- *     initdb - Initialize the key database.
- *     @readonly: If we'll only be reading the DB, not writing to it.
- *
- *     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_keyd_init(struct onak_db_config *dbcfg, bool readonly)
-{
-       struct sockaddr_un sock;
-       uint32_t           cmd = KEYD_CMD_UNKNOWN;
-       uint32_t           reply = KEYD_REPLY_UNKNOWN_CMD;
-       ssize_t            count;
-       int keyd_fd;
-       struct onak_dbctx *dbctx;
-
-       dbctx = malloc(sizeof(*dbctx));
-       if (dbctx == NULL) {
-               return NULL;
-       }
-       dbctx->config = dbcfg;
-
-       keyd_fd = socket(PF_UNIX, SOCK_STREAM, 0);
-       if (keyd_fd < 0) {
-               logthing(LOGTHING_CRITICAL,
-                               "Couldn't open socket: %s (%d)",
-                               strerror(errno),
-                               errno);
-               exit(EXIT_FAILURE);
-       }
-
-       sock.sun_family = AF_UNIX;
-       snprintf(sock.sun_path, sizeof(sock.sun_path) - 1, "%s/%s",
-                       config.sock_dir,
-                       KEYD_SOCKET);
-       if (connect(keyd_fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) {
-               logthing(LOGTHING_CRITICAL,
-                               "Couldn't connect to socket %s: %s (%d)",
-                               sock.sun_path,
-                               strerror(errno),
-                               errno);
-               exit(EXIT_FAILURE);
-       }
-
-       cmd = KEYD_CMD_VERSION;
-       if (write(keyd_fd, &cmd, sizeof(cmd)) != sizeof(cmd)) {
-               logthing(LOGTHING_CRITICAL,
-                               "Couldn't write version cmd: %s (%d)",
-                               strerror(errno),
-                               errno);
-       } else {
-               count = read(keyd_fd, &reply, sizeof(reply));
-               if (count == sizeof(reply) && reply == KEYD_REPLY_OK) {
-                       count = read(keyd_fd, &reply, sizeof(reply));
-                       if (count != sizeof(reply) || reply != sizeof(reply)) {
-                               logthing(LOGTHING_CRITICAL,
-                                       "Error! Unexpected keyd version "
-                                       "length: %d != %d",
-                                       reply, sizeof(reply));
-                               exit(EXIT_FAILURE);
-                       }
-
-                       count = read(keyd_fd, &reply, sizeof(reply));
-                       if (count != sizeof(reply)) {
-                               logthing(LOGTHING_CRITICAL,
-                                       "Error! Unexpected keyd version "
-                                       "length: %d != %d",
-                                       count, sizeof(reply));
-                               exit(EXIT_FAILURE);
-                       }
-                       logthing(LOGTHING_DEBUG,
-                                       "keyd protocol version %d",
-                                       reply);
-                       if (reply != keyd_version) {
-                               logthing(LOGTHING_CRITICAL,
-                                       "Error! keyd protocol version "
-                                       "mismatch. (us = %d, it = %d)",
-                                               keyd_version, reply);
-                       }
-               }
-       }
-
-       dbctx->priv                     = (void *) (intptr_t) keyd_fd;
-       dbctx->cleanupdb                = keyd_cleanupdb;
-       dbctx->starttrans               = keyd_starttrans;
-       dbctx->endtrans                 = keyd_endtrans;
-       dbctx->fetch_key_id             = keyd_fetch_key_id;
-       dbctx->fetch_key_fp             = keyd_fetch_key_fp;
-       dbctx->fetch_key_text           = keyd_fetch_key_text;
-       dbctx->fetch_key_skshash        = keyd_fetch_key_skshash;
-       dbctx->store_key                = keyd_store_key;
-       dbctx->update_keys              = generic_update_keys;
-       dbctx->delete_key               = keyd_delete_key;
-       dbctx->getkeysigs               = generic_getkeysigs;
-       dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
-       dbctx->keyid2uid                = generic_keyid2uid;
-       dbctx->iterate_keys             = keyd_iterate_keys;
-
-       return dbctx;
-}
diff --git a/keydb_keyring.c b/keydb_keyring.c
deleted file mode 100644 (file)
index 9550434..0000000
+++ /dev/null
@@ -1,455 +0,0 @@
-/*
- * keydb_keyring.c - Routines to fetch keys from a PGP keyring file.
- *
- * Copyright 2019 Jonathan McDowell <noodles@earth.li>
- *
- * 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 <sys/mman.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <inttypes.h>
-#include <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "charfuncs.h"
-#include "keyarray.h"
-#include "keydb.h"
-#include "keyid.h"
-#include "keystructs.h"
-#include "log.h"
-#include "mem.h"
-#include "onak.h"
-#include "onak-conf.h"
-#include "parsekey.h"
-
-struct onak_keyring_dbctx {
-       uint8_t *file;
-       size_t   length;
-       unsigned int space;
-       unsigned int count;
-       struct {
-               struct openpgp_fingerprint fp;
-               uint8_t *start;
-               size_t len;
-       } *keys;
-};
-
-/**
- *     starttrans - Start a transaction.
- *
- *     This is just a no-op for keyring file access.
- */
-static bool keyring_starttrans(struct onak_dbctx *dbctx)
-{
-       return true;
-}
-
-/**
- *     endtrans - End a transaction.
- *
- *     This is just a no-op for keyring file access.
- */
-static void keyring_endtrans(struct onak_dbctx *dbctx)
-{
-       return;
-}
-
-/**
- * keyring_fetch_key - fetch a key given its index
- */
-static int keyring_fetch_key(struct onak_keyring_dbctx *privctx,
-               unsigned int index,
-               struct openpgp_publickey **publickey)
-{
-       struct openpgp_packet_list *packets = NULL;
-       struct buffer_ctx buf;
-
-       if (index > privctx->count)
-               return 0;
-
-       buf.buffer = (char *) privctx->keys[index].start;
-       buf.size = privctx->keys[index].len;
-       buf.offset = 0;
-
-       read_openpgp_stream(buffer_fetchchar, &buf, &packets, 0);
-       parse_keys(packets, publickey);
-       free_packet_list(packets);
-       packets = NULL;
-
-       return 1;
-}
-
-static int keyring_fetch_key_fp(struct onak_dbctx *dbctx,
-                       struct openpgp_fingerprint *fingerprint,
-                       struct openpgp_publickey **publickey,
-                       bool intrans)
-{
-       struct onak_keyring_dbctx *privctx =
-               (struct onak_keyring_dbctx *) dbctx->priv;
-       int i;
-
-       for (i = 0; i < privctx->count; i++) {
-               if (fingerprint_cmp(fingerprint, &privctx->keys[i].fp) == 0)
-                       break;
-       }
-
-       if (i < privctx->count) {
-               return keyring_fetch_key(privctx, i, publickey);
-       }
-
-       return 0;
-}
-
-/**
- *     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.
- */
-static int keyring_fetch_key_id(struct onak_dbctx *dbctx,
-               uint64_t keyid,
-               struct openpgp_publickey **publickey,
-               bool intrans)
-{
-       struct onak_keyring_dbctx *privctx =
-               (struct onak_keyring_dbctx *) dbctx->priv;
-       int count, i;
-
-       count = 0;
-       for (i = 0; i < privctx->count; i++) {
-               if (fingerprint2keyid(&privctx->keys[i].fp) == keyid) {
-                       if (keyring_fetch_key(privctx, i, publickey))
-                               count++;
-               }
-       }
-
-       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.
- *
- *     We don't support storing keys into a keyring file.
- */
-static int keyring_store_key(struct onak_dbctx *dbctx,
-               struct openpgp_publickey *publickey, bool intrans,
-               bool update)
-{
-       return 0;
-}
-
-/**
- *     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.
- *
- *     We don't support removing keys from a keyring file.
- */
-static int keyring_delete_key(struct onak_dbctx *dbctx,
-               struct openpgp_fingerprint *fp, bool intrans)
-{
-       return 1;
-}
-
-/**
- *     fetch_key_text - Trys 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 keyring_fetch_key_text(struct onak_dbctx *dbctx,
-               const char *search,
-               struct openpgp_publickey **publickey)
-{
-       return 0;
-}
-
-/**
- *     iterate_keys - call a function once for each key in the db.
- *     @iterfunc: The function to call.
- *     @ctx: A context pointer
- *
- *     Calls iterfunc once for each key in the database. ctx is passed
- *     unaltered to iterfunc. This function is intended to aid database dumps
- *     and statistic calculations.
- *
- *     Returns the number of keys we iterated over.
- */
-static int keyring_iterate_keys(struct onak_dbctx *dbctx,
-               void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
-               void *ctx)
-{
-       struct onak_keyring_dbctx *privctx =
-               (struct onak_keyring_dbctx *) dbctx->priv;
-       struct openpgp_publickey  *key = NULL;
-       int count, i;
-
-       count = 0;
-       for (i = 0; i < privctx->count; i++) {
-               if (keyring_fetch_key(privctx, i, &key)) {
-                       iterfunc(ctx, key);
-                       free_publickey(key);
-                       key = NULL;
-               }
-       }
-
-       return count;
-}
-
-static int keyring_update_keys(struct onak_dbctx *dbctx,
-               struct openpgp_publickey **keys,
-               struct keyarray *blacklist,
-               bool updateonly,
-               bool sendsync)
-{
-       return 0;
-}
-
-/*
- * Include the basic keydb routines.
- */
-#define NEED_KEYID2UID 1
-#define NEED_GETKEYSIGS 1
-#include "keydb.c"
-
-static int keyring_parse_keys(struct onak_keyring_dbctx *privctx)
-{
-       size_t len, pos, start, totlen;
-       struct openpgp_publickey *key;
-       uint8_t tag;
-
-       if (privctx == NULL) {
-               return 0;
-       }
-
-       if (privctx->file == NULL) {
-               return 0;
-       }
-
-       /*
-        * Walk the keyring file, noting the start of each public key and the
-        * total length of packets associated with it.
-        */
-       len = pos = start = totlen = 0;
-       while (((privctx->length - pos) > 5) && (privctx->file[pos] & 0x80)) {
-               if (privctx->file[pos] & 0x40) {
-                       tag = privctx->file[pos] & 0x3F;
-                       len = privctx->file[pos + 1];
-                       if (len > 191 && len < 224) {
-                               len -= 192;
-                               len <<= 8;
-                               len += privctx->file[pos + 2];
-                               len += 192;
-                               len += 1; /* Header */
-                       } else if (len > 223 && len < 255) {
-                               // Unsupported
-                       } else if (len == 255) {
-                               len = privctx->file[pos + 2];
-                               len <<= 8;
-                               len += privctx->file[pos + 3];
-                               len <<= 8;
-                               len += privctx->file[pos + 4];
-                               len <<= 8;
-                               len += privctx->file[pos + 5];
-                               len += 4; /* Header */
-                       }
-                       len += 2; /* Header */
-               } else {
-                       tag = (privctx->file[pos] & 0x3C) >> 2;
-                       switch (privctx->file[pos] & 3) {
-                       case 0:
-                               len = privctx->file[pos + 1];
-                               len += 2; /* Header */
-                               break;
-                       case 1:
-                               len = privctx->file[pos + 1];
-                               len <<= 8;
-                               len += privctx->file[pos + 2];
-                               len += 3; /* Header */
-                               break;
-                       case 2:
-                               len = privctx->file[pos + 1];
-                               len <<= 8;
-                               len += privctx->file[pos + 2];
-                               len <<= 8;
-                               len += privctx->file[pos + 3];
-                               len <<= 8;
-                               len += privctx->file[pos + 4];
-                               len += 5; /* Header */
-                               break;
-                       case 3:
-                               // Unsupported
-                               break;
-                       }
-               }
-               if (tag == OPENPGP_PACKET_PUBLICKEY) {
-                       if (totlen > 0) {
-                               /* Expand the array of keys if necessary */
-                               if (privctx->count == privctx->space) {
-                                       privctx->space *= 2;
-                                       privctx->keys = realloc(privctx->keys,
-                                               privctx->space *
-                                               sizeof(*privctx->keys));
-                               }
-
-                               /* TODO: Sort by fingerprint? */
-                               privctx->keys[privctx->count].start =
-                                       &privctx->file[start];
-                               privctx->keys[privctx->count].len = totlen;
-                               privctx->count++;
-
-                               /*
-                                * We need to fetch the key to calculate the
-                                * fingerprint.
-                                */
-                               keyring_fetch_key(privctx, privctx->count - 1,
-                                               &key);
-                               get_fingerprint(key->publickey,
-                                       &privctx->keys[privctx->count - 1].fp);
-                               free_publickey(key);
-                               key = NULL;
-                       }
-                       start = pos;
-                       totlen = 0;
-               }
-               totlen += len;
-               pos += len;
-       }
-
-       return privctx->count;
-}
-
-/**
- *     cleanupdb - De-initialize the key database.
- *
- *     This is just a no-op for flat file access.
- */
-static void keyring_cleanupdb(struct onak_dbctx *dbctx)
-{
-       struct onak_keyring_dbctx *privctx =
-               (struct onak_keyring_dbctx *) dbctx->priv;
-
-       if (dbctx->priv != NULL) {
-               if (privctx->file != NULL) {
-                       munmap(privctx->file, privctx->length);
-               }
-               free(privctx->keys);
-               free(dbctx->priv);
-               dbctx->priv = NULL;
-       }
-
-       if (dbctx != NULL) {
-               free(dbctx);
-       }
-};
-
-/**
- *     initdb - Initialize the key database.
- *
- *     This is just a no-op for flat file access.
- */
-struct onak_dbctx *keydb_keyring_init(struct onak_db_config *dbcfg,
-               bool readonly)
-{
-       struct onak_keyring_dbctx *privctx;
-       struct onak_dbctx *dbctx;
-       struct stat sb;
-       int fd;
-
-       dbctx = malloc(sizeof(struct onak_dbctx));
-       if (dbctx == NULL) {
-               return NULL;
-       }
-       dbctx->config = dbcfg;
-       dbctx->priv = privctx = calloc(1, sizeof(*privctx));
-       if (privctx == NULL) {
-               free(dbctx);
-               return NULL;
-       }
-       privctx->space = 16;
-       privctx->keys = calloc(privctx->space, sizeof(*privctx->keys));
-
-       fd = open(dbcfg->location, O_RDONLY);
-       if (fd < 0) {
-               logthing(LOGTHING_CRITICAL,
-                               "Couldn't open keyring file %s: %s (%d)",
-                               dbcfg->location,
-                               strerror(errno),
-                               errno);
-               keyring_cleanupdb(dbctx);
-               return NULL;
-       }
-       if (fstat(fd, &sb) < 0) {
-               logthing(LOGTHING_CRITICAL,
-                               "Couldn't stat keyring file %s: %s (%d)",
-                               dbcfg->location,
-                               strerror(errno),
-                               errno);
-               close(fd);
-               keyring_cleanupdb(dbctx);
-               return NULL;
-       }
-       privctx->file = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
-       if (privctx->file == MAP_FAILED) {
-               logthing(LOGTHING_CRITICAL,
-                               "Couldn't mmap keyring file %s: %s (%d)",
-                               dbcfg->location,
-                               strerror(errno),
-                               errno);
-               privctx->file = NULL;
-               close(fd);
-               keyring_cleanupdb(dbctx);
-               return NULL;
-       }
-       privctx->length = sb.st_size;
-       close(fd);
-
-       if (keyring_parse_keys(privctx) == 0) {
-               logthing(LOGTHING_CRITICAL,
-                               "Failed to load any keys from keyring file %s",
-                               dbcfg->location);
-               keyring_cleanupdb(dbctx);
-               return NULL;
-       }
-
-       dbctx->cleanupdb                = keyring_cleanupdb;
-       dbctx->starttrans               = keyring_starttrans;
-       dbctx->endtrans                 = keyring_endtrans;
-       dbctx->fetch_key_id             = keyring_fetch_key_id;
-       dbctx->fetch_key_fp             = keyring_fetch_key_fp;
-       dbctx->fetch_key_text           = keyring_fetch_key_text;
-       dbctx->store_key                = keyring_store_key;
-       dbctx->update_keys              = keyring_update_keys;
-       dbctx->delete_key               = keyring_delete_key;
-       dbctx->getkeysigs               = generic_getkeysigs;
-       dbctx->cached_getkeysigs        = generic_cached_getkeysigs;
-       dbctx->keyid2uid                = generic_keyid2uid;
-       dbctx->iterate_keys             = keyring_iterate_keys;
-
-       return dbctx;
-}
diff --git a/keydb_pg.c b/keydb_pg.c
deleted file mode 100644 (file)
index 7d59640..0000000
+++ /dev/null
@@ -1,717 +0,0 @@
-/*
- * keydb_pg.c - Routines to store and fetch keys in a PostGres database.
- *
- * Copyright 2002-2004 Jonathan McDowell <noodles@earth.li>
- *
- * 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 <postgresql/libpq/libpq-fs.h>
-
-#include <sys/types.h>
-#include <sys/uio.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "hash.h"
-#include "keydb.h"
-#include "keyid.h"
-#include "decodekey.h"
-#include "keystructs.h"
-#include "log.h"
-#include "mem.h"
-#include "onak-conf.h"
-#include "parsekey.h"
-
-struct pg_fc_ctx {
-       PGconn *dbconn;
-       int fd;
-};
-
-/**
- *     keydb_fetchchar - Fetches a char from a file.
- */
-static int keydb_fetchchar(void *_ctx, size_t count, void *c)
-{
-       struct pg_fc_ctx *ctx = (struct pg_fc_ctx *) _ctx;
-
-       return (!lo_read(ctx->dbconn, ctx->fd, (char *) c, count));
-}
-
-/**
- *     keydb_putchar - Puts a char to a file.
- */
-static int keydb_putchar(void *_ctx, size_t count, void *c)
-{
-       struct pg_fc_ctx *ctx = (struct pg_fc_ctx *) _ctx;
-
-       return !(lo_write(ctx->dbconn, ctx->fd, (char *) c, count));
-}
-
-/**
- *     starttrans - Start a transaction.
- *
- *     Start a transaction. Intended to be used if we're about to perform many
- *     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(struct onak_dbctx *dbctx)
-{
-       PGconn *dbconn = (PGconn *) dbctx->priv;
-       PGresult *result = NULL;
-       
-       result = PQexec(dbconn, "BEGIN");
-       PQclear(result);
-
-       return true;
-}
-
-/**
- *     endtrans - End a transaction.
- *
- *     Ends a transaction.
- */
-static void pg_endtrans(struct onak_dbctx *dbctx)
-{
-       PGconn *dbconn = (PGconn *) dbctx->priv;
-       PGresult *result = NULL;
-
-       result = PQexec(dbconn, "COMMIT");
-       PQclear(result);
-
-       return;
-}
-
-/**
- *     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.
- *
- *     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 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 i = 0;
-       int numkeys = 0;
-       Oid key_oid;
-       struct pg_fc_ctx fcctx;
-
-       if (!intrans) {
-               result = PQexec(dbconn, "BEGIN");
-               PQclear(result);
-       }
-       
-       if (keyid > 0xFFFFFFFF) {
-               snprintf(statement, 1023,
-                       "SELECT keydata FROM onak_keys WHERE keyid = '%"
-                       PRIX64 "'",
-                       keyid);
-       } else {
-               snprintf(statement, 1023,
-                       "SELECT keydata FROM onak_keys WHERE keyid "
-                       "LIKE '%%%" PRIX64 "'",
-                       keyid);
-       }
-       result = PQexec(dbconn, statement);
-
-       if (PQresultStatus(result) == PGRES_TUPLES_OK) {
-               numkeys = PQntuples(result);
-               for (i = 0; i < numkeys && numkeys <= config.maxkeys; i++) {
-                       oids = PQgetvalue(result, i, 0);
-                       key_oid = (Oid) atoi(oids);
-
-                       fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
-                       if (fcctx.fd < 0) {
-                               logthing(LOGTHING_ERROR,
-                                               "Can't open large object.");
-                       } else {
-                               fcctx.dbconn = dbconn;
-                               read_openpgp_stream(keydb_fetchchar, &fcctx,
-                                               &packets, 0);
-                               parse_keys(packets, publickey);
-                               lo_close(dbconn, fcctx.fd);
-                               free_packet_list(packets);
-                               packets = NULL;
-                       }
-               }
-       } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
-               logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
-       }
-
-       PQclear(result);
-
-       if (!intrans) {
-               result = PQexec(dbconn, "COMMIT");
-               PQclear(result);
-       }
-       return (numkeys);
-}
-
-/**
- *     fetch_key_text - Trys 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.
- */
-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 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);
-       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 "
-                       "AND onak_uids.uid LIKE '%%%s%%'",
-                       newsearch);
-       result = PQexec(dbconn, statement);
-       free(newsearch);
-       newsearch = NULL;
-
-       if (PQresultStatus(result) == PGRES_TUPLES_OK) {
-               numkeys = PQntuples(result);
-               for (i = 0; i < numkeys && numkeys <= config.maxkeys; i++) {
-                       oids = PQgetvalue(result, i, 0);
-                       key_oid = (Oid) atoi(oids);
-
-                       fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
-                       if (fcctx.fd < 0) {
-                               logthing(LOGTHING_ERROR,
-                                               "Can't open large object.");
-                       } else {
-                               fcctx.dbconn = dbconn;
-                               read_openpgp_stream(keydb_fetchchar, &fcctx,
-                                               &packets,
-                                               0);
-                               parse_keys(packets, publickey);
-                               lo_close(dbconn, fcctx.fd);
-                               free_packet_list(packets);
-                               packets = NULL;
-                       }
-               }
-       } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
-               logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
-       }
-
-       PQclear(result);
-
-       result = PQexec(dbconn, "COMMIT");
-       PQclear(result);
-       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.
- *     @intrans: If we're already in a transaction.
- *     @update: If true the key exists and should be updated.
- *
- *     Again we just use the hex representation of the keyid as the filename
- *     to store the key to. We flatten the public key to a list of OpenPGP
- *     packets and then use write_openpgp_stream() to write the stream out to
- *     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 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;
-       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.
-        *
-        * TODO: Can we optimize this perhaps? Possibly when other data is
-        * involved as well? I suspect this is easiest and doesn't make a lot
-        * of difference though - the largest chunk of data is the keydata and
-        * it definitely needs updated.
-        */
-       if (update) {
-               get_fingerprint(publickey->publickey, &fp);
-               pg_delete_key(dbctx, &fp, true);
-       }
-
-       next = publickey->next;
-       publickey->next = NULL;
-       flatten_publickey(publickey, &packets, &list_end);
-       publickey->next = next;
-               
-       key_oid = lo_creat(dbconn, INV_READ | INV_WRITE);
-       if (key_oid == 0) {
-               logthing(LOGTHING_ERROR, "Can't create key OID");
-       } else {
-               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 "
-                       "('%" PRIX64 "', '%d')", 
-                       keyid,
-                       key_oid);
-       result = PQexec(dbconn, statement);
-
-       if (PQresultStatus(result) != PGRES_COMMAND_OK) {
-               logthing(LOGTHING_ERROR, "Problem storing key in DB.");
-               logthing(LOGTHING_ERROR, "%s", PQresultErrorMessage(result));
-       }
-       PQclear(result);
-
-       uids = keyuids(publickey, &primary);
-       if (uids != NULL) {
-               for (i = 0; uids[i] != NULL; i++) {
-                       safeuid = malloc(strlen(uids[i]) * 2 + 1);
-                       if (safeuid != NULL) {
-                               memset(safeuid, 0, strlen(uids[i]) * 2 + 1);
-                               PQescapeStringConn(dbconn, safeuid, uids[i],
-                                               strlen(uids[i]), NULL);
-
-                               snprintf(statement, 1023,
-                                       "INSERT INTO onak_uids "
-                                       "(keyid, uid, pri) "
-                                       "VALUES ('%" PRIX64 "', '%s', '%c')",
-                                       keyid,
-                                       safeuid,
-                                       (uids[i] == primary) ? 't' : 'f');
-                               result = PQexec(dbconn, statement);
-
-                               free(safeuid);
-                               safeuid = NULL;
-                       }
-                       if (uids[i] != NULL) {
-                               free(uids[i]);
-                               uids[i] = NULL;
-                       }
-
-                       if (PQresultStatus(result) != PGRES_COMMAND_OK) {
-                               logthing(LOGTHING_ERROR,
-                                               "Problem storing key in DB.");
-                               logthing(LOGTHING_ERROR, "%s",
-                                               PQresultErrorMessage(result));
-                       }
-                       /*
-                        * TODO: Check result.
-                        */
-                       PQclear(result);
-               }
-               free(uids);
-               uids = NULL;
-       }
-
-       for (curuid = publickey->uids; curuid != NULL; curuid = curuid->next) {
-               for (packets = curuid->sigs; packets != NULL; 
-                               packets = packets->next) {
-                       snprintf(statement, 1023,
-                               "INSERT INTO onak_sigs (signer, signee) "
-                               "VALUES ('%" PRIX64 "', '%" PRIX64 "')",
-                               sig_keyid(packets->packet),
-                               keyid);
-                       result = PQexec(dbconn, statement);
-                       PQclear(result);
-               }
-       }
-
-       if (!intrans) {
-               result = PQexec(dbconn, "COMMIT");
-               PQclear(result);
-       }
-       
-       return 0;
-}
-
-/**
- *     keyid2uid - Takes a keyid and returns the primary UID for it.
- *     @keyid: The keyid to lookup.
- */
-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 = '%" PRIX64
-               "' AND pri = 't'",
-               keyid);
-       result = PQexec(dbconn, statement);
-
-       /*
-        * Technically we only expect one response to the query; a key only has
-        * one primary ID. Better to return something than nothing though.
-        *
-        * TODO: Log if we get more than one response? Needs logging framework
-        * first though.
-        */
-       if (PQresultStatus(result) == PGRES_TUPLES_OK &&
-                       PQntuples(result) >= 1) {
-               uid = strdup(PQgetvalue(result, 0, 0));
-       } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
-               logthing(LOGTHING_ERROR,
-                               "Problem retrieving key (%" PRIX64
-                               ") from DB.",
-                               keyid);
-       }
-
-       PQclear(result);
-
-       return uid;
-}
-
-/**
- *     getkeysigs - Gets a linked list of the signatures on a key.
- *     @keyid: The keyid to get the sigs for.
- *     @revoked: If the key is revoked.
- *
- *     This function gets the list of signatures on a key. Used for key 
- *     indexing and doing stats bits.
- */
-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];
-       int i, j;
-       int numsigs = 0;
-       bool intrans = false;
-       char *str;
-
-       if (!intrans) {
-               result = PQexec(dbconn, "BEGIN");
-               PQclear(result);
-       }
-
-       snprintf(statement, 1023,
-               "SELECT DISTINCT signer FROM onak_sigs WHERE signee = '%"
-               PRIX64 "'",
-               keyid);
-       result = PQexec(dbconn, statement);
-
-       if (PQresultStatus(result) == PGRES_TUPLES_OK) {
-               numsigs = PQntuples(result);
-               for (i = 0; i < numsigs;  i++) {
-                       j = 0;
-                       signer = 0;
-                       str = PQgetvalue(result, i, 0);
-                       while (str[j] != 0) {
-                               signer <<= 4;
-                               if (str[j] >= '0' && str[j] <= '9') {
-                                       signer += str[j] - '0';
-                               } else {
-                                       signer += str[j] - 'A' + 10;
-                               }
-                               j++;
-                       }
-                       sigs = lladd(sigs, createandaddtohash(signer));
-               }
-       } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
-               logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
-       }
-
-       PQclear(result);
-
-       if (!intrans) {
-               result = PQexec(dbconn, "COMMIT");
-               PQclear(result);
-       }
-
-       /*
-        * TODO: What do we do about revocations? We don't have the details
-        * stored in a separate table, so we'd have to grab the key and decode
-        * it, which we're trying to avoid by having a signers table.
-        */
-       if (revoked != NULL) {
-               *revoked = false;
-       }
-       
-       return sigs;
-}
-
-/**
- *     iterate_keys - call a function once for each key in the db.
- *     @iterfunc: The function to call.
- *     @ctx: A context pointer
- *
- *     Calls iterfunc once for each key in the database. ctx is passed
- *     unaltered to iterfunc. This function is intended to aid database dumps
- *     and statistic calculations.
- *
- *     Returns the number of keys we iterated over.
- */
-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;
-       int i = 0;
-       int numkeys = 0;
-       Oid key_oid;
-       struct pg_fc_ctx fcctx;
-
-       result = PQexec(dbconn, "SELECT keydata FROM onak_keys;");
-
-       if (PQresultStatus(result) == PGRES_TUPLES_OK) {
-               numkeys = PQntuples(result);
-               for (i = 0; i < numkeys; i++) {
-                       oids = PQgetvalue(result, i, 0);
-                       key_oid = (Oid) atoi(oids);
-
-                       fcctx.fd = lo_open(dbconn, key_oid, INV_READ);
-                       if (fcctx.fd < 0) {
-                               logthing(LOGTHING_ERROR,
-                                               "Can't open large object.");
-                       } else {
-                               fcctx.dbconn = dbconn;
-                               read_openpgp_stream(keydb_fetchchar, &fcctx,
-                                               &packets, 0);
-                               parse_keys(packets, &key);
-                               lo_close(dbconn, fcctx.fd);
-
-                               iterfunc(ctx, key);
-                                       
-                               free_publickey(key);
-                               key = NULL;
-                               free_packet_list(packets);
-                               packets = NULL;
-                       }
-               }
-       } else if (PQresultStatus(result) != PGRES_TUPLES_OK) {
-               logthing(LOGTHING_ERROR, "Problem retrieving key from DB.");
-       }
-
-       PQclear(result);
-
-       return (numkeys);
-}
-
-/*
- * Include the basic keydb routines.
- */
-#define NEED_UPDATEKEYS 1
-#define NEED_GET_FP 1
-#include "keydb.c"
-
-/**
- *     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;
-}
diff --git a/keydb_stacked.c b/keydb_stacked.c
deleted file mode 100644 (file)
index 7e997d9..0000000
+++ /dev/null
@@ -1,386 +0,0 @@
-/*
- * keydb_stacked.c - backend that stacks other backends together
- *
- * Copyright 2016 Jonathan McDowell <noodles@earth.li>
- *
- * 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 <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-#include "cleankey.h"
-#include "keydb.h"
-#include "keystructs.h"
-#include "ll.h"
-#include "log.h"
-#include "onak-conf.h"
-
-struct onak_stacked_dbctx {
-       struct ll *backends;
-       bool store_on_fallback;
-};
-
-/*
- * The following functions only apply to the first backend.
- */
-
-static bool stacked_starttrans(struct onak_dbctx *dbctx)
-{
-       struct onak_stacked_dbctx *privctx =
-                       (struct onak_stacked_dbctx *) dbctx->priv;
-       struct onak_dbctx *backend =
-                       (struct onak_dbctx *) privctx->backends->object;
-
-       return backend->starttrans(backend);
-}
-
-static void stacked_endtrans(struct onak_dbctx *dbctx)
-{
-       struct onak_stacked_dbctx *privctx =
-                       (struct onak_stacked_dbctx *) dbctx->priv;
-       struct onak_dbctx *backend =
-                       (struct onak_dbctx *) privctx->backends->object;
-
-       backend->starttrans(backend);
-}
-
-static int stacked_store_key(struct onak_dbctx *dbctx,
-               struct openpgp_publickey *publickey, bool intrans,
-               bool update)
-{
-       struct onak_stacked_dbctx *privctx =
-                       (struct onak_stacked_dbctx *) dbctx->priv;
-       struct onak_dbctx *backend =
-                       (struct onak_dbctx *) privctx->backends->object;
-
-       return backend->store_key(backend,
-                       publickey, intrans, update);
-}
-
-static int stacked_delete_key(struct onak_dbctx *dbctx,
-               struct openpgp_fingerprint *fp,
-               bool intrans)
-{
-       struct onak_stacked_dbctx *privctx =
-                       (struct onak_stacked_dbctx *) dbctx->priv;
-       struct onak_dbctx *backend =
-                       (struct onak_dbctx *) privctx->backends->object;
-
-       return backend->delete_key(backend,
-                       fp, intrans);
-}
-
-static int stacked_update_keys(struct onak_dbctx *dbctx,
-               struct openpgp_publickey **keys,
-               struct keyarray *blacklist,
-               bool updateonly,
-               bool sendsync)
-{
-       struct onak_stacked_dbctx *privctx =
-                       (struct onak_stacked_dbctx *) dbctx->priv;
-       struct onak_dbctx *backend =
-                       (struct onak_dbctx *) privctx->backends->object;
-
-       return backend->update_keys(backend, keys, blacklist, updateonly,
-                       sendsync);
-}
-
-static int stacked_iterate_keys(struct onak_dbctx *dbctx,
-               void (*iterfunc)(void *ctx, struct openpgp_publickey *key),
-               void *ctx)
-{
-       struct onak_stacked_dbctx *privctx =
-                       (struct onak_stacked_dbctx *) dbctx->priv;
-       struct onak_dbctx *backend =
-                       (struct onak_dbctx *) privctx->backends->object;
-
-       return backend->iterate_keys(backend, iterfunc, ctx);
-}
-
-static void store_on_fallback(struct onak_stacked_dbctx *privctx,
-               struct openpgp_publickey *publickey, bool intrans)
-{
-       struct onak_dbctx *backend =
-                       (struct onak_dbctx *) privctx->backends->object;
-       struct openpgp_publickey *curkey;
-
-       cleankeys(&publickey, config.clean_policies);
-       /*
-        * If we walked the stack at all, store the key in the first
-        * backend if configured to do so. It's not an update as we
-        * know it's not there or we wouldn't have fallen back.
-        */
-       for (curkey = publickey; curkey != NULL; curkey = curkey->next) {
-               backend->store_key(backend, curkey, intrans, false);
-       }
-}
-
-/*
- * The functions below will walk along the backend stack until they
- * reach the end or get a successful result.
- */
-
-static int stacked_fetch_key_id(struct onak_dbctx *dbctx, uint64_t keyid,
-               struct openpgp_publickey **publickey, bool intrans)
-{
-       struct onak_stacked_dbctx *privctx =
-                       (struct onak_stacked_dbctx *) dbctx->priv;
-       struct onak_dbctx *backend;
-       struct ll *cur;
-       int res = 0;
-
-       for (cur = privctx->backends; cur != NULL && res == 0;
-                       cur = cur->next) {
-               backend = (struct onak_dbctx *) cur->object;
-               res = backend->fetch_key_id(backend, keyid, publickey,
-                               intrans);
-       }
-
-       if (privctx->store_on_fallback && cur != privctx->backends) {
-               store_on_fallback(privctx, *publickey, intrans);
-       }
-
-       return res;
-}
-
-static int stacked_fetch_key_fp(struct onak_dbctx *dbctx,
-               struct openpgp_fingerprint *fingerprint,
-               struct openpgp_publickey **publickey, bool intrans)
-{
-       struct onak_stacked_dbctx *privctx =
-                       (struct onak_stacked_dbctx *) dbctx->priv;
-       struct onak_dbctx *backend;
-       struct ll *cur;
-       int res = 0;
-
-       for (cur = privctx->backends; cur != NULL && res == 0;
-                       cur = cur->next) {
-               backend = (struct onak_dbctx *) cur->object;
-               res = backend->fetch_key_fp(backend, fingerprint, publickey,
-                               intrans);
-       }
-
-       if (privctx->store_on_fallback && cur != privctx->backends) {
-               store_on_fallback(privctx, *publickey, intrans);
-       }
-
-       return res;
-}
-
-static int stacked_fetch_key_text(struct onak_dbctx *dbctx,
-               const char *search,
-               struct openpgp_publickey **publickey)
-{
-       struct onak_stacked_dbctx *privctx =
-                       (struct onak_stacked_dbctx *) dbctx->priv;
-       struct onak_dbctx *backend;
-       struct ll *cur;
-       int res = 0;
-
-       for (cur = privctx->backends; cur != NULL && res == 0;
-                       cur = cur->next) {
-               backend = (struct onak_dbctx *) cur->object;
-               res = backend->fetch_key_text(backend, search, publickey);
-       }
-
-       if (privctx->store_on_fallback && cur != privctx->backends) {
-               store_on_fallback(privctx, *publickey, false);
-       }
-
-       return res;
-}
-
-static int stacked_fetch_key_skshash(struct onak_dbctx *dbctx,
-               const struct skshash *hash,
-               struct openpgp_publickey **publickey)
-{
-       struct onak_stacked_dbctx *privctx =
-                       (struct onak_stacked_dbctx *) dbctx->priv;
-       struct onak_dbctx *backend;
-       struct ll *cur;
-       int res = 0;
-
-       for (cur = privctx->backends; cur != NULL && res == 0;
-                       cur = cur->next) {
-               backend = (struct onak_dbctx *) cur->object;
-               res = backend->fetch_key_skshash(backend, hash, publickey);
-       }
-
-       if (privctx->store_on_fallback && cur != privctx->backends) {
-               store_on_fallback(privctx, *publickey, false);
-       }
-
-       return res;
-}
-
-/*
- * Include the basic keydb routines so we can use them for fall back.
- * For all of the following we try the top keydb backend and if that doesn't
- * have answer fall back to the generics, which will do a retrieve from a
- * backend further down the stack, then a fallback store.
- */
-#define NEED_KEYID2UID 1
-#define NEED_GETKEYSIGS 1
-#define NEED_UPDATEKEYS 1
-#include "keydb.c"
-
-static struct ll *stacked_getkeysigs(struct onak_dbctx *dbctx,
-               uint64_t keyid, bool *revoked)
-{
-       struct onak_stacked_dbctx *privctx =
-                       (struct onak_stacked_dbctx *) dbctx->priv;
-       struct onak_dbctx *backend =
-                       (struct onak_dbctx *) privctx->backends->object;
-       struct ll *res;
-
-       res = backend->getkeysigs(backend, keyid, revoked);
-       if (res == NULL) {
-               res = generic_getkeysigs(dbctx, keyid, revoked);
-       }
-
-       return res;
-}
-
-static struct ll *stacked_cached_getkeysigs(struct onak_dbctx *dbctx,
-               uint64_t keyid)
-{
-       struct onak_stacked_dbctx *privctx =
-                       (struct onak_stacked_dbctx *) dbctx->priv;
-       struct onak_dbctx *backend =
-                       (struct onak_dbctx *) privctx->backends->object;
-       struct ll *res;
-
-       res = backend->cached_getkeysigs(backend, keyid);
-       if (res == NULL) {
-               res = generic_cached_getkeysigs(dbctx, keyid);
-       }
-
-       return res;
-}
-
-static char *stacked_keyid2uid(struct onak_dbctx *dbctx,
-                       uint64_t keyid)
-{
-       struct onak_stacked_dbctx *privctx =
-                       (struct onak_stacked_dbctx *) dbctx->priv;
-       struct onak_dbctx *backend =
-                       (struct onak_dbctx *) privctx->backends->object;
-       char *res = NULL;
-
-       res = backend->keyid2uid(backend, keyid);
-       if (!res) {
-               res = generic_keyid2uid(dbctx, keyid);
-       }
-
-       return res;
-}
-
-static void stacked_cleanupdb(struct onak_dbctx *dbctx)
-{
-       struct onak_stacked_dbctx *privctx =
-                       (struct onak_stacked_dbctx *) dbctx->priv;
-       struct onak_dbctx *backend;
-       struct ll *cur;
-       int res = 0;
-
-       for (cur = privctx->backends; cur != NULL && res == 0;
-                       cur = cur->next) {
-               backend = (struct onak_dbctx *) cur->object;
-               backend->cleanupdb(backend);
-       }
-
-       if (dbctx->priv != NULL) {
-               free(dbctx->priv);
-               dbctx->priv = NULL;
-       }
-
-       if (dbctx != NULL) {
-               free(dbctx);
-       }
-}
-
-struct onak_dbctx *keydb_stacked_init(struct onak_db_config *dbcfg,
-               bool readonly)
-{
-       struct onak_dbctx *dbctx;
-       struct onak_stacked_dbctx *privctx;
-       struct onak_dbctx *backend;
-       struct onak_db_config *backend_cfg;
-       char *backend_name, *saveptr = NULL;
-
-       if (dbcfg == NULL) {
-               logthing(LOGTHING_CRITICAL,
-                       "No backend database configuration supplied.");
-               return NULL;
-       }
-
-       dbctx = malloc(sizeof(struct onak_dbctx));
-
-       if (dbctx == NULL) {
-               return NULL;
-       }
-
-       dbctx->config = dbcfg;
-       dbctx->priv = privctx = malloc(sizeof(struct onak_stacked_dbctx));
-       if (dbctx->priv == NULL) {
-               free(dbctx);
-               return (NULL);
-       }
-
-       /* TODO: Make configurable? */
-       privctx->store_on_fallback = true;
-       privctx->backends = NULL;
-
-       backend_name = strtok_r(dbcfg->location, ":", &saveptr);
-       while (backend_name != NULL) {
-               backend_cfg = find_db_backend_config(config.backends,
-                               backend_name);
-               if (backend_cfg == NULL) {
-                       logthing(LOGTHING_CRITICAL,
-                               "Couldn't find configuration for %s backend",
-                               backend_name);
-                       stacked_cleanupdb(dbctx);
-                       return NULL;
-               }
-               logthing(LOGTHING_INFO, "Loading stacked backend: %s",
-                               backend_cfg->name);
-
-               backend = config.dbinit(backend_cfg, readonly);
-               privctx->backends = lladdend(privctx->backends, backend);
-
-               backend_name = strtok_r(NULL, ":", &saveptr);
-       }
-
-       if (privctx->backends != NULL) {
-               dbctx->cleanupdb = stacked_cleanupdb;
-               dbctx->starttrans = stacked_starttrans;
-               dbctx->endtrans = stacked_endtrans;
-               dbctx->fetch_key_id = stacked_fetch_key_id;
-               dbctx->fetch_key_fp = stacked_fetch_key_fp;
-               dbctx->fetch_key_text = stacked_fetch_key_text;
-               dbctx->fetch_key_skshash = stacked_fetch_key_skshash;
-               dbctx->store_key = stacked_store_key;
-               dbctx->update_keys = stacked_update_keys;
-               dbctx->delete_key = stacked_delete_key;
-               dbctx->getkeysigs = stacked_getkeysigs;
-               dbctx->cached_getkeysigs = stacked_cached_getkeysigs;
-               dbctx->keyid2uid = stacked_keyid2uid;
-               dbctx->iterate_keys = stacked_iterate_keys;
-       }
-
-       return dbctx;
-}
diff --git a/keydctl.8 b/keydctl.8
deleted file mode 100644 (file)
index 430da37..0000000
--- a/keydctl.8
+++ /dev/null
@@ -1,51 +0,0 @@
-.TH KEYD 8
-.SH NAME
-keydctl \- control an onak keyd instance
-.SH SYNOPSIS
-.PP
-.B keydctl
-[
-.B options
-]
-.B command
-.SH DESCRIPTION
-.PP
-keydctl is a command line client for interacting with a backend keyd
-daemon. It's intended to perform functions that are specifically related
-to keyd rather than being generic keyserver functions. See
-.BR onak(1)
-for details about how to perform key related operations.
-.SS "Options"
-.TP
-\fB\-c \fIFILE\fR\fR
-Use \fIFILE\fR as the config file instead of the default.
-.TP
-\fB\-h\fR
-Display help text.
-.SS "Commands"
-.TP
-.B check
-Query if keyd is running and accepting commands. Returns 0 if it is, 1
-otherwise. Outputs nothing to stdout/stderr.
-.TP
-.B quit
-Request that keyd exits cleanly
-.TP
-.B status
-Display the status of a running keyd. Currently the protocol version in use,
-when keyd was started, the number of client connections seen and a breakdown
-of the commands seen.
-.SH FILES
-.br
-.nf
-.\" set tabstop to longest possible filename, plus a wee bit
-.ta \w'/usr/lib/perl/getopts.pl   'u
-\fI/etc/onak.ini\fR    default configuration file
-.SH NOTES
-This man page could probably do with some more details.
-.SH "SEE ALSO"
-.BR onak (1)
-.BR keyd (8)
-.SH AUTHOR
-onak was written by Jonathan McDowell <noodles@earth.li>. It can be found at
-http://www.earth.li/projectpurple/progs/onak.html
diff --git a/keydctl.c b/keydctl.c
deleted file mode 100644 (file)
index 8712f63..0000000
--- a/keydctl.c
+++ /dev/null
@@ -1,257 +0,0 @@
-/*
- * keydctl.c - A simple program to control a running keyd instance
- *
- * Copyright 2011 Jonathan McDowell <noodles@earth.li>
- *
- * 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 <errno.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-
-#include "build-config.h"
-
-#include "keyd.h"
-#include "onak-conf.h"
-
-static int keyd_fd = -1;
-static int verbose = 0;
-
-static int keyd_do_command(enum keyd_ops cmd, void *buf, size_t len)
-{
-       uint32_t tmp;
-
-       if (keyd_fd < 0) {
-               return -1;
-       }
-
-       tmp = cmd;
-       if (write(keyd_fd, &tmp, sizeof(tmp)) != sizeof(tmp)) {
-               if (verbose >= 0) {
-                       fprintf(stderr,
-                               "Couldn't write keyd command %d: %s (%d)\n",
-                               cmd, strerror(errno), errno);
-               }
-               exit(EXIT_FAILURE);
-       } else if (read(keyd_fd, &tmp, sizeof(tmp)) != sizeof(tmp)) {
-               if (verbose >= 0) {
-                       fprintf(stderr,
-                               "Couldn't read keyd command %d reply: "
-                               "%s (%d)\n",
-                               cmd, strerror(errno), errno);
-                       }
-               exit(EXIT_FAILURE);
-       } else if (tmp != KEYD_REPLY_OK) {
-               return -1;
-       } else if (buf == NULL) {
-               return 0;
-       } else if (read(keyd_fd, &tmp, sizeof(tmp)) != sizeof(tmp)) {
-               if (verbose >= 0) {
-                       fprintf(stderr,
-                               "Couldn't read keyd command %d reply length: "
-                               "%s (%d)\n",
-                               cmd, strerror(errno), errno);
-               }
-               exit(EXIT_FAILURE);
-       } else if (tmp > len) {
-               /* TODO: Read what we can into buf and skip the rest */
-               return -1;
-       } else {
-               return read(keyd_fd, buf, tmp);
-       }
-}
-
-static void keyd_connect(void)
-{
-       struct sockaddr_un sock;
-       uint32_t           reply = KEYD_REPLY_UNKNOWN_CMD;
-
-       keyd_fd = socket(PF_UNIX, SOCK_STREAM, 0);
-       if (keyd_fd < 0) {
-               if (verbose >= 0) {
-                       fprintf(stderr,
-                               "Couldn't open socket: %s (%d)\n",
-                               strerror(errno),
-                               errno);
-               }
-               exit(EXIT_FAILURE);
-       }
-
-       sock.sun_family = AF_UNIX;
-       snprintf(sock.sun_path, sizeof(sock.sun_path) - 1, "%s/%s",
-                       config.sock_dir,
-                       KEYD_SOCKET);
-       if (connect(keyd_fd, (struct sockaddr *) &sock, sizeof(sock)) < 0) {
-               if (verbose >= 0) {
-                       fprintf(stderr,
-                               "Couldn't connect to socket %s: %s (%d)\n",
-                               sock.sun_path,
-                               strerror(errno),
-                               errno);
-               }
-               exit(EXIT_FAILURE);
-       }
-
-       keyd_do_command(KEYD_CMD_VERSION, &reply, sizeof(reply));
-       if (reply != keyd_version) {
-               if (verbose >= 0) {
-                       fprintf(stderr, "Error! keyd protocol version "
-                               "mismatch. (us = %d, it = %d)\n",
-                               keyd_version, reply);
-               }
-               exit(EXIT_FAILURE);
-       }
-
-       return;
-}
-
-static void keyd_close(void)
-{
-       uint32_t cmd = KEYD_CMD_CLOSE;
-
-       if (write(keyd_fd, &cmd, sizeof(cmd)) != sizeof(cmd) && verbose >= 0) {
-               fprintf(stderr, "Couldn't send close cmd: %s (%d)\n",
-                               strerror(errno),
-                               errno);
-       }
-
-       if (shutdown(keyd_fd, SHUT_RDWR) < 0 && verbose >= 0) {
-               fprintf(stderr, "Error shutting down socket: %d\n",
-                               errno);
-       }
-       if (close(keyd_fd) < 0 && verbose >= 0) {
-               fprintf(stderr, "Error closing down socket: %d\n",
-                               errno);
-       }
-       keyd_fd = -1;
-
-       return;
-
-}
-
-static void keyd_status(void)
-{
-       uint32_t reply;
-       struct keyd_stats stats;
-
-       if (keyd_do_command(KEYD_CMD_VERSION, &reply, sizeof(reply)) == -1) {
-               printf("Got failure asking for keyd version.\n");
-               return;
-       }
-       printf("Using keyd protocol version %d.\n", reply);
-
-       if (keyd_do_command(KEYD_CMD_STATS, &stats, sizeof(stats)) == -1) {
-               printf("Got failure asking for keyd statistics.\n");
-               return;
-       }
-
-       printf("keyd running since %s", ctime(&stats.started));
-       printf("%d client connections received\n", stats.connects);
-
-       printf("Command statistics:\n");
-       printf("  Version:          %d\n",
-               stats.command_stats[KEYD_CMD_VERSION]);
-       printf("  Get key by ID:    %d\n",
-               stats.command_stats[KEYD_CMD_GET_ID]);
-       printf("  Get key by FP:    %d\n",
-               stats.command_stats[KEYD_CMD_GET_FP]);
-       printf("  Get key by hash:  %d\n",
-               stats.command_stats[KEYD_CMD_GET_SKSHASH]);
-       printf("  Store key:        %d\n",
-               stats.command_stats[KEYD_CMD_STORE]);
-       printf("  Delete key:       %d\n",
-               stats.command_stats[KEYD_CMD_DELETE]);
-       printf("  Update key:       %d\n",
-               stats.command_stats[KEYD_CMD_UPDATE]);
-       printf("  Search key:       %d\n",
-               stats.command_stats[KEYD_CMD_GET_TEXT]);
-       printf("  Get full keyid:   %d\n",
-               stats.command_stats[KEYD_CMD_GETFULLKEYID]);
-       printf("  Iterate all keys: %d\n",
-               stats.command_stats[KEYD_CMD_KEYITER]);
-       printf("  Close:            %d\n",
-               stats.command_stats[KEYD_CMD_CLOSE]);
-       printf("  Quit:             %d\n", stats.command_stats[KEYD_CMD_QUIT]);
-       printf("  Get statistics:   %d\n",
-               stats.command_stats[KEYD_CMD_STATS]);
-       printf("  Unknown:          %d\n",
-               stats.command_stats[KEYD_CMD_UNKNOWN]);
-
-       return;
-}
-
-static void usage(void)
-{
-       puts("keydctl " ONAK_VERSION " - control an onak keyd instance.\n");
-       puts("Usage:\n");
-       puts("\tkeydctl [options] <command> <parameters>\n");
-       puts("\tCommands:\n");
-       puts("\tcheck    - check if keyd is running");
-       puts("\tquit     - request that keyd cleanly shuts down");
-       puts("\tstatus   - display running keyd status");
-       exit(EXIT_FAILURE);
-}
-
-int main(int argc, char *argv[])
-{
-       int      optchar;
-       char    *configfile = NULL;
-
-       while ((optchar = getopt(argc, argv, "c:h")) != -1 ) {
-               switch (optchar) {
-               case 'c':
-                       configfile = strdup(optarg);
-                       break;
-               case 'h':
-               default:
-                       usage();
-                       break;
-               }
-       }
-
-       readconfig(configfile);
-       free(configfile);
-       configfile = NULL;
-
-       if ((argc - optind) < 1) {
-               cleanupconfig();
-               usage();
-       } else if (!strcmp("check", argv[optind])) {
-               /* Just do the connect and close quietly */
-               verbose = -1;
-               keyd_connect();
-               keyd_close();
-       } else if (!strcmp("status", argv[optind])) {
-               keyd_connect();
-               keyd_status();
-               keyd_close();
-       } else if (!strcmp("quit", argv[optind])) {
-               keyd_connect();
-               keyd_do_command(KEYD_CMD_QUIT, NULL, 0);
-               keyd_close();
-       } else {
-               cleanupconfig();
-               usage();
-       }
-
-       cleanupconfig();
-
-       exit(EXIT_SUCCESS);
-}
index 6b5b830dd6c9db123a5d2f664e5c3763cf008706..3a74d2456e28d1d40b69a93a595a47d6c0c823a9 100755 (executable)
--- a/runtests
+++ b/runtests
@@ -29,8 +29,8 @@ echo "WORKDIR : ${WORKDIR}"
 fail=0
 total=0
 
-for t in libkeydb_*.so; do
-       backend=${t##libkeydb_}
+for t in keydb/libkeydb_*.so; do
+       backend=${t##keydb/libkeydb_}
        backend=${backend%%.so}
        if [ "`echo ${TESTSDIR}/$backend-*`" != "${TESTSDIR}/$backend-*" ]; then
                echo "* testing $backend backend"
index e40c0e2439c74c10e0747f26a7cd2b2306842c8e..7d1b561ddf073049f419fd44375c2db92fae6efa 100644 (file)
@@ -1,6 +1,6 @@
 [main]
 backend=test-DB
-backends_dir=BUILDDIR
+backends_dir=BUILDDIR/keydb
 logfile=onak.log
 loglevel=7
 use_keyd=false