Add new features to kbxutil.

Fixed bug 829 (can't encrypt if duplicated certs are in the keybox)
This commit is contained in:
Werner Koch 2007-08-23 17:41:22 +00:00
parent 367a8b9111
commit 698ba5ae3c
8 changed files with 370 additions and 25 deletions

View File

@ -1,3 +1,12 @@
2007-08-23 Werner Koch <wk@g10code.com>
* 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 <wk@g10code.com>
* kbxutil.c: Include init.h

View File

@ -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 <ctype.h>
#include <sys/stat.h>
#include <unistd.h>
#include <limits.h>
#include <assert.h>
#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)

View File

@ -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)

View File

@ -24,6 +24,11 @@
#include <errno.h>
#include "keybox-defs.h"
#include <gcrypt.h>
/* 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;
}

View File

@ -1,3 +1,11 @@
2007-08-23 Werner Koch <wk@g10code.com>
* certlist.c (gpgsm_certs_identical_p): New.
(gpgsm_add_to_certlist): Ignore duplicate certificates in
ambigious name detection.
(gpgsm_find_cert): Ditto.
* export.c (gpgsm_p12_export): Ditto.
2007-08-22 Werner Koch <wk@g10code.com>
* certreqgen.c (create_request): Replace open coding by bin2hex.

View File

@ -1,5 +1,5 @@
/* certlist.c - build list of certificates
* Copyright (C) 2001, 2003, 2004, 2005 Free Software Foundation, Inc.
* Copyright (C) 2001, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -225,6 +225,25 @@ same_subject_issuer (const char *subject, const char *issuer, ksba_cert_t cert)
return tmp;
}
/* Return true if CERT_A is the same as CERT_B. */
int
gpgsm_certs_identical_p (ksba_cert_t cert_a, ksba_cert_t cert_b)
{
const unsigned char *img_a, *img_b;
size_t len_a, len_b;
img_a = ksba_cert_get_image (cert_a, &len_a);
if (img_a)
{
img_b = ksba_cert_get_image (cert_b, &len_b);
if (img_b && len_a == len_b && !memcmp (img_a, img_b, len_a))
return 1; /* Identical. */
}
return 0;
}
/* Return true if CERT is already contained in CERTLIST. */
static int
is_cert_in_certlist (ksba_cert_t cert, certlist_t certlist)
@ -330,6 +349,8 @@ gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret,
if (!rc)
{
certlist_t dup_certs = NULL;
next_ambigious:
rc = keydb_search (kh, &desc, 1);
if (rc == -1)
@ -337,23 +358,45 @@ gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret,
else if (!rc)
{
ksba_cert_t cert2 = NULL;
/* If this is the first possible duplicate, add thye orginal
certificate to our list of duplicates. */
if (!dup_certs)
gpgsm_add_cert_to_certlist (ctrl, cert, &dup_certs, 0);
/* We have to ignore ambigious names as long as
there only fault is a bad key usage */
there only fault is a bad key usage. This is
required to support encryption and signing
certifciates of the same subject.
Further we ignore them if they are due to an
identical certificate (which may happen if a
certificate is accidential duplicated in the
keybox). */
if (!keydb_get_cert (kh, &cert2))
{
int tmp = (same_subject_issuer (subject, issuer, cert2)
&& ((gpg_err_code (
secret? gpgsm_cert_use_sign_p (cert2)
: gpgsm_cert_use_encrypt_p (cert2)
: gpgsm_cert_use_encrypt_p (cert2)
)
) == GPG_ERR_WRONG_KEY_USAGE));
if (tmp)
gpgsm_add_cert_to_certlist (ctrl, cert2,
&dup_certs, 0);
else
{
if (is_cert_in_certlist (cert2, dup_certs))
tmp = 1;
}
ksba_cert_release (cert2);
if (tmp)
goto next_ambigious;
}
rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
}
gpgsm_release_certlist (dup_certs);
}
xfree (subject);
xfree (issuer);
@ -464,13 +507,27 @@ gpgsm_find_cert (const char *name, ksba_sexp_t keyid, ksba_cert_t *r_cert)
won't lead to ambiguous names. */
if (!rc && !keyid)
{
next_ambiguous:
rc = keydb_search (kh, &desc, 1);
if (rc == -1)
rc = 0;
else
{
if (!rc)
rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
{
ksba_cert_t cert2 = NULL;
if (!keydb_get_cert (kh, &cert2))
{
if (gpgsm_certs_identical_p (*r_cert, cert2))
{
ksba_cert_release (cert2);
goto next_ambiguous;
}
ksba_cert_release (cert2);
}
rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
}
ksba_cert_release (*r_cert);
*r_cert = NULL;
}

View File

@ -1,5 +1,5 @@
/* export.c
* Copyright (C) 2002, 2003, 2004 Free Software Foundation, Inc.
/* export.c - Export certificates and private keys.
* Copyright (C) 2002, 2003, 2004, 2007 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -379,10 +379,24 @@ gpgsm_p12_export (ctrl_t ctrl, const char *name, FILE *fp)
log_error ("keydb_get_cert failed: %s\n", gpg_strerror (rc));
goto leave;
}
next_ambiguous:
rc = keydb_search (hd, desc, 1);
if (!rc)
rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
{
ksba_cert_t cert2 = NULL;
if (!keydb_get_cert (hd, &cert2))
{
if (gpgsm_certs_identical_p (cert, cert2))
{
ksba_cert_release (cert2);
goto next_ambiguous;
}
ksba_cert_release (cert2);
}
rc = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
}
else if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
rc = 0;
if (rc)

View File

@ -1,5 +1,5 @@
/* gpgsm.h - Global definitions for GpgSM
* Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
* Copyright (C) 2001, 2003, 2004, 2007 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -287,6 +287,7 @@ int gpgsm_cert_use_verify_p (ksba_cert_t cert);
int gpgsm_cert_use_decrypt_p (ksba_cert_t cert);
int gpgsm_cert_use_cert_p (ksba_cert_t cert);
int gpgsm_cert_use_ocsp_p (ksba_cert_t cert);
int gpgsm_certs_identical_p (ksba_cert_t cert_a, ksba_cert_t cert_b);
int gpgsm_add_cert_to_certlist (ctrl_t ctrl, ksba_cert_t cert,
certlist_t *listaddr, int is_encrypt_to);
int gpgsm_add_to_certlist (ctrl_t ctrl, const char *name, int secret,