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.
27 #include <sys/select.h>
28 #include <sys/socket.h>
29 #include <sys/types.h>
34 #include "charfuncs.h"
39 #include "keystructs.h"
42 #include "onak-conf.h"
46 /* Maximum number of clients we're prepared to accept at once */
47 #define MAX_CLIENTS 16
49 static struct keyd_stats *stats;
58 logthing(LOGTHING_CRITICAL,
59 "Failed to fork into background: %d (%s)",
64 logthing(LOGTHING_INFO, "Backgrounded as pid %d.", pid);
69 logthing(LOGTHING_CRITICAL,
70 "Couldn't set process group leader: %d (%s)",
76 if (!freopen("/dev/null", "r", stdin)) {
77 logthing(LOGTHING_CRITICAL,
78 "Couldn't reopen stdin to NULL: %d (%s)",
83 if (!freopen("/dev/null", "w", stdout)) {
84 logthing(LOGTHING_CRITICAL,
85 "Couldn't reopen stdout to NULL: %d (%s)",
90 if (!freopen("/dev/null", "w", stderr)) {
91 logthing(LOGTHING_CRITICAL,
92 "Couldn't reopen stderr to NULL: %d (%s)",
101 void iteratefunc(void *ctx, struct openpgp_publickey *key)
103 struct openpgp_packet_list *packets = NULL;
104 struct openpgp_packet_list *list_end = NULL;
105 struct buffer_ctx storebuf;
107 int *fd = (int *) ctx;
112 storebuf.size = 8192;
113 storebuf.buffer = malloc(8192);
115 get_keyid(key, &keyid);
116 logthing(LOGTHING_TRACE,
117 "Iterating over 0x%016" PRIX64 ".",
120 flatten_publickey(key,
123 write_openpgp_stream(buffer_putchar,
126 logthing(LOGTHING_TRACE,
129 ret = write(*fd, &storebuf.offset,
130 sizeof(storebuf.offset));
132 write(*fd, storebuf.buffer,
136 free(storebuf.buffer);
137 storebuf.buffer = NULL;
138 storebuf.size = storebuf.offset = 0;
139 free_packet_list(packets);
140 packets = list_end = NULL;
146 int sock_init(const char *sockname)
148 struct sockaddr_un sock;
152 fd = socket(PF_UNIX, SOCK_STREAM, 0);
154 ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
158 sock.sun_family = AF_UNIX;
159 strncpy(sock.sun_path, sockname, sizeof(sock.sun_path) - 1);
161 ret = bind(fd, (struct sockaddr *) &sock, sizeof(sock));
175 int sock_do(struct onak_dbctx *dbctx, int fd)
177 uint32_t cmd = KEYD_CMD_UNKNOWN;
183 struct openpgp_publickey *key = NULL;
184 struct openpgp_packet_list *packets = NULL;
185 struct openpgp_packet_list *list_end = NULL;
186 struct buffer_ctx storebuf;
188 struct openpgp_fingerprint fingerprint;
191 * Get the command from the client.
193 bytes = read(fd, &cmd, sizeof(cmd));
195 logthing(LOGTHING_DEBUG, "Read %d bytes, command: %d", bytes, cmd);
197 if (bytes != sizeof(cmd)) {
202 if (cmd < KEYD_CMD_LAST) {
203 stats->command_stats[cmd]++;
205 stats->command_stats[KEYD_CMD_UNKNOWN]++;
208 case KEYD_CMD_VERSION:
210 write(fd, &cmd, sizeof(cmd));
211 cmd = sizeof(keyd_version);
212 write(fd, &cmd, sizeof(cmd));
213 write(fd, &keyd_version, sizeof(keyd_version));
215 case KEYD_CMD_GET_ID:
217 write(fd, &cmd, sizeof(cmd));
218 bytes = read(fd, &keyid, sizeof(keyid));
219 if (bytes != sizeof(keyid)) {
224 logthing(LOGTHING_INFO,
225 "Fetching 0x%" PRIX64
228 dbctx->fetch_key_id(dbctx,
232 storebuf.size = 8192;
233 storebuf.buffer = malloc(8192);
235 flatten_publickey(key,
238 write_openpgp_stream(buffer_putchar,
241 logthing(LOGTHING_TRACE,
244 write(fd, &storebuf.offset,
245 sizeof(storebuf.offset));
246 write(fd, storebuf.buffer,
249 free(storebuf.buffer);
250 storebuf.buffer = NULL;
251 storebuf.size = storebuf.offset = 0;
252 free_packet_list(packets);
253 packets = list_end = NULL;
257 write(fd, &storebuf.offset,
258 sizeof(storebuf.offset));
262 case KEYD_CMD_GET_FP:
264 write(fd, &cmd, sizeof(cmd));
266 if (bytes > MAX_FINGERPRINT_LEN) {
269 fingerprint.length = bytes;
270 read(fd, fingerprint.fp, bytes);
274 logthing(LOGTHING_INFO,
275 "Fetching by fingerprint"
277 dbctx->fetch_key_fp(dbctx,
281 storebuf.size = 8192;
282 storebuf.buffer = malloc(8192);
284 flatten_publickey(key,
287 write_openpgp_stream(buffer_putchar,
290 logthing(LOGTHING_TRACE,
293 write(fd, &storebuf.offset,
294 sizeof(storebuf.offset));
295 write(fd, storebuf.buffer,
298 free(storebuf.buffer);
299 storebuf.buffer = NULL;
300 storebuf.size = storebuf.offset = 0;
301 free_packet_list(packets);
302 packets = list_end = NULL;
306 write(fd, &storebuf.offset,
307 sizeof(storebuf.offset));
312 case KEYD_CMD_GET_TEXT:
314 write(fd, &cmd, sizeof(cmd));
315 bytes = read(fd, &count, sizeof(count));
316 if (bytes != sizeof(count)) {
321 search = malloc(count+1);
322 read(fd, search, count);
324 logthing(LOGTHING_INFO,
325 "Fetching %s, result: %d",
327 dbctx->fetch_key_text(dbctx,
330 storebuf.size = 8192;
331 storebuf.buffer = malloc(8192);
333 flatten_publickey(key,
336 write_openpgp_stream(buffer_putchar,
339 logthing(LOGTHING_TRACE,
342 write(fd, &storebuf.offset,
343 sizeof(storebuf.offset));
344 write(fd, storebuf.buffer,
347 free(storebuf.buffer);
348 storebuf.buffer = NULL;
349 storebuf.size = storebuf.offset = 0;
350 free_packet_list(packets);
351 packets = list_end = NULL;
355 write(fd, &storebuf.offset,
356 sizeof(storebuf.offset));
363 write(fd, &cmd, sizeof(cmd));
365 bytes = read(fd, &storebuf.size,
366 sizeof(storebuf.size));
367 logthing(LOGTHING_TRACE, "Reading %d bytes.",
369 if (bytes != sizeof(storebuf.size)) {
372 if (ret == 0 && storebuf.size > 0) {
373 storebuf.buffer = malloc(storebuf.size);
375 while (bytes >= 0 && count < storebuf.size) {
377 &storebuf.buffer[count],
378 storebuf.size - count);
379 logthing(LOGTHING_TRACE,
384 read_openpgp_stream(buffer_fetchchar,
388 parse_keys(packets, &key);
389 dbctx->store_key(dbctx, key, false, false);
390 free_packet_list(packets);
394 free(storebuf.buffer);
395 storebuf.buffer = NULL;
396 storebuf.size = storebuf.offset = 0;
399 case KEYD_CMD_DELETE:
401 write(fd, &cmd, sizeof(cmd));
402 bytes = read(fd, &keyid, sizeof(keyid));
403 if (bytes != sizeof(keyid)) {
407 logthing(LOGTHING_INFO,
408 "Deleting 0x%" PRIX64
411 dbctx->delete_key(dbctx,
415 case KEYD_CMD_GETFULLKEYID:
417 write(fd, &cmd, sizeof(cmd));
418 bytes = read(fd, &keyid, sizeof(keyid));
419 if (bytes != sizeof(keyid)) {
423 keyid = dbctx->getfullkeyid(dbctx, keyid);
425 write(fd, &cmd, sizeof(cmd));
426 write(fd, &keyid, sizeof(keyid));
429 case KEYD_CMD_KEYITER:
431 write(fd, &cmd, sizeof(cmd));
432 dbctx->iterate_keys(dbctx, iteratefunc,
435 write(fd, &bytes, sizeof(bytes));
439 write(fd, &cmd, sizeof(cmd));
444 write(fd, &cmd, sizeof(cmd));
445 logthing(LOGTHING_NOTICE,
446 "Exiting due to quit request.");
452 write(fd, &cmd, sizeof(cmd));
453 cmd = sizeof(*stats);
454 write(fd, &cmd, sizeof(cmd));
458 case KEYD_CMD_GET_SKSHASH:
460 write(fd, &cmd, sizeof(cmd));
461 bytes = read(fd, hash.hash, sizeof(hash.hash));
462 if (bytes != sizeof(hash.hash)) {
467 logthing(LOGTHING_INFO,
470 dbctx->fetch_key_skshash(dbctx,
473 storebuf.size = 8192;
474 storebuf.buffer = malloc(8192);
476 flatten_publickey(key,
479 write_openpgp_stream(buffer_putchar,
482 logthing(LOGTHING_TRACE,
485 write(fd, &storebuf.offset,
486 sizeof(storebuf.offset));
487 write(fd, storebuf.buffer,
490 free(storebuf.buffer);
491 storebuf.buffer = NULL;
492 storebuf.size = storebuf.offset = 0;
493 free_packet_list(packets);
494 packets = list_end = NULL;
498 write(fd, &storebuf.offset,
499 sizeof(storebuf.offset));
505 logthing(LOGTHING_ERROR, "Got unknown command: %d",
507 cmd = KEYD_REPLY_UNKNOWN_CMD;
508 write(fd, &cmd, sizeof(cmd));
515 int sock_close(int fd)
517 shutdown(fd, SHUT_RDWR);
521 int sock_accept(int fd)
523 struct sockaddr_un sock;
528 socklen = sizeof(sock);
529 srv = accept(fd, (struct sockaddr *) &sock, &socklen);
531 ret = fcntl(srv, F_SETFD, FD_CLOEXEC);
541 static void usage(void)
543 puts("keyd " ONAK_VERSION " - backend key serving daemon for the "
544 "onak PGP keyserver.\n");
546 puts("\tkeyd [options]\n");
547 puts("\tOptions:\n:");
548 puts("-c <file> - use <file> as the config file");
549 puts("-f - run in the foreground");
550 puts("-h - show this help text");
554 int main(int argc, char *argv[])
556 int fd = -1, maxfd, i, clients[MAX_CLIENTS];
559 char *configfile = NULL;
560 bool foreground = false;
562 struct onak_dbctx *dbctx;
564 while ((optchar = getopt(argc, argv, "c:fh")) != -1 ) {
567 if (configfile != NULL) {
570 configfile = strdup(optarg);
582 readconfig(configfile);
585 initlogthing("keyd", config.logfile);
586 config.use_keyd = false;
593 signal(SIGPIPE, SIG_IGN);
596 stats = calloc(1, sizeof(*stats));
598 logthing(LOGTHING_ERROR,
599 "Couldn't allocate memory for stats structure.");
602 stats->started = time(NULL);
604 snprintf(sockname, 1023, "%s/%s", config.db_dir, KEYD_SOCKET);
605 fd = sock_init(sockname);
611 memset(clients, -1, sizeof (clients));
613 dbctx = config.dbinit(false);
615 logthing(LOGTHING_NOTICE, "Accepting connections.");
616 while (!cleanup() && select(maxfd + 1, &rfds, NULL, NULL, NULL) != -1) {
618 * Deal with existing clients first; if we're at our
619 * connection limit then processing them might free
620 * things up and let us accept the next client below.
622 for (i = 0; i < MAX_CLIENTS; i++) {
623 if (clients[i] != -1 &&
624 FD_ISSET(clients[i], &rfds)) {
625 logthing(LOGTHING_DEBUG,
626 "Handling connection for client %d.", i);
627 if (sock_do(dbctx, clients[i])) {
628 sock_close(clients[i]);
630 logthing(LOGTHING_DEBUG,
631 "Closed connection for client %d.", i);
636 * Check if we have a new incoming connection to accept.
638 if (FD_ISSET(fd, &rfds)) {
639 for (i = 0; i < MAX_CLIENTS; i++) {
640 if (clients[i] == -1) {
644 if (i < MAX_CLIENTS) {
645 logthing(LOGTHING_INFO,
646 "Accepted connection %d.", i);
647 clients[i] = sock_accept(fd);
653 for (i = 0; i < MAX_CLIENTS; i++) {
654 if (clients[i] != -1) {
655 FD_SET(clients[i], &rfds);
656 maxfd = (maxfd > clients[i]) ?
661 dbctx->cleanupdb(dbctx);
671 return(EXIT_SUCCESS);