X-Git-Url: http://the.earth.li/gitweb/?a=blobdiff_plain;f=keyd.c;h=f3966db96dcdbafde6b92b341566b1199da791a7;hb=8e0907be1d73011075a99a0c029c56664e12843e;hp=7b6226e3877abbd8c885e3763667f18d72500bba;hpb=66748d60571a5ebbbc0f0cb84c061f9c62f5aef7;p=onak.git diff --git a/keyd.c b/keyd.c index 7b6226e..f3966db 100644 --- a/keyd.c +++ b/keyd.c @@ -1,29 +1,123 @@ /* * keyd.c - key retrieval daemon * - * Jonathan McDowell - * - * Copyright 2004 Project Purple + * 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, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ +#include #include +#include +#include #include #include #include +#include #include #include #include +#include #include #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" +#include "version.h" + +/* Maximum number of clients we're prepared to accept at once */ +#define MAX_CLIENTS 16 + +static struct keyd_stats *stats; + +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); + } + + pid = setsid(); + + freopen("/dev/null", "r", stdin); + freopen("/dev/null", "w", stdout); + freopen("/dev/null", "w", stderr); + + return; +} + +void iteratefunc(void *ctx, struct openpgp_publickey *key) +{ + struct openpgp_packet_list *packets = NULL; + struct openpgp_packet_list *list_end = NULL; + struct buffer_ctx storebuf; + int ret = 0; + int *fd = (int *) ctx; + uint64_t keyid; + + if (key != NULL) { + storebuf.offset = 0; + storebuf.size = 8192; + storebuf.buffer = malloc(8192); + + get_keyid(key, &keyid); + logthing(LOGTHING_TRACE, + "Iterating over 0x%016" PRIX64 ".", + keyid); + + flatten_publickey(key, + &packets, + &list_end); + write_openpgp_stream(buffer_putchar, + &storebuf, + packets); + logthing(LOGTHING_TRACE, + "Sending %d bytes.", + storebuf.offset); + ret = write(*fd, &storebuf.offset, + sizeof(storebuf.offset)); + if (ret != 0) { + write(*fd, storebuf.buffer, + storebuf.offset); + } + + free(storebuf.buffer); + storebuf.buffer = NULL; + storebuf.size = storebuf.offset = 0; + free_packet_list(packets); + packets = list_end = NULL; + } + + return; +} int sock_init(const char *sockname) { @@ -33,7 +127,7 @@ int sock_init(const char *sockname) fd = socket(PF_UNIX, SOCK_STREAM, 0); if (fd != -1) { - ret = fcntl(fd, F_SETFD, 1); + ret = fcntl(fd, F_SETFD, FD_CLOEXEC); } if (ret != -1) { @@ -50,18 +144,20 @@ int sock_init(const char *sockname) return fd; } -int sock_do(int fd) +int sock_do(struct onak_dbctx *dbctx, int fd) { - int cmd = KEYD_CMD_UNKNOWN; + uint32_t cmd = KEYD_CMD_UNKNOWN; ssize_t bytes = 0; ssize_t count = 0; int ret = 0; uint64_t keyid = 0; + uint8_t fp[MAX_FINGERPRINT_LEN]; char *search = NULL; struct openpgp_publickey *key = NULL; struct openpgp_packet_list *packets = NULL; struct openpgp_packet_list *list_end = NULL; struct buffer_ctx storebuf; + struct skshash hash; /* * Get the command from the client. @@ -75,13 +171,20 @@ int sock_do(int fd) } 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: cmd = KEYD_REPLY_OK; write(fd, &cmd, sizeof(cmd)); + cmd = sizeof(keyd_version); + write(fd, &cmd, sizeof(cmd)); write(fd, &keyd_version, sizeof(keyd_version)); break; - case KEYD_CMD_GET: + case KEYD_CMD_GET_ID: cmd = KEYD_REPLY_OK; write(fd, &cmd, sizeof(cmd)); bytes = read(fd, &keyid, sizeof(keyid)); @@ -91,9 +194,12 @@ int sock_do(int fd) storebuf.offset = 0; if (ret == 0) { logthing(LOGTHING_INFO, - "Fetching 0x%llX, result: %d", + "Fetching 0x%" PRIX64 + ", result: %d", keyid, - fetch_key(keyid, &key, false)); + dbctx->fetch_key_id(dbctx, + keyid, + &key, false)); if (key != NULL) { storebuf.size = 8192; storebuf.buffer = malloc(8192); @@ -125,7 +231,56 @@ int sock_do(int fd) } } break; - case KEYD_CMD_GETTEXT: + case KEYD_CMD_GET_FP: + cmd = KEYD_REPLY_OK; + write(fd, &cmd, sizeof(cmd)); + read(fd, &bytes, 1); + if (bytes > MAX_FINGERPRINT_LEN) { + ret = 1; + } else { + read(fd, fp, bytes); + } + storebuf.offset = 0; + if (ret == 0) { + logthing(LOGTHING_INFO, + "Fetching by fingerprint" + ", result: %d", + dbctx->fetch_key_fp(dbctx, + fp, bytes, + &key, false)); + if (key != NULL) { + 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); + write(fd, &storebuf.offset, + sizeof(storebuf.offset)); + write(fd, storebuf.buffer, + storebuf.offset); + + free(storebuf.buffer); + storebuf.buffer = NULL; + storebuf.size = storebuf.offset = 0; + free_packet_list(packets); + packets = list_end = NULL; + free_publickey(key); + key = NULL; + } else { + write(fd, &storebuf.offset, + sizeof(storebuf.offset)); + } + } + break; + + case KEYD_CMD_GET_TEXT: cmd = KEYD_REPLY_OK; write(fd, &cmd, sizeof(cmd)); bytes = read(fd, &count, sizeof(count)); @@ -140,7 +295,8 @@ int sock_do(int fd) logthing(LOGTHING_INFO, "Fetching %s, result: %d", search, - fetch_key_text(search, &key)); + dbctx->fetch_key_text(dbctx, + search, &key)); if (key != NULL) { storebuf.size = 8192; storebuf.buffer = malloc(8192); @@ -170,6 +326,7 @@ int sock_do(int fd) write(fd, &storebuf.offset, sizeof(storebuf.offset)); } + free(search); } break; case KEYD_CMD_STORE: @@ -200,7 +357,7 @@ int sock_do(int fd) &packets, 0); parse_keys(packets, &key); - store_key(key, false, false); + dbctx->store_key(dbctx, key, false, false); free_packet_list(packets); packets = NULL; free_publickey(key); @@ -219,9 +376,11 @@ int sock_do(int fd) } if (ret == 0) { logthing(LOGTHING_INFO, - "Deleting 0x%llX, result: %d", + "Deleting 0x%" PRIX64 + ", result: %d", keyid, - delete_key(keyid, false)); + dbctx->delete_key(dbctx, + keyid, false)); } break; case KEYD_CMD_GETFULLKEYID: @@ -232,16 +391,87 @@ int sock_do(int fd) ret = 1; } if (ret == 0) { - keyid = getfullkeyid(keyid); + keyid = dbctx->getfullkeyid(dbctx, keyid); + cmd = sizeof(keyid); + write(fd, &cmd, sizeof(cmd)); write(fd, &keyid, sizeof(keyid)); } break; + case KEYD_CMD_KEYITER: + cmd = KEYD_REPLY_OK; + write(fd, &cmd, sizeof(cmd)); + dbctx->iterate_keys(dbctx, iteratefunc, + &fd); + bytes = 0; + write(fd, &bytes, sizeof(bytes)); + break; case KEYD_CMD_CLOSE: + cmd = KEYD_REPLY_OK; + write(fd, &cmd, sizeof(cmd)); ret = 1; break; case KEYD_CMD_QUIT: + cmd = KEYD_REPLY_OK; + write(fd, &cmd, sizeof(cmd)); + logthing(LOGTHING_NOTICE, + "Exiting due to quit request."); + ret = 1; trytocleanup(); break; + case KEYD_CMD_STATS: + cmd = KEYD_REPLY_OK; + write(fd, &cmd, sizeof(cmd)); + cmd = sizeof(*stats); + write(fd, &cmd, sizeof(cmd)); + write(fd, stats, + sizeof(*stats)); + break; + case KEYD_CMD_GET_SKSHASH: + cmd = KEYD_REPLY_OK; + write(fd, &cmd, sizeof(cmd)); + bytes = read(fd, hash.hash, sizeof(hash.hash)); + if (bytes != sizeof(hash.hash)) { + ret = 1; + } + storebuf.offset = 0; + if (ret == 0) { + logthing(LOGTHING_INFO, + "Fetching by hash" + ", result: %d", + dbctx->fetch_key_skshash(dbctx, + &hash, &key)); + if (key != NULL) { + 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); + write(fd, &storebuf.offset, + sizeof(storebuf.offset)); + write(fd, storebuf.buffer, + storebuf.offset); + + free(storebuf.buffer); + storebuf.buffer = NULL; + storebuf.size = storebuf.offset = 0; + free_packet_list(packets); + packets = list_end = NULL; + free_publickey(key); + key = NULL; + } else { + write(fd, &storebuf.offset, + sizeof(storebuf.offset)); + } + } + break; + default: logthing(LOGTHING_ERROR, "Got unknown command: %d", cmd); @@ -255,7 +485,8 @@ int sock_do(int fd) int sock_close(int fd) { - return shutdown(fd, SHUT_RDWR); + shutdown(fd, SHUT_RDWR); + return close(fd); } int sock_accept(int fd) @@ -268,50 +499,142 @@ int sock_accept(int fd) socklen = sizeof(sock); srv = accept(fd, (struct sockaddr *) &sock, &socklen); if (srv != -1) { - ret = fcntl(srv, F_SETFD, 1); + ret = fcntl(srv, F_SETFD, FD_CLOEXEC); } if (ret != -1) { - while (!sock_do(srv)) ; - sock_close(srv); + stats->connects++; } - return 1; + 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; + int fd = -1, maxfd, i, clients[MAX_CLIENTS]; fd_set rfds; char sockname[1024]; + char *configfile = NULL; + bool foreground = false; + int optchar; + struct onak_dbctx *dbctx; + + while ((optchar = getopt(argc, argv, "c:fh")) != -1 ) { + switch (optchar) { + case 'c': + configfile = strdup(optarg); + break; + case 'f': + foreground = true; + break; + case 'h': + default: + usage(); + break; + } + } - readconfig(NULL); + 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, 1023, "%s/%s", config.db_dir, KEYD_SOCKET); fd = sock_init(sockname); if (fd != -1) { FD_ZERO(&rfds); FD_SET(fd, &rfds); + maxfd = fd; + memset(clients, -1, sizeof (clients)); - initdb(false); + dbctx = config.dbinit(false); logthing(LOGTHING_NOTICE, "Accepting connections."); - while (!cleanup() && select(fd + 1, &rfds, NULL, NULL, NULL) != -1) { - logthing(LOGTHING_INFO, "Accepted connection."); - sock_accept(fd); + 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]; + } + } } - cleanupdb(); + dbctx->cleanupdb(dbctx); sock_close(fd); unlink(sockname); } + free(stats); + cleanuplogthing(); cleanupconfig(); - + return(EXIT_SUCCESS); }