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)
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)
+++ /dev/null
-.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
+++ /dev/null
-/*
- * 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);
-}
+++ /dev/null
-/**
- * @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__ */
+++ /dev/null
-/*
- * 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
--- /dev/null
+# 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()
--- /dev/null
+.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
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+/**
+ * @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__ */
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+.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
--- /dev/null
+/*
+ * 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);
+}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-/*
- * 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;
-}
+++ /dev/null
-.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
+++ /dev/null
-/*
- * 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);
-}
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"
[main]
backend=test-DB
-backends_dir=BUILDDIR
+backends_dir=BUILDDIR/keydb
logfile=onak.log
loglevel=7
use_keyd=false