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