2 * keyd.c - key retrieval daemon
4 * Copyright 2004,2011 Jonathan McDowell <noodles@earth.li>
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the Free
8 * Software Foundation; version 2 of the License.
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
15 * You should have received a copy of the GNU General Public License along with
16 * this program; if not, write to the Free Software Foundation, Inc., 51
17 * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
29 #include <sys/select.h>
30 #include <sys/socket.h>
31 #include <sys/types.h>
39 #include <systemd/sd-daemon.h>
42 #include "charfuncs.h"
47 #include "keystructs.h"
50 #include "onak-conf.h"
54 /* Maximum number of clients we're prepared to accept at once */
55 #define MAX_CLIENTS 16
58 static bool using_socket_activation = false;
61 static struct keyd_stats *stats;
63 static void daemonize(void)
70 logthing(LOGTHING_CRITICAL,
71 "Failed to fork into background: %d (%s)",
76 logthing(LOGTHING_INFO, "Backgrounded as pid %d.", pid);
81 logthing(LOGTHING_CRITICAL,
82 "Couldn't set process group leader: %d (%s)",
88 if (!freopen("/dev/null", "r", stdin)) {
89 logthing(LOGTHING_CRITICAL,
90 "Couldn't reopen stdin to NULL: %d (%s)",
95 if (!freopen("/dev/null", "w", stdout)) {
96 logthing(LOGTHING_CRITICAL,
97 "Couldn't reopen stdout to NULL: %d (%s)",
102 if (!freopen("/dev/null", "w", stderr)) {
103 logthing(LOGTHING_CRITICAL,
104 "Couldn't reopen stderr to NULL: %d (%s)",
113 static bool keyd_write_key(int fd, struct openpgp_publickey *key)
115 struct openpgp_packet_list *packets = NULL;
116 struct openpgp_packet_list *list_end = NULL;
117 struct buffer_ctx storebuf;
122 storebuf.size = 8192;
123 storebuf.buffer = malloc(8192);
125 flatten_publickey(key,
128 write_openpgp_stream(buffer_putchar,
131 logthing(LOGTHING_TRACE,
134 written = write(fd, &storebuf.offset,
135 sizeof(storebuf.offset));
139 written = write(fd, storebuf.buffer,
141 if (written != storebuf.offset) {
146 free(storebuf.buffer);
147 storebuf.buffer = NULL;
148 storebuf.size = storebuf.offset = 0;
149 free_packet_list(packets);
150 packets = list_end = NULL;
155 static bool keyd_write_reply(int fd, enum keyd_reply _reply)
157 uint32_t reply = _reply;
161 written = write(fd, &reply, sizeof(reply));
162 if (written != sizeof(reply)) {
169 static bool keyd_write_size(int fd, size_t size)
174 written = write(fd, &size, sizeof(size));
175 if (written != sizeof(size)) {
182 static void iteratefunc(void *ctx, struct openpgp_publickey *key)
184 int *fd = (int *) ctx;
188 get_keyid(key, &keyid);
189 logthing(LOGTHING_TRACE,
190 "Iterating over 0x%016" PRIX64 ".",
193 keyd_write_key(*fd, key);
199 static int sock_init(const char *sockname)
201 struct sockaddr_un sock;
207 n = sd_listen_fds(0);
209 logthing(LOGTHING_ERROR,
210 "Too many file descriptors received from systemd.");
212 fd = SD_LISTEN_FDS_START + 0;
213 if (sd_is_socket_unix(fd, SOCK_STREAM, 1, NULL, 0) <= 0) {
214 logthing(LOGTHING_ERROR,
215 "systemd passed an invalid socket.");
218 using_socket_activation = true;
221 fd = socket(PF_UNIX, SOCK_STREAM, 0);
223 ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
227 sock.sun_family = AF_UNIX;
228 strncpy(sock.sun_path, sockname,
229 sizeof(sock.sun_path) - 1);
231 ret = bind(fd, (struct sockaddr *) &sock,
249 static int sock_do(struct onak_dbctx *dbctx, int fd)
251 uint32_t cmd = KEYD_CMD_UNKNOWN;
257 struct openpgp_publickey *key = NULL;
258 struct openpgp_packet_list *packets = NULL;
259 struct buffer_ctx storebuf;
261 struct openpgp_fingerprint fingerprint;
264 * Get the command from the client.
266 bytes = read(fd, &cmd, sizeof(cmd));
268 logthing(LOGTHING_DEBUG, "Read %d bytes, command: %d", bytes, cmd);
270 if (bytes != sizeof(cmd)) {
275 if (cmd < KEYD_CMD_LAST) {
276 stats->command_stats[cmd]++;
278 stats->command_stats[KEYD_CMD_UNKNOWN]++;
281 case KEYD_CMD_VERSION:
282 if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
286 cmd = sizeof(keyd_version);
287 bytes = write(fd, &cmd, sizeof(cmd));
288 if (bytes != sizeof(cmd)) {
293 bytes = write(fd, &keyd_version,
294 sizeof(keyd_version));
295 if (bytes != sizeof(keyd_version)) {
300 case KEYD_CMD_GET_ID:
301 if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
305 bytes = read(fd, &keyid, sizeof(keyid));
306 if (bytes != sizeof(keyid)) {
311 logthing(LOGTHING_INFO,
312 "Fetching 0x%" PRIX64
315 dbctx->fetch_key_id(dbctx,
319 keyd_write_key(fd, key);
323 if (!keyd_write_size(fd, 0)) {
329 case KEYD_CMD_GET_FP:
330 if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
334 if ((read(fd, &bytes, 1) != 1) ||
335 (bytes > MAX_FINGERPRINT_LEN)) {
338 fingerprint.length = bytes;
339 bytes = read(fd, fingerprint.fp,
341 if (bytes != fingerprint.length) {
347 logthing(LOGTHING_INFO,
348 "Fetching by fingerprint"
350 dbctx->fetch_key_fp(dbctx,
354 keyd_write_key(fd, key);
358 if (!keyd_write_size(fd, 0)) {
365 case KEYD_CMD_GET_TEXT:
366 if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
370 bytes = read(fd, &count, sizeof(count));
371 if (bytes != sizeof(count)) {
376 search = malloc(count+1);
377 bytes = read(fd, search, count);
378 if (bytes != count) {
384 logthing(LOGTHING_INFO,
385 "Fetching %s, result: %d",
387 dbctx->fetch_key_text(dbctx,
390 keyd_write_key(fd, key);
394 if (!keyd_write_size(fd, 0)) {
402 case KEYD_CMD_UPDATE:
403 if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
407 bytes = read(fd, &storebuf.size,
408 sizeof(storebuf.size));
409 logthing(LOGTHING_TRACE, "Reading %d bytes.",
411 if (bytes != sizeof(storebuf.size)) {
415 if (ret == 0 && storebuf.size > 0) {
416 storebuf.buffer = malloc(storebuf.size);
419 while (bytes >= 0 && count < storebuf.size) {
421 &storebuf.buffer[count],
422 storebuf.size - count);
423 logthing(LOGTHING_TRACE,
428 read_openpgp_stream(buffer_fetchchar,
432 parse_keys(packets, &key);
433 dbctx->store_key(dbctx, key, false,
434 (cmd == KEYD_CMD_UPDATE));
435 free_packet_list(packets);
439 free(storebuf.buffer);
440 storebuf.buffer = NULL;
441 storebuf.size = storebuf.offset = 0;
444 case KEYD_CMD_DELETE:
445 if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
449 bytes = read(fd, &keyid, sizeof(keyid));
450 if (bytes != sizeof(keyid)) {
455 logthing(LOGTHING_INFO,
456 "Deleting 0x%" PRIX64
459 dbctx->delete_key(dbctx,
463 case KEYD_CMD_GETFULLKEYID:
464 if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
468 bytes = read(fd, &keyid, sizeof(keyid));
469 if (bytes != sizeof(keyid)) {
474 keyid = dbctx->getfullkeyid(dbctx, keyid);
476 bytes = write(fd, &cmd, sizeof(cmd));
477 if (bytes != sizeof(cmd)) {
482 bytes = write(fd, &keyid, sizeof(keyid));
483 if (bytes != sizeof(keyid)) {
488 case KEYD_CMD_KEYITER:
489 if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
493 dbctx->iterate_keys(dbctx, iteratefunc,
495 if (!keyd_write_size(fd, 0)) {
501 /* We're going to close the FD even if this fails */
502 (void) keyd_write_reply(fd, KEYD_REPLY_OK);
506 /* We're going to quit even if this fails */
507 (void) keyd_write_reply(fd, KEYD_REPLY_OK);
508 logthing(LOGTHING_NOTICE,
509 "Exiting due to quit request.");
514 if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
518 cmd = sizeof(*stats);
519 bytes = write(fd, &cmd, sizeof(cmd));
520 if (bytes != sizeof(cmd)) {
525 bytes = write(fd, stats, sizeof(*stats));
526 if (bytes != sizeof(*stats)) {
531 case KEYD_CMD_GET_SKSHASH:
532 if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
536 bytes = read(fd, hash.hash, sizeof(hash.hash));
537 if (bytes != sizeof(hash.hash)) {
542 logthing(LOGTHING_INFO,
545 dbctx->fetch_key_skshash(dbctx,
548 keyd_write_key(fd, key);
552 if (!keyd_write_size(fd, 0)) {
560 logthing(LOGTHING_ERROR, "Got unknown command: %d",
562 if (!keyd_write_reply(fd, KEYD_REPLY_UNKNOWN_CMD)) {
571 static int sock_close(int fd)
573 shutdown(fd, SHUT_RDWR);
577 static int sock_accept(int fd)
579 struct sockaddr_un sock;
584 socklen = sizeof(sock);
585 srv = accept(fd, (struct sockaddr *) &sock, &socklen);
587 ret = fcntl(srv, F_SETFD, FD_CLOEXEC);
597 static void usage(void)
599 puts("keyd " ONAK_VERSION " - backend key serving daemon for the "
600 "onak PGP keyserver.\n");
602 puts("\tkeyd [options]\n");
603 puts("\tOptions:\n:");
604 puts("-c <file> - use <file> as the config file");
605 puts("-f - run in the foreground");
606 puts("-h - show this help text");
610 int main(int argc, char *argv[])
612 int fd = -1, maxfd, i, clients[MAX_CLIENTS];
613 fd_set rfds = { 0 }; /* Avoid scan-build false report for FD_SET */
615 char *configfile = NULL;
616 bool foreground = false;
618 struct onak_dbctx *dbctx;
620 while ((optchar = getopt(argc, argv, "c:fh")) != -1 ) {
623 if (configfile != NULL) {
626 configfile = strdup(optarg);
638 readconfig(configfile);
641 initlogthing("keyd", config.logfile);
642 config.use_keyd = false;
649 signal(SIGPIPE, SIG_IGN);
652 stats = calloc(1, sizeof(*stats));
654 logthing(LOGTHING_ERROR,
655 "Couldn't allocate memory for stats structure.");
658 stats->started = time(NULL);
660 snprintf(sockname, sizeof(sockname) - 1, "%s/%s",
661 config.sock_dir, KEYD_SOCKET);
662 fd = sock_init(sockname);
668 memset(clients, -1, sizeof (clients));
670 dbctx = config.dbinit(config.backend, false);
672 logthing(LOGTHING_NOTICE, "Accepting connections.");
673 while (!cleanup() && select(maxfd + 1, &rfds, NULL, NULL, NULL) != -1) {
675 * Deal with existing clients first; if we're at our
676 * connection limit then processing them might free
677 * things up and let us accept the next client below.
679 for (i = 0; i < MAX_CLIENTS; i++) {
680 if (clients[i] != -1 &&
681 FD_ISSET(clients[i], &rfds)) {
682 logthing(LOGTHING_DEBUG,
683 "Handling connection for client %d.", i);
684 if (sock_do(dbctx, clients[i])) {
685 sock_close(clients[i]);
687 logthing(LOGTHING_DEBUG,
688 "Closed connection for client %d.", i);
693 * Check if we have a new incoming connection to accept.
695 if (FD_ISSET(fd, &rfds)) {
696 for (i = 0; i < MAX_CLIENTS; i++) {
697 if (clients[i] == -1) {
701 if (i < MAX_CLIENTS) {
702 logthing(LOGTHING_INFO,
703 "Accepted connection %d.", i);
704 clients[i] = sock_accept(fd);
710 for (i = 0; i < MAX_CLIENTS; i++) {
711 if (clients[i] != -1) {
712 FD_SET(clients[i], &rfds);
713 maxfd = (maxfd > clients[i]) ?
718 dbctx->cleanupdb(dbctx);
720 if (!using_socket_activation) {
734 return(EXIT_SUCCESS);