]> the.earth.li Git - onak.git/blob - keyd.c
5cd7b57564968e68680b3562103959638c025ef1
[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         uint8_t  fp[MAX_FINGERPRINT_LEN];
159         char     *search = NULL;
160         struct openpgp_publickey *key = NULL;
161         struct openpgp_packet_list *packets = NULL;
162         struct openpgp_packet_list *list_end = NULL;
163         struct buffer_ctx storebuf;
164         struct skshash hash;
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                                 read(fd, fp, bytes);
246                         }
247                         storebuf.offset = 0;
248                         if (ret == 0) {
249                                 logthing(LOGTHING_INFO,
250                                                 "Fetching by fingerprint"
251                                                 ", result: %d",
252                                                 dbctx->fetch_key_fp(dbctx,
253                                                         fp, bytes,
254                                                         &key, false));
255                                 if (key != NULL) {
256                                         storebuf.size = 8192;
257                                         storebuf.buffer = malloc(8192);
258
259                                         flatten_publickey(key,
260                                                         &packets,
261                                                         &list_end);
262                                         write_openpgp_stream(buffer_putchar,
263                                                         &storebuf,
264                                                         packets);
265                                         logthing(LOGTHING_TRACE,
266                                                         "Sending %d bytes.",
267                                                         storebuf.offset);
268                                         write(fd, &storebuf.offset,
269                                                 sizeof(storebuf.offset));
270                                         write(fd, storebuf.buffer,
271                                                 storebuf.offset);
272
273                                         free(storebuf.buffer);
274                                         storebuf.buffer = NULL;
275                                         storebuf.size = storebuf.offset = 0;
276                                         free_packet_list(packets);
277                                         packets = list_end = NULL;
278                                         free_publickey(key);
279                                         key = NULL;
280                                 } else {
281                                         write(fd, &storebuf.offset,
282                                                 sizeof(storebuf.offset));
283                                 }
284                         }
285                         break;
286
287                 case KEYD_CMD_GET_TEXT:
288                         cmd = KEYD_REPLY_OK;
289                         write(fd, &cmd, sizeof(cmd));
290                         bytes = read(fd, &count, sizeof(count));
291                         if (bytes != sizeof(count)) {
292                                 ret = 1;
293                         }
294                         storebuf.offset = 0;
295                         if (ret == 0) {
296                                 search = malloc(count+1);
297                                 read(fd, search, count);
298                                 search[count] = 0;
299                                 logthing(LOGTHING_INFO,
300                                                 "Fetching %s, result: %d",
301                                                 search,
302                                                 dbctx->fetch_key_text(dbctx,
303                                                         search, &key));
304                                 if (key != NULL) {
305                                         storebuf.size = 8192;
306                                         storebuf.buffer = malloc(8192);
307
308                                         flatten_publickey(key,
309                                                         &packets,
310                                                         &list_end);
311                                         write_openpgp_stream(buffer_putchar,
312                                                         &storebuf,
313                                                         packets);
314                                         logthing(LOGTHING_TRACE,
315                                                         "Sending %d bytes.",
316                                                         storebuf.offset);
317                                         write(fd, &storebuf.offset,
318                                                 sizeof(storebuf.offset));
319                                         write(fd, storebuf.buffer,
320                                                 storebuf.offset);
321
322                                         free(storebuf.buffer);
323                                         storebuf.buffer = NULL;
324                                         storebuf.size = storebuf.offset = 0;
325                                         free_packet_list(packets);
326                                         packets = list_end = NULL;
327                                         free_publickey(key);
328                                         key = NULL;
329                                 } else {
330                                         write(fd, &storebuf.offset,
331                                                 sizeof(storebuf.offset));
332                                 }
333                                 free(search);
334                         }
335                         break;
336                 case KEYD_CMD_STORE:
337                         cmd = KEYD_REPLY_OK;
338                         write(fd, &cmd, sizeof(cmd));
339                         storebuf.offset = 0;
340                         bytes = read(fd, &storebuf.size,
341                                         sizeof(storebuf.size));
342                         logthing(LOGTHING_TRACE, "Reading %d bytes.",
343                                         storebuf.size);
344                         if (bytes != sizeof(storebuf.size)) {
345                                 ret = 1;
346                         }
347                         if (ret == 0 && storebuf.size > 0) {
348                                 storebuf.buffer = malloc(storebuf.size);
349                                 bytes = count = 0;
350                                 while (bytes >= 0 && count < storebuf.size) {
351                                         bytes = read(fd,
352                                                 &storebuf.buffer[count],
353                                                 storebuf.size - count);
354                                         logthing(LOGTHING_TRACE,
355                                                         "Read %d bytes.",
356                                                         bytes);
357                                         count += bytes;
358                                 }
359                                 read_openpgp_stream(buffer_fetchchar,
360                                                 &storebuf,
361                                                 &packets,
362                                                 0);
363                                 parse_keys(packets, &key);
364                                 dbctx->store_key(dbctx, key, false, false);
365                                 free_packet_list(packets);
366                                 packets = NULL;
367                                 free_publickey(key);
368                                 key = NULL;
369                                 free(storebuf.buffer);
370                                 storebuf.buffer = NULL;
371                                 storebuf.size = storebuf.offset = 0;
372                         }
373                         break;
374                 case KEYD_CMD_DELETE:
375                         cmd = KEYD_REPLY_OK;
376                         write(fd, &cmd, sizeof(cmd));
377                         bytes = read(fd, &keyid, sizeof(keyid));
378                         if (bytes != sizeof(keyid)) {
379                                 ret = 1;
380                         }
381                         if (ret == 0) {
382                                 logthing(LOGTHING_INFO,
383                                                 "Deleting 0x%" PRIX64
384                                                 ", result: %d",
385                                                 keyid,
386                                                 dbctx->delete_key(dbctx,
387                                                         keyid, false));
388                         }
389                         break;
390                 case KEYD_CMD_GETFULLKEYID:
391                         cmd = KEYD_REPLY_OK;
392                         write(fd, &cmd, sizeof(cmd));
393                         bytes = read(fd, &keyid, sizeof(keyid));
394                         if (bytes != sizeof(keyid)) {
395                                 ret = 1;
396                         }
397                         if (ret == 0) {
398                                 keyid = dbctx->getfullkeyid(dbctx, keyid);
399                                 cmd = sizeof(keyid);
400                                 write(fd, &cmd, sizeof(cmd));
401                                 write(fd, &keyid, sizeof(keyid));
402                         }
403                         break;
404                 case KEYD_CMD_KEYITER:
405                         cmd = KEYD_REPLY_OK;
406                         write(fd, &cmd, sizeof(cmd));
407                         dbctx->iterate_keys(dbctx, iteratefunc,
408                                         &fd);
409                         bytes = 0;
410                         write(fd, &bytes, sizeof(bytes));
411                         break;
412                 case KEYD_CMD_CLOSE:
413                         cmd = KEYD_REPLY_OK;
414                         write(fd, &cmd, sizeof(cmd));
415                         ret = 1;
416                         break;
417                 case KEYD_CMD_QUIT:
418                         cmd = KEYD_REPLY_OK;
419                         write(fd, &cmd, sizeof(cmd));
420                         logthing(LOGTHING_NOTICE,
421                                 "Exiting due to quit request.");
422                         ret = 1;
423                         trytocleanup();
424                         break;
425                 case KEYD_CMD_STATS:
426                         cmd = KEYD_REPLY_OK;
427                         write(fd, &cmd, sizeof(cmd));
428                         cmd = sizeof(*stats);
429                         write(fd, &cmd, sizeof(cmd));
430                         write(fd, stats,
431                                 sizeof(*stats));
432                         break;
433                 case KEYD_CMD_GET_SKSHASH:
434                         cmd = KEYD_REPLY_OK;
435                         write(fd, &cmd, sizeof(cmd));
436                         bytes = read(fd, hash.hash, sizeof(hash.hash));
437                         if (bytes != sizeof(hash.hash)) {
438                                 ret = 1;
439                         }
440                         storebuf.offset = 0;
441                         if (ret == 0) {
442                                 logthing(LOGTHING_INFO,
443                                                 "Fetching by hash"
444                                                 ", result: %d",
445                                                 dbctx->fetch_key_skshash(dbctx,
446                                                         &hash, &key));
447                                 if (key != NULL) {
448                                         storebuf.size = 8192;
449                                         storebuf.buffer = malloc(8192);
450
451                                         flatten_publickey(key,
452                                                         &packets,
453                                                         &list_end);
454                                         write_openpgp_stream(buffer_putchar,
455                                                         &storebuf,
456                                                         packets);
457                                         logthing(LOGTHING_TRACE,
458                                                         "Sending %d bytes.",
459                                                         storebuf.offset);
460                                         write(fd, &storebuf.offset,
461                                                 sizeof(storebuf.offset));
462                                         write(fd, storebuf.buffer,
463                                                 storebuf.offset);
464
465                                         free(storebuf.buffer);
466                                         storebuf.buffer = NULL;
467                                         storebuf.size = storebuf.offset = 0;
468                                         free_packet_list(packets);
469                                         packets = list_end = NULL;
470                                         free_publickey(key);
471                                         key = NULL;
472                                 } else {
473                                         write(fd, &storebuf.offset,
474                                                 sizeof(storebuf.offset));
475                                 }
476                         }
477                         break;
478
479                 default:
480                         logthing(LOGTHING_ERROR, "Got unknown command: %d",
481                                         cmd);
482                         cmd = KEYD_REPLY_UNKNOWN_CMD;
483                         write(fd, &cmd, sizeof(cmd));
484                 }
485         }
486
487         return(ret);
488 }
489
490 int sock_close(int fd)
491 {
492         shutdown(fd, SHUT_RDWR);
493         return close(fd);
494 }
495
496 int sock_accept(int fd)
497 {
498         struct sockaddr_un sock;
499         socklen_t          socklen;
500         int    srv = -1;
501         int    ret = -1;
502
503         socklen = sizeof(sock);
504         srv = accept(fd, (struct sockaddr *) &sock, &socklen);
505         if (srv != -1) {
506                 ret = fcntl(srv, F_SETFD, FD_CLOEXEC);
507         }
508
509         if (ret != -1) {
510                 stats->connects++;
511         }
512
513         return (srv);
514 }
515
516 static void usage(void)
517 {
518         puts("keyd " ONAK_VERSION " - backend key serving daemon for the "
519                 "onak PGP keyserver.\n");
520         puts("Usage:\n");
521         puts("\tkeyd [options]\n");
522         puts("\tOptions:\n:");
523         puts("-c <file> - use <file> as the config file");
524         puts("-f        - run in the foreground");
525         puts("-h        - show this help text");
526         exit(EXIT_FAILURE);
527 }
528
529 int main(int argc, char *argv[])
530 {
531         int fd = -1, maxfd, i, clients[MAX_CLIENTS];
532         fd_set rfds;
533         char sockname[1024];
534         char *configfile = NULL;
535         bool foreground = false;
536         int optchar;
537         struct onak_dbctx *dbctx;
538
539         while ((optchar = getopt(argc, argv, "c:fh")) != -1 ) {
540                 switch (optchar) {
541                 case 'c':
542                         if (configfile != NULL) {
543                                 free(configfile);
544                         }
545                         configfile = strdup(optarg);
546                         break;
547                 case 'f':
548                         foreground = true;
549                         break;
550                 case 'h':
551                 default:
552                         usage();
553                         break;
554                 }
555         }
556
557         readconfig(configfile);
558         free(configfile);
559         configfile = NULL;
560         initlogthing("keyd", config.logfile);
561         config.use_keyd = false;
562
563         if (!foreground) {
564                 daemonize();
565         }
566
567         catchsignals();
568         signal(SIGPIPE, SIG_IGN);
569
570
571         stats = calloc(1, sizeof(*stats));
572         if (!stats) {
573                 logthing(LOGTHING_ERROR,
574                         "Couldn't allocate memory for stats structure.");
575                 exit(EXIT_FAILURE);
576         }
577         stats->started = time(NULL);
578
579         snprintf(sockname, 1023, "%s/%s", config.db_dir, KEYD_SOCKET);
580         fd = sock_init(sockname);
581
582         if (fd != -1) {
583                 FD_ZERO(&rfds);
584                 FD_SET(fd, &rfds);
585                 maxfd = fd;
586                 memset(clients, -1, sizeof (clients));
587
588                 dbctx = config.dbinit(false);
589
590                 logthing(LOGTHING_NOTICE, "Accepting connections.");
591                 while (!cleanup() && select(maxfd + 1, &rfds, NULL, NULL, NULL) != -1) {
592                         /*
593                          * Deal with existing clients first; if we're at our
594                          * connection limit then processing them might free
595                          * things up and let us accept the next client below.
596                          */
597                         for (i = 0; i < MAX_CLIENTS; i++) {
598                                 if (clients[i] != -1 &&
599                                                 FD_ISSET(clients[i], &rfds)) {
600                                         logthing(LOGTHING_DEBUG,
601                                                 "Handling connection for client %d.", i);
602                                         if (sock_do(dbctx, clients[i])) {
603                                                 sock_close(clients[i]);
604                                                 clients[i] = -1;
605                                                 logthing(LOGTHING_DEBUG,
606                                                         "Closed connection for client %d.", i);
607                                         }
608                                 }
609                         }
610                         /*
611                          * Check if we have a new incoming connection to accept.
612                          */
613                         if (FD_ISSET(fd, &rfds)) {
614                                 for (i = 0; i < MAX_CLIENTS; i++) {
615                                         if (clients[i] == -1) {
616                                                 break;
617                                         }
618                                 }
619                                 if (i < MAX_CLIENTS) {
620                                         logthing(LOGTHING_INFO,
621                                                 "Accepted connection %d.", i);
622                                         clients[i] = sock_accept(fd);
623                                 }
624                         }
625                         FD_ZERO(&rfds);
626                         FD_SET(fd, &rfds);
627                         maxfd = fd;
628                         for (i = 0; i < MAX_CLIENTS; i++) {
629                                 if (clients[i] != -1) {
630                                         FD_SET(clients[i], &rfds);
631                                         maxfd = (maxfd > clients[i]) ?
632                                                         maxfd : clients[i];
633                                 }
634                         }
635                 }
636                 dbctx->cleanupdb(dbctx);
637                 sock_close(fd);
638                 unlink(sockname);
639         }
640
641         free(stats);
642
643         cleanuplogthing();
644         cleanupconfig();
645
646         return(EXIT_SUCCESS);
647 }