Add support for displaying/retrieving by SKS hash to lookup and onak CLI
[onak.git] / onak.c
1 /*
2  * onak.c - An OpenPGP keyserver.
3  *
4  * This is the main swiss army knife binary.
5  *
6  * Jonathan McDowell <noodles@earth.li>
7  * 
8  * Copyright 2002 Project Purple
9  */
10
11 #include <fcntl.h>
12 #include <getopt.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <sys/types.h>
17 #include <sys/stat.h>
18 #include <unistd.h>
19
20 #include "armor.h"
21 #include "charfuncs.h"
22 #include "cleankey.h"
23 #include "cleanup.h"
24 #include "keydb.h"
25 #include "keyid.h"
26 #include "keyindex.h"
27 #include "keystructs.h"
28 #include "log.h"
29 #include "mem.h"
30 #include "merge.h"
31 #include "onak-conf.h"
32 #include "parsekey.h"
33 #include "photoid.h"
34 #include "version.h"
35
36 void find_keys(char *search, uint64_t keyid, bool ishex,
37                 bool fingerprint, bool skshash, bool exact, bool verbose)
38 {
39         struct openpgp_publickey *publickey = NULL;
40         int count = 0;
41
42         if (ishex) {
43                 count = config.dbbackend->fetch_key(keyid, &publickey, false);
44         } else {
45                 count = config.dbbackend->fetch_key_text(search, &publickey);
46         }
47         if (publickey != NULL) {
48                 key_index(publickey, verbose, fingerprint, skshash, false);
49                 free_publickey(publickey);
50         } else if (count == 0) {
51                 puts("Key not found.");
52         } else {
53                 printf("Found %d keys, but maximum number to return is %d.\n",
54                                 count,
55                                 config.maxkeys);
56                 puts("Try again with a more specific search.");
57         }
58 }
59
60 struct dump_ctx {
61         int count;
62         int maxcount;
63         int fd;
64         int filenum;
65         char *filebase;
66 };
67
68 void dump_func(void *ctx, struct openpgp_publickey *key)
69 {
70         struct openpgp_packet_list *packets = NULL;
71         struct openpgp_packet_list *list_end = NULL;
72         struct dump_ctx *state;
73         char filename[1024];
74
75         state = (struct dump_ctx *) ctx;
76
77         if (state->fd == -1 || state->count++ > state->maxcount) {
78                 if (state->fd != -1) {
79                         close(state->fd);
80                         state->fd = -1;
81                 }
82                 snprintf(filename, 1023, state->filebase, state->filenum);
83                 state->fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0640);
84                 state->filenum++;
85                 state->count = 0;
86         }
87         flatten_publickey(key, &packets, &list_end);
88         write_openpgp_stream(file_putchar, &state->fd, packets);
89         free_packet_list(packets);
90         packets = list_end = NULL;
91
92         return;
93 }
94
95 void usage(void) {
96         puts("onak " ONAK_VERSION " - an OpenPGP keyserver.\n");
97         puts("Usage:\n");
98         puts("\tonak [options] <command> <parameters>\n");
99         puts("\tCommands:\n");
100         puts("\tadd      - read armored OpenPGP keys from stdin and add to the"
101                 " keyserver");
102         puts("\tclean    - read armored OpenPGP keys from stdin, run the"
103                 " cleaning\n\t             routines against them and dump to"
104                 " stdout");
105         puts("\tdelete   - delete a given key from the keyserver");
106         puts("\tdump     - dump all the keys from the keyserver to a file or"
107                 " files\n\t           starting keydump*");
108         puts("\tget      - retrieves the key requested from the keyserver");
109         puts("\tgetphoto - retrieves the first photoid on the given key and"
110                 " dumps to\n\t           stdout");
111         puts("\tindex    - search for a key and list it");
112         puts("\tvindex   - search for a key and list it and its signatures");
113 }
114
115 int main(int argc, char *argv[])
116 {
117         struct openpgp_packet_list      *packets = NULL;
118         struct openpgp_packet_list      *list_end = NULL;
119         struct openpgp_publickey        *keys = NULL;
120         char                            *configfile = NULL;
121         int                              rc = EXIT_SUCCESS;
122         int                              result = 0;
123         char                            *search = NULL;
124         char                            *end = NULL;
125         uint64_t                         keyid = 0;
126         bool                             ishex = false;
127         bool                             verbose = false;
128         bool                             update = false;
129         bool                             binary = false;
130         bool                             fingerprint = false;
131         bool                             skshash = false;
132         int                              optchar;
133         struct dump_ctx                  dumpstate;
134         struct skshash                   hash;
135
136         while ((optchar = getopt(argc, argv, "bc:fsuv")) != -1 ) {
137                 switch (optchar) {
138                 case 'b': 
139                         binary = true;
140                         break;
141                 case 'c':
142                         configfile = strdup(optarg);
143                         break;
144                 case 'f': 
145                         fingerprint = true;
146                         break;
147                 case 's': 
148                         skshash = true;
149                         break;
150                 case 'u': 
151                         update = true;
152                         break;
153                 case 'v': 
154                         verbose = true;
155                         setlogthreshold(LOGTHING_INFO);
156                         break;
157                 }
158         }
159
160         readconfig(configfile);
161         initlogthing("onak", config.logfile);
162         catchsignals();
163
164         if ((argc - optind) < 1) {
165                 usage();
166         } else if (!strcmp("dump", argv[optind])) {
167                 config.dbbackend->initdb(true);
168                 dumpstate.count = dumpstate.filenum = 0;
169                 dumpstate.maxcount = 100000;
170                 dumpstate.fd = -1;
171                 dumpstate.filebase = "keydump.%d.pgp";
172                 config.dbbackend->iterate_keys(dump_func, &dumpstate);
173                 if (dumpstate.fd != -1) {
174                         close(dumpstate.fd);
175                         dumpstate.fd = -1;
176                 }
177                 config.dbbackend->cleanupdb();
178         } else if (!strcmp("add", argv[optind])) {
179                 if (binary) {
180                         result = read_openpgp_stream(stdin_getchar, NULL,
181                                  &packets, 0);
182                         logthing(LOGTHING_INFO,
183                                         "read_openpgp_stream: %d", result);
184                 } else {
185                         dearmor_openpgp_stream(stdin_getchar, NULL, &packets);
186                 }
187                 if (packets != NULL) {
188                         result = parse_keys(packets, &keys);
189                         free_packet_list(packets);
190                         packets = NULL;
191                         logthing(LOGTHING_INFO, "Finished reading %d keys.",
192                                         result);
193
194                         result = cleankeys(keys);
195                         logthing(LOGTHING_INFO, "%d keys cleaned.",
196                                         result);
197
198                         config.dbbackend->initdb(false);
199                         logthing(LOGTHING_NOTICE, "Got %d new keys.",
200                                         config.dbbackend->update_keys(&keys,
201                                         false));
202                         if (keys != NULL && update) {
203                                 flatten_publickey(keys,
204                                         &packets,
205                                         &list_end);
206                                 if (binary) {
207                                         write_openpgp_stream(stdout_putchar,
208                                                         NULL,
209                                                         packets);
210                                 } else {
211                                         armor_openpgp_stream(stdout_putchar,
212                                                 NULL,
213                                                 packets);
214                                 }
215                                 free_packet_list(packets);
216                                 packets = NULL;
217                         }
218                         config.dbbackend->cleanupdb();
219                 } else {
220                         rc = 1;
221                         logthing(LOGTHING_NOTICE, "No keys read.");
222                 }
223
224                 if (keys != NULL) {
225                         free_publickey(keys);
226                         keys = NULL;
227                 } else {
228                         rc = 1;
229                         logthing(LOGTHING_NOTICE, "No changes.");
230                 }
231         } else if (!strcmp("clean", argv[optind])) {
232                 if (binary) {
233                         result = read_openpgp_stream(stdin_getchar, NULL,
234                                  &packets, 0);
235                         logthing(LOGTHING_INFO,
236                                         "read_openpgp_stream: %d", result);
237                 } else {
238                         dearmor_openpgp_stream(stdin_getchar, NULL, &packets);
239                 }
240
241                 if (packets != NULL) {
242                         result = parse_keys(packets, &keys);
243                         free_packet_list(packets);
244                         packets = NULL;
245                         logthing(LOGTHING_INFO, "Finished reading %d keys.",
246                                         result);
247
248                         if (keys != NULL) {
249                                 result = cleankeys(keys);
250                                 logthing(LOGTHING_INFO, "%d keys cleaned.",
251                                                 result);
252
253                                 flatten_publickey(keys,
254                                         &packets,
255                                         &list_end);
256
257                                 if (binary) {
258                                         write_openpgp_stream(stdout_putchar,
259                                                         NULL,
260                                                         packets);
261                                 } else {
262                                         armor_openpgp_stream(stdout_putchar,
263                                                 NULL,
264                                                 packets);
265                                 }
266                                 free_packet_list(packets);
267                                 packets = NULL;
268                         }
269                 } else {
270                         rc = 1;
271                         logthing(LOGTHING_NOTICE, "No keys read.");
272                 }
273                 
274                 if (keys != NULL) {
275                         free_publickey(keys);
276                         keys = NULL;
277                 }
278         } else if ((argc - optind) == 2) {
279                 search = argv[optind+1];
280                 if (search != NULL) {
281                         keyid = strtoul(search, &end, 16);
282                         if (*search != 0 &&
283                                         end != NULL &&
284                                         *end == 0) {
285                                 ishex = true;
286                         }
287                 }
288                 config.dbbackend->initdb(false);
289                 if (!strcmp("index", argv[optind])) {
290                         find_keys(search, keyid, ishex, fingerprint, skshash,
291                                         false, false);
292                 } else if (!strcmp("vindex", argv[optind])) {
293                         find_keys(search, keyid, ishex, fingerprint, skshash,
294                                         false, true);
295                 } else if (!strcmp("getphoto", argv[optind])) {
296                         if (!ishex) {
297                                 puts("Can't get a key on uid text."
298                                         " You must supply a keyid.");
299                         } else if (config.dbbackend->fetch_key(keyid, &keys,
300                                         false)) {
301                                 unsigned char *photo = NULL;
302                                 size_t         length = 0;
303
304                                 if (getphoto(keys, 0, &photo, &length)) {
305                                         fwrite(photo,
306                                                 1,
307                                                 length,
308                                                 stdout);
309                                 }
310                                 free_publickey(keys);
311                                 keys = NULL;
312                         } else {
313                                 puts("Key not found");
314                         }
315                 } else if (!strcmp("delete", argv[optind])) {
316                         config.dbbackend->delete_key(
317                                         config.dbbackend->getfullkeyid(keyid),
318                                         false);
319                 } else if (!strcmp("get", argv[optind])) {
320                         if (!ishex) {
321                                 puts("Can't get a key on uid text."
322                                         " You must supply a keyid.");
323                         } else if (config.dbbackend->fetch_key(keyid, &keys,
324                                         false)) {
325                                 logthing(LOGTHING_INFO, "Got key.");
326                                 flatten_publickey(keys,
327                                                 &packets,
328                                                 &list_end);
329                                 free_publickey(keys);
330                                 if (binary) {
331                                         write_openpgp_stream(stdout_putchar,
332                                                 NULL,
333                                                 packets);
334                                 } else {
335                                         armor_openpgp_stream(stdout_putchar,
336                                                 NULL,
337                                                 packets);
338                                 }
339                                 free_packet_list(packets);
340                                 packets = NULL;
341                         } else {
342                                 puts("Key not found");
343                         }
344                 } else if (!strcmp("hget", argv[optind])) {
345                         if (!parse_skshash(search, &hash)) {
346                                 puts("Couldn't parse sks hash.");
347                         } else if (config.dbbackend->fetch_key_skshash(&hash,
348                                         &keys)) {
349                                 logthing(LOGTHING_INFO, "Got key.");
350                                 flatten_publickey(keys,
351                                                 &packets,
352                                                 &list_end);
353                                 free_publickey(keys);
354                                 if (binary) {
355                                         write_openpgp_stream(stdout_putchar,
356                                                 NULL,
357                                                 packets);
358                                 } else {
359                                         armor_openpgp_stream(stdout_putchar,
360                                                 NULL,
361                                                 packets);
362                                 }
363                                 free_packet_list(packets);
364                                 packets = NULL;
365                         } else {
366                                 puts("Key not found");
367                         }
368                 }
369                 config.dbbackend->cleanupdb();
370         } else {
371                 usage();
372         }
373
374         cleanuplogthing();
375         cleanupconfig();
376
377         return rc;
378 }