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