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