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