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