Add /pks/hashquery
[onak.git] / keyd.c
1 /*
2  * keyd.c - key retrieval daemon
3  *
4  * Jonathan McDowell <noodles@earth.li>
5  *
6  * Copyright 2004 Project Purple
7  */
8
9 #include <errno.h>
10 #include <fcntl.h>
11 #include <getopt.h>
12 #include <signal.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/select.h>
17 #include <sys/socket.h>
18 #include <sys/types.h>
19 #include <sys/un.h>
20 #include <time.h>
21 #include <unistd.h>
22
23 #include "charfuncs.h"
24 #include "cleanup.h"
25 #include "keyd.h"
26 #include "keydb.h"
27 #include "keyid.h"
28 #include "keystructs.h"
29 #include "log.h"
30 #include "mem.h"
31 #include "onak-conf.h"
32 #include "parsekey.h"
33 #include "version.h"
34
35 static struct keyd_stats *stats;
36
37 void daemonize(void)
38 {
39         pid_t pid;
40
41         pid = fork();
42
43         if (pid < 0) {
44                 logthing(LOGTHING_CRITICAL,
45                         "Failed to fork into background: %d (%s)",
46                         errno,
47                         strerror(errno));
48                 exit(EXIT_FAILURE);
49         } else if (pid > 0) {
50                 logthing(LOGTHING_INFO, "Backgrounded as pid %d.", pid);
51                 exit(EXIT_SUCCESS);
52         }
53
54         pid = setsid();
55
56         freopen("/dev/null", "r", stdin);
57         freopen("/dev/null", "w", stdout);
58         freopen("/dev/null", "w", stderr);
59
60         return;
61 }
62
63 void iteratefunc(void *ctx, struct openpgp_publickey *key)
64 {
65         struct openpgp_packet_list *packets = NULL;
66         struct openpgp_packet_list *list_end = NULL;
67         struct buffer_ctx           storebuf;
68         int                         ret = 0;
69         int                         *fd = (int *) ctx;
70
71         if (key != NULL) {
72                 storebuf.offset = 0;
73                 storebuf.size = 8192;
74                 storebuf.buffer = malloc(8192);
75
76                 logthing(LOGTHING_TRACE,
77                                 "Iterating over 0x%016" PRIX64 ".",
78                                 get_keyid(key));
79
80                 flatten_publickey(key,
81                                 &packets,
82                                 &list_end);
83                 write_openpgp_stream(buffer_putchar,
84                                 &storebuf,
85                                 packets);
86                 logthing(LOGTHING_TRACE,
87                                 "Sending %d bytes.",
88                                 storebuf.offset);
89                 ret = write(*fd, &storebuf.offset,
90                         sizeof(storebuf.offset));
91                 if (ret != 0) {
92                         write(*fd, storebuf.buffer,
93                                 storebuf.offset);
94                 }
95
96                 free(storebuf.buffer);
97                 storebuf.buffer = NULL;
98                 storebuf.size = storebuf.offset = 0;
99                 free_packet_list(packets);
100                 packets = list_end = NULL;
101         }
102
103         return;
104 }
105
106 int sock_init(const char *sockname)
107 {
108         struct sockaddr_un sock;
109         int                fd = -1;
110         int                ret = -1;
111
112         fd = socket(PF_UNIX, SOCK_STREAM, 0);
113         if (fd != -1) {
114                 ret = fcntl(fd, F_SETFD, 1);
115         }
116
117         if (ret != -1) {
118                 sock.sun_family = AF_UNIX;
119                 strncpy(sock.sun_path, sockname, sizeof(sock.sun_path) - 1);
120                 unlink(sockname);
121                 ret = bind(fd, (struct sockaddr *) &sock, sizeof(sock));
122         }
123
124         if (ret != -1) {
125                 ret = listen(fd, 5);
126         }
127         
128         return fd;
129 }
130
131 int sock_do(int fd)
132 {
133         uint32_t cmd = KEYD_CMD_UNKNOWN;
134         ssize_t  bytes = 0;
135         ssize_t  count = 0;
136         int      ret = 0;
137         uint64_t keyid = 0;
138         char     *search = NULL;
139         struct openpgp_publickey *key = NULL;
140         struct openpgp_packet_list *packets = NULL;
141         struct openpgp_packet_list *list_end = NULL;
142         struct buffer_ctx storebuf;
143         struct skshash hash;
144
145         /*
146          * Get the command from the client.
147          */
148         bytes = read(fd, &cmd, sizeof(cmd));
149
150         logthing(LOGTHING_DEBUG, "Read %d bytes, command: %d", bytes, cmd);
151
152         if (bytes != sizeof(cmd)) {
153                 ret = 1;
154         }
155         
156         if (ret == 0) {
157                 if (cmd < KEYD_CMD_LAST) {
158                         stats->command_stats[cmd]++;
159                 } else {
160                         stats->command_stats[KEYD_CMD_UNKNOWN]++;
161                 }
162                 switch (cmd) {
163                 case KEYD_CMD_VERSION:
164                         cmd = KEYD_REPLY_OK;
165                         write(fd, &cmd, sizeof(cmd));
166                         cmd = sizeof(keyd_version);
167                         write(fd, &cmd, sizeof(cmd));
168                         write(fd, &keyd_version, sizeof(keyd_version));
169                         break;
170                 case KEYD_CMD_GET:
171                         cmd = KEYD_REPLY_OK;
172                         write(fd, &cmd, sizeof(cmd));
173                         bytes = read(fd, &keyid, sizeof(keyid));
174                         if (bytes != sizeof(keyid)) {
175                                 ret = 1;
176                         }
177                         storebuf.offset = 0;
178                         if (ret == 0) {
179                                 logthing(LOGTHING_INFO,
180                                                 "Fetching 0x%" PRIX64
181                                                 ", result: %d",
182                                                 keyid,
183                                                 config.dbbackend->
184                                                 fetch_key(keyid, &key, false));
185                                 if (key != NULL) {
186                                         storebuf.size = 8192;
187                                         storebuf.buffer = malloc(8192);
188
189                                         flatten_publickey(key,
190                                                         &packets,
191                                                         &list_end);
192                                         write_openpgp_stream(buffer_putchar,
193                                                         &storebuf,
194                                                         packets);
195                                         logthing(LOGTHING_TRACE,
196                                                         "Sending %d bytes.",
197                                                         storebuf.offset);
198                                         write(fd, &storebuf.offset,
199                                                 sizeof(storebuf.offset));
200                                         write(fd, storebuf.buffer,
201                                                 storebuf.offset);
202
203                                         free(storebuf.buffer);
204                                         storebuf.buffer = NULL;
205                                         storebuf.size = storebuf.offset = 0;
206                                         free_packet_list(packets);
207                                         packets = list_end = NULL;
208                                         free_publickey(key);
209                                         key = NULL;
210                                 } else {
211                                         write(fd, &storebuf.offset,
212                                                 sizeof(storebuf.offset));
213                                 }
214                         }
215                         break;
216                 case KEYD_CMD_GETTEXT:
217                         cmd = KEYD_REPLY_OK;
218                         write(fd, &cmd, sizeof(cmd));
219                         bytes = read(fd, &count, sizeof(count));
220                         if (bytes != sizeof(count)) {
221                                 ret = 1;
222                         }
223                         storebuf.offset = 0;
224                         if (ret == 0) {
225                                 search = malloc(count+1);
226                                 read(fd, search, count);
227                                 search[count] = 0;
228                                 logthing(LOGTHING_INFO,
229                                                 "Fetching %s, result: %d",
230                                                 search,
231                                                 config.dbbackend->
232                                                 fetch_key_text(search, &key));
233                                 if (key != NULL) {
234                                         storebuf.size = 8192;
235                                         storebuf.buffer = malloc(8192);
236
237                                         flatten_publickey(key,
238                                                         &packets,
239                                                         &list_end);
240                                         write_openpgp_stream(buffer_putchar,
241                                                         &storebuf,
242                                                         packets);
243                                         logthing(LOGTHING_TRACE,
244                                                         "Sending %d bytes.",
245                                                         storebuf.offset);
246                                         write(fd, &storebuf.offset,
247                                                 sizeof(storebuf.offset));
248                                         write(fd, storebuf.buffer,
249                                                 storebuf.offset);
250
251                                         free(storebuf.buffer);
252                                         storebuf.buffer = NULL;
253                                         storebuf.size = storebuf.offset = 0;
254                                         free_packet_list(packets);
255                                         packets = list_end = NULL;
256                                         free_publickey(key);
257                                         key = NULL;
258                                 } else {
259                                         write(fd, &storebuf.offset,
260                                                 sizeof(storebuf.offset));
261                                 }
262                         }
263                         break;
264                 case KEYD_CMD_STORE:
265                         cmd = KEYD_REPLY_OK;
266                         write(fd, &cmd, sizeof(cmd));
267                         storebuf.offset = 0;
268                         bytes = read(fd, &storebuf.size,
269                                         sizeof(storebuf.size));
270                         logthing(LOGTHING_TRACE, "Reading %d bytes.",
271                                         storebuf.size);
272                         if (bytes != sizeof(storebuf.size)) {
273                                 ret = 1;
274                         }
275                         if (ret == 0 && storebuf.size > 0) {
276                                 storebuf.buffer = malloc(storebuf.size);
277                                 bytes = count = 0;
278                                 while (bytes >= 0 && count < storebuf.size) {
279                                         bytes = read(fd,
280                                                 &storebuf.buffer[count],
281                                                 storebuf.size - count);
282                                         logthing(LOGTHING_TRACE,
283                                                         "Read %d bytes.",
284                                                         bytes);
285                                         count += bytes;
286                                 }
287                                 read_openpgp_stream(buffer_fetchchar,
288                                                 &storebuf,
289                                                 &packets,
290                                                 0);
291                                 parse_keys(packets, &key);
292                                 config.dbbackend->store_key(key, false, false);
293                                 free_packet_list(packets);
294                                 packets = NULL;
295                                 free_publickey(key);
296                                 key = NULL;
297                                 free(storebuf.buffer);
298                                 storebuf.buffer = NULL;
299                                 storebuf.size = storebuf.offset = 0;
300                         }
301                         break;
302                 case KEYD_CMD_DELETE:
303                         cmd = KEYD_REPLY_OK;
304                         write(fd, &cmd, sizeof(cmd));
305                         bytes = read(fd, &keyid, sizeof(keyid));
306                         if (bytes != sizeof(keyid)) {
307                                 ret = 1;
308                         }
309                         if (ret == 0) {
310                                 logthing(LOGTHING_INFO,
311                                                 "Deleting 0x%" PRIX64
312                                                 ", result: %d",
313                                                 keyid,
314                                                 config.dbbackend->delete_key(
315                                                         keyid, false));
316                         }
317                         break;
318                 case KEYD_CMD_GETFULLKEYID:
319                         cmd = KEYD_REPLY_OK;
320                         write(fd, &cmd, sizeof(cmd));
321                         bytes = read(fd, &keyid, sizeof(keyid));
322                         if (bytes != sizeof(keyid)) {
323                                 ret = 1;
324                         }
325                         if (ret == 0) {
326                                 keyid = config.dbbackend->getfullkeyid(keyid);
327                                 cmd = sizeof(keyid);
328                                 write(fd, &cmd, sizeof(cmd));
329                                 write(fd, &keyid, sizeof(keyid));
330                         }
331                         break;
332                 case KEYD_CMD_KEYITER:
333                         cmd = KEYD_REPLY_OK;
334                         write(fd, &cmd, sizeof(cmd));
335                         config.dbbackend->iterate_keys(iteratefunc,
336                                         &fd);
337                         bytes = 0;
338                         write(fd, &bytes, sizeof(bytes));
339                         break;
340                 case KEYD_CMD_CLOSE:
341                         cmd = KEYD_REPLY_OK;
342                         write(fd, &cmd, sizeof(cmd));
343                         ret = 1;
344                         break;
345                 case KEYD_CMD_QUIT:
346                         cmd = KEYD_REPLY_OK;
347                         write(fd, &cmd, sizeof(cmd));
348                         logthing(LOGTHING_NOTICE,
349                                 "Exiting due to quit request.");
350                         ret = 1;
351                         trytocleanup();
352                         break;
353                 case KEYD_CMD_STATS:
354                         cmd = KEYD_REPLY_OK;
355                         write(fd, &cmd, sizeof(cmd));
356                         cmd = sizeof(*stats);
357                         write(fd, &cmd, sizeof(cmd));
358                         write(fd, stats,
359                                 sizeof(*stats));
360                         break;
361                 case KEYD_CMD_GETSKSHASH:
362                         cmd = KEYD_REPLY_OK;
363                         write(fd, &cmd, sizeof(cmd));
364                         bytes = read(fd, hash.hash, sizeof(hash.hash));
365                         if (bytes != sizeof(hash.hash)) {
366                                 ret = 1;
367                         }
368                         storebuf.offset = 0;
369                         if (ret == 0) {
370                                 logthing(LOGTHING_INFO,
371                                                 "Fetching by hash"
372                                                 ", result: %d",
373                                                 config.dbbackend->
374                                                 fetch_key_skshash(&hash,
375                                                         &key));
376                                 if (key != NULL) {
377                                         storebuf.size = 8192;
378                                         storebuf.buffer = malloc(8192);
379
380                                         flatten_publickey(key,
381                                                         &packets,
382                                                         &list_end);
383                                         write_openpgp_stream(buffer_putchar,
384                                                         &storebuf,
385                                                         packets);
386                                         logthing(LOGTHING_TRACE,
387                                                         "Sending %d bytes.",
388                                                         storebuf.offset);
389                                         write(fd, &storebuf.offset,
390                                                 sizeof(storebuf.offset));
391                                         write(fd, storebuf.buffer,
392                                                 storebuf.offset);
393
394                                         free(storebuf.buffer);
395                                         storebuf.buffer = NULL;
396                                         storebuf.size = storebuf.offset = 0;
397                                         free_packet_list(packets);
398                                         packets = list_end = NULL;
399                                         free_publickey(key);
400                                         key = NULL;
401                                 } else {
402                                         write(fd, &storebuf.offset,
403                                                 sizeof(storebuf.offset));
404                                 }
405                         }
406                         break;
407
408                 default:
409                         logthing(LOGTHING_ERROR, "Got unknown command: %d",
410                                         cmd);
411                         cmd = KEYD_REPLY_UNKNOWN_CMD;
412                         write(fd, &cmd, sizeof(cmd));
413                 }
414         }
415
416         return(ret);
417 }
418
419 int sock_close(int fd)
420 {
421         shutdown(fd, SHUT_RDWR);
422         return close(fd);
423 }
424
425 int sock_accept(int fd)
426 {
427         struct sockaddr_un sock;
428         socklen_t          socklen;
429         int    srv = -1;
430         int    ret = -1;
431
432         socklen = sizeof(sock);
433         srv = accept(fd, (struct sockaddr *) &sock, &socklen);
434         if (srv != -1) {
435                 ret = fcntl(srv, F_SETFD, 1);
436         }
437
438         if (ret != -1) {
439                 stats->connects++;
440                 while (!sock_do(srv)) ;
441                 sock_close(srv);
442         }
443
444         return 1;
445 }
446
447 static void usage(void)
448 {
449         puts("keyd " ONAK_VERSION " - backend key serving daemon for the "
450                 "onak PGP keyserver.\n");
451         puts("Usage:\n");
452         puts("\tkeyd [options]\n");
453         puts("\tOptions:\n:");
454         puts("-c <file> - use <file> as the config file");
455         puts("-f        - run in the foreground");
456         puts("-h        - show this help text");
457         exit(EXIT_FAILURE);
458 }
459
460 int main(int argc, char *argv[])
461 {
462         int fd = -1;
463         fd_set rfds;
464         char sockname[1024];
465         char *configfile = NULL;
466         bool foreground = false;
467         int optchar;
468
469         while ((optchar = getopt(argc, argv, "c:fh")) != -1 ) {
470                 switch (optchar) {
471                 case 'c':
472                         configfile = strdup(optarg);
473                         break;
474                 case 'f':
475                         foreground = true;
476                         break;
477                 case 'h':
478                 default:
479                         usage();
480                         break;
481                 }
482         }
483
484         readconfig(configfile);
485         free(configfile);
486         configfile = NULL;
487         initlogthing("keyd", config.logfile);
488         config.use_keyd = false;
489
490         if (!foreground) {
491                 daemonize();
492         }
493
494         catchsignals();
495         signal(SIGPIPE, SIG_IGN);
496
497
498         stats = calloc(1, sizeof(*stats));
499         if (!stats) {
500                 logthing(LOGTHING_ERROR,
501                         "Couldn't allocate memory for stats structure.");
502                 exit(EXIT_FAILURE);
503         }
504         stats->started = time(NULL);
505
506         snprintf(sockname, 1023, "%s/%s", config.db_dir, KEYD_SOCKET);
507         fd = sock_init(sockname);
508
509         if (fd != -1) {
510                 FD_ZERO(&rfds);
511                 FD_SET(fd, &rfds);
512
513                 config.dbbackend->initdb(false);
514
515                 logthing(LOGTHING_NOTICE, "Accepting connections.");
516                 while (!cleanup() && select(fd + 1, &rfds, NULL, NULL, NULL) != -1) {
517                         logthing(LOGTHING_INFO, "Accepted connection.");
518                         sock_accept(fd);
519                         FD_SET(fd, &rfds);
520                 }
521                 config.dbbackend->cleanupdb();
522                 sock_close(fd);
523                 unlink(sockname);
524         }
525
526         free(stats);
527
528         cleanuplogthing();
529         cleanupconfig();
530
531         return(EXIT_SUCCESS);
532 }