From 698ba5ae3cc6ceee476188ad794b295e664793bf Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 23 Aug 2007 17:41:22 +0000 Subject: Add new features to kbxutil. Fixed bug 829 (can't encrypt if duplicated certs are in the keybox) --- kbx/ChangeLog | 9 ++ kbx/kbxutil.c | 43 +++++++++- kbx/keybox-defs.h | 5 +- kbx/keybox-dump.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 281 insertions(+), 16 deletions(-) (limited to 'kbx') diff --git a/kbx/ChangeLog b/kbx/ChangeLog index b36746d0b..edcf917fd 100644 --- a/kbx/ChangeLog +++ b/kbx/ChangeLog @@ -1,3 +1,12 @@ +2007-08-23 Werner Koch + + * kbxutil.c: New commands --find-dups and --cut. New options + --from an --to. + * keybox-dump.c (hash_blob_rawdata): New. + (_keybox_dump_find_dups): New. + (open_file): Factor some code out to this. + (_keybox_dump_cut_records): New. + 2007-06-26 Werner Koch * kbxutil.c: Include init.h diff --git a/kbx/kbxutil.c b/kbx/kbxutil.c index 8bc545dc9..cadc06775 100644 --- a/kbx/kbxutil.c +++ b/kbx/kbxutil.c @@ -1,5 +1,5 @@ /* kbxutil.c - The Keybox utility - * Copyright (C) 2000, 2001, 2004 Free Software Foundation, Inc. + * Copyright (C) 2000, 2001, 2004, 2007 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -25,6 +25,7 @@ #include #include #include +#include #include #define JNLIB_NEED_LOG_LOGV @@ -52,12 +53,15 @@ enum cmd_and_opt_values { aFindByUid, aStats, aImportOpenPGP, + aFindDups, + aCut, oDebug, oDebugAll, oNoArmor, - + oFrom, + oTo, aTest }; @@ -71,9 +75,13 @@ static ARGPARSE_OPTS opts[] = { /* { aFindByUid, "find-by-uid", 0, "|NAME| find key by user name" }, */ { aStats, "stats", 0, "show key statistics" }, { aImportOpenPGP, "import-openpgp", 0, "import OpenPGP keyblocks"}, + { aFindDups, "find-dups", 0, "find duplicates" }, + { aCut, "cut", 0, "export records" }, { 301, NULL, 0, N_("@\nOptions:\n ") }, + { oFrom, "from", 4, "|N|first record to export" }, + { oTo, "to", 4, "|N|last record to export" }, /* { oArmor, "armor", 0, N_("create ascii armored output")}, */ /* { oArmor, "armour", 0, "@" }, */ /* { oOutput, "output", 2, N_("use as output file")}, */ @@ -402,6 +410,7 @@ main( int argc, char **argv ) { ARGPARSE_ARGS pargs; enum cmd_and_opt_values cmd = 0; + unsigned long from = 0, to = ULONG_MAX; set_strusage( my_strusage ); gcry_control (GCRYCTL_DISABLE_SECMEM); @@ -452,14 +461,24 @@ main( int argc, char **argv ) case aFindByUid: case aStats: case aImportOpenPGP: + case aFindDups: + case aCut: cmd = pargs.r_opt; break; + case oFrom: from = pargs.r.ret_ulong; break; + case oTo: to = pargs.r.ret_ulong; break; + default: pargs.err = 2; break; } } + + if (to < from) + log_error ("record number of \"--to\" is lower than \"--from\" one\n"); + + if (log_get_errorcount(0) ) myexit(2); @@ -483,6 +502,26 @@ main( int argc, char **argv ) _keybox_dump_file (*argv, 1, stdout); } } + else if (cmd == aFindDups ) + { + if (!argc) + _keybox_dump_find_dups (NULL, 0, stdout); + else + { + for (; argc; argc--, argv++) + _keybox_dump_find_dups (*argv, 0, stdout); + } + } + else if (cmd == aCut ) + { + if (!argc) + _keybox_dump_cut_records (NULL, from, to, stdout); + else + { + for (; argc; argc--, argv++) + _keybox_dump_cut_records (*argv, from, to, stdout); + } + } else if (cmd == aImportOpenPGP) { if (!argc) diff --git a/kbx/keybox-defs.h b/kbx/keybox-defs.h index ffdca2b9b..c425cdd40 100644 --- a/kbx/keybox-defs.h +++ b/kbx/keybox-defs.h @@ -169,6 +169,9 @@ gpg_err_code_t _keybox_get_flag_location (const unsigned char *buffer, /*-- keybox-dump.c --*/ int _keybox_dump_blob (KEYBOXBLOB blob, FILE *fp); int _keybox_dump_file (const char *filename, int stats_only, FILE *outfp); +int _keybox_dump_find_dups (const char *filename, int print_them, FILE *outfp); +int _keybox_dump_cut_records (const char *filename, unsigned long from, + unsigned long to, FILE *outfp); /*-- keybox-util.c --*/ @@ -186,7 +189,7 @@ void _keybox_free (void *p); #define DIM(v) (sizeof(v)/sizeof((v)[0])) #define DIMof(type,member) DIM(((type *)0)->member) #ifndef STR - #define STR(v) #v +# define STR(v) #v #endif #define STR2(v) STR(v) diff --git a/kbx/keybox-dump.c b/kbx/keybox-dump.c index 1525a033f..fe68bf1fc 100644 --- a/kbx/keybox-dump.c +++ b/kbx/keybox-dump.c @@ -24,6 +24,11 @@ #include #include "keybox-defs.h" +#include + +/* Argg, we can't include ../common/util.h */ +char *bin2hexcolon (const void *buffer, size_t length, char *stringbuf); + static ulong get32 (const byte *buffer) @@ -183,6 +188,9 @@ _keybox_dump_blob (KEYBOXBLOB blob, FILE *fp) fprintf( fp, "Data-Offset: %lu\n", rawdata_off ); fprintf( fp, "Data-Length: %lu\n", rawdata_len ); + if (rawdata_off > length || rawdata_len > length + || rawdata_off+rawdata_off > length) + fprintf (fp, "[Error: raw data larger than blob]\n"); nkeys = get16 (buffer + 16); fprintf (fp, "Key-Count: %lu\n", nkeys ); @@ -322,6 +330,53 @@ _keybox_dump_blob (KEYBOXBLOB blob, FILE *fp) } +/* Compute the SHA_1 checksum of teh rawdata in BLOB and aput it into + DIGEST. */ +static int +hash_blob_rawdata (KEYBOXBLOB blob, unsigned char *digest) +{ + const unsigned char *buffer; + size_t n, length; + int type; + ulong rawdata_off, rawdata_len; + + buffer = _keybox_get_blob_image (blob, &length); + + if (length < 32) + return -1; + n = get32 (buffer); + if (n < length) + length = n; /* Blob larger than length in header - ignore the rest. */ + + type = buffer[4]; + switch (type) + { + case BLOBTYPE_PGP: + case BLOBTYPE_X509: + break; + + case BLOBTYPE_EMPTY: + case BLOBTYPE_HEADER: + default: + memset (digest, 0, 20); + return 0; + } + + if (length < 40) + return -1; + + rawdata_off = get32 (buffer + 8); + rawdata_len = get32 (buffer + 12); + + if (rawdata_off > length || rawdata_len > length + || rawdata_off+rawdata_off > length) + return -1; /* Out of bounds. */ + + gcry_md_hash_buffer (GCRY_MD_SHA1, digest, buffer+rawdata_off, rawdata_len); + return 0; +} + + struct file_stats_s { unsigned long too_short_blobs; @@ -401,6 +456,29 @@ update_stats (KEYBOXBLOB blob, struct file_stats_s *s) +static FILE * +open_file (const char **filename, FILE *outfp) +{ + FILE *fp; + + if (!*filename) + { + *filename = "-"; + fp = stdin; + } + else + fp = fopen (*filename, "rb"); + if (!fp) + { + int save_errno = errno; + fprintf (outfp, "can't open `%s': %s\n", *filename, strerror(errno)); + errno = save_errno; + } + return fp; +} + + + int _keybox_dump_file (const char *filename, int stats_only, FILE *outfp) { @@ -412,19 +490,8 @@ _keybox_dump_file (const char *filename, int stats_only, FILE *outfp) memset (&stats, 0, sizeof stats); - if (!filename) - { - filename = "-"; - fp = stdin; - } - else - fp = fopen (filename, "rb"); - if (!fp) - { - gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno)); - fprintf (outfp, "can't open `%s': %s\n", filename, strerror(errno)); - return tmperr; - } + if (!(fp = open_file (&filename, outfp))) + return gpg_error_from_syserror (); while ( !(rc = _keybox_read_blob (&blob, fp)) ) { @@ -481,3 +548,150 @@ _keybox_dump_file (const char *filename, int stats_only, FILE *outfp) return rc; } + + + +struct dupitem_s +{ + unsigned long recno; + unsigned char digest[20]; +}; + + +static int +cmp_dupitems (const void *arg_a, const void *arg_b) +{ + struct dupitem_s *a = (struct dupitem_s *)arg_a; + struct dupitem_s *b = (struct dupitem_s *)arg_b; + + return memcmp (a->digest, b->digest, 20); +} + + +int +_keybox_dump_find_dups (const char *filename, int print_them, FILE *outfp) +{ + FILE *fp; + KEYBOXBLOB blob; + int rc; + unsigned long recno = 0; + unsigned char zerodigest[20]; + struct dupitem_s *dupitems; + size_t dupitems_size, dupitems_count, lastn, n; + char fprbuf[3*20+1]; + + memset (zerodigest, 0, sizeof zerodigest); + + if (!(fp = open_file (&filename, outfp))) + return gpg_error_from_syserror (); + + dupitems_size = 1000; + dupitems = malloc (dupitems_size * sizeof *dupitems); + if (!dupitems) + { + gpg_error_t tmperr = gpg_error_from_syserror (); + fprintf (outfp, "error allocating array for `%s': %s\n", + filename, strerror(errno)); + return tmperr; + } + dupitems_count = 0; + + while ( !(rc = _keybox_read_blob (&blob, fp)) ) + { + unsigned char digest[20]; + + if (hash_blob_rawdata (blob, digest)) + fprintf (outfp, "error in blob %ld of `%s'\n", recno, filename); + else if (memcmp (digest, zerodigest, 20)) + { + if (dupitems_count >= dupitems_size) + { + struct dupitem_s *tmp; + + dupitems_size += 1000; + tmp = realloc (dupitems, dupitems_size * sizeof *dupitems); + if (!tmp) + { + gpg_error_t tmperr = gpg_error_from_syserror (); + fprintf (outfp, "error reallocating array for `%s': %s\n", + filename, strerror(errno)); + free (dupitems); + return tmperr; + } + dupitems = tmp; + } + dupitems[dupitems_count].recno = recno; + memcpy (dupitems[dupitems_count].digest, digest, 20); + dupitems_count++; + } + _keybox_release_blob (blob); + recno++; + } + if (rc == -1) + rc = 0; + if (rc) + fprintf (outfp, "error reading `%s': %s\n", filename, gpg_strerror (rc)); + if (fp != stdin) + fclose (fp); + + qsort (dupitems, dupitems_count, sizeof *dupitems, cmp_dupitems); + + for (lastn=0, n=1; n < dupitems_count; lastn=n, n++) + { + if (!memcmp (dupitems[lastn].digest, dupitems[n].digest, 20)) + { + bin2hexcolon (dupitems[lastn].digest, 20, fprbuf); + fprintf (outfp, "fpr=%s recno=%lu", fprbuf, dupitems[lastn].recno); + do + fprintf (outfp, " %lu", dupitems[n].recno); + while (++n < dupitems_count + && !memcmp (dupitems[lastn].digest, dupitems[n].digest, 20)); + putc ('\n', outfp); + n--; + } + } + + free (dupitems); + + return rc; +} + + +/* Print records with record numbers FROM to TO to OUTFP. */ +int +_keybox_dump_cut_records (const char *filename, unsigned long from, + unsigned long to, FILE *outfp) +{ + FILE *fp; + KEYBOXBLOB blob; + int rc; + unsigned long recno = 0; + + if (!(fp = open_file (&filename, stderr))) + return gpg_error_from_syserror (); + + while ( !(rc = _keybox_read_blob (&blob, fp)) ) + { + if (recno > to) + break; /* Ready. */ + if (recno >= from) + { + if ((rc = _keybox_write_blob (blob, outfp))) + { + fprintf (stderr, "error writing output: %s\n", + gpg_strerror (rc)); + goto leave; + } + } + _keybox_release_blob (blob); + recno++; + } + if (rc == -1) + rc = 0; + if (rc) + fprintf (stderr, "error reading `%s': %s\n", filename, gpg_strerror (rc)); + leave: + if (fp != stdin) + fclose (fp); + return rc; +} -- cgit v1.2.3