mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-23 10:29:58 +01:00
163e4ff195
* common/sysutils.c (gnupg_fopen) [W32]: Use _wfopen if needed. Use new function in most places where fopen is used. -- The config files in 2.2 are still read using fopen - we need to change this to allow Unicode directory names. There is also one case where files are written using the old fopen. The new option parser in 2.3 does not have this problem but at some places fopen is also still used. GnuPG-bug-id: 5098 Signed-off-by: Werner Koch <wk@gnupg.org>
642 lines
15 KiB
C
642 lines
15 KiB
C
/* kbxutil.c - The Keybox utility
|
|
* Copyright (C) 2000, 2001, 2004, 2007, 2011 Free Software Foundation, Inc.
|
|
*
|
|
* This file is part of GnuPG.
|
|
*
|
|
* GnuPG is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* GnuPG is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
#include <limits.h>
|
|
#include <assert.h>
|
|
|
|
#include <gpg-error.h>
|
|
#include "../common/logging.h"
|
|
#include "../common/argparse.h"
|
|
#include "../common/stringhelp.h"
|
|
#include "../common/utf8conv.h"
|
|
#include "../common/i18n.h"
|
|
#include "keybox-defs.h"
|
|
#include "../common/init.h"
|
|
#include <gcrypt.h>
|
|
|
|
|
|
enum cmd_and_opt_values {
|
|
aNull = 0,
|
|
oArmor = 'a',
|
|
oDryRun = 'n',
|
|
oOutput = 'o',
|
|
oQuiet = 'q',
|
|
oVerbose = 'v',
|
|
|
|
aNoSuchCmd = 500, /* force other values not to be a letter */
|
|
aFindByFpr,
|
|
aFindByKid,
|
|
aFindByUid,
|
|
aStats,
|
|
aImportOpenPGP,
|
|
aFindDups,
|
|
aCut,
|
|
|
|
oDebug,
|
|
oDebugAll,
|
|
|
|
oNoArmor,
|
|
oFrom,
|
|
oTo,
|
|
|
|
aTest
|
|
};
|
|
|
|
|
|
static ARGPARSE_OPTS opts[] = {
|
|
{ 300, NULL, 0, N_("@Commands:\n ") },
|
|
|
|
/* { aFindByFpr, "find-by-fpr", 0, "|FPR| find key using it's fingerprnt" }, */
|
|
/* { aFindByKid, "find-by-kid", 0, "|KID| find key using it's keyid" }, */
|
|
/* { 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")}, */
|
|
{ oVerbose, "verbose", 0, N_("verbose") },
|
|
{ oQuiet, "quiet", 0, N_("be somewhat more quiet") },
|
|
{ oDryRun, "dry-run", 0, N_("do not make any changes") },
|
|
|
|
{ oDebug, "debug" ,4|16, N_("set debugging flags")},
|
|
{ oDebugAll, "debug-all" ,0, N_("enable full debugging")},
|
|
|
|
ARGPARSE_end () /* end of list */
|
|
};
|
|
|
|
|
|
void myexit (int rc);
|
|
|
|
int keybox_errors_seen = 0;
|
|
|
|
|
|
static const char *
|
|
my_strusage( int level )
|
|
{
|
|
const char *p;
|
|
switch( level ) {
|
|
case 11: p = "kbxutil (@GNUPG@)";
|
|
break;
|
|
case 13: p = VERSION; break;
|
|
case 17: p = PRINTABLE_OS_NAME; break;
|
|
case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break;
|
|
|
|
case 1:
|
|
case 40: p =
|
|
_("Usage: kbxutil [options] [files] (-h for help)");
|
|
break;
|
|
case 41: p =
|
|
_("Syntax: kbxutil [options] [files]\n"
|
|
"List, export, import Keybox data\n");
|
|
break;
|
|
|
|
|
|
default: p = NULL;
|
|
}
|
|
return p;
|
|
}
|
|
|
|
|
|
/* Used by gcry for logging */
|
|
static void
|
|
my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
|
|
{
|
|
(void)dummy;
|
|
|
|
/* Map the log levels. */
|
|
switch (level)
|
|
{
|
|
case GCRY_LOG_CONT: level = GPGRT_LOG_CONT; break;
|
|
case GCRY_LOG_INFO: level = GPGRT_LOG_INFO; break;
|
|
case GCRY_LOG_WARN: level = GPGRT_LOG_WARN; break;
|
|
case GCRY_LOG_ERROR:level = GPGRT_LOG_ERROR; break;
|
|
case GCRY_LOG_FATAL:level = GPGRT_LOG_FATAL; break;
|
|
case GCRY_LOG_BUG: level = GPGRT_LOG_BUG; break;
|
|
case GCRY_LOG_DEBUG:level = GPGRT_LOG_DEBUG; break;
|
|
default: level = GPGRT_LOG_ERROR; break;
|
|
}
|
|
log_logv (level, fmt, arg_ptr);
|
|
}
|
|
|
|
|
|
|
|
/* static void */
|
|
/* wrong_args( const char *text ) */
|
|
/* { */
|
|
/* log_error("usage: kbxutil %s\n", text); */
|
|
/* myexit ( 1 ); */
|
|
/* } */
|
|
|
|
|
|
#if 0
|
|
static int
|
|
hextobyte( const byte *s )
|
|
{
|
|
int c;
|
|
|
|
if( *s >= '0' && *s <= '9' )
|
|
c = 16 * (*s - '0');
|
|
else if( *s >= 'A' && *s <= 'F' )
|
|
c = 16 * (10 + *s - 'A');
|
|
else if( *s >= 'a' && *s <= 'f' )
|
|
c = 16 * (10 + *s - 'a');
|
|
else
|
|
return -1;
|
|
s++;
|
|
if( *s >= '0' && *s <= '9' )
|
|
c += *s - '0';
|
|
else if( *s >= 'A' && *s <= 'F' )
|
|
c += 10 + *s - 'A';
|
|
else if( *s >= 'a' && *s <= 'f' )
|
|
c += 10 + *s - 'a';
|
|
else
|
|
return -1;
|
|
return c;
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
static char *
|
|
format_fingerprint ( const char *s )
|
|
{
|
|
int i, c;
|
|
byte fpr[20];
|
|
|
|
for (i=0; i < 20 && *s; ) {
|
|
if ( *s == ' ' || *s == '\t' ) {
|
|
s++;
|
|
continue;
|
|
}
|
|
c = hextobyte(s);
|
|
if (c == -1) {
|
|
return NULL;
|
|
}
|
|
fpr[i++] = c;
|
|
s += 2;
|
|
}
|
|
return gcry_xstrdup ( fpr );
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
static int
|
|
format_keyid ( const char *s, u32 *kid )
|
|
{
|
|
char helpbuf[9];
|
|
switch ( strlen ( s ) ) {
|
|
case 8:
|
|
kid[0] = 0;
|
|
kid[1] = strtoul( s, NULL, 16 );
|
|
return 10;
|
|
|
|
case 16:
|
|
mem2str( helpbuf, s, 9 );
|
|
kid[0] = strtoul( helpbuf, NULL, 16 );
|
|
kid[1] = strtoul( s+8, NULL, 16 );
|
|
return 11;
|
|
}
|
|
return 0; /* error */
|
|
}
|
|
#endif
|
|
|
|
static char *
|
|
read_file (const char *fname, size_t *r_length)
|
|
{
|
|
FILE *fp;
|
|
char *buf;
|
|
size_t buflen;
|
|
|
|
if (!strcmp (fname, "-"))
|
|
{
|
|
size_t nread, bufsize = 0;
|
|
|
|
fp = stdin;
|
|
buf = NULL;
|
|
buflen = 0;
|
|
#define NCHUNK 8192
|
|
do
|
|
{
|
|
bufsize += NCHUNK;
|
|
if (!buf)
|
|
buf = xtrymalloc (bufsize);
|
|
else
|
|
buf = xtryrealloc (buf, bufsize);
|
|
if (!buf)
|
|
log_fatal ("can't allocate buffer: %s\n", strerror (errno));
|
|
|
|
nread = fread (buf+buflen, 1, NCHUNK, fp);
|
|
if (nread < NCHUNK && ferror (fp))
|
|
{
|
|
log_error ("error reading '[stdin]': %s\n", strerror (errno));
|
|
xfree (buf);
|
|
return NULL;
|
|
}
|
|
buflen += nread;
|
|
}
|
|
while (nread == NCHUNK);
|
|
#undef NCHUNK
|
|
|
|
}
|
|
else
|
|
{
|
|
struct stat st;
|
|
|
|
fp = gnupg_fopen (fname, "rb");
|
|
if (!fp)
|
|
{
|
|
log_error ("can't open '%s': %s\n", fname, strerror (errno));
|
|
return NULL;
|
|
}
|
|
|
|
if (fstat (fileno(fp), &st))
|
|
{
|
|
log_error ("can't stat '%s': %s\n", fname, strerror (errno));
|
|
fclose (fp);
|
|
return NULL;
|
|
}
|
|
|
|
buflen = st.st_size;
|
|
buf = xtrymalloc (buflen+1);
|
|
if (!buf)
|
|
log_fatal ("can't allocate buffer: %s\n", strerror (errno));
|
|
if (fread (buf, buflen, 1, fp) != 1)
|
|
{
|
|
log_error ("error reading '%s': %s\n", fname, strerror (errno));
|
|
fclose (fp);
|
|
xfree (buf);
|
|
return NULL;
|
|
}
|
|
fclose (fp);
|
|
}
|
|
|
|
*r_length = buflen;
|
|
return buf;
|
|
}
|
|
|
|
|
|
static void
|
|
dump_fpr (const unsigned char *buffer, size_t len)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i < len; i++, buffer++)
|
|
{
|
|
if (len == 20)
|
|
{
|
|
if (i == 10)
|
|
putchar (' ');
|
|
printf (" %02X%02X", buffer[0], buffer[1]);
|
|
i++; buffer++;
|
|
}
|
|
else
|
|
{
|
|
if (i && !(i % 8))
|
|
putchar (' ');
|
|
printf (" %02X", buffer[0]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
dump_grip (const unsigned char *buffer, size_t len)
|
|
{
|
|
int i;
|
|
|
|
for (i=0; i < len; i++, buffer++)
|
|
{
|
|
printf ("%02X", buffer[0]);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
dump_openpgp_key (keybox_openpgp_info_t info, const unsigned char *image)
|
|
{
|
|
printf ("pub %2d %02X%02X%02X%02X",
|
|
info->primary.algo,
|
|
info->primary.keyid[4], info->primary.keyid[5],
|
|
info->primary.keyid[6], info->primary.keyid[7] );
|
|
dump_fpr (info->primary.fpr, info->primary.fprlen);
|
|
putchar ('\n');
|
|
fputs ("grp ", stdout);
|
|
dump_grip (info->primary.grip, 20);
|
|
putchar ('\n');
|
|
if (info->nsubkeys)
|
|
{
|
|
struct _keybox_openpgp_key_info *k;
|
|
|
|
k = &info->subkeys;
|
|
do
|
|
{
|
|
printf ("sub %2d %02X%02X%02X%02X",
|
|
k->algo,
|
|
k->keyid[4], k->keyid[5],
|
|
k->keyid[6], k->keyid[7] );
|
|
dump_fpr (k->fpr, k->fprlen);
|
|
putchar ('\n');
|
|
fputs ("grp ", stdout);
|
|
dump_grip (k->grip, 20);
|
|
putchar ('\n');
|
|
k = k->next;
|
|
}
|
|
while (k);
|
|
}
|
|
if (info->nuids)
|
|
{
|
|
struct _keybox_openpgp_uid_info *u;
|
|
|
|
u = &info->uids;
|
|
do
|
|
{
|
|
printf ("uid\t\t%.*s\n", (int)u->len, image + u->off);
|
|
u = u->next;
|
|
}
|
|
while (u);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
import_openpgp (const char *filename, int dryrun)
|
|
{
|
|
gpg_error_t err;
|
|
char *buffer;
|
|
size_t buflen, nparsed;
|
|
unsigned char *p;
|
|
struct _keybox_openpgp_info info;
|
|
KEYBOXBLOB blob;
|
|
|
|
buffer = read_file (filename, &buflen);
|
|
if (!buffer)
|
|
return;
|
|
p = (unsigned char *)buffer;
|
|
for (;;)
|
|
{
|
|
err = _keybox_parse_openpgp (p, buflen, &nparsed, &info);
|
|
assert (nparsed <= buflen);
|
|
if (err)
|
|
{
|
|
if (gpg_err_code (err) == GPG_ERR_NO_DATA)
|
|
break;
|
|
if (gpg_err_code (err) == GPG_ERR_UNSUPPORTED_ALGORITHM)
|
|
{
|
|
/* This is likely a v3 key packet with a non-RSA
|
|
algorithm. These are keys from very early versions
|
|
of GnuPG (pre-OpenPGP). */
|
|
}
|
|
else
|
|
{
|
|
es_fflush (es_stdout);
|
|
log_info ("%s: failed to parse OpenPGP keyblock: %s\n",
|
|
filename, gpg_strerror (err));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (dryrun)
|
|
dump_openpgp_key (&info, p);
|
|
else
|
|
{
|
|
err = _keybox_create_openpgp_blob (&blob, &info, p, nparsed, 0);
|
|
if (err)
|
|
{
|
|
es_fflush (es_stdout);
|
|
log_error ("%s: failed to create OpenPGP keyblock: %s\n",
|
|
filename, gpg_strerror (err));
|
|
}
|
|
else
|
|
{
|
|
err = _keybox_write_blob (blob, es_stdout, NULL);
|
|
_keybox_release_blob (blob);
|
|
if (err)
|
|
{
|
|
es_fflush (es_stdout);
|
|
log_error ("%s: failed to write OpenPGP keyblock: %s\n",
|
|
filename, gpg_strerror (err));
|
|
}
|
|
}
|
|
}
|
|
|
|
_keybox_destroy_openpgp_info (&info);
|
|
}
|
|
p += nparsed;
|
|
buflen -= nparsed;
|
|
}
|
|
xfree (buffer);
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
main( int argc, char **argv )
|
|
{
|
|
ARGPARSE_ARGS pargs;
|
|
enum cmd_and_opt_values cmd = 0;
|
|
unsigned long from = 0, to = ULONG_MAX;
|
|
int dry_run = 0;
|
|
|
|
early_system_init ();
|
|
set_strusage( my_strusage );
|
|
gcry_control (GCRYCTL_DISABLE_SECMEM);
|
|
log_set_prefix ("kbxutil", GPGRT_LOG_WITH_PREFIX);
|
|
|
|
/* Make sure that our subsystems are ready. */
|
|
i18n_init ();
|
|
init_common_subsystems (&argc, &argv);
|
|
|
|
gcry_set_log_handler (my_gcry_logger, NULL);
|
|
|
|
/*create_dotlock(NULL); register locking cleanup */
|
|
|
|
/* We need to use the gcry malloc function because jnlib uses them. */
|
|
ksba_set_malloc_hooks (gcry_malloc, gcry_realloc, gcry_free );
|
|
|
|
|
|
pargs.argc = &argc;
|
|
pargs.argv = &argv;
|
|
pargs.flags= 1; /* do not remove the args */
|
|
while (arg_parse( &pargs, opts) )
|
|
{
|
|
switch (pargs.r_opt)
|
|
{
|
|
case oVerbose:
|
|
/*opt.verbose++;*/
|
|
/*gcry_control( GCRYCTL_SET_VERBOSITY, (int)opt.verbose );*/
|
|
break;
|
|
case oDebug:
|
|
/*opt.debug |= pargs.r.ret_ulong; */
|
|
break;
|
|
case oDebugAll:
|
|
/*opt.debug = ~0;*/
|
|
break;
|
|
|
|
case aFindByFpr:
|
|
case aFindByKid:
|
|
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;
|
|
|
|
case oDryRun: dry_run = 1; 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);
|
|
|
|
if (!cmd)
|
|
{ /* Default is to list a KBX file */
|
|
if (!argc)
|
|
_keybox_dump_file (NULL, 0, stdout);
|
|
else
|
|
{
|
|
for (; argc; argc--, argv++)
|
|
_keybox_dump_file (*argv, 0, stdout);
|
|
}
|
|
}
|
|
else if (cmd == aStats )
|
|
{
|
|
if (!argc)
|
|
_keybox_dump_file (NULL, 1, stdout);
|
|
else
|
|
{
|
|
for (; argc; argc--, 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)
|
|
import_openpgp ("-", dry_run);
|
|
else
|
|
{
|
|
for (; argc; argc--, argv++)
|
|
import_openpgp (*argv, dry_run);
|
|
}
|
|
}
|
|
#if 0
|
|
else if ( cmd == aFindByFpr )
|
|
{
|
|
char *fpr;
|
|
if ( argc != 2 )
|
|
wrong_args ("kbxfile foingerprint");
|
|
fpr = format_fingerprint ( argv[1] );
|
|
if ( !fpr )
|
|
log_error ("invalid formatted fingerprint\n");
|
|
else
|
|
{
|
|
kbxfile_search_by_fpr ( argv[0], fpr );
|
|
gcry_free ( fpr );
|
|
}
|
|
}
|
|
else if ( cmd == aFindByKid )
|
|
{
|
|
u32 kid[2];
|
|
int mode;
|
|
|
|
if ( argc != 2 )
|
|
wrong_args ("kbxfile short-or-long-keyid");
|
|
mode = format_keyid ( argv[1], kid );
|
|
if ( !mode )
|
|
log_error ("invalid formatted keyID\n");
|
|
else
|
|
{
|
|
kbxfile_search_by_kid ( argv[0], kid, mode );
|
|
}
|
|
}
|
|
else if ( cmd == aFindByUid )
|
|
{
|
|
if ( argc != 2 )
|
|
wrong_args ("kbxfile userID");
|
|
kbxfile_search_by_uid ( argv[0], argv[1] );
|
|
}
|
|
#endif
|
|
else
|
|
log_error ("unsupported action\n");
|
|
|
|
myexit(0);
|
|
return 8; /*NEVER REACHED*/
|
|
}
|
|
|
|
|
|
void
|
|
myexit( int rc )
|
|
{
|
|
/* if( opt.debug & DBG_MEMSTAT_VALUE ) {*/
|
|
/* gcry_control( GCRYCTL_DUMP_MEMORY_STATS ); */
|
|
/* gcry_control( GCRYCTL_DUMP_RANDOM_STATS ); */
|
|
/* }*/
|
|
/* if( opt.debug ) */
|
|
/* gcry_control( GCRYCTL_DUMP_SECMEM_STATS ); */
|
|
rc = rc? rc : log_get_errorcount(0)? 2 :
|
|
keybox_errors_seen? 1 : 0;
|
|
exit(rc );
|
|
}
|