]> the.earth.li Git - onak.git/blob - keyd.c
Cleanup various includes
[onak.git] / keyd.c
1 /*
2  * keyd.c - key retrieval daemon
3  *
4  * Copyright 2004,2011 Jonathan McDowell <noodles@earth.li>
5  *
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.
9  *
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
13  * more details.
14  *
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.
18  */
19
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <getopt.h>
23 #include <inttypes.h>
24 #include <signal.h>
25 #include <stdbool.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/select.h>
30 #include <sys/socket.h>
31 #include <sys/types.h>
32 #include <sys/un.h>
33 #include <time.h>
34 #include <unistd.h>
35
36 #include "config.h"
37
38 #ifdef HAVE_SYSTEMD
39 #include <systemd/sd-daemon.h>
40 #endif
41
42 #include "charfuncs.h"
43 #include "cleanup.h"
44 #include "keyd.h"
45 #include "keydb.h"
46 #include "keyid.h"
47 #include "keystructs.h"
48 #include "log.h"
49 #include "mem.h"
50 #include "onak-conf.h"
51 #include "parsekey.h"
52 #include "version.h"
53
54 /* Maximum number of clients we're prepared to accept at once */
55 #define MAX_CLIENTS 16
56
57 #ifdef HAVE_SYSTEMD
58 static bool using_socket_activation = false;
59 #endif
60
61 static struct keyd_stats *stats;
62
63 static void daemonize(void)
64 {
65         pid_t pid;
66
67         pid = fork();
68
69         if (pid < 0) {
70                 logthing(LOGTHING_CRITICAL,
71                         "Failed to fork into background: %d (%s)",
72                         errno,
73                         strerror(errno));
74                 exit(EXIT_FAILURE);
75         } else if (pid > 0) {
76                 logthing(LOGTHING_INFO, "Backgrounded as pid %d.", pid);
77                 exit(EXIT_SUCCESS);
78         }
79
80         if (setsid() == -1) {
81                 logthing(LOGTHING_CRITICAL,
82                         "Couldn't set process group leader: %d (%s)",
83                         errno,
84                         strerror(errno));
85                 exit(EXIT_FAILURE);
86         }
87
88         if (!freopen("/dev/null", "r", stdin)) {
89                 logthing(LOGTHING_CRITICAL,
90                         "Couldn't reopen stdin to NULL: %d (%s)",
91                         errno,
92                         strerror(errno));
93                 exit(EXIT_FAILURE);
94         }
95         if (!freopen("/dev/null", "w", stdout)) {
96                 logthing(LOGTHING_CRITICAL,
97                         "Couldn't reopen stdout to NULL: %d (%s)",
98                         errno,
99                         strerror(errno));
100                 exit(EXIT_FAILURE);
101         }
102         if (!freopen("/dev/null", "w", stderr)) {
103                 logthing(LOGTHING_CRITICAL,
104                         "Couldn't reopen stderr to NULL: %d (%s)",
105                         errno,
106                         strerror(errno));
107                 exit(EXIT_FAILURE);
108         }
109
110         return;
111 }
112
113 static bool keyd_write_key(int fd, struct openpgp_publickey *key)
114 {
115         struct openpgp_packet_list *packets = NULL;
116         struct openpgp_packet_list *list_end = NULL;
117         struct buffer_ctx           storebuf;
118         ssize_t written;
119         bool    ok = true;
120
121         storebuf.offset = 0;
122         storebuf.size = 8192;
123         storebuf.buffer = malloc(8192);
124
125         flatten_publickey(key,
126                                 &packets,
127                                 &list_end);
128         write_openpgp_stream(buffer_putchar,
129                                 &storebuf,
130                                 packets);
131         logthing(LOGTHING_TRACE,
132                                 "Sending %d bytes.",
133                                 storebuf.offset);
134         written = write(fd, &storebuf.offset,
135                         sizeof(storebuf.offset));
136         if (written == 0) {
137                 ok = false;
138         } else {
139                 written = write(fd, storebuf.buffer,
140                         storebuf.offset);
141                 if (written != storebuf.offset) {
142                         ok = false;
143                 }
144         }
145
146         free(storebuf.buffer);
147         storebuf.buffer = NULL;
148         storebuf.size = storebuf.offset = 0;
149         free_packet_list(packets);
150         packets = list_end = NULL;
151
152         return (ok);
153 }
154
155 static bool keyd_write_reply(int fd, enum keyd_reply _reply)
156 {
157         uint32_t reply = _reply;
158         ssize_t written;
159         bool ok = true;
160
161         written = write(fd, &reply, sizeof(reply));
162         if (written != sizeof(reply)) {
163                 ok = false;
164         }
165
166         return (ok);
167 }
168
169 static bool keyd_write_size(int fd, size_t size)
170 {
171         ssize_t written;
172         bool ok = true;
173
174         written = write(fd, &size, sizeof(size));
175         if (written != sizeof(size)) {
176                 ok = false;
177         }
178
179         return (ok);
180 }
181
182 static void iteratefunc(void *ctx, struct openpgp_publickey *key)
183 {
184         int      *fd = (int *) ctx;
185         uint64_t  keyid;
186
187         if (key != NULL) {
188                 get_keyid(key, &keyid);
189                 logthing(LOGTHING_TRACE,
190                                 "Iterating over 0x%016" PRIX64 ".",
191                                 keyid);
192
193                 keyd_write_key(*fd, key);
194         }
195
196         return;
197 }
198
199 static int sock_init(const char *sockname)
200 {
201         struct sockaddr_un sock;
202         int                fd = -1;
203         int                ret = -1;
204 #ifdef HAVE_SYSTEMD
205         int                n;
206
207         n = sd_listen_fds(0);
208         if (n > 1) {
209                 logthing(LOGTHING_ERROR,
210                         "Too many file descriptors received from systemd.");
211         } else if (n == 1) {
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.");
216                         fd = -1;
217                 }
218                 using_socket_activation = true;
219         } else {
220 #endif
221                 fd = socket(PF_UNIX, SOCK_STREAM, 0);
222                 if (fd != -1) {
223                         ret = fcntl(fd, F_SETFD, FD_CLOEXEC);
224                 }
225
226                 if (ret != -1) {
227                         sock.sun_family = AF_UNIX;
228                         strncpy(sock.sun_path, sockname,
229                                         sizeof(sock.sun_path) - 1);
230                         unlink(sockname);
231                         ret = bind(fd, (struct sockaddr *) &sock,
232                                         sizeof(sock));
233                 }
234
235                 if (ret != -1) {
236                         ret = listen(fd, 5);
237                         if (ret == -1) {
238                                 close(fd);
239                                 fd = -1;
240                         }
241                 }
242 #ifdef HAVE_SYSTEMD
243         }
244 #endif
245
246         return fd;
247 }
248
249 static int sock_do(struct onak_dbctx *dbctx, int fd)
250 {
251         uint32_t cmd = KEYD_CMD_UNKNOWN;
252         ssize_t  bytes = 0;
253         ssize_t  count = 0;
254         int      ret = 0;
255         uint64_t keyid = 0;
256         char     *search = NULL;
257         struct openpgp_publickey *key = NULL;
258         struct openpgp_packet_list *packets = NULL;
259         struct buffer_ctx storebuf;
260         struct skshash hash;
261         struct openpgp_fingerprint fingerprint;
262
263         /*
264          * Get the command from the client.
265          */
266         bytes = read(fd, &cmd, sizeof(cmd));
267
268         logthing(LOGTHING_DEBUG, "Read %d bytes, command: %d", bytes, cmd);
269
270         if (bytes != sizeof(cmd)) {
271                 ret = 1;
272         }
273         
274         if (ret == 0) {
275                 if (cmd < KEYD_CMD_LAST) {
276                         stats->command_stats[cmd]++;
277                 } else {
278                         stats->command_stats[KEYD_CMD_UNKNOWN]++;
279                 }
280                 switch (cmd) {
281                 case KEYD_CMD_VERSION:
282                         if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
283                                 ret = 1;
284                         }
285                         if (ret == 0) {
286                                 cmd = sizeof(keyd_version);
287                                 bytes = write(fd, &cmd, sizeof(cmd));
288                                 if (bytes != sizeof(cmd)) {
289                                         ret = 1;
290                                 }
291                         }
292                         if (ret == 0) {
293                                 bytes = write(fd, &keyd_version,
294                                         sizeof(keyd_version));
295                                 if (bytes != sizeof(keyd_version)) {
296                                         ret = 1;
297                                 }
298                         }
299                         break;
300                 case KEYD_CMD_GET_ID:
301                         if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
302                                 ret = 1;
303                         }
304                         if (ret == 0) {
305                                 bytes = read(fd, &keyid, sizeof(keyid));
306                                 if (bytes != sizeof(keyid)) {
307                                         ret = 1;
308                                 }
309                         }
310                         if (ret == 0) {
311                                 logthing(LOGTHING_INFO,
312                                                 "Fetching 0x%" PRIX64
313                                                 ", result: %d",
314                                                 keyid,
315                                                 dbctx->fetch_key_id(dbctx,
316                                                         keyid,
317                                                         &key, false));
318                                 if (key != NULL) {
319                                         keyd_write_key(fd, key);
320                                         free_publickey(key);
321                                         key = NULL;
322                                 } else {
323                                         if (!keyd_write_size(fd, 0)) {
324                                                 ret = 1;
325                                         }
326                                 }
327                         }
328                         break;
329                 case KEYD_CMD_GET_FP:
330                         if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
331                                 ret = 1;
332                         }
333                         if (ret == 0) {
334                                 if ((read(fd, &bytes, 1) != 1) ||
335                                                 (bytes > MAX_FINGERPRINT_LEN)) {
336                                         ret = 1;
337                                 } else {
338                                         fingerprint.length = bytes;
339                                         bytes = read(fd, fingerprint.fp,
340                                                 fingerprint.length);
341                                         if (bytes != fingerprint.length) {
342                                                 ret = 1;
343                                         }
344                                 }
345                         }
346                         if (ret == 0) {
347                                 logthing(LOGTHING_INFO,
348                                                 "Fetching by fingerprint"
349                                                 ", result: %d",
350                                                 dbctx->fetch_key_fp(dbctx,
351                                                         &fingerprint,
352                                                         &key, false));
353                                 if (key != NULL) {
354                                         keyd_write_key(fd, key);
355                                         free_publickey(key);
356                                         key = NULL;
357                                 } else {
358                                         if (!keyd_write_size(fd, 0)) {
359                                                 ret = 1;
360                                         }
361                                 }
362                         }
363                         break;
364
365                 case KEYD_CMD_GET_TEXT:
366                         if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
367                                 ret = 1;
368                         }
369                         if (ret == 0) {
370                                 bytes = read(fd, &count, sizeof(count));
371                                 if (bytes != sizeof(count)) {
372                                         ret = 1;
373                                 }
374                         }
375                         if (ret == 0) {
376                                 search = malloc(count+1);
377                                 bytes = read(fd, search, count);
378                                 if (bytes != count) {
379                                         ret = 1;
380                                         free(search);
381                                         break;
382                                 }
383                                 search[count] = 0;
384                                 logthing(LOGTHING_INFO,
385                                                 "Fetching %s, result: %d",
386                                                 search,
387                                                 dbctx->fetch_key_text(dbctx,
388                                                         search, &key));
389                                 if (key != NULL) {
390                                         keyd_write_key(fd, key);
391                                         free_publickey(key);
392                                         key = NULL;
393                                 } else {
394                                         if (!keyd_write_size(fd, 0)) {
395                                                 ret = 1;
396                                         }
397                                 }
398                                 free(search);
399                         }
400                         break;
401                 case KEYD_CMD_STORE:
402                 case KEYD_CMD_UPDATE:
403                         if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
404                                 ret = 1;
405                         }
406                         if (ret == 0) {
407                                 bytes = read(fd, &storebuf.size,
408                                         sizeof(storebuf.size));
409                                 logthing(LOGTHING_TRACE, "Reading %d bytes.",
410                                         storebuf.size);
411                                 if (bytes != sizeof(storebuf.size)) {
412                                         ret = 1;
413                                 }
414                         }
415                         if (ret == 0 && storebuf.size > 0) {
416                                 storebuf.buffer = malloc(storebuf.size);
417                                 storebuf.offset = 0;
418                                 bytes = count = 0;
419                                 while (bytes >= 0 && count < storebuf.size) {
420                                         bytes = read(fd,
421                                                 &storebuf.buffer[count],
422                                                 storebuf.size - count);
423                                         logthing(LOGTHING_TRACE,
424                                                         "Read %d bytes.",
425                                                         bytes);
426                                         count += bytes;
427                                 }
428                                 read_openpgp_stream(buffer_fetchchar,
429                                                 &storebuf,
430                                                 &packets,
431                                                 0);
432                                 parse_keys(packets, &key);
433                                 dbctx->store_key(dbctx, key, false,
434                                         (cmd == KEYD_CMD_UPDATE));
435                                 free_packet_list(packets);
436                                 packets = NULL;
437                                 free_publickey(key);
438                                 key = NULL;
439                                 free(storebuf.buffer);
440                                 storebuf.buffer = NULL;
441                                 storebuf.size = storebuf.offset = 0;
442                         }
443                         break;
444                 case KEYD_CMD_DELETE:
445                         if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
446                                 ret = 1;
447                         }
448                         if (ret == 0) {
449                                 bytes = read(fd, &keyid, sizeof(keyid));
450                                 if (bytes != sizeof(keyid)) {
451                                         ret = 1;
452                                 }
453                         }
454                         if (ret == 0) {
455                                 logthing(LOGTHING_INFO,
456                                                 "Deleting 0x%" PRIX64
457                                                 ", result: %d",
458                                                 keyid,
459                                                 dbctx->delete_key(dbctx,
460                                                         keyid, false));
461                         }
462                         break;
463                 case KEYD_CMD_GETFULLKEYID:
464                         if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
465                                 ret = 1;
466                         }
467                         if (ret == 0) {
468                                 bytes = read(fd, &keyid, sizeof(keyid));
469                                 if (bytes != sizeof(keyid)) {
470                                         ret = 1;
471                                 }
472                         }
473                         if (ret == 0) {
474                                 keyid = dbctx->getfullkeyid(dbctx, keyid);
475                                 cmd = sizeof(keyid);
476                                 bytes = write(fd, &cmd, sizeof(cmd));
477                                 if (bytes != sizeof(cmd)) {
478                                         ret = 1;
479                                 }
480                         }
481                         if (ret == 0) {
482                                 bytes = write(fd, &keyid, sizeof(keyid));
483                                 if (bytes != sizeof(keyid)) {
484                                         ret = 1;
485                                 }
486                         }
487                         break;
488                 case KEYD_CMD_KEYITER:
489                         if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
490                                 ret = 1;
491                         }
492                         if (ret == 0) {
493                                 dbctx->iterate_keys(dbctx, iteratefunc,
494                                         &fd);
495                                 if (!keyd_write_size(fd, 0)) {
496                                         ret = 1;
497                                 }
498                         }
499                         break;
500                 case KEYD_CMD_CLOSE:
501                         /* We're going to close the FD even if this fails */
502                         (void) keyd_write_reply(fd, KEYD_REPLY_OK);
503                         ret = 1;
504                         break;
505                 case KEYD_CMD_QUIT:
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.");
510                         ret = 1;
511                         trytocleanup();
512                         break;
513                 case KEYD_CMD_STATS:
514                         if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
515                                 ret = 1;
516                         }
517                         if (ret == 0) {
518                                 cmd = sizeof(*stats);
519                                 bytes = write(fd, &cmd, sizeof(cmd));
520                                 if (bytes != sizeof(cmd)) {
521                                         ret = 1;
522                                 }
523                         }
524                         if (ret == 0) {
525                                 bytes = write(fd, stats, sizeof(*stats));
526                                 if (bytes != sizeof(*stats)) {
527                                         ret = 1;
528                                 }
529                         }
530                         break;
531                 case KEYD_CMD_GET_SKSHASH:
532                         if (!keyd_write_reply(fd, KEYD_REPLY_OK)) {
533                                 ret = 1;
534                         }
535                         if (ret == 0) {
536                                 bytes = read(fd, hash.hash, sizeof(hash.hash));
537                                 if (bytes != sizeof(hash.hash)) {
538                                         ret = 1;
539                                 }
540                         }
541                         if (ret == 0) {
542                                 logthing(LOGTHING_INFO,
543                                                 "Fetching by hash"
544                                                 ", result: %d",
545                                                 dbctx->fetch_key_skshash(dbctx,
546                                                         &hash, &key));
547                                 if (key != NULL) {
548                                         keyd_write_key(fd, key);
549                                         free_publickey(key);
550                                         key = NULL;
551                                 } else {
552                                         if (!keyd_write_size(fd, 0)) {
553                                                 ret = 1;
554                                         }
555                                 }
556                         }
557                         break;
558
559                 default:
560                         logthing(LOGTHING_ERROR, "Got unknown command: %d",
561                                         cmd);
562                         if (!keyd_write_reply(fd, KEYD_REPLY_UNKNOWN_CMD)) {
563                                 ret = 1;
564                         }
565                 }
566         }
567
568         return(ret);
569 }
570
571 static int sock_close(int fd)
572 {
573         shutdown(fd, SHUT_RDWR);
574         return close(fd);
575 }
576
577 static int sock_accept(int fd)
578 {
579         struct sockaddr_un sock;
580         socklen_t          socklen;
581         int    srv = -1;
582         int    ret = -1;
583
584         socklen = sizeof(sock);
585         srv = accept(fd, (struct sockaddr *) &sock, &socklen);
586         if (srv != -1) {
587                 ret = fcntl(srv, F_SETFD, FD_CLOEXEC);
588         }
589
590         if (ret != -1) {
591                 stats->connects++;
592         }
593
594         return (srv);
595 }
596
597 static void usage(void)
598 {
599         puts("keyd " ONAK_VERSION " - backend key serving daemon for the "
600                 "onak PGP keyserver.\n");
601         puts("Usage:\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");
607         exit(EXIT_FAILURE);
608 }
609
610 int main(int argc, char *argv[])
611 {
612         int fd = -1, maxfd, i, clients[MAX_CLIENTS];
613         fd_set rfds = { 0 }; /* Avoid scan-build false report for FD_SET */
614         char sockname[100];
615         char *configfile = NULL;
616         bool foreground = false;
617         int optchar;
618         struct onak_dbctx *dbctx;
619
620         while ((optchar = getopt(argc, argv, "c:fh")) != -1 ) {
621                 switch (optchar) {
622                 case 'c':
623                         if (configfile != NULL) {
624                                 free(configfile);
625                         }
626                         configfile = strdup(optarg);
627                         break;
628                 case 'f':
629                         foreground = true;
630                         break;
631                 case 'h':
632                 default:
633                         usage();
634                         break;
635                 }
636         }
637
638         readconfig(configfile);
639         free(configfile);
640         configfile = NULL;
641         initlogthing("keyd", config.logfile);
642         config.use_keyd = false;
643
644         if (!foreground) {
645                 daemonize();
646         }
647
648         catchsignals();
649         signal(SIGPIPE, SIG_IGN);
650
651
652         stats = calloc(1, sizeof(*stats));
653         if (!stats) {
654                 logthing(LOGTHING_ERROR,
655                         "Couldn't allocate memory for stats structure.");
656                 exit(EXIT_FAILURE);
657         }
658         stats->started = time(NULL);
659
660         snprintf(sockname, sizeof(sockname) - 1, "%s/%s",
661                         config.sock_dir, KEYD_SOCKET);
662         fd = sock_init(sockname);
663
664         if (fd != -1) {
665                 FD_ZERO(&rfds);
666                 FD_SET(fd, &rfds);
667                 maxfd = fd;
668                 memset(clients, -1, sizeof (clients));
669
670                 dbctx = config.dbinit(config.backend, false);
671
672                 logthing(LOGTHING_NOTICE, "Accepting connections.");
673                 while (!cleanup() && select(maxfd + 1, &rfds, NULL, NULL, NULL) != -1) {
674                         /*
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.
678                          */
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]);
686                                                 clients[i] = -1;
687                                                 logthing(LOGTHING_DEBUG,
688                                                         "Closed connection for client %d.", i);
689                                         }
690                                 }
691                         }
692                         /*
693                          * Check if we have a new incoming connection to accept.
694                          */
695                         if (FD_ISSET(fd, &rfds)) {
696                                 for (i = 0; i < MAX_CLIENTS; i++) {
697                                         if (clients[i] == -1) {
698                                                 break;
699                                         }
700                                 }
701                                 if (i < MAX_CLIENTS) {
702                                         logthing(LOGTHING_INFO,
703                                                 "Accepted connection %d.", i);
704                                         clients[i] = sock_accept(fd);
705                                 }
706                         }
707                         FD_ZERO(&rfds);
708                         FD_SET(fd, &rfds);
709                         maxfd = 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]) ?
714                                                         maxfd : clients[i];
715                                 }
716                         }
717                 }
718                 dbctx->cleanupdb(dbctx);
719 #ifdef HAVE_SYSTEMD
720                 if (!using_socket_activation) {
721 #endif
722                         sock_close(fd);
723                         unlink(sockname);
724 #ifdef HAVE_SYSTEMD
725                 }
726 #endif
727         }
728
729         free(stats);
730
731         cleanuplogthing();
732         cleanupconfig();
733
734         return(EXIT_SUCCESS);
735 }