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