]> the.earth.li Git - onak.git/blob - onak.c
0.6.3 release
[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, see <https://www.gnu.org/licenses/>.
19  */
20
21 #include <fcntl.h>
22 #include <getopt.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <unistd.h>
29
30 #include "build-config.h"
31
32 #include "armor.h"
33 #include "charfuncs.h"
34 #include "cleankey.h"
35 #include "cleanup.h"
36 #include "keydb.h"
37 #include "keyid.h"
38 #include "keyindex.h"
39 #include "keystructs.h"
40 #include "log.h"
41 #include "mem.h"
42 #include "merge.h"
43 #include "onak-conf.h"
44 #include "parsekey.h"
45 #include "photoid.h"
46
47 void find_keys(struct onak_dbctx *dbctx,
48                 char *search, uint64_t keyid,
49                 struct openpgp_fingerprint *fingerprint,
50                 bool ishex, bool isfp, bool dispfp, bool skshash,
51                 bool exact, bool verbose)
52 {
53         struct openpgp_publickey *publickey = NULL;
54         int count = 0;
55
56         if (ishex) {
57                 count = dbctx->fetch_key_id(dbctx, keyid, &publickey,
58                                 false);
59         } else if (isfp && exact) {
60                 count = dbctx->fetch_key(dbctx, fingerprint,
61                                 &publickey, false);
62         } else if (isfp && !exact) {
63                 count = dbctx->fetch_key_fp(dbctx, fingerprint,
64                                 &publickey, false);
65         } else {
66                 count = dbctx->fetch_key_text(dbctx, search, &publickey);
67         }
68         if (publickey != NULL) {
69                 key_index(dbctx, publickey, verbose, dispfp, skshash,
70                         false);
71                 free_publickey(publickey);
72         } else if (count == 0) {
73                 puts("Key not found.");
74         } else {
75                 printf("Found %d keys, but maximum number to return is %d.\n",
76                                 count,
77                                 config.maxkeys);
78                 puts("Try again with a more specific search.");
79         }
80 }
81
82 /**
83  * @brief Context for the keyserver dumping function
84  */
85 struct dump_ctx {
86         /** Keys we've dumped so far to this file */
87         int count;
88         /** Maximum keys to dump per file */
89         int maxcount;
90         /** File descriptor for the current dump file */
91         int fd;
92         /** Number of the current dump file */
93         int filenum;
94         /** Base filename to use for dump files */
95         char *filebase;
96 };
97
98 void dump_func(void *ctx, struct openpgp_publickey *key)
99 {
100         struct openpgp_packet_list *packets = NULL;
101         struct openpgp_packet_list *list_end = NULL;
102         struct dump_ctx *state;
103         char filename[1024];
104
105         state = (struct dump_ctx *) ctx;
106
107         if (state->fd == -1 || state->count++ > state->maxcount) {
108                 if (state->fd != -1) {
109                         close(state->fd);
110                         state->fd = -1;
111                 }
112                 snprintf(filename, 1023, state->filebase, state->filenum);
113                 state->fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0640);
114                 state->filenum++;
115                 state->count = 0;
116         }
117         flatten_publickey(key, &packets, &list_end);
118         write_openpgp_stream(file_putchar, &state->fd, packets);
119         free_packet_list(packets);
120         packets = list_end = NULL;
121
122         return;
123 }
124
125 static uint8_t hex2bin(char c)
126 {
127         if (c >= '0' && c <= '9') {
128                 return (c - '0');
129         } else if (c >= 'a' && c <= 'f') {
130                 return (c - 'a' + 10);
131         } else if (c >= 'A' && c <= 'F') {
132                 return (c - 'A' + 10);
133         }
134
135         return 255;
136 }
137
138 void usage(void) {
139         puts("onak " ONAK_VERSION " - an OpenPGP keyserver.\n");
140         puts("Usage:\n");
141         puts("\tonak [options] <command> <parameters>\n");
142         puts("\tCommands:\n");
143         puts("\tadd      - read armored OpenPGP keys from stdin and add to the"
144                 " keyserver");
145         puts("\tclean    - read armored OpenPGP keys from stdin, run the"
146                 " cleaning\n\t             routines against them and dump to"
147                 " stdout");
148         puts("\tdelete   - delete a given key from the keyserver");
149         puts("\tdump     - dump all the keys from the keyserver to a file or"
150                 " files\n\t           starting keydump*");
151         puts("\tget      - retrieves the key requested from the keyserver");
152         puts("\tgetphoto - retrieves the first photoid on the given key and"
153                 " dumps to\n\t           stdout");
154         puts("\tindex    - search for a key and list it");
155         puts("\treindex  - retrieve and re-store a key in the backend db");
156         puts("\tvindex   - search for a key and list it and its signatures");
157 }
158
159 int main(int argc, char *argv[])
160 {
161         struct openpgp_packet_list      *packets = NULL;
162         struct openpgp_packet_list      *list_end = NULL;
163         struct openpgp_publickey        *keys = NULL;
164         char                            *configfile = NULL;
165         int                              rc = EXIT_SUCCESS;
166         int                              result = 0;
167         char                            *search = NULL;
168         char                            *end = NULL;
169         uint64_t                         keyid = 0;
170         int                              i;
171         bool                             exact = false;
172         bool                             ishex = false;
173         bool                             isfp = false;
174         bool                             update = false;
175         bool                             binary = false;
176         bool                             dispfp = false;
177         bool                             skshash = false;
178         int                              optchar;
179         struct dump_ctx                  dumpstate;
180         struct skshash                   hash;
181         struct onak_dbctx               *dbctx;
182         struct openpgp_fingerprint       fingerprint;
183
184         while ((optchar = getopt(argc, argv, "bc:efsuv")) != -1 ) {
185                 switch (optchar) {
186                 case 'b': 
187                         binary = true;
188                         break;
189                 case 'c':
190                         if (configfile != NULL) {
191                                 free(configfile);
192                         }
193                         configfile = strdup(optarg);
194                         break;
195                 case 'e':
196                         exact = true;
197                         break;
198                 case 'f': 
199                         dispfp = true;
200                         break;
201                 case 's': 
202                         skshash = true;
203                         break;
204                 case 'u': 
205                         update = true;
206                         break;
207                 case 'v': 
208                         setlogthreshold(LOGTHING_INFO);
209                         break;
210                 }
211         }
212
213         readconfig(configfile);
214         initlogthing("onak", config.logfile);
215         catchsignals();
216
217         if ((argc - optind) < 1) {
218                 usage();
219         } else if (!strcmp("dump", argv[optind])) {
220                 dbctx = config.dbinit(config.backend, true);
221                 if (dbctx == NULL) {
222                         logthing(LOGTHING_ERROR,
223                                 "Failed to open key database.");
224                         rc = EXIT_FAILURE;
225                         goto err;
226                 }
227                 dumpstate.count = dumpstate.filenum = 0;
228                 dumpstate.maxcount = 100000;
229                 dumpstate.fd = -1;
230                 dumpstate.filebase = "keydump.%d.pgp";
231                 dbctx->iterate_keys(dbctx, dump_func, &dumpstate);
232                 if (dumpstate.fd != -1) {
233                         close(dumpstate.fd);
234                         dumpstate.fd = -1;
235                 }
236                 dbctx->cleanupdb(dbctx);
237         } else if (!strcmp("add", argv[optind])) {
238                 if (binary) {
239                         result = read_openpgp_stream(stdin_getchar, NULL,
240                                  &packets, 0);
241                         logthing(LOGTHING_INFO,
242                                         "read_openpgp_stream: %d", result);
243                 } else {
244                         dearmor_openpgp_stream(stdin_getchar, NULL, &packets);
245                 }
246                 if (packets != NULL) {
247                         result = parse_keys(packets, &keys);
248                         free_packet_list(packets);
249                         packets = NULL;
250                         logthing(LOGTHING_INFO, "Finished reading %d keys.",
251                                         result);
252
253                         dbctx = config.dbinit(config.backend, false);
254                         if (dbctx == NULL) {
255                                 logthing(LOGTHING_ERROR,
256                                         "Failed to open key database.");
257                                 rc = EXIT_FAILURE;
258                                 goto err;
259                         }
260                         result = cleankeys(dbctx, &keys,
261                                         config.clean_policies);
262                         logthing(LOGTHING_INFO, "%d keys cleaned.",
263                                         result);
264
265                         logthing(LOGTHING_NOTICE, "Got %d new keys.",
266                                         dbctx->update_keys(dbctx, &keys,
267                                                 &config.blacklist,
268                                                 (config.clean_policies &
269                                                  ONAK_CLEAN_UPDATE_ONLY),
270                                                 false));
271                         if (keys != NULL && update) {
272                                 flatten_publickey(keys,
273                                         &packets,
274                                         &list_end);
275                                 if (binary) {
276                                         write_openpgp_stream(stdout_putchar,
277                                                         NULL,
278                                                         packets);
279                                 } else {
280                                         armor_openpgp_stream(stdout_putchar,
281                                                 NULL,
282                                                 packets);
283                                 }
284                                 free_packet_list(packets);
285                                 packets = NULL;
286                         }
287                         dbctx->cleanupdb(dbctx);
288                 } else {
289                         rc = 1;
290                         logthing(LOGTHING_NOTICE, "No keys read.");
291                 }
292
293                 if (keys != NULL) {
294                         free_publickey(keys);
295                         keys = NULL;
296                 } else {
297                         rc = 1;
298                         logthing(LOGTHING_NOTICE, "No changes.");
299                 }
300         } else if (!strcmp("clean", argv[optind])) {
301                 dbctx = config.dbinit(config.backend, true);
302                 if (dbctx == NULL) {
303                         logthing(LOGTHING_ERROR,
304                                 "Failed to open key database.");
305                         rc = EXIT_FAILURE;
306                         goto err;
307                 }
308                 if (binary) {
309                         result = read_openpgp_stream(stdin_getchar, NULL,
310                                  &packets, 0);
311                         logthing(LOGTHING_INFO,
312                                         "read_openpgp_stream: %d", result);
313                 } else {
314                         dearmor_openpgp_stream(stdin_getchar, NULL, &packets);
315                 }
316
317                 if (packets != NULL) {
318                         result = parse_keys(packets, &keys);
319                         free_packet_list(packets);
320                         packets = NULL;
321                         logthing(LOGTHING_INFO, "Finished reading %d keys.",
322                                         result);
323
324                         if (keys != NULL) {
325                                 result = cleankeys(dbctx, &keys,
326                                                 config.clean_policies);
327                                 logthing(LOGTHING_INFO, "%d keys cleaned.",
328                                                 result);
329
330                                 flatten_publickey(keys,
331                                         &packets,
332                                         &list_end);
333
334                                 if (binary) {
335                                         write_openpgp_stream(stdout_putchar,
336                                                         NULL,
337                                                         packets);
338                                 } else {
339                                         armor_openpgp_stream(stdout_putchar,
340                                                 NULL,
341                                                 packets);
342                                 }
343                                 free_packet_list(packets);
344                                 packets = NULL;
345                         }
346                 } else {
347                         rc = 1;
348                         logthing(LOGTHING_NOTICE, "No keys read.");
349                 }
350                 
351                 if (keys != NULL) {
352                         free_publickey(keys);
353                         keys = NULL;
354                 }
355                 dbctx->cleanupdb(dbctx);
356         } else if (!strcmp("dumpconfig", argv[optind])) {
357                 if ((argc - optind) == 2) {
358                         writeconfig(argv[optind + 1]);
359                 } else {
360                         /* Dump config to stdout */
361                         writeconfig(NULL);
362                 }
363         } else if ((argc - optind) == 2) {
364                 search = argv[optind+1];
365                 if (search != NULL && strlen(search) == 42 &&
366                                 search[0] == '0' && search[1] == 'x') {
367                         /* v4 fingerprint */
368                         fingerprint.length = 20;
369                         for (i = 0; i < 20; i++) {
370                                 fingerprint.fp[i] =
371                                         (hex2bin(search[2 + i * 2]) << 4) +
372                                                 hex2bin(search[3 + i * 2]);
373                         }
374                         isfp = true;
375                 } else if (search != NULL && strlen(search) == 66 &&
376                                 search[0] == '0' && search[1] == 'x') {
377                         /* v5 fingerprint */
378                         fingerprint.length = 32;
379                         for (i = 0; i < 32; i++) {
380                                 fingerprint.fp[i] =
381                                         (hex2bin(search[2 + i * 2]) << 4) +
382                                                 hex2bin(search[3 + i * 2]);
383                         }
384                         isfp = true;
385                 } else if (search != NULL) {
386                         keyid = strtouq(search, &end, 16);
387                         if (*search != 0 &&
388                                         end != NULL &&
389                                         *end == 0) {
390                                 ishex = true;
391                         }
392                 }
393                 dbctx = config.dbinit(config.backend, false);
394                 if (dbctx == NULL) {
395                         logthing(LOGTHING_ERROR,
396                                 "Failed to open key database.");
397                         rc = EXIT_FAILURE;
398                         goto err;
399                 }
400                 if (!strcmp("index", argv[optind])) {
401                         find_keys(dbctx, search, keyid, &fingerprint, ishex,
402                                         isfp, dispfp, skshash,
403                                         exact, false);
404                 } else if (!strcmp("vindex", argv[optind])) {
405                         find_keys(dbctx, search, keyid, &fingerprint, ishex,
406                                         isfp, dispfp, skshash,
407                                         exact, true);
408                 } else if (!strcmp("getphoto", argv[optind])) {
409                         if (!ishex) {
410                                 puts("Can't get a key on uid text."
411                                         " You must supply a keyid.");
412                         } else if (dbctx->fetch_key_id(dbctx, keyid, &keys,
413                                         false)) {
414                                 unsigned char *photo = NULL;
415                                 size_t         length = 0;
416
417                                 if (getphoto(keys, 0, &photo,
418                                                 &length) == ONAK_E_OK) {
419                                         fwrite(photo,
420                                                 1,
421                                                 length,
422                                                 stdout);
423                                 }
424                                 free_publickey(keys);
425                                 keys = NULL;
426                         } else {
427                                 puts("Key not found");
428                         }
429                 } else if (!strcmp("delete", argv[optind])) {
430                         if (!isfp) {
431                                 if (dbctx->fetch_key_id(dbctx, keyid, &keys,
432                                                         false)) {
433                                         get_fingerprint(keys->publickey,
434                                                         &fingerprint);
435                                         dbctx->delete_key(dbctx, &fingerprint,
436                                                         false);
437                                         free_publickey(keys);
438                                         keys = NULL;
439                                 }
440                         } else
441                                 dbctx->delete_key(dbctx, &fingerprint, false);
442                 } else if (!strcmp("get", argv[optind])) {
443                         if (!(ishex || isfp)) {
444                                 puts("Can't get a key on uid text."
445                                         " You must supply a keyid / "
446                                         "fingerprint.");
447                         } else if ((isfp &&
448                                         dbctx->fetch_key_fp(dbctx,
449                                                 &fingerprint,
450                                                 &keys, false)) ||
451                                         (ishex &&
452                                         dbctx->fetch_key_id(dbctx, keyid,
453                                                 &keys, false))) {
454                                 logthing(LOGTHING_INFO, "Got key.");
455                                 flatten_publickey(keys,
456                                                 &packets,
457                                                 &list_end);
458                                 free_publickey(keys);
459                                 if (binary) {
460                                         write_openpgp_stream(stdout_putchar,
461                                                 NULL,
462                                                 packets);
463                                 } else {
464                                         armor_openpgp_stream(stdout_putchar,
465                                                 NULL,
466                                                 packets);
467                                 }
468                                 free_packet_list(packets);
469                                 packets = NULL;
470                         } else {
471                                 puts("Key not found");
472                         }
473                 } else if (!strcmp("hget", argv[optind])) {
474                         if (!parse_skshash(search, &hash)) {
475                                 puts("Couldn't parse sks hash.");
476                         } else if (dbctx->fetch_key_skshash(dbctx, &hash,
477                                         &keys)) {
478                                 logthing(LOGTHING_INFO, "Got key.");
479                                 flatten_publickey(keys,
480                                                 &packets,
481                                                 &list_end);
482                                 free_publickey(keys);
483                                 if (binary) {
484                                         write_openpgp_stream(stdout_putchar,
485                                                 NULL,
486                                                 packets);
487                                 } else {
488                                         armor_openpgp_stream(stdout_putchar,
489                                                 NULL,
490                                                 packets);
491                                 }
492                                 free_packet_list(packets);
493                                 packets = NULL;
494                         } else {
495                                 puts("Key not found");
496                         }
497                 } else if (!strcmp("reindex", argv[optind])) {
498                         dbctx->starttrans(dbctx);
499                         if (dbctx->fetch_key_id(dbctx, keyid, &keys, true)) {
500                                 get_fingerprint(keys->publickey, &fingerprint);
501                                 dbctx->delete_key(dbctx, &fingerprint, true);
502                                 cleankeys(dbctx, &keys, config.clean_policies);
503                                 dbctx->store_key(dbctx, keys, true, false);
504                         } else {
505                                 puts("Key not found");
506                         }
507                         dbctx->endtrans(dbctx);
508                 }
509                 dbctx->cleanupdb(dbctx);
510         } else {
511                 usage();
512         }
513
514 err:
515         cleanuplogthing();
516         cleanupconfig();
517         free(configfile);
518
519         return rc;
520 }