Line data Source code
1 : /* kbxutil.c - The Keybox utility
2 : * Copyright (C) 2000, 2001, 2004, 2007, 2011 Free Software Foundation, Inc.
3 : *
4 : * This file is part of GnuPG.
5 : *
6 : * GnuPG is free software; you can redistribute it and/or modify
7 : * it under the terms of the GNU General Public License as published by
8 : * the Free Software Foundation; either version 3 of the License, or
9 : * (at your option) any later version.
10 : *
11 : * GnuPG is distributed in the hope that it will be useful,
12 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 : * GNU General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU General Public License
17 : * along with this program; if not, see <https://www.gnu.org/licenses/>.
18 : */
19 :
20 : #include <config.h>
21 : #include <errno.h>
22 : #include <stdio.h>
23 : #include <stdlib.h>
24 : #include <string.h>
25 : #include <ctype.h>
26 : #include <sys/stat.h>
27 : #include <unistd.h>
28 : #include <limits.h>
29 : #include <assert.h>
30 :
31 : #include <gpg-error.h>
32 : #include "../common/logging.h"
33 : #include "../common/argparse.h"
34 : #include "../common/stringhelp.h"
35 : #include "../common/utf8conv.h"
36 : #include "i18n.h"
37 : #include "keybox-defs.h"
38 : #include "../common/init.h"
39 : #include <gcrypt.h>
40 :
41 :
42 : enum cmd_and_opt_values {
43 : aNull = 0,
44 : oArmor = 'a',
45 : oDryRun = 'n',
46 : oOutput = 'o',
47 : oQuiet = 'q',
48 : oVerbose = 'v',
49 :
50 : aNoSuchCmd = 500, /* force other values not to be a letter */
51 : aFindByFpr,
52 : aFindByKid,
53 : aFindByUid,
54 : aStats,
55 : aImportOpenPGP,
56 : aFindDups,
57 : aCut,
58 :
59 : oDebug,
60 : oDebugAll,
61 :
62 : oNoArmor,
63 : oFrom,
64 : oTo,
65 :
66 : aTest
67 : };
68 :
69 :
70 : static ARGPARSE_OPTS opts[] = {
71 : { 300, NULL, 0, N_("@Commands:\n ") },
72 :
73 : /* { aFindByFpr, "find-by-fpr", 0, "|FPR| find key using it's fingerprnt" }, */
74 : /* { aFindByKid, "find-by-kid", 0, "|KID| find key using it's keyid" }, */
75 : /* { aFindByUid, "find-by-uid", 0, "|NAME| find key by user name" }, */
76 : { aStats, "stats", 0, "show key statistics" },
77 : { aImportOpenPGP, "import-openpgp", 0, "import OpenPGP keyblocks"},
78 : { aFindDups, "find-dups", 0, "find duplicates" },
79 : { aCut, "cut", 0, "export records" },
80 :
81 : { 301, NULL, 0, N_("@\nOptions:\n ") },
82 :
83 : { oFrom, "from", 4, "|N|first record to export" },
84 : { oTo, "to", 4, "|N|last record to export" },
85 : /* { oArmor, "armor", 0, N_("create ascii armored output")}, */
86 : /* { oArmor, "armour", 0, "@" }, */
87 : /* { oOutput, "output", 2, N_("use as output file")}, */
88 : { oVerbose, "verbose", 0, N_("verbose") },
89 : { oQuiet, "quiet", 0, N_("be somewhat more quiet") },
90 : { oDryRun, "dry-run", 0, N_("do not make any changes") },
91 :
92 : { oDebug, "debug" ,4|16, N_("set debugging flags")},
93 : { oDebugAll, "debug-all" ,0, N_("enable full debugging")},
94 :
95 : {0} /* end of list */
96 : };
97 :
98 :
99 : void myexit (int rc);
100 :
101 : int keybox_errors_seen = 0;
102 :
103 :
104 : static const char *
105 0 : my_strusage( int level )
106 : {
107 : const char *p;
108 0 : switch( level ) {
109 0 : case 11: p = "kbxutil (@GNUPG@)";
110 0 : break;
111 0 : case 13: p = VERSION; break;
112 0 : case 17: p = PRINTABLE_OS_NAME; break;
113 0 : case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
114 :
115 : case 1:
116 0 : case 40: p =
117 : _("Usage: kbxutil [options] [files] (-h for help)");
118 0 : break;
119 0 : case 41: p =
120 : _("Syntax: kbxutil [options] [files]\n"
121 : "List, export, import Keybox data\n");
122 0 : break;
123 :
124 :
125 0 : default: p = NULL;
126 : }
127 0 : return p;
128 : }
129 :
130 :
131 : /* Used by gcry for logging */
132 : static void
133 0 : my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
134 : {
135 : (void)dummy;
136 :
137 : /* Map the log levels. */
138 0 : switch (level)
139 : {
140 0 : case GCRY_LOG_CONT: level = GPGRT_LOG_CONT; break;
141 0 : case GCRY_LOG_INFO: level = GPGRT_LOG_INFO; break;
142 0 : case GCRY_LOG_WARN: level = GPGRT_LOG_WARN; break;
143 0 : case GCRY_LOG_ERROR:level = GPGRT_LOG_ERROR; break;
144 0 : case GCRY_LOG_FATAL:level = GPGRT_LOG_FATAL; break;
145 0 : case GCRY_LOG_BUG: level = GPGRT_LOG_BUG; break;
146 0 : case GCRY_LOG_DEBUG:level = GPGRT_LOG_DEBUG; break;
147 0 : default: level = GPGRT_LOG_ERROR; break;
148 : }
149 0 : log_logv (level, fmt, arg_ptr);
150 0 : }
151 :
152 :
153 :
154 : /* static void */
155 : /* wrong_args( const char *text ) */
156 : /* { */
157 : /* log_error("usage: kbxutil %s\n", text); */
158 : /* myexit ( 1 ); */
159 : /* } */
160 :
161 :
162 : #if 0
163 : static int
164 : hextobyte( const byte *s )
165 : {
166 : int c;
167 :
168 : if( *s >= '0' && *s <= '9' )
169 : c = 16 * (*s - '0');
170 : else if( *s >= 'A' && *s <= 'F' )
171 : c = 16 * (10 + *s - 'A');
172 : else if( *s >= 'a' && *s <= 'f' )
173 : c = 16 * (10 + *s - 'a');
174 : else
175 : return -1;
176 : s++;
177 : if( *s >= '0' && *s <= '9' )
178 : c += *s - '0';
179 : else if( *s >= 'A' && *s <= 'F' )
180 : c += 10 + *s - 'A';
181 : else if( *s >= 'a' && *s <= 'f' )
182 : c += 10 + *s - 'a';
183 : else
184 : return -1;
185 : return c;
186 : }
187 : #endif
188 :
189 : #if 0
190 : static char *
191 : format_fingerprint ( const char *s )
192 : {
193 : int i, c;
194 : byte fpr[20];
195 :
196 : for (i=0; i < 20 && *s; ) {
197 : if ( *s == ' ' || *s == '\t' ) {
198 : s++;
199 : continue;
200 : }
201 : c = hextobyte(s);
202 : if (c == -1) {
203 : return NULL;
204 : }
205 : fpr[i++] = c;
206 : s += 2;
207 : }
208 : return gcry_xstrdup ( fpr );
209 : }
210 : #endif
211 :
212 : #if 0
213 : static int
214 : format_keyid ( const char *s, u32 *kid )
215 : {
216 : char helpbuf[9];
217 : switch ( strlen ( s ) ) {
218 : case 8:
219 : kid[0] = 0;
220 : kid[1] = strtoul( s, NULL, 16 );
221 : return 10;
222 :
223 : case 16:
224 : mem2str( helpbuf, s, 9 );
225 : kid[0] = strtoul( helpbuf, NULL, 16 );
226 : kid[1] = strtoul( s+8, NULL, 16 );
227 : return 11;
228 : }
229 : return 0; /* error */
230 : }
231 : #endif
232 :
233 : static char *
234 0 : read_file (const char *fname, size_t *r_length)
235 : {
236 : FILE *fp;
237 : char *buf;
238 : size_t buflen;
239 :
240 0 : if (!strcmp (fname, "-"))
241 : {
242 0 : size_t nread, bufsize = 0;
243 :
244 0 : fp = stdin;
245 0 : buf = NULL;
246 0 : buflen = 0;
247 : #define NCHUNK 8192
248 : do
249 : {
250 0 : bufsize += NCHUNK;
251 0 : if (!buf)
252 0 : buf = xtrymalloc (bufsize);
253 : else
254 0 : buf = xtryrealloc (buf, bufsize);
255 0 : if (!buf)
256 0 : log_fatal ("can't allocate buffer: %s\n", strerror (errno));
257 :
258 0 : nread = fread (buf+buflen, 1, NCHUNK, fp);
259 0 : if (nread < NCHUNK && ferror (fp))
260 : {
261 0 : log_error ("error reading '[stdin]': %s\n", strerror (errno));
262 0 : xfree (buf);
263 0 : return NULL;
264 : }
265 0 : buflen += nread;
266 : }
267 0 : while (nread == NCHUNK);
268 : #undef NCHUNK
269 :
270 : }
271 : else
272 : {
273 : struct stat st;
274 :
275 0 : fp = fopen (fname, "rb");
276 0 : if (!fp)
277 : {
278 0 : log_error ("can't open '%s': %s\n", fname, strerror (errno));
279 0 : return NULL;
280 : }
281 :
282 0 : if (fstat (fileno(fp), &st))
283 : {
284 0 : log_error ("can't stat '%s': %s\n", fname, strerror (errno));
285 0 : fclose (fp);
286 0 : return NULL;
287 : }
288 :
289 0 : buflen = st.st_size;
290 0 : buf = xtrymalloc (buflen+1);
291 0 : if (!buf)
292 0 : log_fatal ("can't allocate buffer: %s\n", strerror (errno));
293 0 : if (fread (buf, buflen, 1, fp) != 1)
294 : {
295 0 : log_error ("error reading '%s': %s\n", fname, strerror (errno));
296 0 : fclose (fp);
297 0 : xfree (buf);
298 0 : return NULL;
299 : }
300 0 : fclose (fp);
301 : }
302 :
303 0 : *r_length = buflen;
304 0 : return buf;
305 : }
306 :
307 :
308 : static void
309 0 : dump_fpr (const unsigned char *buffer, size_t len)
310 : {
311 : int i;
312 :
313 0 : for (i=0; i < len; i++, buffer++)
314 : {
315 0 : if (len == 20)
316 : {
317 0 : if (i == 10)
318 0 : putchar (' ');
319 0 : printf (" %02X%02X", buffer[0], buffer[1]);
320 0 : i++; buffer++;
321 : }
322 : else
323 : {
324 0 : if (i && !(i % 8))
325 0 : putchar (' ');
326 0 : printf (" %02X", buffer[0]);
327 : }
328 : }
329 0 : }
330 :
331 :
332 : static void
333 0 : dump_openpgp_key (keybox_openpgp_info_t info, const unsigned char *image)
334 : {
335 0 : printf ("pub %2d %02X%02X%02X%02X",
336 : info->primary.algo,
337 0 : info->primary.keyid[4], info->primary.keyid[5],
338 0 : info->primary.keyid[6], info->primary.keyid[7] );
339 0 : dump_fpr (info->primary.fpr, info->primary.fprlen);
340 0 : putchar ('\n');
341 0 : if (info->nsubkeys)
342 : {
343 : struct _keybox_openpgp_key_info *k;
344 :
345 0 : k = &info->subkeys;
346 : do
347 : {
348 0 : printf ("sub %2d %02X%02X%02X%02X",
349 : k->algo,
350 0 : k->keyid[4], k->keyid[5],
351 0 : k->keyid[6], k->keyid[7] );
352 0 : dump_fpr (k->fpr, k->fprlen);
353 0 : putchar ('\n');
354 0 : k = k->next;
355 : }
356 0 : while (k);
357 : }
358 0 : if (info->nuids)
359 : {
360 : struct _keybox_openpgp_uid_info *u;
361 :
362 0 : u = &info->uids;
363 : do
364 : {
365 0 : printf ("uid\t\t%.*s\n", (int)u->len, image + u->off);
366 0 : u = u->next;
367 : }
368 0 : while (u);
369 : }
370 0 : }
371 :
372 :
373 : static void
374 0 : import_openpgp (const char *filename, int dryrun)
375 : {
376 : gpg_error_t err;
377 : char *buffer;
378 : size_t buflen, nparsed;
379 : unsigned char *p;
380 : struct _keybox_openpgp_info info;
381 : KEYBOXBLOB blob;
382 :
383 0 : buffer = read_file (filename, &buflen);
384 0 : if (!buffer)
385 0 : return;
386 0 : p = (unsigned char *)buffer;
387 : for (;;)
388 : {
389 0 : err = _keybox_parse_openpgp (p, buflen, &nparsed, &info);
390 0 : assert (nparsed <= buflen);
391 0 : if (err)
392 : {
393 0 : if (gpg_err_code (err) == GPG_ERR_NO_DATA)
394 0 : break;
395 0 : if (gpg_err_code (err) == GPG_ERR_UNSUPPORTED_ALGORITHM)
396 : {
397 : /* This is likely a v3 key packet with a non-RSA
398 : algorithm. These are keys from very early versions
399 : of GnuPG (pre-OpenPGP). */
400 : }
401 : else
402 : {
403 0 : fflush (stdout);
404 0 : log_info ("%s: failed to parse OpenPGP keyblock: %s\n",
405 : filename, gpg_strerror (err));
406 : }
407 : }
408 : else
409 : {
410 0 : if (dryrun)
411 0 : dump_openpgp_key (&info, p);
412 : else
413 : {
414 0 : err = _keybox_create_openpgp_blob (&blob, &info, p, nparsed,
415 : NULL, 0);
416 0 : if (err)
417 : {
418 0 : fflush (stdout);
419 0 : log_error ("%s: failed to create OpenPGP keyblock: %s\n",
420 : filename, gpg_strerror (err));
421 : }
422 : else
423 : {
424 0 : err = _keybox_write_blob (blob, stdout);
425 0 : _keybox_release_blob (blob);
426 0 : if (err)
427 : {
428 0 : fflush (stdout);
429 0 : log_error ("%s: failed to write OpenPGP keyblock: %s\n",
430 : filename, gpg_strerror (err));
431 : }
432 : }
433 : }
434 :
435 0 : _keybox_destroy_openpgp_info (&info);
436 : }
437 0 : p += nparsed;
438 0 : buflen -= nparsed;
439 0 : }
440 0 : xfree (buffer);
441 : }
442 :
443 :
444 :
445 :
446 : int
447 0 : main( int argc, char **argv )
448 : {
449 : ARGPARSE_ARGS pargs;
450 0 : enum cmd_and_opt_values cmd = 0;
451 0 : unsigned long from = 0, to = ULONG_MAX;
452 0 : int dry_run = 0;
453 :
454 0 : early_system_init ();
455 0 : set_strusage( my_strusage );
456 0 : gcry_control (GCRYCTL_DISABLE_SECMEM);
457 0 : log_set_prefix ("kbxutil", GPGRT_LOG_WITH_PREFIX);
458 :
459 : /* Make sure that our subsystems are ready. */
460 0 : i18n_init ();
461 0 : init_common_subsystems (&argc, &argv);
462 :
463 0 : gcry_set_log_handler (my_gcry_logger, NULL);
464 :
465 : /*create_dotlock(NULL); register locking cleanup */
466 :
467 : /* We need to use the gcry malloc function because jnlib uses them. */
468 0 : keybox_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
469 0 : ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free );
470 :
471 :
472 0 : pargs.argc = &argc;
473 0 : pargs.argv = &argv;
474 0 : pargs.flags= 1; /* do not remove the args */
475 0 : while (arg_parse( &pargs, opts) )
476 : {
477 0 : switch (pargs.r_opt)
478 : {
479 : case oVerbose:
480 : /*opt.verbose++;*/
481 : /*gcry_control( GCRYCTL_SET_VERBOSITY, (int)opt.verbose );*/
482 0 : break;
483 : case oDebug:
484 : /*opt.debug |= pargs.r.ret_ulong; */
485 0 : break;
486 : case oDebugAll:
487 : /*opt.debug = ~0;*/
488 0 : break;
489 :
490 : case aFindByFpr:
491 : case aFindByKid:
492 : case aFindByUid:
493 : case aStats:
494 : case aImportOpenPGP:
495 : case aFindDups:
496 : case aCut:
497 0 : cmd = pargs.r_opt;
498 0 : break;
499 :
500 0 : case oFrom: from = pargs.r.ret_ulong; break;
501 0 : case oTo: to = pargs.r.ret_ulong; break;
502 :
503 0 : case oDryRun: dry_run = 1; break;
504 :
505 : default:
506 0 : pargs.err = 2;
507 0 : break;
508 : }
509 : }
510 :
511 0 : if (to < from)
512 0 : log_error ("record number of \"--to\" is lower than \"--from\" one\n");
513 :
514 :
515 0 : if (log_get_errorcount(0) )
516 0 : myexit(2);
517 :
518 0 : if (!cmd)
519 : { /* Default is to list a KBX file */
520 0 : if (!argc)
521 0 : _keybox_dump_file (NULL, 0, stdout);
522 : else
523 : {
524 0 : for (; argc; argc--, argv++)
525 0 : _keybox_dump_file (*argv, 0, stdout);
526 : }
527 : }
528 0 : else if (cmd == aStats )
529 : {
530 0 : if (!argc)
531 0 : _keybox_dump_file (NULL, 1, stdout);
532 : else
533 : {
534 0 : for (; argc; argc--, argv++)
535 0 : _keybox_dump_file (*argv, 1, stdout);
536 : }
537 : }
538 0 : else if (cmd == aFindDups )
539 : {
540 0 : if (!argc)
541 0 : _keybox_dump_find_dups (NULL, 0, stdout);
542 : else
543 : {
544 0 : for (; argc; argc--, argv++)
545 0 : _keybox_dump_find_dups (*argv, 0, stdout);
546 : }
547 : }
548 0 : else if (cmd == aCut )
549 : {
550 0 : if (!argc)
551 0 : _keybox_dump_cut_records (NULL, from, to, stdout);
552 : else
553 : {
554 0 : for (; argc; argc--, argv++)
555 0 : _keybox_dump_cut_records (*argv, from, to, stdout);
556 : }
557 : }
558 0 : else if (cmd == aImportOpenPGP)
559 : {
560 0 : if (!argc)
561 0 : import_openpgp ("-", dry_run);
562 : else
563 : {
564 0 : for (; argc; argc--, argv++)
565 0 : import_openpgp (*argv, dry_run);
566 : }
567 : }
568 : #if 0
569 : else if ( cmd == aFindByFpr )
570 : {
571 : char *fpr;
572 : if ( argc != 2 )
573 : wrong_args ("kbxfile foingerprint");
574 : fpr = format_fingerprint ( argv[1] );
575 : if ( !fpr )
576 : log_error ("invalid formatted fingerprint\n");
577 : else
578 : {
579 : kbxfile_search_by_fpr ( argv[0], fpr );
580 : gcry_free ( fpr );
581 : }
582 : }
583 : else if ( cmd == aFindByKid )
584 : {
585 : u32 kid[2];
586 : int mode;
587 :
588 : if ( argc != 2 )
589 : wrong_args ("kbxfile short-or-long-keyid");
590 : mode = format_keyid ( argv[1], kid );
591 : if ( !mode )
592 : log_error ("invalid formatted keyID\n");
593 : else
594 : {
595 : kbxfile_search_by_kid ( argv[0], kid, mode );
596 : }
597 : }
598 : else if ( cmd == aFindByUid )
599 : {
600 : if ( argc != 2 )
601 : wrong_args ("kbxfile userID");
602 : kbxfile_search_by_uid ( argv[0], argv[1] );
603 : }
604 : #endif
605 : else
606 0 : log_error ("unsupported action\n");
607 :
608 0 : myexit(0);
609 0 : return 8; /*NEVER REACHED*/
610 : }
611 :
612 :
613 : void
614 0 : myexit( int rc )
615 : {
616 : /* if( opt.debug & DBG_MEMSTAT_VALUE ) {*/
617 : /* gcry_control( GCRYCTL_DUMP_MEMORY_STATS ); */
618 : /* gcry_control( GCRYCTL_DUMP_RANDOM_STATS ); */
619 : /* }*/
620 : /* if( opt.debug ) */
621 : /* gcry_control( GCRYCTL_DUMP_SECMEM_STATS ); */
622 0 : rc = rc? rc : log_get_errorcount(0)? 2 :
623 0 : keybox_errors_seen? 1 : 0;
624 0 : exit(rc );
625 : }
|