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