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