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 <http://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", 1);
458 :
459 : /* Make sure that our subsystems are ready. */
460 0 : i18n_init ();
461 0 : init_common_subsystems (&argc, &argv);
462 :
463 : /* Check that the libraries are suitable. Do it here because
464 : the option parsing may need services of the library. */
465 0 : if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
466 : {
467 0 : log_fatal (_("%s is too old (need %s, have %s)\n"), "libgcrypt",
468 : NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
469 : }
470 :
471 0 : gcry_set_log_handler (my_gcry_logger, NULL);
472 :
473 : /*create_dotlock(NULL); register locking cleanup */
474 :
475 : /* We need to use the gcry malloc function because jnlib uses them. */
476 0 : keybox_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free);
477 0 : ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free );
478 :
479 :
480 0 : pargs.argc = &argc;
481 0 : pargs.argv = &argv;
482 0 : pargs.flags= 1; /* do not remove the args */
483 0 : while (arg_parse( &pargs, opts) )
484 : {
485 0 : switch (pargs.r_opt)
486 : {
487 : case oVerbose:
488 : /*opt.verbose++;*/
489 : /*gcry_control( GCRYCTL_SET_VERBOSITY, (int)opt.verbose );*/
490 0 : break;
491 : case oDebug:
492 : /*opt.debug |= pargs.r.ret_ulong; */
493 0 : break;
494 : case oDebugAll:
495 : /*opt.debug = ~0;*/
496 0 : break;
497 :
498 : case aFindByFpr:
499 : case aFindByKid:
500 : case aFindByUid:
501 : case aStats:
502 : case aImportOpenPGP:
503 : case aFindDups:
504 : case aCut:
505 0 : cmd = pargs.r_opt;
506 0 : break;
507 :
508 0 : case oFrom: from = pargs.r.ret_ulong; break;
509 0 : case oTo: to = pargs.r.ret_ulong; break;
510 :
511 0 : case oDryRun: dry_run = 1; break;
512 :
513 : default:
514 0 : pargs.err = 2;
515 0 : break;
516 : }
517 : }
518 :
519 0 : if (to < from)
520 0 : log_error ("record number of \"--to\" is lower than \"--from\" one\n");
521 :
522 :
523 0 : if (log_get_errorcount(0) )
524 0 : myexit(2);
525 :
526 0 : if (!cmd)
527 : { /* Default is to list a KBX file */
528 0 : if (!argc)
529 0 : _keybox_dump_file (NULL, 0, stdout);
530 : else
531 : {
532 0 : for (; argc; argc--, argv++)
533 0 : _keybox_dump_file (*argv, 0, stdout);
534 : }
535 : }
536 0 : else if (cmd == aStats )
537 : {
538 0 : if (!argc)
539 0 : _keybox_dump_file (NULL, 1, stdout);
540 : else
541 : {
542 0 : for (; argc; argc--, argv++)
543 0 : _keybox_dump_file (*argv, 1, stdout);
544 : }
545 : }
546 0 : else if (cmd == aFindDups )
547 : {
548 0 : if (!argc)
549 0 : _keybox_dump_find_dups (NULL, 0, stdout);
550 : else
551 : {
552 0 : for (; argc; argc--, argv++)
553 0 : _keybox_dump_find_dups (*argv, 0, stdout);
554 : }
555 : }
556 0 : else if (cmd == aCut )
557 : {
558 0 : if (!argc)
559 0 : _keybox_dump_cut_records (NULL, from, to, stdout);
560 : else
561 : {
562 0 : for (; argc; argc--, argv++)
563 0 : _keybox_dump_cut_records (*argv, from, to, stdout);
564 : }
565 : }
566 0 : else if (cmd == aImportOpenPGP)
567 : {
568 0 : if (!argc)
569 0 : import_openpgp ("-", dry_run);
570 : else
571 : {
572 0 : for (; argc; argc--, argv++)
573 0 : import_openpgp (*argv, dry_run);
574 : }
575 : }
576 : #if 0
577 : else if ( cmd == aFindByFpr )
578 : {
579 : char *fpr;
580 : if ( argc != 2 )
581 : wrong_args ("kbxfile foingerprint");
582 : fpr = format_fingerprint ( argv[1] );
583 : if ( !fpr )
584 : log_error ("invalid formatted fingerprint\n");
585 : else
586 : {
587 : kbxfile_search_by_fpr ( argv[0], fpr );
588 : gcry_free ( fpr );
589 : }
590 : }
591 : else if ( cmd == aFindByKid )
592 : {
593 : u32 kid[2];
594 : int mode;
595 :
596 : if ( argc != 2 )
597 : wrong_args ("kbxfile short-or-long-keyid");
598 : mode = format_keyid ( argv[1], kid );
599 : if ( !mode )
600 : log_error ("invalid formatted keyID\n");
601 : else
602 : {
603 : kbxfile_search_by_kid ( argv[0], kid, mode );
604 : }
605 : }
606 : else if ( cmd == aFindByUid )
607 : {
608 : if ( argc != 2 )
609 : wrong_args ("kbxfile userID");
610 : kbxfile_search_by_uid ( argv[0], argv[1] );
611 : }
612 : #endif
613 : else
614 0 : log_error ("unsupported action\n");
615 :
616 0 : myexit(0);
617 0 : return 8; /*NEVER REACHED*/
618 : }
619 :
620 :
621 : void
622 0 : myexit( int rc )
623 : {
624 : /* if( opt.debug & DBG_MEMSTAT_VALUE ) {*/
625 : /* gcry_control( GCRYCTL_DUMP_MEMORY_STATS ); */
626 : /* gcry_control( GCRYCTL_DUMP_RANDOM_STATS ); */
627 : /* }*/
628 : /* if( opt.debug ) */
629 : /* gcry_control( GCRYCTL_DUMP_SECMEM_STATS ); */
630 0 : rc = rc? rc : log_get_errorcount(0)? 2 :
631 0 : keybox_errors_seen? 1 : 0;
632 0 : exit(rc );
633 : }
|