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