Add -1 to Debian package version
[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
144         /*
145          * Get the command from the client.
146          */
147         bytes = read(fd, &cmd, sizeof(cmd));
148
149         logthing(LOGTHING_DEBUG, "Read %d bytes, command: %d", bytes, cmd);
150
151         if (bytes != sizeof(cmd)) {
152                 ret = 1;
153         }
154         
155         if (ret == 0) {
156                 if (cmd < KEYD_CMD_LAST) {
157                         stats->command_stats[cmd]++;
158                 } else {
159                         stats->command_stats[KEYD_CMD_UNKNOWN]++;
160                 }
161                 switch (cmd) {
162                 case KEYD_CMD_VERSION:
163                         cmd = KEYD_REPLY_OK;
164                         write(fd, &cmd, sizeof(cmd));
165                         cmd = sizeof(keyd_version);
166                         write(fd, &cmd, sizeof(cmd));
167                         write(fd, &keyd_version, sizeof(keyd_version));
168                         break;
169                 case KEYD_CMD_GET:
170                         cmd = KEYD_REPLY_OK;
171                         write(fd, &cmd, sizeof(cmd));
172                         bytes = read(fd, &keyid, sizeof(keyid));
173                         if (bytes != sizeof(keyid)) {
174                                 ret = 1;
175                         }
176                         storebuf.offset = 0;
177                         if (ret == 0) {
178                                 logthing(LOGTHING_INFO,
179                                                 "Fetching 0x%" PRIX64
180                                                 ", result: %d",
181                                                 keyid,
182                                                 config.dbbackend->
183                                                 fetch_key(keyid, &key, false));
184                                 if (key != NULL) {
185                                         storebuf.size = 8192;
186                                         storebuf.buffer = malloc(8192);
187
188                                         flatten_publickey(key,
189                                                         &packets,
190                                                         &list_end);
191                                         write_openpgp_stream(buffer_putchar,
192                                                         &storebuf,
193                                                         packets);
194                                         logthing(LOGTHING_TRACE,
195                                                         "Sending %d bytes.",
196                                                         storebuf.offset);
197                                         write(fd, &storebuf.offset,
198                                                 sizeof(storebuf.offset));
199                                         write(fd, storebuf.buffer,
200                                                 storebuf.offset);
201
202                                         free(storebuf.buffer);
203                                         storebuf.buffer = NULL;
204                                         storebuf.size = storebuf.offset = 0;
205                                         free_packet_list(packets);
206                                         packets = list_end = NULL;
207                                         free_publickey(key);
208                                         key = NULL;
209                                 } else {
210                                         write(fd, &storebuf.offset,
211                                                 sizeof(storebuf.offset));
212                                 }
213                         }
214                         break;
215                 case KEYD_CMD_GETTEXT:
216                         cmd = KEYD_REPLY_OK;
217                         write(fd, &cmd, sizeof(cmd));
218                         bytes = read(fd, &count, sizeof(count));
219                         if (bytes != sizeof(count)) {
220                                 ret = 1;
221                         }
222                         storebuf.offset = 0;
223                         if (ret == 0) {
224                                 search = malloc(count+1);
225                                 read(fd, search, count);
226                                 search[count] = 0;
227                                 logthing(LOGTHING_INFO,
228                                                 "Fetching %s, result: %d",
229                                                 search,
230                                                 config.dbbackend->
231                                                 fetch_key_text(search, &key));
232                                 if (key != NULL) {
233                                         storebuf.size = 8192;
234                                         storebuf.buffer = malloc(8192);
235
236                                         flatten_publickey(key,
237                                                         &packets,
238                                                         &list_end);
239                                         write_openpgp_stream(buffer_putchar,
240                                                         &storebuf,
241                                                         packets);
242                                         logthing(LOGTHING_TRACE,
243                                                         "Sending %d bytes.",
244                                                         storebuf.offset);
245                                         write(fd, &storebuf.offset,
246                                                 sizeof(storebuf.offset));
247                                         write(fd, storebuf.buffer,
248                                                 storebuf.offset);
249
250                                         free(storebuf.buffer);
251                                         storebuf.buffer = NULL;
252                                         storebuf.size = storebuf.offset = 0;
253                                         free_packet_list(packets);
254                                         packets = list_end = NULL;
255                                         free_publickey(key);
256                                         key = NULL;
257                                 } else {
258                                         write(fd, &storebuf.offset,
259                                                 sizeof(storebuf.offset));
260                                 }
261                         }
262                         break;
263                 case KEYD_CMD_STORE:
264                         cmd = KEYD_REPLY_OK;
265                         write(fd, &cmd, sizeof(cmd));
266                         storebuf.offset = 0;
267                         bytes = read(fd, &storebuf.size,
268                                         sizeof(storebuf.size));
269                         logthing(LOGTHING_TRACE, "Reading %d bytes.",
270                                         storebuf.size);
271                         if (bytes != sizeof(storebuf.size)) {
272                                 ret = 1;
273                         }
274                         if (ret == 0 && storebuf.size > 0) {
275                                 storebuf.buffer = malloc(storebuf.size);
276                                 bytes = count = 0;
277                                 while (bytes >= 0 && count < storebuf.size) {
278                                         bytes = read(fd,
279                                                 &storebuf.buffer[count],
280                                                 storebuf.size - count);
281                                         logthing(LOGTHING_TRACE,
282                                                         "Read %d bytes.",
283                                                         bytes);
284                                         count += bytes;
285                                 }
286                                 read_openpgp_stream(buffer_fetchchar,
287                                                 &storebuf,
288                                                 &packets,
289                                                 0);
290                                 parse_keys(packets, &key);
291                                 config.dbbackend->store_key(key, false, false);
292                                 free_packet_list(packets);
293                                 packets = NULL;
294                                 free_publickey(key);
295                                 key = NULL;
296                                 free(storebuf.buffer);
297                                 storebuf.buffer = NULL;
298                                 storebuf.size = storebuf.offset = 0;
299                         }
300                         break;
301                 case KEYD_CMD_DELETE:
302                         cmd = KEYD_REPLY_OK;
303                         write(fd, &cmd, sizeof(cmd));
304                         bytes = read(fd, &keyid, sizeof(keyid));
305                         if (bytes != sizeof(keyid)) {
306                                 ret = 1;
307                         }
308                         if (ret == 0) {
309                                 logthing(LOGTHING_INFO,
310                                                 "Deleting 0x%" PRIX64
311                                                 ", result: %d",
312                                                 keyid,
313                                                 config.dbbackend->delete_key(
314                                                         keyid, false));
315                         }
316                         break;
317                 case KEYD_CMD_GETFULLKEYID:
318                         cmd = KEYD_REPLY_OK;
319                         write(fd, &cmd, sizeof(cmd));
320                         bytes = read(fd, &keyid, sizeof(keyid));
321                         if (bytes != sizeof(keyid)) {
322                                 ret = 1;
323                         }
324                         if (ret == 0) {
325                                 keyid = config.dbbackend->getfullkeyid(keyid);
326                                 cmd = sizeof(keyid);
327                                 write(fd, &cmd, sizeof(cmd));
328                                 write(fd, &keyid, sizeof(keyid));
329                         }
330                         break;
331                 case KEYD_CMD_KEYITER:
332                         cmd = KEYD_REPLY_OK;
333                         write(fd, &cmd, sizeof(cmd));
334                         config.dbbackend->iterate_keys(iteratefunc,
335                                         &fd);
336                         bytes = 0;
337                         write(fd, &bytes, sizeof(bytes));
338                         break;
339                 case KEYD_CMD_CLOSE:
340                         cmd = KEYD_REPLY_OK;
341                         write(fd, &cmd, sizeof(cmd));
342                         ret = 1;
343                         break;
344                 case KEYD_CMD_QUIT:
345                         cmd = KEYD_REPLY_OK;
346                         write(fd, &cmd, sizeof(cmd));
347                         logthing(LOGTHING_NOTICE,
348                                 "Exiting due to quit request.");
349                         ret = 1;
350                         trytocleanup();
351                         break;
352                 case KEYD_CMD_STATS:
353                         cmd = KEYD_REPLY_OK;
354                         write(fd, &cmd, sizeof(cmd));
355                         cmd = sizeof(*stats);
356                         write(fd, &cmd, sizeof(cmd));
357                         write(fd, stats,
358                                 sizeof(*stats));
359                         break;
360                 default:
361                         logthing(LOGTHING_ERROR, "Got unknown command: %d",
362                                         cmd);
363                         cmd = KEYD_REPLY_UNKNOWN_CMD;
364                         write(fd, &cmd, sizeof(cmd));
365                 }
366         }
367
368         return(ret);
369 }
370
371 int sock_close(int fd)
372 {
373         shutdown(fd, SHUT_RDWR);
374         return close(fd);
375 }
376
377 int sock_accept(int fd)
378 {
379         struct sockaddr_un sock;
380         socklen_t          socklen;
381         int    srv = -1;
382         int    ret = -1;
383
384         socklen = sizeof(sock);
385         srv = accept(fd, (struct sockaddr *) &sock, &socklen);
386         if (srv != -1) {
387                 ret = fcntl(srv, F_SETFD, 1);
388         }
389
390         if (ret != -1) {
391                 stats->connects++;
392                 while (!sock_do(srv)) ;
393                 sock_close(srv);
394         }
395
396         return 1;
397 }
398
399 static void usage(void)
400 {
401         puts("keyd " ONAK_VERSION " - backend key serving daemon for the "
402                 "onak PGP keyserver.\n");
403         puts("Usage:\n");
404         puts("\tkeyd [options]\n");
405         puts("\tOptions:\n:");
406         puts("-c <file> - use <file> as the config file");
407         puts("-f        - run in the foreground");
408         puts("-h        - show this help text");
409         exit(EXIT_FAILURE);
410 }
411
412 int main(int argc, char *argv[])
413 {
414         int fd = -1;
415         fd_set rfds;
416         char sockname[1024];
417         char *configfile = NULL;
418         bool foreground = false;
419         int optchar;
420
421         while ((optchar = getopt(argc, argv, "c:fh")) != -1 ) {
422                 switch (optchar) {
423                 case 'c':
424                         configfile = strdup(optarg);
425                         break;
426                 case 'f':
427                         foreground = true;
428                         break;
429                 case 'h':
430                 default:
431                         usage();
432                         break;
433                 }
434         }
435
436         readconfig(configfile);
437         free(configfile);
438         configfile = NULL;
439         initlogthing("keyd", config.logfile);
440         config.use_keyd = false;
441
442         if (!foreground) {
443                 daemonize();
444         }
445
446         catchsignals();
447         signal(SIGPIPE, SIG_IGN);
448
449
450         stats = calloc(1, sizeof(*stats));
451         if (!stats) {
452                 logthing(LOGTHING_ERROR,
453                         "Couldn't allocate memory for stats structure.");
454                 exit(EXIT_FAILURE);
455         }
456         stats->started = time(NULL);
457
458         snprintf(sockname, 1023, "%s/%s", config.db_dir, KEYD_SOCKET);
459         fd = sock_init(sockname);
460
461         if (fd != -1) {
462                 FD_ZERO(&rfds);
463                 FD_SET(fd, &rfds);
464
465                 config.dbbackend->initdb(false);
466
467                 logthing(LOGTHING_NOTICE, "Accepting connections.");
468                 while (!cleanup() && select(fd + 1, &rfds, NULL, NULL, NULL) != -1) {
469                         logthing(LOGTHING_INFO, "Accepted connection.");
470                         sock_accept(fd);
471                         FD_SET(fd, &rfds);
472                 }
473                 config.dbbackend->cleanupdb();
474                 sock_close(fd);
475                 unlink(sockname);
476         }
477
478         free(stats);
479
480         cleanuplogthing();
481         cleanupconfig();
482
483         return(EXIT_SUCCESS);
484 }