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