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