2010-10-01 20:33:53 +00:00
|
|
|
|
/* export.c - Export keys in the OpenPGP defined format.
|
2006-04-19 11:26:11 +00:00
|
|
|
|
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
|
2010-10-01 20:33:53 +00:00
|
|
|
|
* 2005, 2010 Free Software Foundation, Inc.
|
2016-01-08 17:22:32 +01:00
|
|
|
|
* Copyright (C) 1998-2016 Werner Koch
|
2003-06-05 07:14:21 +00:00
|
|
|
|
*
|
|
|
|
|
* 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
|
2007-07-04 19:49:40 +00:00
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
2003-06-05 07:14:21 +00:00
|
|
|
|
* (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
|
2016-11-05 12:02:19 +01:00
|
|
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
2003-06-05 07:14:21 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
2006-04-19 11:26:11 +00:00
|
|
|
|
#include "gpg.h"
|
2003-06-05 07:14:21 +00:00
|
|
|
|
#include "options.h"
|
|
|
|
|
#include "packet.h"
|
2017-03-07 20:21:23 +09:00
|
|
|
|
#include "../common/status.h"
|
2003-06-05 07:14:21 +00:00
|
|
|
|
#include "keydb.h"
|
2017-03-07 20:21:23 +09:00
|
|
|
|
#include "../common/util.h"
|
2003-06-05 07:14:21 +00:00
|
|
|
|
#include "main.h"
|
2017-03-07 20:21:23 +09:00
|
|
|
|
#include "../common/i18n.h"
|
|
|
|
|
#include "../common/membuf.h"
|
|
|
|
|
#include "../common/host2net.h"
|
|
|
|
|
#include "../common/zb32.h"
|
|
|
|
|
#include "../common/recsel.h"
|
|
|
|
|
#include "../common/mbox-util.h"
|
|
|
|
|
#include "../common/init.h"
|
2006-04-19 11:26:11 +00:00
|
|
|
|
#include "trustdb.h"
|
2010-10-01 20:33:53 +00:00
|
|
|
|
#include "call-agent.h"
|
2018-07-06 11:40:16 +02:00
|
|
|
|
#include "key-clean.h"
|
|
|
|
|
|
2006-04-19 11:26:11 +00:00
|
|
|
|
|
|
|
|
|
/* An object to keep track of subkeys. */
|
|
|
|
|
struct subkey_list_s
|
|
|
|
|
{
|
|
|
|
|
struct subkey_list_s *next;
|
|
|
|
|
u32 kid[2];
|
|
|
|
|
};
|
|
|
|
|
typedef struct subkey_list_s *subkey_list_t;
|
|
|
|
|
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2015-11-12 16:02:35 +01:00
|
|
|
|
/* An object to track statistics for export operations. */
|
|
|
|
|
struct export_stats_s
|
|
|
|
|
{
|
|
|
|
|
ulong count; /* Number of processed keys. */
|
|
|
|
|
ulong secret_count; /* Number of secret keys seen. */
|
|
|
|
|
ulong exported; /* Number of actual exported keys. */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
2016-07-01 16:50:12 +02:00
|
|
|
|
/* A global variable to store the selector created from
|
|
|
|
|
* --export-filter keep-uid=EXPR.
|
2016-09-05 09:51:16 +02:00
|
|
|
|
* --export-filter drop-subkey=EXPR.
|
2016-07-01 16:50:12 +02:00
|
|
|
|
*
|
|
|
|
|
* FIXME: We should put this into the CTRL object but that requires a
|
|
|
|
|
* lot more changes right now.
|
|
|
|
|
*/
|
|
|
|
|
static recsel_expr_t export_keep_uid;
|
2016-09-05 09:51:16 +02:00
|
|
|
|
static recsel_expr_t export_drop_subkey;
|
2016-07-01 16:50:12 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2015-11-12 16:02:35 +01:00
|
|
|
|
/* Local prototypes. */
|
|
|
|
|
static int do_export (ctrl_t ctrl, strlist_t users, int secret,
|
|
|
|
|
unsigned int options, export_stats_t stats);
|
2010-10-01 20:33:53 +00:00
|
|
|
|
static int do_export_stream (ctrl_t ctrl, iobuf_t out,
|
|
|
|
|
strlist_t users, int secret,
|
|
|
|
|
kbnode_t *keyblock_out, unsigned int options,
|
2015-11-12 16:02:35 +01:00
|
|
|
|
export_stats_t stats, int *any);
|
2016-07-12 15:09:18 +02:00
|
|
|
|
static gpg_error_t print_pka_or_dane_records
|
|
|
|
|
/**/ (iobuf_t out, kbnode_t keyblock, PKT_public_key *pk,
|
|
|
|
|
const void *data, size_t datalen,
|
|
|
|
|
int print_pka, int print_dane);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2015-11-12 16:02:35 +01:00
|
|
|
|
|
2016-07-01 16:50:12 +02:00
|
|
|
|
static void
|
|
|
|
|
cleanup_export_globals (void)
|
|
|
|
|
{
|
|
|
|
|
recsel_release (export_keep_uid);
|
|
|
|
|
export_keep_uid = NULL;
|
2016-09-05 09:51:16 +02:00
|
|
|
|
recsel_release (export_drop_subkey);
|
|
|
|
|
export_drop_subkey = NULL;
|
2016-07-01 16:50:12 +02:00
|
|
|
|
}
|
2006-04-19 11:26:11 +00:00
|
|
|
|
|
2015-11-12 16:02:35 +01:00
|
|
|
|
|
|
|
|
|
/* Option parser for export options. See parse_options fro
|
|
|
|
|
details. */
|
2003-06-05 07:14:21 +00:00
|
|
|
|
int
|
2006-04-19 11:26:11 +00:00
|
|
|
|
parse_export_options(char *str,unsigned int *options,int noisy)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
|
|
|
|
struct parse_options export_opts[]=
|
|
|
|
|
{
|
2006-04-19 11:26:11 +00:00
|
|
|
|
{"export-local-sigs",EXPORT_LOCAL_SIGS,NULL,
|
|
|
|
|
N_("export signatures that are marked as local-only")},
|
|
|
|
|
{"export-attributes",EXPORT_ATTRIBUTES,NULL,
|
|
|
|
|
N_("export attribute user IDs (generally photo IDs)")},
|
|
|
|
|
{"export-sensitive-revkeys",EXPORT_SENSITIVE_REVKEYS,NULL,
|
|
|
|
|
N_("export revocation keys marked as \"sensitive\"")},
|
|
|
|
|
{"export-clean",EXPORT_CLEAN,NULL,
|
|
|
|
|
N_("remove unusable parts from key during export")},
|
|
|
|
|
{"export-minimal",EXPORT_MINIMAL|EXPORT_CLEAN,NULL,
|
|
|
|
|
N_("remove as much as possible from key during export")},
|
2016-07-07 17:02:58 +02:00
|
|
|
|
|
|
|
|
|
{"export-pka", EXPORT_PKA_FORMAT, NULL, NULL },
|
|
|
|
|
{"export-dane", EXPORT_DANE_FORMAT, NULL, NULL },
|
|
|
|
|
|
2017-01-23 10:12:18 +01:00
|
|
|
|
{"backup", EXPORT_BACKUP, NULL,
|
|
|
|
|
N_("use the GnuPG key backup format")},
|
|
|
|
|
{"export-backup", EXPORT_BACKUP, NULL, NULL },
|
|
|
|
|
|
2006-04-19 11:26:11 +00:00
|
|
|
|
/* Aliases for backward compatibility */
|
|
|
|
|
{"include-local-sigs",EXPORT_LOCAL_SIGS,NULL,NULL},
|
|
|
|
|
{"include-attributes",EXPORT_ATTRIBUTES,NULL,NULL},
|
|
|
|
|
{"include-sensitive-revkeys",EXPORT_SENSITIVE_REVKEYS,NULL,NULL},
|
|
|
|
|
/* dummy */
|
|
|
|
|
{"export-unusable-sigs",0,NULL,NULL},
|
|
|
|
|
{"export-clean-sigs",0,NULL,NULL},
|
|
|
|
|
{"export-clean-uids",0,NULL,NULL},
|
|
|
|
|
{NULL,0,NULL,NULL}
|
2003-06-05 07:14:21 +00:00
|
|
|
|
/* add tags for include revoked and disabled? */
|
|
|
|
|
};
|
2017-01-23 10:12:18 +01:00
|
|
|
|
int rc;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2017-01-23 10:12:18 +01:00
|
|
|
|
rc = parse_options (str, options, export_opts, noisy);
|
|
|
|
|
if (rc && (*options & EXPORT_BACKUP))
|
|
|
|
|
{
|
|
|
|
|
/* Alter other options we want or don't want for restore. */
|
|
|
|
|
*options |= (EXPORT_LOCAL_SIGS | EXPORT_ATTRIBUTES
|
|
|
|
|
| EXPORT_SENSITIVE_REVKEYS);
|
|
|
|
|
*options &= ~(EXPORT_CLEAN | EXPORT_MINIMAL
|
|
|
|
|
| EXPORT_PKA_FORMAT | EXPORT_DANE_FORMAT);
|
|
|
|
|
}
|
|
|
|
|
return rc;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-04-19 11:26:11 +00:00
|
|
|
|
|
2016-07-01 16:50:12 +02:00
|
|
|
|
/* Parse and set an export filter from string. STRING has the format
|
|
|
|
|
* "NAME=EXPR" with NAME being the name of the filter. Spaces before
|
|
|
|
|
* and after NAME are not allowed. If this function is called several
|
|
|
|
|
* times all expressions for the same NAME are concatenated.
|
|
|
|
|
* Supported filter names are:
|
|
|
|
|
*
|
|
|
|
|
* - keep-uid :: If the expression evaluates to true for a certain
|
|
|
|
|
* user ID packet, that packet and all it dependencies
|
|
|
|
|
* will be exported. The expression may use these
|
|
|
|
|
* variables:
|
|
|
|
|
*
|
|
|
|
|
* - uid :: The entire user ID.
|
|
|
|
|
* - mbox :: The mail box part of the user ID.
|
|
|
|
|
* - primary :: Evaluate to true for the primary user ID.
|
2016-09-05 09:51:16 +02:00
|
|
|
|
*
|
|
|
|
|
* - drop-subkey :: If the expression evaluates to true for a subkey
|
|
|
|
|
* packet that subkey and all it dependencies will be
|
|
|
|
|
* remove from the keyblock. The expression may use these
|
|
|
|
|
* variables:
|
|
|
|
|
*
|
|
|
|
|
* - secret :: 1 for a secret subkey, else 0.
|
|
|
|
|
* - key_algo :: Public key algorithm id
|
2016-07-01 16:50:12 +02:00
|
|
|
|
*/
|
|
|
|
|
gpg_error_t
|
|
|
|
|
parse_and_set_export_filter (const char *string)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
|
|
|
|
/* Auto register the cleanup function. */
|
|
|
|
|
register_mem_cleanup_func (cleanup_export_globals);
|
|
|
|
|
|
|
|
|
|
if (!strncmp (string, "keep-uid=", 9))
|
|
|
|
|
err = recsel_parse_expr (&export_keep_uid, string+9);
|
2016-09-05 09:51:16 +02:00
|
|
|
|
else if (!strncmp (string, "drop-subkey=", 12))
|
|
|
|
|
err = recsel_parse_expr (&export_drop_subkey, string+12);
|
2016-07-01 16:50:12 +02:00
|
|
|
|
else
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_NAME);
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-11-12 16:02:35 +01:00
|
|
|
|
/* Create a new export stats object initialized to zero. On error
|
|
|
|
|
returns NULL and sets ERRNO. */
|
|
|
|
|
export_stats_t
|
|
|
|
|
export_new_stats (void)
|
|
|
|
|
{
|
|
|
|
|
export_stats_t stats;
|
|
|
|
|
|
|
|
|
|
return xtrycalloc (1, sizeof *stats);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Release an export stats object. */
|
|
|
|
|
void
|
|
|
|
|
export_release_stats (export_stats_t stats)
|
|
|
|
|
{
|
|
|
|
|
xfree (stats);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Print export statistics using the status interface. */
|
|
|
|
|
void
|
|
|
|
|
export_print_stats (export_stats_t stats)
|
|
|
|
|
{
|
|
|
|
|
if (!stats)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (is_status_enabled ())
|
|
|
|
|
{
|
|
|
|
|
char buf[15*20];
|
|
|
|
|
|
|
|
|
|
snprintf (buf, sizeof buf, "%lu %lu %lu",
|
|
|
|
|
stats->count,
|
|
|
|
|
stats->secret_count,
|
|
|
|
|
stats->exported );
|
|
|
|
|
write_status_text (STATUS_EXPORT_RES, buf);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Export public keys (to stdout or to --output FILE).
|
|
|
|
|
*
|
|
|
|
|
* Depending on opt.armor the output is armored. OPTIONS are defined
|
|
|
|
|
* in main.h. If USERS is NULL, all keys will be exported. STATS is
|
|
|
|
|
* either an export stats object for update or NULL.
|
|
|
|
|
*
|
|
|
|
|
* This function is the core of "gpg --export".
|
|
|
|
|
*/
|
2003-06-05 07:14:21 +00:00
|
|
|
|
int
|
2015-11-12 16:02:35 +01:00
|
|
|
|
export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options,
|
|
|
|
|
export_stats_t stats)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
2015-11-12 16:02:35 +01:00
|
|
|
|
return do_export (ctrl, users, 0, options, stats);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-11-12 16:02:35 +01:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Export secret keys (to stdout or to --output FILE).
|
|
|
|
|
*
|
2017-03-01 14:41:47 +01:00
|
|
|
|
* Depending on opt.armor the output is armored. OPTIONS are defined
|
|
|
|
|
* in main.h. If USERS is NULL, all secret keys will be exported.
|
|
|
|
|
* STATS is either an export stats object for update or NULL.
|
2015-11-12 16:02:35 +01:00
|
|
|
|
*
|
|
|
|
|
* This function is the core of "gpg --export-secret-keys".
|
2003-06-05 07:14:21 +00:00
|
|
|
|
*/
|
|
|
|
|
int
|
2017-03-01 14:41:47 +01:00
|
|
|
|
export_seckeys (ctrl_t ctrl, strlist_t users, unsigned int options,
|
|
|
|
|
export_stats_t stats)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
2017-03-01 14:41:47 +01:00
|
|
|
|
return do_export (ctrl, users, 1, options, stats);
|
2015-11-12 16:02:35 +01:00
|
|
|
|
}
|
2011-02-02 15:48:54 +01:00
|
|
|
|
|
2015-11-12 16:02:35 +01:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Export secret sub keys (to stdout or to --output FILE).
|
|
|
|
|
*
|
|
|
|
|
* This is the same as export_seckeys but replaces the primary key by
|
2017-03-01 14:41:47 +01:00
|
|
|
|
* a stub key. Depending on opt.armor the output is armored. OPTIONS
|
|
|
|
|
* are defined in main.h. If USERS is NULL, all secret subkeys will
|
|
|
|
|
* be exported. STATS is either an export stats object for update or
|
|
|
|
|
* NULL.
|
2015-11-12 16:02:35 +01:00
|
|
|
|
*
|
|
|
|
|
* This function is the core of "gpg --export-secret-subkeys".
|
|
|
|
|
*/
|
|
|
|
|
int
|
2017-03-01 14:41:47 +01:00
|
|
|
|
export_secsubkeys (ctrl_t ctrl, strlist_t users, unsigned int options,
|
|
|
|
|
export_stats_t stats)
|
2015-11-12 16:02:35 +01:00
|
|
|
|
{
|
2017-03-01 14:41:47 +01:00
|
|
|
|
return do_export (ctrl, users, 2, options, stats);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-01-20 14:12:53 +01:00
|
|
|
|
|
|
|
|
|
/*
|
2015-11-12 16:02:35 +01:00
|
|
|
|
* Export a single key into a memory buffer. STATS is either an
|
|
|
|
|
* export stats object for update or NULL.
|
2011-01-20 14:12:53 +01:00
|
|
|
|
*/
|
|
|
|
|
gpg_error_t
|
|
|
|
|
export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options,
|
2015-11-12 16:02:35 +01:00
|
|
|
|
export_stats_t stats,
|
2011-01-20 14:12:53 +01:00
|
|
|
|
kbnode_t *r_keyblock, void **r_data, size_t *r_datalen)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
iobuf_t iobuf;
|
|
|
|
|
int any;
|
|
|
|
|
strlist_t helplist;
|
|
|
|
|
|
|
|
|
|
*r_keyblock = NULL;
|
|
|
|
|
*r_data = NULL;
|
|
|
|
|
*r_datalen = 0;
|
|
|
|
|
|
|
|
|
|
helplist = NULL;
|
|
|
|
|
if (!add_to_strlist_try (&helplist, keyspec))
|
|
|
|
|
return gpg_error_from_syserror ();
|
|
|
|
|
|
|
|
|
|
iobuf = iobuf_temp ();
|
2015-11-12 16:02:35 +01:00
|
|
|
|
err = do_export_stream (ctrl, iobuf, helplist, 0, r_keyblock, options,
|
|
|
|
|
stats, &any);
|
2011-01-20 14:12:53 +01:00
|
|
|
|
if (!err && !any)
|
|
|
|
|
err = gpg_error (GPG_ERR_NOT_FOUND);
|
|
|
|
|
if (!err)
|
|
|
|
|
{
|
|
|
|
|
const void *src;
|
|
|
|
|
size_t datalen;
|
|
|
|
|
|
|
|
|
|
iobuf_flush_temp (iobuf);
|
|
|
|
|
src = iobuf_get_temp_buffer (iobuf);
|
|
|
|
|
datalen = iobuf_get_temp_length (iobuf);
|
|
|
|
|
if (!datalen)
|
|
|
|
|
err = gpg_error (GPG_ERR_NO_PUBKEY);
|
|
|
|
|
else if (!(*r_data = xtrymalloc (datalen)))
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
memcpy (*r_data, src, datalen);
|
|
|
|
|
*r_datalen = datalen;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
iobuf_close (iobuf);
|
|
|
|
|
free_strlist (helplist);
|
|
|
|
|
if (err && *r_keyblock)
|
|
|
|
|
{
|
|
|
|
|
release_kbnode (*r_keyblock);
|
|
|
|
|
*r_keyblock = NULL;
|
|
|
|
|
}
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2010-10-01 20:33:53 +00:00
|
|
|
|
/* Export the keys identified by the list of strings in USERS. If
|
|
|
|
|
Secret is false public keys will be exported. With secret true
|
|
|
|
|
secret keys will be exported; in this case 1 means the entire
|
|
|
|
|
secret keyblock and 2 only the subkeys. OPTIONS are the export
|
|
|
|
|
options to apply. */
|
2003-06-05 07:14:21 +00:00
|
|
|
|
static int
|
2015-11-12 16:02:35 +01:00
|
|
|
|
do_export (ctrl_t ctrl, strlist_t users, int secret, unsigned int options,
|
|
|
|
|
export_stats_t stats)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
2006-04-19 11:26:11 +00:00
|
|
|
|
IOBUF out = NULL;
|
2003-07-16 13:45:15 +00:00
|
|
|
|
int any, rc;
|
2006-12-06 10:16:50 +00:00
|
|
|
|
armor_filter_context_t *afx = NULL;
|
2003-07-16 13:45:15 +00:00
|
|
|
|
compress_filter_context_t zfx;
|
2011-02-02 15:48:54 +01:00
|
|
|
|
|
2006-04-19 11:26:11 +00:00
|
|
|
|
memset( &zfx, 0, sizeof zfx);
|
2011-02-02 15:48:54 +01:00
|
|
|
|
|
2017-08-01 19:08:16 +02:00
|
|
|
|
rc = open_outfile (-1, NULL, 0, !!secret, &out );
|
2003-07-16 13:45:15 +00:00
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2016-07-07 17:02:58 +02:00
|
|
|
|
if ( opt.armor && !(options & (EXPORT_PKA_FORMAT|EXPORT_DANE_FORMAT)) )
|
2003-07-16 13:45:15 +00:00
|
|
|
|
{
|
2015-10-08 15:04:45 +02:00
|
|
|
|
afx = new_armor_context ();
|
|
|
|
|
afx->what = secret? 5 : 1;
|
|
|
|
|
push_armor_filter (afx, out);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
2003-07-16 13:45:15 +00:00
|
|
|
|
|
2015-11-12 16:02:35 +01:00
|
|
|
|
rc = do_export_stream (ctrl, out, users, secret, NULL, options, stats, &any);
|
2006-04-19 11:26:11 +00:00
|
|
|
|
|
|
|
|
|
if ( rc || !any )
|
2003-07-16 13:45:15 +00:00
|
|
|
|
iobuf_cancel (out);
|
|
|
|
|
else
|
|
|
|
|
iobuf_close (out);
|
2006-12-06 10:16:50 +00:00
|
|
|
|
release_armor_context (afx);
|
2003-07-16 13:45:15 +00:00
|
|
|
|
return rc;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2006-04-19 11:26:11 +00:00
|
|
|
|
|
|
|
|
|
/* Release an entire subkey list. */
|
|
|
|
|
static void
|
|
|
|
|
release_subkey_list (subkey_list_t list)
|
|
|
|
|
{
|
|
|
|
|
while (list)
|
|
|
|
|
{
|
|
|
|
|
subkey_list_t tmp = list->next;;
|
|
|
|
|
xfree (list);
|
|
|
|
|
list = tmp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Returns true if NODE is a subkey and contained in LIST. */
|
|
|
|
|
static int
|
|
|
|
|
subkey_in_list_p (subkey_list_t list, KBNODE node)
|
|
|
|
|
{
|
|
|
|
|
if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
|
|
|
|
|
|| node->pkt->pkttype == PKT_SECRET_SUBKEY )
|
|
|
|
|
{
|
|
|
|
|
u32 kid[2];
|
|
|
|
|
|
2010-09-06 19:57:42 +00:00
|
|
|
|
keyid_from_pk (node->pkt->pkt.public_key, kid);
|
2011-02-02 15:48:54 +01:00
|
|
|
|
|
2006-04-19 11:26:11 +00:00
|
|
|
|
for (; list; list = list->next)
|
|
|
|
|
if (list->kid[0] == kid[0] && list->kid[1] == kid[1])
|
|
|
|
|
return 1;
|
|
|
|
|
}
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Allocate a new subkey list item from NODE. */
|
|
|
|
|
static subkey_list_t
|
|
|
|
|
new_subkey_list_item (KBNODE node)
|
|
|
|
|
{
|
|
|
|
|
subkey_list_t list = xcalloc (1, sizeof *list);
|
|
|
|
|
|
2010-09-06 19:57:42 +00:00
|
|
|
|
if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
|
|
|
|
|
|| node->pkt->pkttype == PKT_SECRET_SUBKEY)
|
2006-04-19 11:26:11 +00:00
|
|
|
|
keyid_from_pk (node->pkt->pkt.public_key, list->kid);
|
|
|
|
|
|
|
|
|
|
return list;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Helper function to check whether the subkey at NODE actually
|
|
|
|
|
matches the description at DESC. The function returns true if the
|
|
|
|
|
key under question has been specified by an exact specification
|
|
|
|
|
(keyID or fingerprint) and does match the one at NODE. It is
|
|
|
|
|
assumed that the packet at NODE is either a public or secret
|
|
|
|
|
subkey. */
|
2019-05-27 10:40:38 +02:00
|
|
|
|
int
|
|
|
|
|
exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, kbnode_t node)
|
2006-04-19 11:26:11 +00:00
|
|
|
|
{
|
|
|
|
|
u32 kid[2];
|
|
|
|
|
byte fpr[MAX_FINGERPRINT_LEN];
|
|
|
|
|
size_t fprlen;
|
|
|
|
|
int result = 0;
|
|
|
|
|
|
|
|
|
|
switch(desc->mode)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_SEARCH_MODE_SHORT_KID:
|
|
|
|
|
case KEYDB_SEARCH_MODE_LONG_KID:
|
2010-09-06 19:57:42 +00:00
|
|
|
|
keyid_from_pk (node->pkt->pkt.public_key, kid);
|
2006-04-19 11:26:11 +00:00
|
|
|
|
break;
|
2011-02-02 15:48:54 +01:00
|
|
|
|
|
2006-04-19 11:26:11 +00:00
|
|
|
|
case KEYDB_SEARCH_MODE_FPR16:
|
|
|
|
|
case KEYDB_SEARCH_MODE_FPR20:
|
|
|
|
|
case KEYDB_SEARCH_MODE_FPR:
|
2010-09-06 19:57:42 +00:00
|
|
|
|
fingerprint_from_pk (node->pkt->pkt.public_key, fpr,&fprlen);
|
2006-04-19 11:26:11 +00:00
|
|
|
|
break;
|
2011-02-02 15:48:54 +01:00
|
|
|
|
|
2006-04-19 11:26:11 +00:00
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
2011-02-02 15:48:54 +01:00
|
|
|
|
|
2006-04-19 11:26:11 +00:00
|
|
|
|
switch(desc->mode)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_SEARCH_MODE_SHORT_KID:
|
|
|
|
|
if (desc->u.kid[1] == kid[1])
|
|
|
|
|
result = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KEYDB_SEARCH_MODE_LONG_KID:
|
|
|
|
|
if (desc->u.kid[0] == kid[0] && desc->u.kid[1] == kid[1])
|
|
|
|
|
result = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KEYDB_SEARCH_MODE_FPR16:
|
|
|
|
|
if (!memcmp (desc->u.fpr, fpr, 16))
|
|
|
|
|
result = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case KEYDB_SEARCH_MODE_FPR20:
|
|
|
|
|
case KEYDB_SEARCH_MODE_FPR:
|
|
|
|
|
if (!memcmp (desc->u.fpr, fpr, 20))
|
|
|
|
|
result = 1;
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-11 20:42:28 +02:00
|
|
|
|
|
|
|
|
|
/* Return an error if the key represented by the S-expression S_KEY
|
|
|
|
|
* and the OpenPGP key represented by PK do not use the same curve. */
|
2016-06-10 16:15:35 -04:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
match_curve_skey_pk (gcry_sexp_t s_key, PKT_public_key *pk)
|
|
|
|
|
{
|
2016-06-11 20:42:28 +02:00
|
|
|
|
gcry_sexp_t curve = NULL;
|
|
|
|
|
gcry_sexp_t flags = NULL;
|
|
|
|
|
char *curve_str = NULL;
|
|
|
|
|
char *flag;
|
2016-06-10 16:15:35 -04:00
|
|
|
|
const char *oidstr = NULL;
|
|
|
|
|
gcry_mpi_t curve_as_mpi = NULL;
|
|
|
|
|
gpg_error_t err;
|
2016-06-11 20:42:28 +02:00
|
|
|
|
int is_eddsa = 0;
|
|
|
|
|
int idx = 0;
|
2016-06-10 16:15:35 -04:00
|
|
|
|
|
2016-06-11 20:42:28 +02:00
|
|
|
|
if (!(pk->pubkey_algo==PUBKEY_ALGO_ECDH
|
|
|
|
|
|| pk->pubkey_algo==PUBKEY_ALGO_ECDSA
|
|
|
|
|
|| pk->pubkey_algo==PUBKEY_ALGO_EDDSA))
|
2016-06-10 16:15:35 -04:00
|
|
|
|
return gpg_error (GPG_ERR_PUBKEY_ALGO);
|
|
|
|
|
|
|
|
|
|
curve = gcry_sexp_find_token (s_key, "curve", 0);
|
|
|
|
|
if (!curve)
|
|
|
|
|
{
|
|
|
|
|
log_error ("no reported curve\n");
|
2016-06-11 20:42:28 +02:00
|
|
|
|
return gpg_error (GPG_ERR_UNKNOWN_CURVE);
|
2016-06-10 16:15:35 -04:00
|
|
|
|
}
|
|
|
|
|
curve_str = gcry_sexp_nth_string (curve, 1);
|
|
|
|
|
gcry_sexp_release (curve); curve = NULL;
|
|
|
|
|
if (!curve_str)
|
|
|
|
|
{
|
|
|
|
|
log_error ("no curve name\n");
|
|
|
|
|
return gpg_error (GPG_ERR_UNKNOWN_CURVE);
|
|
|
|
|
}
|
|
|
|
|
oidstr = openpgp_curve_to_oid (curve_str, NULL);
|
|
|
|
|
if (!oidstr)
|
|
|
|
|
{
|
|
|
|
|
log_error ("no OID known for curve '%s'\n", curve_str);
|
2016-06-11 20:42:28 +02:00
|
|
|
|
xfree (curve_str);
|
2016-06-10 16:15:35 -04:00
|
|
|
|
return gpg_error (GPG_ERR_UNKNOWN_CURVE);
|
|
|
|
|
}
|
2016-06-11 20:42:28 +02:00
|
|
|
|
xfree (curve_str);
|
2016-06-10 16:15:35 -04:00
|
|
|
|
err = openpgp_oid_from_str (oidstr, &curve_as_mpi);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
2016-06-11 20:42:28 +02:00
|
|
|
|
if (gcry_mpi_cmp (pk->pkey[0], curve_as_mpi))
|
2016-06-10 16:15:35 -04:00
|
|
|
|
{
|
|
|
|
|
log_error ("curves do not match\n");
|
2016-06-11 20:42:28 +02:00
|
|
|
|
gcry_mpi_release (curve_as_mpi);
|
|
|
|
|
return gpg_error (GPG_ERR_INV_CURVE);
|
2016-06-10 16:15:35 -04:00
|
|
|
|
}
|
|
|
|
|
gcry_mpi_release (curve_as_mpi);
|
|
|
|
|
flags = gcry_sexp_find_token (s_key, "flags", 0);
|
|
|
|
|
if (flags)
|
2016-06-11 20:42:28 +02:00
|
|
|
|
{
|
|
|
|
|
for (idx = 1; idx < gcry_sexp_length (flags); idx++)
|
|
|
|
|
{
|
|
|
|
|
flag = gcry_sexp_nth_string (flags, idx);
|
|
|
|
|
if (flag && (strcmp ("eddsa", flag) == 0))
|
|
|
|
|
is_eddsa = 1;
|
|
|
|
|
gcry_free (flag);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (is_eddsa != (pk->pubkey_algo == PUBKEY_ALGO_EDDSA))
|
2016-06-10 16:15:35 -04:00
|
|
|
|
{
|
|
|
|
|
log_error ("disagreement about EdDSA\n");
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_CURVE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
2006-04-19 11:26:11 +00:00
|
|
|
|
|
2016-06-11 20:42:28 +02:00
|
|
|
|
|
2010-10-01 20:33:53 +00:00
|
|
|
|
/* Return a canonicalized public key algoithms. This is used to
|
|
|
|
|
compare different flavors of algorithms (e.g. ELG and ELG_E are
|
|
|
|
|
considered the same). */
|
2014-06-20 14:54:01 +02:00
|
|
|
|
static enum gcry_pk_algos
|
|
|
|
|
canon_pk_algo (enum gcry_pk_algos algo)
|
2010-10-01 20:33:53 +00:00
|
|
|
|
{
|
|
|
|
|
switch (algo)
|
|
|
|
|
{
|
|
|
|
|
case GCRY_PK_RSA:
|
|
|
|
|
case GCRY_PK_RSA_E:
|
|
|
|
|
case GCRY_PK_RSA_S: return GCRY_PK_RSA;
|
2011-02-02 15:48:54 +01:00
|
|
|
|
case GCRY_PK_ELG:
|
2010-10-01 20:33:53 +00:00
|
|
|
|
case GCRY_PK_ELG_E: return GCRY_PK_ELG;
|
2014-06-20 14:54:01 +02:00
|
|
|
|
case GCRY_PK_ECC:
|
|
|
|
|
case GCRY_PK_ECDSA:
|
|
|
|
|
case GCRY_PK_ECDH: return GCRY_PK_ECC;
|
2010-10-01 20:33:53 +00:00
|
|
|
|
default: return algo;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-11 20:42:28 +02:00
|
|
|
|
|
|
|
|
|
/* Take a cleartext dump of a secret key in PK and change the
|
|
|
|
|
* parameter array in PK to include the secret parameters. */
|
2016-06-10 16:15:35 -04:00
|
|
|
|
static gpg_error_t
|
|
|
|
|
cleartext_secret_key_to_openpgp (gcry_sexp_t s_key, PKT_public_key *pk)
|
|
|
|
|
{
|
2017-04-17 09:15:13 +09:00
|
|
|
|
gpg_error_t err;
|
2016-06-10 16:15:35 -04:00
|
|
|
|
gcry_sexp_t top_list;
|
|
|
|
|
gcry_sexp_t key = NULL;
|
|
|
|
|
char *key_type = NULL;
|
|
|
|
|
enum gcry_pk_algos pk_algo;
|
|
|
|
|
struct seckey_info *ski;
|
|
|
|
|
int idx, sec_start;
|
|
|
|
|
gcry_mpi_t pub_params[10] = { NULL };
|
|
|
|
|
|
|
|
|
|
/* we look for a private-key, then the first element in it tells us
|
|
|
|
|
the type */
|
|
|
|
|
top_list = gcry_sexp_find_token (s_key, "private-key", 0);
|
|
|
|
|
if (!top_list)
|
|
|
|
|
goto bad_seckey;
|
2019-05-13 21:22:38 -04:00
|
|
|
|
|
|
|
|
|
/* ignore all S-expression after the first sublist -- we assume that
|
|
|
|
|
they are comments or otherwise irrelevant to OpenPGP */
|
|
|
|
|
if (gcry_sexp_length(top_list) < 2)
|
2016-06-10 16:15:35 -04:00
|
|
|
|
goto bad_seckey;
|
|
|
|
|
key = gcry_sexp_nth (top_list, 1);
|
|
|
|
|
if (!key)
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
key_type = gcry_sexp_nth_string(key, 0);
|
|
|
|
|
pk_algo = gcry_pk_map_name (key_type);
|
|
|
|
|
|
2016-06-11 20:42:28 +02:00
|
|
|
|
log_assert (!pk->seckey_info);
|
2016-06-10 16:15:35 -04:00
|
|
|
|
|
|
|
|
|
pk->seckey_info = ski = xtrycalloc (1, sizeof *ski);
|
|
|
|
|
if (!ski)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (canon_pk_algo (pk_algo))
|
|
|
|
|
{
|
|
|
|
|
case GCRY_PK_RSA:
|
|
|
|
|
if (!is_RSA (pk->pubkey_algo))
|
|
|
|
|
goto bad_pubkey_algo;
|
|
|
|
|
err = gcry_sexp_extract_param (key, NULL, "ne",
|
|
|
|
|
&pub_params[0],
|
|
|
|
|
&pub_params[1],
|
|
|
|
|
NULL);
|
|
|
|
|
for (idx=0; idx < 2 && !err; idx++)
|
|
|
|
|
if (gcry_mpi_cmp(pk->pkey[idx], pub_params[idx]))
|
|
|
|
|
err = gpg_error (GPG_ERR_BAD_PUBKEY);
|
|
|
|
|
if (!err)
|
2016-06-11 20:42:28 +02:00
|
|
|
|
{
|
|
|
|
|
for (idx = 2; idx < 6 && !err; idx++)
|
|
|
|
|
{
|
|
|
|
|
gcry_mpi_release (pk->pkey[idx]);
|
|
|
|
|
pk->pkey[idx] = NULL;
|
|
|
|
|
}
|
|
|
|
|
err = gcry_sexp_extract_param (key, NULL, "dpqu",
|
|
|
|
|
&pk->pkey[2],
|
|
|
|
|
&pk->pkey[3],
|
|
|
|
|
&pk->pkey[4],
|
|
|
|
|
&pk->pkey[5],
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
2016-06-10 16:15:35 -04:00
|
|
|
|
if (!err)
|
2016-06-11 20:42:28 +02:00
|
|
|
|
{
|
|
|
|
|
for (idx = 2; idx < 6; idx++)
|
|
|
|
|
ski->csum += checksum_mpi (pk->pkey[idx]);
|
|
|
|
|
}
|
2016-06-10 16:15:35 -04:00
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GCRY_PK_DSA:
|
|
|
|
|
if (!is_DSA (pk->pubkey_algo))
|
|
|
|
|
goto bad_pubkey_algo;
|
|
|
|
|
err = gcry_sexp_extract_param (key, NULL, "pqgy",
|
|
|
|
|
&pub_params[0],
|
|
|
|
|
&pub_params[1],
|
|
|
|
|
&pub_params[2],
|
|
|
|
|
&pub_params[3],
|
|
|
|
|
NULL);
|
|
|
|
|
for (idx=0; idx < 4 && !err; idx++)
|
|
|
|
|
if (gcry_mpi_cmp(pk->pkey[idx], pub_params[idx]))
|
|
|
|
|
err = gpg_error (GPG_ERR_BAD_PUBKEY);
|
|
|
|
|
if (!err)
|
2016-06-11 20:42:28 +02:00
|
|
|
|
{
|
|
|
|
|
gcry_mpi_release (pk->pkey[4]);
|
|
|
|
|
pk->pkey[4] = NULL;
|
|
|
|
|
err = gcry_sexp_extract_param (key, NULL, "x",
|
|
|
|
|
&pk->pkey[4],
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
2016-06-10 16:15:35 -04:00
|
|
|
|
if (!err)
|
|
|
|
|
ski->csum += checksum_mpi (pk->pkey[4]);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GCRY_PK_ELG:
|
|
|
|
|
if (!is_ELGAMAL (pk->pubkey_algo))
|
|
|
|
|
goto bad_pubkey_algo;
|
|
|
|
|
err = gcry_sexp_extract_param (key, NULL, "pgy",
|
|
|
|
|
&pub_params[0],
|
|
|
|
|
&pub_params[1],
|
|
|
|
|
&pub_params[2],
|
|
|
|
|
NULL);
|
|
|
|
|
for (idx=0; idx < 3 && !err; idx++)
|
|
|
|
|
if (gcry_mpi_cmp(pk->pkey[idx], pub_params[idx]))
|
|
|
|
|
err = gpg_error (GPG_ERR_BAD_PUBKEY);
|
|
|
|
|
if (!err)
|
2016-06-11 20:42:28 +02:00
|
|
|
|
{
|
|
|
|
|
gcry_mpi_release (pk->pkey[3]);
|
|
|
|
|
pk->pkey[3] = NULL;
|
|
|
|
|
err = gcry_sexp_extract_param (key, NULL, "x",
|
|
|
|
|
&pk->pkey[3],
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
2016-06-10 16:15:35 -04:00
|
|
|
|
if (!err)
|
|
|
|
|
ski->csum += checksum_mpi (pk->pkey[3]);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case GCRY_PK_ECC:
|
|
|
|
|
err = match_curve_skey_pk (key, pk);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
if (!err)
|
|
|
|
|
err = gcry_sexp_extract_param (key, NULL, "q",
|
|
|
|
|
&pub_params[0],
|
|
|
|
|
NULL);
|
|
|
|
|
if (!err && (gcry_mpi_cmp(pk->pkey[1], pub_params[0])))
|
|
|
|
|
err = gpg_error (GPG_ERR_BAD_PUBKEY);
|
|
|
|
|
|
|
|
|
|
sec_start = 2;
|
|
|
|
|
if (pk->pubkey_algo == PUBKEY_ALGO_ECDH)
|
|
|
|
|
sec_start += 1;
|
|
|
|
|
if (!err)
|
2016-06-11 20:42:28 +02:00
|
|
|
|
{
|
|
|
|
|
gcry_mpi_release (pk->pkey[sec_start]);
|
|
|
|
|
pk->pkey[sec_start] = NULL;
|
|
|
|
|
err = gcry_sexp_extract_param (key, NULL, "d",
|
|
|
|
|
&pk->pkey[sec_start],
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
2016-06-10 16:15:35 -04:00
|
|
|
|
|
|
|
|
|
if (!err)
|
|
|
|
|
ski->csum += checksum_mpi (pk->pkey[sec_start]);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
pk->seckey_info = NULL;
|
2016-06-11 20:42:28 +02:00
|
|
|
|
xfree (ski);
|
2016-06-10 16:15:35 -04:00
|
|
|
|
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
|
2016-06-11 20:42:28 +02:00
|
|
|
|
break;
|
2016-06-10 16:15:35 -04:00
|
|
|
|
}
|
2016-06-11 20:42:28 +02:00
|
|
|
|
|
2016-06-10 16:15:35 -04:00
|
|
|
|
leave:
|
|
|
|
|
gcry_sexp_release (top_list);
|
|
|
|
|
gcry_sexp_release (key);
|
|
|
|
|
gcry_free (key_type);
|
|
|
|
|
|
|
|
|
|
for (idx=0; idx < DIM(pub_params); idx++)
|
|
|
|
|
gcry_mpi_release (pub_params[idx]);
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
bad_pubkey_algo:
|
|
|
|
|
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
|
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
bad_seckey:
|
|
|
|
|
err = gpg_error (GPG_ERR_BAD_SECKEY);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2010-10-01 20:33:53 +00:00
|
|
|
|
|
2016-06-11 20:42:28 +02:00
|
|
|
|
|
2010-10-01 20:33:53 +00:00
|
|
|
|
/* Use the key transfer format given in S_PGP to create the secinfo
|
2011-02-02 15:48:54 +01:00
|
|
|
|
structure in PK and change the parameter array in PK to include the
|
2010-10-01 20:33:53 +00:00
|
|
|
|
secret parameters. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
gcry_sexp_t top_list;
|
|
|
|
|
gcry_sexp_t list = NULL;
|
2014-06-20 14:54:01 +02:00
|
|
|
|
char *curve = NULL;
|
2010-10-01 20:33:53 +00:00
|
|
|
|
const char *value;
|
|
|
|
|
size_t valuelen;
|
|
|
|
|
char *string;
|
|
|
|
|
int idx;
|
|
|
|
|
int is_v4, is_protected;
|
2014-06-20 14:54:01 +02:00
|
|
|
|
enum gcry_pk_algos pk_algo;
|
2010-10-01 20:33:53 +00:00
|
|
|
|
int protect_algo = 0;
|
|
|
|
|
char iv[16];
|
|
|
|
|
int ivlen = 0;
|
|
|
|
|
int s2k_mode = 0;
|
|
|
|
|
int s2k_algo = 0;
|
|
|
|
|
byte s2k_salt[8];
|
|
|
|
|
u32 s2k_count = 0;
|
2014-06-20 14:54:01 +02:00
|
|
|
|
int is_ecdh = 0;
|
2010-10-01 20:33:53 +00:00
|
|
|
|
size_t npkey, nskey;
|
|
|
|
|
gcry_mpi_t skey[10]; /* We support up to 9 parameters. */
|
|
|
|
|
int skeyidx = 0;
|
|
|
|
|
struct seckey_info *ski;
|
|
|
|
|
|
2014-06-20 14:54:01 +02:00
|
|
|
|
/* gcry_log_debugsxp ("transferkey", s_pgp); */
|
2010-10-01 20:33:53 +00:00
|
|
|
|
top_list = gcry_sexp_find_token (s_pgp, "openpgp-private-key", 0);
|
|
|
|
|
if (!top_list)
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
|
|
|
|
|
list = gcry_sexp_find_token (top_list, "version", 0);
|
|
|
|
|
if (!list)
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
value = gcry_sexp_nth_data (list, 1, &valuelen);
|
|
|
|
|
if (!value || valuelen != 1 || !(value[0] == '3' || value[0] == '4'))
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
is_v4 = (value[0] == '4');
|
|
|
|
|
|
|
|
|
|
gcry_sexp_release (list);
|
|
|
|
|
list = gcry_sexp_find_token (top_list, "protection", 0);
|
|
|
|
|
if (!list)
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
value = gcry_sexp_nth_data (list, 1, &valuelen);
|
|
|
|
|
if (!value)
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
if (valuelen == 4 && !memcmp (value, "sha1", 4))
|
|
|
|
|
is_protected = 2;
|
|
|
|
|
else if (valuelen == 3 && !memcmp (value, "sum", 3))
|
|
|
|
|
is_protected = 1;
|
|
|
|
|
else if (valuelen == 4 && !memcmp (value, "none", 4))
|
|
|
|
|
is_protected = 0;
|
|
|
|
|
else
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
if (is_protected)
|
|
|
|
|
{
|
|
|
|
|
string = gcry_sexp_nth_string (list, 2);
|
|
|
|
|
if (!string)
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
protect_algo = gcry_cipher_map_name (string);
|
|
|
|
|
xfree (string);
|
2011-02-02 15:48:54 +01:00
|
|
|
|
|
2010-10-01 20:33:53 +00:00
|
|
|
|
value = gcry_sexp_nth_data (list, 3, &valuelen);
|
|
|
|
|
if (!value || !valuelen || valuelen > sizeof iv)
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
memcpy (iv, value, valuelen);
|
|
|
|
|
ivlen = valuelen;
|
|
|
|
|
|
|
|
|
|
string = gcry_sexp_nth_string (list, 4);
|
|
|
|
|
if (!string)
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
s2k_mode = strtol (string, NULL, 10);
|
|
|
|
|
xfree (string);
|
|
|
|
|
|
|
|
|
|
string = gcry_sexp_nth_string (list, 5);
|
|
|
|
|
if (!string)
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
s2k_algo = gcry_md_map_name (string);
|
|
|
|
|
xfree (string);
|
|
|
|
|
|
|
|
|
|
value = gcry_sexp_nth_data (list, 6, &valuelen);
|
|
|
|
|
if (!value || !valuelen || valuelen > sizeof s2k_salt)
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
memcpy (s2k_salt, value, valuelen);
|
|
|
|
|
|
|
|
|
|
string = gcry_sexp_nth_string (list, 7);
|
|
|
|
|
if (!string)
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
s2k_count = strtoul (string, NULL, 10);
|
|
|
|
|
xfree (string);
|
|
|
|
|
}
|
|
|
|
|
|
2014-06-20 14:54:01 +02:00
|
|
|
|
/* Parse the gcrypt PK algo and check that it is okay. */
|
2010-10-01 20:33:53 +00:00
|
|
|
|
gcry_sexp_release (list);
|
|
|
|
|
list = gcry_sexp_find_token (top_list, "algo", 0);
|
|
|
|
|
if (!list)
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
string = gcry_sexp_nth_string (list, 1);
|
|
|
|
|
if (!string)
|
|
|
|
|
goto bad_seckey;
|
2014-06-20 14:54:01 +02:00
|
|
|
|
pk_algo = gcry_pk_map_name (string);
|
|
|
|
|
xfree (string); string = NULL;
|
|
|
|
|
if (gcry_pk_algo_info (pk_algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &npkey)
|
|
|
|
|
|| gcry_pk_algo_info (pk_algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &nskey)
|
2014-08-26 14:11:47 -07:00
|
|
|
|
|| !npkey || npkey >= nskey)
|
2010-10-01 20:33:53 +00:00
|
|
|
|
goto bad_seckey;
|
|
|
|
|
|
2014-06-20 14:54:01 +02:00
|
|
|
|
/* Check that the pubkey algo matches the one from the public key. */
|
|
|
|
|
switch (canon_pk_algo (pk_algo))
|
|
|
|
|
{
|
|
|
|
|
case GCRY_PK_RSA:
|
|
|
|
|
if (!is_RSA (pk->pubkey_algo))
|
|
|
|
|
pk_algo = 0; /* Does not match. */
|
|
|
|
|
break;
|
|
|
|
|
case GCRY_PK_DSA:
|
|
|
|
|
if (!is_DSA (pk->pubkey_algo))
|
|
|
|
|
pk_algo = 0; /* Does not match. */
|
|
|
|
|
break;
|
|
|
|
|
case GCRY_PK_ELG:
|
|
|
|
|
if (!is_ELGAMAL (pk->pubkey_algo))
|
|
|
|
|
pk_algo = 0; /* Does not match. */
|
|
|
|
|
break;
|
|
|
|
|
case GCRY_PK_ECC:
|
|
|
|
|
if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
|
|
|
|
|
;
|
|
|
|
|
else if (pk->pubkey_algo == PUBKEY_ALGO_ECDH)
|
|
|
|
|
is_ecdh = 1;
|
|
|
|
|
else if (pk->pubkey_algo == PUBKEY_ALGO_EDDSA)
|
|
|
|
|
;
|
|
|
|
|
else
|
|
|
|
|
pk_algo = 0; /* Does not match. */
|
|
|
|
|
/* For ECC we do not have the domain parameters thus fix our info. */
|
|
|
|
|
npkey = 1;
|
|
|
|
|
nskey = 2;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
pk_algo = 0; /* Oops. */
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!pk_algo)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_PUBKEY_ALGO);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
2014-08-26 14:11:47 -07:00
|
|
|
|
/* This check has to go after the ecc adjustments. */
|
|
|
|
|
if (nskey > PUBKEY_MAX_NSKEY)
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
|
2014-06-20 14:54:01 +02:00
|
|
|
|
/* Parse the key parameters. */
|
2010-10-01 20:33:53 +00:00
|
|
|
|
gcry_sexp_release (list);
|
|
|
|
|
list = gcry_sexp_find_token (top_list, "skey", 0);
|
|
|
|
|
if (!list)
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
for (idx=0;;)
|
|
|
|
|
{
|
|
|
|
|
int is_enc;
|
|
|
|
|
|
|
|
|
|
value = gcry_sexp_nth_data (list, ++idx, &valuelen);
|
|
|
|
|
if (!value && skeyidx >= npkey)
|
|
|
|
|
break; /* Ready. */
|
|
|
|
|
|
|
|
|
|
/* Check for too many parameters. Note that depending on the
|
|
|
|
|
protection mode and version number we may see less than NSKEY
|
|
|
|
|
(but at least NPKEY+1) parameters. */
|
|
|
|
|
if (idx >= 2*nskey)
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
if (skeyidx >= DIM (skey)-1)
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
|
|
|
|
|
if (!value || valuelen != 1 || !(value[0] == '_' || value[0] == 'e'))
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
is_enc = (value[0] == 'e');
|
|
|
|
|
value = gcry_sexp_nth_data (list, ++idx, &valuelen);
|
|
|
|
|
if (!value || !valuelen)
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
if (is_enc)
|
|
|
|
|
{
|
|
|
|
|
void *p = xtrymalloc (valuelen);
|
|
|
|
|
if (!p)
|
|
|
|
|
goto outofmem;
|
|
|
|
|
memcpy (p, value, valuelen);
|
|
|
|
|
skey[skeyidx] = gcry_mpi_set_opaque (NULL, p, valuelen*8);
|
|
|
|
|
if (!skey[skeyidx])
|
|
|
|
|
goto outofmem;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (gcry_mpi_scan (skey + skeyidx, GCRYMPI_FMT_STD,
|
|
|
|
|
value, valuelen, NULL))
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
}
|
|
|
|
|
skeyidx++;
|
|
|
|
|
}
|
|
|
|
|
skey[skeyidx++] = NULL;
|
|
|
|
|
|
2011-12-02 17:04:58 +01:00
|
|
|
|
gcry_sexp_release (list); list = NULL;
|
2010-10-01 20:33:53 +00:00
|
|
|
|
|
2014-06-20 14:54:01 +02:00
|
|
|
|
/* We have no need for the CSUM value thus we don't parse it. */
|
2011-08-10 14:11:30 +02:00
|
|
|
|
/* list = gcry_sexp_find_token (top_list, "csum", 0); */
|
|
|
|
|
/* if (list) */
|
|
|
|
|
/* { */
|
|
|
|
|
/* string = gcry_sexp_nth_string (list, 1); */
|
|
|
|
|
/* if (!string) */
|
|
|
|
|
/* goto bad_seckey; */
|
|
|
|
|
/* desired_csum = strtoul (string, NULL, 10); */
|
|
|
|
|
/* xfree (string); */
|
|
|
|
|
/* } */
|
|
|
|
|
/* else */
|
|
|
|
|
/* desired_csum = 0; */
|
|
|
|
|
/* gcry_sexp_release (list); list = NULL; */
|
2010-10-01 20:33:53 +00:00
|
|
|
|
|
2014-06-20 14:54:01 +02:00
|
|
|
|
/* Get the curve name if any, */
|
|
|
|
|
list = gcry_sexp_find_token (top_list, "curve", 0);
|
|
|
|
|
if (list)
|
|
|
|
|
{
|
|
|
|
|
curve = gcry_sexp_nth_string (list, 1);
|
|
|
|
|
gcry_sexp_release (list); list = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-01 20:33:53 +00:00
|
|
|
|
gcry_sexp_release (top_list); top_list = NULL;
|
|
|
|
|
|
|
|
|
|
/* log_debug ("XXX is_v4=%d\n", is_v4); */
|
|
|
|
|
/* log_debug ("XXX pubkey_algo=%d\n", pubkey_algo); */
|
|
|
|
|
/* log_debug ("XXX is_protected=%d\n", is_protected); */
|
|
|
|
|
/* log_debug ("XXX protect_algo=%d\n", protect_algo); */
|
|
|
|
|
/* log_printhex ("XXX iv", iv, ivlen); */
|
|
|
|
|
/* log_debug ("XXX ivlen=%d\n", ivlen); */
|
|
|
|
|
/* log_debug ("XXX s2k_mode=%d\n", s2k_mode); */
|
|
|
|
|
/* log_debug ("XXX s2k_algo=%d\n", s2k_algo); */
|
|
|
|
|
/* log_printhex ("XXX s2k_salt", s2k_salt, sizeof s2k_salt); */
|
|
|
|
|
/* log_debug ("XXX s2k_count=%lu\n", (unsigned long)s2k_count); */
|
|
|
|
|
/* for (idx=0; skey[idx]; idx++) */
|
|
|
|
|
/* { */
|
|
|
|
|
/* int is_enc = gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_OPAQUE); */
|
|
|
|
|
/* log_info ("XXX skey[%d]%s:", idx, is_enc? " (enc)":""); */
|
|
|
|
|
/* if (is_enc) */
|
|
|
|
|
/* { */
|
|
|
|
|
/* void *p; */
|
|
|
|
|
/* unsigned int nbits; */
|
|
|
|
|
/* p = gcry_mpi_get_opaque (skey[idx], &nbits); */
|
|
|
|
|
/* log_printhex (NULL, p, (nbits+7)/8); */
|
|
|
|
|
/* } */
|
|
|
|
|
/* else */
|
|
|
|
|
/* gcry_mpi_dump (skey[idx]); */
|
|
|
|
|
/* log_printf ("\n"); */
|
|
|
|
|
/* } */
|
|
|
|
|
|
|
|
|
|
if (!is_v4 || is_protected != 2 )
|
|
|
|
|
{
|
|
|
|
|
/* We only support the v4 format and a SHA-1 checksum. */
|
|
|
|
|
err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-02 15:48:54 +01:00
|
|
|
|
/* We need to change the received parameters for ECC algorithms.
|
2014-06-20 14:54:01 +02:00
|
|
|
|
The transfer format has the curve name and the parameters
|
|
|
|
|
separate. We put them all into the SKEY array. */
|
|
|
|
|
if (canon_pk_algo (pk_algo) == GCRY_PK_ECC)
|
2011-02-02 15:48:54 +01:00
|
|
|
|
{
|
2014-06-20 14:54:01 +02:00
|
|
|
|
const char *oidstr;
|
|
|
|
|
|
|
|
|
|
/* Assert that all required parameters are available. We also
|
|
|
|
|
check that the array does not contain more parameters than
|
|
|
|
|
needed (this was used by some beta versions of 2.1. */
|
|
|
|
|
if (!curve || !skey[0] || !skey[1] || skey[2])
|
2011-02-02 15:48:54 +01:00
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INTERNAL);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2014-06-20 14:54:01 +02:00
|
|
|
|
|
|
|
|
|
oidstr = openpgp_curve_to_oid (curve, NULL);
|
|
|
|
|
if (!oidstr)
|
2011-02-02 15:48:54 +01:00
|
|
|
|
{
|
2014-06-20 14:54:01 +02:00
|
|
|
|
log_error ("no OID known for curve '%s'\n", curve);
|
|
|
|
|
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
|
2011-02-02 15:48:54 +01:00
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2017-02-20 16:19:50 -05:00
|
|
|
|
/* Put the curve's OID into the MPI array. This requires
|
2014-06-20 14:54:01 +02:00
|
|
|
|
that we shift Q and D. For ECDH also insert the KDF parms. */
|
|
|
|
|
if (is_ecdh)
|
|
|
|
|
{
|
|
|
|
|
skey[4] = NULL;
|
|
|
|
|
skey[3] = skey[1];
|
|
|
|
|
skey[2] = gcry_mpi_copy (pk->pkey[2]);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
skey[3] = NULL;
|
|
|
|
|
skey[2] = skey[1];
|
|
|
|
|
}
|
|
|
|
|
skey[1] = skey[0];
|
|
|
|
|
skey[0] = NULL;
|
|
|
|
|
err = openpgp_oid_from_str (oidstr, skey + 0);
|
2011-02-02 15:48:54 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
/* Fixup the NPKEY and NSKEY to match OpenPGP reality. */
|
2014-06-20 14:54:01 +02:00
|
|
|
|
npkey = 2 + is_ecdh;
|
|
|
|
|
nskey = 3 + is_ecdh;
|
2011-02-02 15:48:54 +01:00
|
|
|
|
|
|
|
|
|
/* for (idx=0; skey[idx]; idx++) */
|
|
|
|
|
/* { */
|
|
|
|
|
/* log_info ("YYY skey[%d]:", idx); */
|
|
|
|
|
/* if (gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_OPAQUE)) */
|
|
|
|
|
/* { */
|
|
|
|
|
/* void *p; */
|
|
|
|
|
/* unsigned int nbits; */
|
|
|
|
|
/* p = gcry_mpi_get_opaque (skey[idx], &nbits); */
|
|
|
|
|
/* log_printhex (NULL, p, (nbits+7)/8); */
|
|
|
|
|
/* } */
|
|
|
|
|
/* else */
|
|
|
|
|
/* gcry_mpi_dump (skey[idx]); */
|
|
|
|
|
/* log_printf ("\n"); */
|
|
|
|
|
/* } */
|
|
|
|
|
}
|
|
|
|
|
|
2010-10-01 20:33:53 +00:00
|
|
|
|
/* Do some sanity checks. */
|
2011-04-26 20:33:46 +02:00
|
|
|
|
if (s2k_count > 255)
|
2010-10-01 20:33:53 +00:00
|
|
|
|
{
|
2011-04-26 20:33:46 +02:00
|
|
|
|
/* We expect an already encoded S2K count. */
|
2010-10-01 20:33:53 +00:00
|
|
|
|
err = gpg_error (GPG_ERR_INV_DATA);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
err = openpgp_cipher_test_algo (protect_algo);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
err = openpgp_md_test_algo (s2k_algo);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
2011-02-02 15:48:54 +01:00
|
|
|
|
|
2011-03-08 12:23:59 +01:00
|
|
|
|
/* Check that the public key parameters match. Note that since
|
|
|
|
|
Libgcrypt 1.5 gcry_mpi_cmp handles opaque MPI correctly. */
|
2010-10-01 20:33:53 +00:00
|
|
|
|
for (idx=0; idx < npkey; idx++)
|
2011-03-08 12:23:59 +01:00
|
|
|
|
if (gcry_mpi_cmp (pk->pkey[idx], skey[idx]))
|
2010-10-01 20:33:53 +00:00
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_BAD_PUBKEY);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Check that the first secret key parameter in SKEY is encrypted
|
|
|
|
|
and that there are no more secret key parameters. The latter is
|
|
|
|
|
guaranteed by the v4 packet format. */
|
|
|
|
|
if (!gcry_mpi_get_flag (skey[npkey], GCRYMPI_FLAG_OPAQUE))
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
if (npkey+1 < DIM (skey) && skey[npkey+1])
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
|
|
|
|
|
/* Check that the secret key parameters in PK are all set to NULL. */
|
|
|
|
|
for (idx=npkey; idx < nskey; idx++)
|
|
|
|
|
if (pk->pkey[idx])
|
|
|
|
|
goto bad_seckey;
|
|
|
|
|
|
|
|
|
|
/* Now build the protection info. */
|
|
|
|
|
pk->seckey_info = ski = xtrycalloc (1, sizeof *ski);
|
|
|
|
|
if (!ski)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2011-02-02 15:48:54 +01:00
|
|
|
|
|
2010-10-01 20:33:53 +00:00
|
|
|
|
ski->is_protected = 1;
|
|
|
|
|
ski->sha1chk = 1;
|
|
|
|
|
ski->algo = protect_algo;
|
|
|
|
|
ski->s2k.mode = s2k_mode;
|
|
|
|
|
ski->s2k.hash_algo = s2k_algo;
|
2016-04-29 11:05:24 +02:00
|
|
|
|
log_assert (sizeof ski->s2k.salt == sizeof s2k_salt);
|
2010-10-01 20:33:53 +00:00
|
|
|
|
memcpy (ski->s2k.salt, s2k_salt, sizeof s2k_salt);
|
2011-04-26 20:33:46 +02:00
|
|
|
|
ski->s2k.count = s2k_count;
|
2016-04-29 11:05:24 +02:00
|
|
|
|
log_assert (ivlen <= sizeof ski->iv);
|
2010-10-01 20:33:53 +00:00
|
|
|
|
memcpy (ski->iv, iv, ivlen);
|
|
|
|
|
ski->ivlen = ivlen;
|
|
|
|
|
|
|
|
|
|
/* Store the protected secret key parameter. */
|
|
|
|
|
pk->pkey[npkey] = skey[npkey];
|
|
|
|
|
skey[npkey] = NULL;
|
|
|
|
|
|
|
|
|
|
/* That's it. */
|
|
|
|
|
|
|
|
|
|
leave:
|
2014-06-20 14:54:01 +02:00
|
|
|
|
gcry_free (curve);
|
2010-10-01 20:33:53 +00:00
|
|
|
|
gcry_sexp_release (list);
|
|
|
|
|
gcry_sexp_release (top_list);
|
|
|
|
|
for (idx=0; idx < skeyidx; idx++)
|
|
|
|
|
gcry_mpi_release (skey[idx]);
|
|
|
|
|
return err;
|
|
|
|
|
|
|
|
|
|
bad_seckey:
|
|
|
|
|
err = gpg_error (GPG_ERR_BAD_SECKEY);
|
|
|
|
|
goto leave;
|
2011-02-02 15:48:54 +01:00
|
|
|
|
|
2010-10-01 20:33:53 +00:00
|
|
|
|
outofmem:
|
|
|
|
|
err = gpg_error (GPG_ERR_ENOMEM);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
2015-11-12 17:02:18 +01:00
|
|
|
|
|
|
|
|
|
/* Print an "EXPORTED" status line. PK is the primary public key. */
|
|
|
|
|
static void
|
|
|
|
|
print_status_exported (PKT_public_key *pk)
|
|
|
|
|
{
|
|
|
|
|
char *hexfpr;
|
|
|
|
|
|
|
|
|
|
if (!is_status_enabled ())
|
|
|
|
|
return;
|
|
|
|
|
|
2015-11-14 09:13:02 +01:00
|
|
|
|
hexfpr = hexfingerprint (pk, NULL, 0);
|
2015-11-12 17:02:18 +01:00
|
|
|
|
write_status_text (STATUS_EXPORTED, hexfpr? hexfpr : "[?]");
|
|
|
|
|
xfree (hexfpr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-12-17 13:03:56 +09:00
|
|
|
|
/*
|
|
|
|
|
* Receive a secret key from agent specified by HEXGRIP.
|
|
|
|
|
*
|
2016-12-05 10:58:39 +01:00
|
|
|
|
* Since the key data from the agent is encrypted, decrypt it using
|
|
|
|
|
* CIPHERHD context. Then, parse the decrypted key data into transfer
|
|
|
|
|
* format, and put secret parameters into PK.
|
2015-12-17 13:03:56 +09:00
|
|
|
|
*
|
2016-06-11 20:42:28 +02:00
|
|
|
|
* If CLEARTEXT is 0, store the secret key material
|
|
|
|
|
* passphrase-protected. Otherwise, store secret key material in the
|
2016-06-10 16:15:35 -04:00
|
|
|
|
* clear.
|
|
|
|
|
*
|
2015-12-17 13:03:56 +09:00
|
|
|
|
* CACHE_NONCE_ADDR is used to share nonce for multple key retrievals.
|
|
|
|
|
*/
|
|
|
|
|
gpg_error_t
|
|
|
|
|
receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd,
|
2016-06-10 16:15:35 -04:00
|
|
|
|
int cleartext,
|
2015-12-17 13:03:56 +09:00
|
|
|
|
char **cache_nonce_addr, const char *hexgrip,
|
|
|
|
|
PKT_public_key *pk)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err = 0;
|
|
|
|
|
unsigned char *wrappedkey = NULL;
|
|
|
|
|
size_t wrappedkeylen;
|
|
|
|
|
unsigned char *key = NULL;
|
|
|
|
|
size_t keylen, realkeylen;
|
|
|
|
|
gcry_sexp_t s_skey;
|
|
|
|
|
char *prompt;
|
|
|
|
|
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
log_info ("key %s: asking agent for the secret parts\n", hexgrip);
|
|
|
|
|
|
2017-03-31 20:03:52 +02:00
|
|
|
|
prompt = gpg_format_keydesc (ctrl, pk, FORMAT_KEYDESC_EXPORT,1);
|
2016-06-10 16:15:35 -04:00
|
|
|
|
err = agent_export_key (ctrl, hexgrip, prompt, !cleartext, cache_nonce_addr,
|
2017-07-24 16:03:25 +02:00
|
|
|
|
&wrappedkey, &wrappedkeylen,
|
|
|
|
|
pk->keyid, pk->main_keyid, pk->pubkey_algo);
|
2015-12-17 13:03:56 +09:00
|
|
|
|
xfree (prompt);
|
|
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
|
goto unwraperror;
|
|
|
|
|
if (wrappedkeylen < 24)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_INV_LENGTH);
|
|
|
|
|
goto unwraperror;
|
|
|
|
|
}
|
|
|
|
|
keylen = wrappedkeylen - 8;
|
|
|
|
|
key = xtrymalloc_secure (keylen);
|
|
|
|
|
if (!key)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto unwraperror;
|
|
|
|
|
}
|
|
|
|
|
err = gcry_cipher_decrypt (cipherhd, key, keylen, wrappedkey, wrappedkeylen);
|
|
|
|
|
if (err)
|
|
|
|
|
goto unwraperror;
|
|
|
|
|
realkeylen = gcry_sexp_canon_len (key, keylen, NULL, &err);
|
|
|
|
|
if (!realkeylen)
|
|
|
|
|
goto unwraperror; /* Invalid csexp. */
|
|
|
|
|
|
|
|
|
|
err = gcry_sexp_sscan (&s_skey, NULL, key, realkeylen);
|
|
|
|
|
if (!err)
|
|
|
|
|
{
|
2016-06-10 16:15:35 -04:00
|
|
|
|
if (cleartext)
|
|
|
|
|
err = cleartext_secret_key_to_openpgp (s_skey, pk);
|
|
|
|
|
else
|
|
|
|
|
err = transfer_format_to_openpgp (s_skey, pk);
|
2015-12-17 13:03:56 +09:00
|
|
|
|
gcry_sexp_release (s_skey);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unwraperror:
|
|
|
|
|
xfree (key);
|
|
|
|
|
xfree (wrappedkey);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("key %s: error receiving key from agent:"
|
|
|
|
|
" %s%s\n", hexgrip, gpg_strerror (err),
|
|
|
|
|
gpg_err_code (err) == GPG_ERR_FULLY_CANCELED?
|
|
|
|
|
"":_(" - skipped"));
|
|
|
|
|
}
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-07-12 13:59:10 +02:00
|
|
|
|
/* Write KEYBLOCK either to stdout or to the file set with the
|
2016-07-12 15:09:18 +02:00
|
|
|
|
* --output option. This is a simplified version of do_export_stream
|
|
|
|
|
* which supports only a few export options. */
|
2016-07-12 13:59:10 +02:00
|
|
|
|
gpg_error_t
|
2016-07-12 15:09:18 +02:00
|
|
|
|
write_keyblock_to_output (kbnode_t keyblock, int with_armor,
|
|
|
|
|
unsigned int options)
|
2016-07-12 13:59:10 +02:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
const char *fname;
|
|
|
|
|
iobuf_t out;
|
|
|
|
|
kbnode_t node;
|
|
|
|
|
armor_filter_context_t *afx = NULL;
|
2016-07-12 15:09:18 +02:00
|
|
|
|
iobuf_t out_help = NULL;
|
|
|
|
|
PKT_public_key *pk = NULL;
|
2016-07-12 13:59:10 +02:00
|
|
|
|
|
|
|
|
|
fname = opt.outfile? opt.outfile : "-";
|
|
|
|
|
if (is_secured_filename (fname) )
|
|
|
|
|
return gpg_error (GPG_ERR_EPERM);
|
|
|
|
|
|
|
|
|
|
out = iobuf_create (fname, 0);
|
|
|
|
|
if (!out)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
log_error(_("can't create '%s': %s\n"), fname, gpg_strerror (err));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
if (opt.verbose)
|
|
|
|
|
log_info (_("writing to '%s'\n"), iobuf_get_fname_nonnull (out));
|
|
|
|
|
|
2016-07-12 15:09:18 +02:00
|
|
|
|
if ((options & (EXPORT_PKA_FORMAT|EXPORT_DANE_FORMAT)))
|
|
|
|
|
{
|
|
|
|
|
with_armor = 0;
|
|
|
|
|
out_help = iobuf_temp ();
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-12 13:59:10 +02:00
|
|
|
|
if (with_armor)
|
|
|
|
|
{
|
|
|
|
|
afx = new_armor_context ();
|
|
|
|
|
afx->what = 1;
|
|
|
|
|
push_armor_filter (afx, out);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (node = keyblock; node; node = node->next)
|
|
|
|
|
{
|
2017-03-29 08:44:52 +02:00
|
|
|
|
if (is_deleted_kbnode (node))
|
2016-07-12 15:09:18 +02:00
|
|
|
|
continue;
|
gpg: Revamp reading and writing of ring trust packets.
* g10/parse-packet.c (parse_trust): Rename to ...
(parse_ring_trust): this. Change args and implement new ring trust
packet format.
(parse): Add special ring trust packet handling.
* g10/packet.h (PKT_user_id): New fields KEYUPDATE, UPDATEURL, and
KEYSRC.
(PKT_public_key): Ditto.
(RING_TRUST_SIG, RING_TRUST_KEY, RING_TRUST_UID): New consts.
(PKT_ring_trust): New.
(struct packet_struct): Remove member RING_TRUST.
(strcu parse_packet_ctx_s): Add field SKIP_META.
(init_parse_packet): Init SKIPT_META.
* g10/free-packet.c (release_public_key_parts): Free UDPATEURL.
(free_user_id): Ditto.
* g10/mainproc.c (list_node): Remove printing of non-documented "rtv"
lines.
* g10/build-packet.c (build_packet_and_meta): New.
(do_ring_trust): New.
* g10/export.c (write_keyblock_to_output): Use build_packet_and_meta
in backup mode.
(do_export_one_keyblock): Ditto.
* g10/import.c (read_block): Add arg WITH_META. Skip ring trust
packets if that ism not set.
(import): Call read_block WITH_META in restore mode.
* g10/keydb.h (KEYSRC_UNKNOWN, KEYSRC_FILE, KEYSRC_KS, KEYSRC_PREF_KS)
(KEYSRC_WKD, KEYSRC_WKD_SD, KEYSRC_DANE): New constants. They are not
yet used, though.
* g10/keydb.c (parse_keyblock_image): Allow ring trust packets.
(build_keyblock_image): Ditto. Use build_packet_and_meta.
* g10/keyring.c (keyring_get_keyblock): Remove specila treatment of
ring trust packets.
(write_keyblock): Use build_packet_and_meta. Remove special treatment
of ring trust packets and initialization of the signature caches.
--
This patch introduced the framework to store meta data for keys and
user ids in the keyrings/keyboxes. Ring trust packets are
implementation defined and have always been used in gpg to cache the
signature verification status.
Ring trust packets are only exported with the export option "backup"
and only imported with the import option "restore".
The new code uses a cleaner way to handle the ring trust packets: When
the parser reads a ring trust packet and the previously read packet
matches the type of that ring trust packet, the information is stored
in that previously read packet (signature, user id, or primary key)
and the next packet is read immediately. Thus only the parser sees
the ring trust packets. Ring trust packets are written by using the
new function build_packet_and_meta instead of build_packet. That
function writes a ring trust packet when the needed information is
available.
As a side-effect of this patch the signature status cache works again
and "gpg --check-sigs" is thus much faster.
Signed-off-by: Werner Koch <wk@gnupg.org>
2017-03-30 09:07:02 +02:00
|
|
|
|
if (node->pkt->pkttype == PKT_RING_TRUST)
|
|
|
|
|
continue; /* Skip - they should not be here anyway. */
|
2017-03-29 08:44:52 +02:00
|
|
|
|
|
2016-07-12 15:09:18 +02:00
|
|
|
|
if (!pk && (node->pkt->pkttype == PKT_PUBLIC_KEY
|
|
|
|
|
|| node->pkt->pkttype == PKT_SECRET_KEY))
|
|
|
|
|
pk = node->pkt->pkt.public_key;
|
|
|
|
|
|
gpg: Revamp reading and writing of ring trust packets.
* g10/parse-packet.c (parse_trust): Rename to ...
(parse_ring_trust): this. Change args and implement new ring trust
packet format.
(parse): Add special ring trust packet handling.
* g10/packet.h (PKT_user_id): New fields KEYUPDATE, UPDATEURL, and
KEYSRC.
(PKT_public_key): Ditto.
(RING_TRUST_SIG, RING_TRUST_KEY, RING_TRUST_UID): New consts.
(PKT_ring_trust): New.
(struct packet_struct): Remove member RING_TRUST.
(strcu parse_packet_ctx_s): Add field SKIP_META.
(init_parse_packet): Init SKIPT_META.
* g10/free-packet.c (release_public_key_parts): Free UDPATEURL.
(free_user_id): Ditto.
* g10/mainproc.c (list_node): Remove printing of non-documented "rtv"
lines.
* g10/build-packet.c (build_packet_and_meta): New.
(do_ring_trust): New.
* g10/export.c (write_keyblock_to_output): Use build_packet_and_meta
in backup mode.
(do_export_one_keyblock): Ditto.
* g10/import.c (read_block): Add arg WITH_META. Skip ring trust
packets if that ism not set.
(import): Call read_block WITH_META in restore mode.
* g10/keydb.h (KEYSRC_UNKNOWN, KEYSRC_FILE, KEYSRC_KS, KEYSRC_PREF_KS)
(KEYSRC_WKD, KEYSRC_WKD_SD, KEYSRC_DANE): New constants. They are not
yet used, though.
* g10/keydb.c (parse_keyblock_image): Allow ring trust packets.
(build_keyblock_image): Ditto. Use build_packet_and_meta.
* g10/keyring.c (keyring_get_keyblock): Remove specila treatment of
ring trust packets.
(write_keyblock): Use build_packet_and_meta. Remove special treatment
of ring trust packets and initialization of the signature caches.
--
This patch introduced the framework to store meta data for keys and
user ids in the keyrings/keyboxes. Ring trust packets are
implementation defined and have always been used in gpg to cache the
signature verification status.
Ring trust packets are only exported with the export option "backup"
and only imported with the import option "restore".
The new code uses a cleaner way to handle the ring trust packets: When
the parser reads a ring trust packet and the previously read packet
matches the type of that ring trust packet, the information is stored
in that previously read packet (signature, user id, or primary key)
and the next packet is read immediately. Thus only the parser sees
the ring trust packets. Ring trust packets are written by using the
new function build_packet_and_meta instead of build_packet. That
function writes a ring trust packet when the needed information is
available.
As a side-effect of this patch the signature status cache works again
and "gpg --check-sigs" is thus much faster.
Signed-off-by: Werner Koch <wk@gnupg.org>
2017-03-30 09:07:02 +02:00
|
|
|
|
if ((options & EXPORT_BACKUP))
|
|
|
|
|
err = build_packet_and_meta (out_help? out_help : out, node->pkt);
|
|
|
|
|
else
|
|
|
|
|
err = build_packet (out_help? out_help : out, node->pkt);
|
2016-07-12 15:09:18 +02:00
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("build_packet(%d) failed: %s\n",
|
|
|
|
|
node->pkt->pkttype, gpg_strerror (err) );
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2016-07-12 13:59:10 +02:00
|
|
|
|
}
|
|
|
|
|
err = 0;
|
|
|
|
|
|
2016-07-12 15:09:18 +02:00
|
|
|
|
if (out_help && pk)
|
|
|
|
|
{
|
|
|
|
|
const void *data;
|
|
|
|
|
size_t datalen;
|
|
|
|
|
|
|
|
|
|
iobuf_flush_temp (out_help);
|
|
|
|
|
data = iobuf_get_temp_buffer (out_help);
|
|
|
|
|
datalen = iobuf_get_temp_length (out_help);
|
|
|
|
|
|
|
|
|
|
err = print_pka_or_dane_records (out,
|
|
|
|
|
keyblock, pk, data, datalen,
|
|
|
|
|
(options & EXPORT_PKA_FORMAT),
|
|
|
|
|
(options & EXPORT_DANE_FORMAT));
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-12 13:59:10 +02:00
|
|
|
|
leave:
|
|
|
|
|
if (err)
|
|
|
|
|
iobuf_cancel (out);
|
|
|
|
|
else
|
|
|
|
|
iobuf_close (out);
|
2016-07-12 15:09:18 +02:00
|
|
|
|
iobuf_cancel (out_help);
|
2016-07-12 13:59:10 +02:00
|
|
|
|
release_armor_context (afx);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-07-01 16:50:12 +02:00
|
|
|
|
/*
|
|
|
|
|
* Apply the keep-uid filter to the keyblock. The deleted nodes are
|
|
|
|
|
* marked and thus the caller should call commit_kbnode afterwards.
|
|
|
|
|
* KEYBLOCK must not have any blocks marked as deleted.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
2017-03-31 20:03:52 +02:00
|
|
|
|
apply_keep_uid_filter (ctrl_t ctrl, kbnode_t keyblock, recsel_expr_t selector)
|
2016-07-01 16:50:12 +02:00
|
|
|
|
{
|
|
|
|
|
kbnode_t node;
|
2017-03-31 20:03:52 +02:00
|
|
|
|
struct impex_filter_parm_s parm;
|
|
|
|
|
|
|
|
|
|
parm.ctrl = ctrl;
|
2016-07-01 16:50:12 +02:00
|
|
|
|
|
|
|
|
|
for (node = keyblock->next; node; node = node->next )
|
|
|
|
|
{
|
|
|
|
|
if (node->pkt->pkttype == PKT_USER_ID)
|
|
|
|
|
{
|
2017-03-31 20:03:52 +02:00
|
|
|
|
parm.node = node;
|
|
|
|
|
if (!recsel_select (selector, impex_filter_getval, &parm))
|
2016-07-01 16:50:12 +02:00
|
|
|
|
{
|
2016-07-07 17:02:58 +02:00
|
|
|
|
/* log_debug ("keep-uid: deleting '%s'\n", */
|
|
|
|
|
/* node->pkt->pkt.user_id->name); */
|
2016-07-01 16:50:12 +02:00
|
|
|
|
/* The UID packet and all following packets up to the
|
|
|
|
|
* next UID or a subkey. */
|
|
|
|
|
delete_kbnode (node);
|
|
|
|
|
for (; node->next
|
|
|
|
|
&& node->next->pkt->pkttype != PKT_USER_ID
|
|
|
|
|
&& node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY
|
|
|
|
|
&& node->next->pkt->pkttype != PKT_SECRET_SUBKEY ;
|
|
|
|
|
node = node->next)
|
|
|
|
|
delete_kbnode (node->next);
|
|
|
|
|
}
|
2016-07-07 17:02:58 +02:00
|
|
|
|
/* else */
|
|
|
|
|
/* log_debug ("keep-uid: keeping '%s'\n", */
|
|
|
|
|
/* node->pkt->pkt.user_id->name); */
|
2016-07-01 16:50:12 +02:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-09-05 09:51:16 +02:00
|
|
|
|
/*
|
|
|
|
|
* Apply the drop-subkey filter to the keyblock. The deleted nodes are
|
|
|
|
|
* marked and thus the caller should call commit_kbnode afterwards.
|
|
|
|
|
* KEYBLOCK must not have any blocks marked as deleted.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
2017-03-31 20:03:52 +02:00
|
|
|
|
apply_drop_subkey_filter (ctrl_t ctrl, kbnode_t keyblock,
|
|
|
|
|
recsel_expr_t selector)
|
2016-09-05 09:51:16 +02:00
|
|
|
|
{
|
|
|
|
|
kbnode_t node;
|
2017-03-31 20:03:52 +02:00
|
|
|
|
struct impex_filter_parm_s parm;
|
|
|
|
|
|
|
|
|
|
parm.ctrl = ctrl;
|
2016-09-05 09:51:16 +02:00
|
|
|
|
|
|
|
|
|
for (node = keyblock->next; node; node = node->next )
|
|
|
|
|
{
|
|
|
|
|
if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY
|
|
|
|
|
|| node->pkt->pkttype == PKT_SECRET_SUBKEY)
|
|
|
|
|
{
|
2017-03-31 20:03:52 +02:00
|
|
|
|
parm.node = node;
|
|
|
|
|
if (recsel_select (selector, impex_filter_getval, &parm))
|
2016-09-05 09:51:16 +02:00
|
|
|
|
{
|
2017-03-03 09:22:40 +01:00
|
|
|
|
/*log_debug ("drop-subkey: deleting a key\n");*/
|
2016-09-05 09:51:16 +02:00
|
|
|
|
/* The subkey packet and all following packets up to the
|
|
|
|
|
* next subkey. */
|
|
|
|
|
delete_kbnode (node);
|
|
|
|
|
for (; node->next
|
|
|
|
|
&& node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY
|
|
|
|
|
&& node->next->pkt->pkttype != PKT_SECRET_SUBKEY ;
|
|
|
|
|
node = node->next)
|
|
|
|
|
delete_kbnode (node->next);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-07-12 15:09:18 +02:00
|
|
|
|
/* Print DANE or PKA records for all user IDs in KEYBLOCK to OUT. The
|
|
|
|
|
* data for the record is taken from (DATA,DATELEN). PK is the public
|
|
|
|
|
* key packet with the primary key. */
|
2016-07-07 17:02:58 +02:00
|
|
|
|
static gpg_error_t
|
2016-07-12 15:09:18 +02:00
|
|
|
|
print_pka_or_dane_records (iobuf_t out, kbnode_t keyblock, PKT_public_key *pk,
|
|
|
|
|
const void *data, size_t datalen,
|
2016-07-07 17:02:58 +02:00
|
|
|
|
int print_pka, int print_dane)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err = 0;
|
|
|
|
|
kbnode_t kbctx, node;
|
|
|
|
|
PKT_user_id *uid;
|
|
|
|
|
char *mbox = NULL;
|
|
|
|
|
char hashbuf[32];
|
|
|
|
|
char *hash = NULL;
|
|
|
|
|
char *domain;
|
|
|
|
|
const char *s;
|
|
|
|
|
unsigned int len;
|
2016-07-12 15:09:18 +02:00
|
|
|
|
estream_t fp = NULL;
|
|
|
|
|
char *hexdata = NULL;
|
|
|
|
|
char *hexfpr;
|
|
|
|
|
|
|
|
|
|
hexfpr = hexfingerprint (pk, NULL, 0);
|
2017-12-13 10:52:34 +01:00
|
|
|
|
if (!hexfpr)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2016-07-12 15:09:18 +02:00
|
|
|
|
hexdata = bin2hex (data, datalen, NULL);
|
|
|
|
|
if (!hexdata)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
ascii_strlwr (hexdata);
|
|
|
|
|
fp = es_fopenmem (0, "rw,samethread");
|
|
|
|
|
if (!fp)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2016-07-07 17:02:58 +02:00
|
|
|
|
|
|
|
|
|
for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));)
|
|
|
|
|
{
|
|
|
|
|
if (node->pkt->pkttype != PKT_USER_ID)
|
|
|
|
|
continue;
|
|
|
|
|
uid = node->pkt->pkt.user_id;
|
|
|
|
|
|
2017-03-08 11:01:22 +01:00
|
|
|
|
if (uid->flags.expired || uid->flags.revoked)
|
2016-07-07 17:02:58 +02:00
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
xfree (mbox);
|
|
|
|
|
mbox = mailbox_from_userid (uid->name);
|
|
|
|
|
if (!mbox)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
domain = strchr (mbox, '@');
|
|
|
|
|
*domain++ = 0;
|
|
|
|
|
|
|
|
|
|
if (print_pka)
|
|
|
|
|
{
|
|
|
|
|
es_fprintf (fp, "$ORIGIN _pka.%s.\n; %s\n; ", domain, hexfpr);
|
|
|
|
|
print_utf8_buffer (fp, uid->name, uid->len);
|
|
|
|
|
es_putc ('\n', fp);
|
|
|
|
|
gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf, mbox, strlen (mbox));
|
|
|
|
|
xfree (hash);
|
|
|
|
|
hash = zb32_encode (hashbuf, 8*20);
|
|
|
|
|
if (!hash)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
len = strlen (hexfpr)/2;
|
|
|
|
|
es_fprintf (fp, "%s TYPE37 \\# %u 0006 0000 00 %02X %s\n\n",
|
|
|
|
|
hash, 6 + len, len, hexfpr);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (print_dane && hexdata)
|
|
|
|
|
{
|
|
|
|
|
es_fprintf (fp, "$ORIGIN _openpgpkey.%s.\n; %s\n; ", domain, hexfpr);
|
|
|
|
|
print_utf8_buffer (fp, uid->name, uid->len);
|
|
|
|
|
es_putc ('\n', fp);
|
|
|
|
|
gcry_md_hash_buffer (GCRY_MD_SHA256, hashbuf, mbox, strlen (mbox));
|
|
|
|
|
xfree (hash);
|
|
|
|
|
hash = bin2hex (hashbuf, 28, NULL);
|
|
|
|
|
if (!hash)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
ascii_strlwr (hash);
|
|
|
|
|
len = strlen (hexdata)/2;
|
|
|
|
|
es_fprintf (fp, "%s TYPE61 \\# %u (\n", hash, len);
|
|
|
|
|
for (s = hexdata; ;)
|
|
|
|
|
{
|
|
|
|
|
es_fprintf (fp, "\t%.64s\n", s);
|
|
|
|
|
if (strlen (s) < 64)
|
|
|
|
|
break;
|
|
|
|
|
s += 64;
|
|
|
|
|
}
|
|
|
|
|
es_fputs ("\t)\n\n", fp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-07-12 15:09:18 +02:00
|
|
|
|
/* Make sure it is a string and write it. */
|
|
|
|
|
es_fputc (0, fp);
|
|
|
|
|
{
|
|
|
|
|
void *vp;
|
|
|
|
|
|
|
|
|
|
if (es_fclose_snatch (fp, &vp, NULL))
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
fp = NULL;
|
|
|
|
|
iobuf_writestr (out, vp);
|
|
|
|
|
es_free (vp);
|
|
|
|
|
}
|
|
|
|
|
err = 0;
|
|
|
|
|
|
2016-07-07 17:02:58 +02:00
|
|
|
|
leave:
|
|
|
|
|
xfree (hash);
|
|
|
|
|
xfree (mbox);
|
2016-07-12 15:09:18 +02:00
|
|
|
|
es_fclose (fp);
|
|
|
|
|
xfree (hexdata);
|
|
|
|
|
xfree (hexfpr);
|
2016-07-07 17:02:58 +02:00
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-07-07 14:16:21 +02:00
|
|
|
|
/* Helper for do_export_stream which writes one keyblock to OUT. */
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid,
|
|
|
|
|
iobuf_t out, int secret, unsigned int options,
|
|
|
|
|
export_stats_t stats, int *any,
|
|
|
|
|
KEYDB_SEARCH_DESC *desc, size_t ndesc,
|
|
|
|
|
size_t descindex, gcry_cipher_hd_t cipherhd)
|
|
|
|
|
{
|
2017-04-17 09:15:13 +09:00
|
|
|
|
gpg_error_t err = gpg_error (GPG_ERR_NOT_FOUND);
|
2016-07-07 14:16:21 +02:00
|
|
|
|
char *cache_nonce = NULL;
|
|
|
|
|
subkey_list_t subkey_list = NULL; /* Track already processed subkeys. */
|
|
|
|
|
int skip_until_subkey = 0;
|
|
|
|
|
int cleartext = 0;
|
|
|
|
|
char *hexgrip = NULL;
|
|
|
|
|
char *serialno = NULL;
|
|
|
|
|
PKT_public_key *pk;
|
|
|
|
|
u32 subkidbuf[2], *subkid;
|
|
|
|
|
kbnode_t kbctx, node;
|
|
|
|
|
|
2017-01-17 10:26:34 +01:00
|
|
|
|
/* NB: walk_kbnode skips packets marked as deleted. */
|
2016-07-07 14:16:21 +02:00
|
|
|
|
for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); )
|
|
|
|
|
{
|
|
|
|
|
if (skip_until_subkey)
|
|
|
|
|
{
|
|
|
|
|
if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
|
|
|
|
|
skip_until_subkey = 0;
|
|
|
|
|
else
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* We used to use comment packets, but not any longer. In
|
|
|
|
|
* case we still have comments on a key, strip them here
|
|
|
|
|
* before we call build_packet(). */
|
|
|
|
|
if (node->pkt->pkttype == PKT_COMMENT)
|
|
|
|
|
continue;
|
|
|
|
|
|
gpg: Revamp reading and writing of ring trust packets.
* g10/parse-packet.c (parse_trust): Rename to ...
(parse_ring_trust): this. Change args and implement new ring trust
packet format.
(parse): Add special ring trust packet handling.
* g10/packet.h (PKT_user_id): New fields KEYUPDATE, UPDATEURL, and
KEYSRC.
(PKT_public_key): Ditto.
(RING_TRUST_SIG, RING_TRUST_KEY, RING_TRUST_UID): New consts.
(PKT_ring_trust): New.
(struct packet_struct): Remove member RING_TRUST.
(strcu parse_packet_ctx_s): Add field SKIP_META.
(init_parse_packet): Init SKIPT_META.
* g10/free-packet.c (release_public_key_parts): Free UDPATEURL.
(free_user_id): Ditto.
* g10/mainproc.c (list_node): Remove printing of non-documented "rtv"
lines.
* g10/build-packet.c (build_packet_and_meta): New.
(do_ring_trust): New.
* g10/export.c (write_keyblock_to_output): Use build_packet_and_meta
in backup mode.
(do_export_one_keyblock): Ditto.
* g10/import.c (read_block): Add arg WITH_META. Skip ring trust
packets if that ism not set.
(import): Call read_block WITH_META in restore mode.
* g10/keydb.h (KEYSRC_UNKNOWN, KEYSRC_FILE, KEYSRC_KS, KEYSRC_PREF_KS)
(KEYSRC_WKD, KEYSRC_WKD_SD, KEYSRC_DANE): New constants. They are not
yet used, though.
* g10/keydb.c (parse_keyblock_image): Allow ring trust packets.
(build_keyblock_image): Ditto. Use build_packet_and_meta.
* g10/keyring.c (keyring_get_keyblock): Remove specila treatment of
ring trust packets.
(write_keyblock): Use build_packet_and_meta. Remove special treatment
of ring trust packets and initialization of the signature caches.
--
This patch introduced the framework to store meta data for keys and
user ids in the keyrings/keyboxes. Ring trust packets are
implementation defined and have always been used in gpg to cache the
signature verification status.
Ring trust packets are only exported with the export option "backup"
and only imported with the import option "restore".
The new code uses a cleaner way to handle the ring trust packets: When
the parser reads a ring trust packet and the previously read packet
matches the type of that ring trust packet, the information is stored
in that previously read packet (signature, user id, or primary key)
and the next packet is read immediately. Thus only the parser sees
the ring trust packets. Ring trust packets are written by using the
new function build_packet_and_meta instead of build_packet. That
function writes a ring trust packet when the needed information is
available.
As a side-effect of this patch the signature status cache works again
and "gpg --check-sigs" is thus much faster.
Signed-off-by: Werner Koch <wk@gnupg.org>
2017-03-30 09:07:02 +02:00
|
|
|
|
/* Skip ring trust packets - they should not ne here anyway. */
|
|
|
|
|
if (node->pkt->pkttype == PKT_RING_TRUST)
|
2016-07-07 14:16:21 +02:00
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* If exact is set, then we only export what was requested
|
|
|
|
|
* (plus the primary key, if the user didn't specifically
|
|
|
|
|
* request it). */
|
|
|
|
|
if (desc[descindex].exact && node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
|
|
|
|
|
{
|
|
|
|
|
if (!exact_subkey_match_p (desc+descindex, node))
|
|
|
|
|
{
|
|
|
|
|
/* Before skipping this subkey, check whether any
|
|
|
|
|
* other description wants an exact match on a
|
|
|
|
|
* subkey and include that subkey into the output
|
|
|
|
|
* too. Need to add this subkey to a list so that
|
|
|
|
|
* it won't get processed a second time.
|
|
|
|
|
*
|
|
|
|
|
* So the first step here is to check that list and
|
|
|
|
|
* skip in any case if the key is in that list.
|
|
|
|
|
*
|
|
|
|
|
* We need this whole mess because the import
|
|
|
|
|
* function of GnuPG < 2.1 is not able to merge
|
|
|
|
|
* secret keys and thus it is useless to output them
|
|
|
|
|
* as two separate keys and have import merge them.
|
|
|
|
|
*/
|
|
|
|
|
if (subkey_in_list_p (subkey_list, node))
|
|
|
|
|
skip_until_subkey = 1; /* Already processed this one. */
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
size_t j;
|
|
|
|
|
|
|
|
|
|
for (j=0; j < ndesc; j++)
|
|
|
|
|
if (j != descindex && desc[j].exact
|
|
|
|
|
&& exact_subkey_match_p (desc+j, node))
|
|
|
|
|
break;
|
|
|
|
|
if (!(j < ndesc))
|
|
|
|
|
skip_until_subkey = 1; /* No other one matching. */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (skip_until_subkey)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
/* Mark this one as processed. */
|
|
|
|
|
{
|
|
|
|
|
subkey_list_t tmp = new_subkey_list_item (node);
|
|
|
|
|
tmp->next = subkey_list;
|
|
|
|
|
subkey_list = tmp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (node->pkt->pkttype == PKT_SIGNATURE)
|
|
|
|
|
{
|
|
|
|
|
/* Do not export packets which are marked as not
|
|
|
|
|
* exportable. */
|
|
|
|
|
if (!(options & EXPORT_LOCAL_SIGS)
|
|
|
|
|
&& !node->pkt->pkt.signature->flags.exportable)
|
|
|
|
|
continue; /* not exportable */
|
|
|
|
|
|
|
|
|
|
/* Do not export packets with a "sensitive" revocation key
|
|
|
|
|
* unless the user wants us to. Note that we do export
|
|
|
|
|
* these when issuing the actual revocation (see revoke.c). */
|
|
|
|
|
if (!(options & EXPORT_SENSITIVE_REVKEYS)
|
|
|
|
|
&& node->pkt->pkt.signature->revkey)
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < node->pkt->pkt.signature->numrevkeys; i++)
|
|
|
|
|
if ((node->pkt->pkt.signature->revkey[i].class & 0x40))
|
|
|
|
|
break;
|
|
|
|
|
if (i < node->pkt->pkt.signature->numrevkeys)
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Don't export attribs? */
|
|
|
|
|
if (!(options & EXPORT_ATTRIBUTES)
|
|
|
|
|
&& node->pkt->pkttype == PKT_USER_ID
|
|
|
|
|
&& node->pkt->pkt.user_id->attrib_data)
|
|
|
|
|
{
|
|
|
|
|
/* Skip until we get to something that is not an attrib or a
|
|
|
|
|
* signature on an attrib. */
|
|
|
|
|
while (kbctx->next && kbctx->next->pkt->pkttype == PKT_SIGNATURE)
|
|
|
|
|
kbctx = kbctx->next;
|
|
|
|
|
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (secret && (node->pkt->pkttype == PKT_PUBLIC_KEY
|
|
|
|
|
|| node->pkt->pkttype == PKT_PUBLIC_SUBKEY))
|
|
|
|
|
{
|
|
|
|
|
pk = node->pkt->pkt.public_key;
|
|
|
|
|
if (node->pkt->pkttype == PKT_PUBLIC_KEY)
|
|
|
|
|
subkid = NULL;
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
keyid_from_pk (pk, subkidbuf);
|
|
|
|
|
subkid = subkidbuf;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (pk->seckey_info)
|
|
|
|
|
{
|
|
|
|
|
log_error ("key %s: oops: seckey_info already set"
|
|
|
|
|
" - skipped\n", keystr_with_sub (keyid, subkid));
|
|
|
|
|
skip_until_subkey = 1;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xfree (hexgrip);
|
|
|
|
|
err = hexkeygrip_from_pk (pk, &hexgrip);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("key %s: error computing keygrip: %s"
|
|
|
|
|
" - skipped\n", keystr_with_sub (keyid, subkid),
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
skip_until_subkey = 1;
|
|
|
|
|
err = 0;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xfree (serialno);
|
|
|
|
|
serialno = NULL;
|
|
|
|
|
if (secret == 2 && node->pkt->pkttype == PKT_PUBLIC_KEY)
|
|
|
|
|
{
|
|
|
|
|
/* We are asked not to export the secret parts of the
|
|
|
|
|
* primary key. Make up an error code to create the
|
|
|
|
|
* stub. */
|
|
|
|
|
err = GPG_ERR_NOT_FOUND;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
err = agent_get_keyinfo (ctrl, hexgrip, &serialno, &cleartext);
|
|
|
|
|
|
|
|
|
|
if ((!err && serialno)
|
|
|
|
|
&& secret == 2 && node->pkt->pkttype == PKT_PUBLIC_KEY)
|
|
|
|
|
{
|
|
|
|
|
/* It does not make sense to export a key with its
|
|
|
|
|
* primary key on card using a non-key stub. Thus we
|
|
|
|
|
* skip those keys when used with --export-secret-subkeys. */
|
|
|
|
|
log_info (_("key %s: key material on-card - skipped\n"),
|
|
|
|
|
keystr_with_sub (keyid, subkid));
|
|
|
|
|
skip_until_subkey = 1;
|
|
|
|
|
}
|
|
|
|
|
else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND
|
|
|
|
|
|| (!err && serialno))
|
|
|
|
|
{
|
|
|
|
|
/* Create a key stub. */
|
|
|
|
|
struct seckey_info *ski;
|
|
|
|
|
const char *s;
|
|
|
|
|
|
|
|
|
|
pk->seckey_info = ski = xtrycalloc (1, sizeof *ski);
|
|
|
|
|
if (!ski)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ski->is_protected = 1;
|
|
|
|
|
if (err)
|
|
|
|
|
ski->s2k.mode = 1001; /* GNU dummy (no secret key). */
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
ski->s2k.mode = 1002; /* GNU-divert-to-card. */
|
|
|
|
|
for (s=serialno; sizeof (ski->ivlen) && *s && s[1];
|
|
|
|
|
ski->ivlen++, s += 2)
|
|
|
|
|
ski->iv[ski->ivlen] = xtoi_2 (s);
|
|
|
|
|
}
|
|
|
|
|
|
gpg: Revamp reading and writing of ring trust packets.
* g10/parse-packet.c (parse_trust): Rename to ...
(parse_ring_trust): this. Change args and implement new ring trust
packet format.
(parse): Add special ring trust packet handling.
* g10/packet.h (PKT_user_id): New fields KEYUPDATE, UPDATEURL, and
KEYSRC.
(PKT_public_key): Ditto.
(RING_TRUST_SIG, RING_TRUST_KEY, RING_TRUST_UID): New consts.
(PKT_ring_trust): New.
(struct packet_struct): Remove member RING_TRUST.
(strcu parse_packet_ctx_s): Add field SKIP_META.
(init_parse_packet): Init SKIPT_META.
* g10/free-packet.c (release_public_key_parts): Free UDPATEURL.
(free_user_id): Ditto.
* g10/mainproc.c (list_node): Remove printing of non-documented "rtv"
lines.
* g10/build-packet.c (build_packet_and_meta): New.
(do_ring_trust): New.
* g10/export.c (write_keyblock_to_output): Use build_packet_and_meta
in backup mode.
(do_export_one_keyblock): Ditto.
* g10/import.c (read_block): Add arg WITH_META. Skip ring trust
packets if that ism not set.
(import): Call read_block WITH_META in restore mode.
* g10/keydb.h (KEYSRC_UNKNOWN, KEYSRC_FILE, KEYSRC_KS, KEYSRC_PREF_KS)
(KEYSRC_WKD, KEYSRC_WKD_SD, KEYSRC_DANE): New constants. They are not
yet used, though.
* g10/keydb.c (parse_keyblock_image): Allow ring trust packets.
(build_keyblock_image): Ditto. Use build_packet_and_meta.
* g10/keyring.c (keyring_get_keyblock): Remove specila treatment of
ring trust packets.
(write_keyblock): Use build_packet_and_meta. Remove special treatment
of ring trust packets and initialization of the signature caches.
--
This patch introduced the framework to store meta data for keys and
user ids in the keyrings/keyboxes. Ring trust packets are
implementation defined and have always been used in gpg to cache the
signature verification status.
Ring trust packets are only exported with the export option "backup"
and only imported with the import option "restore".
The new code uses a cleaner way to handle the ring trust packets: When
the parser reads a ring trust packet and the previously read packet
matches the type of that ring trust packet, the information is stored
in that previously read packet (signature, user id, or primary key)
and the next packet is read immediately. Thus only the parser sees
the ring trust packets. Ring trust packets are written by using the
new function build_packet_and_meta instead of build_packet. That
function writes a ring trust packet when the needed information is
available.
As a side-effect of this patch the signature status cache works again
and "gpg --check-sigs" is thus much faster.
Signed-off-by: Werner Koch <wk@gnupg.org>
2017-03-30 09:07:02 +02:00
|
|
|
|
if ((options & EXPORT_BACKUP))
|
|
|
|
|
err = build_packet_and_meta (out, node->pkt);
|
|
|
|
|
else
|
|
|
|
|
err = build_packet (out, node->pkt);
|
2016-07-07 14:16:21 +02:00
|
|
|
|
if (!err && node->pkt->pkttype == PKT_PUBLIC_KEY)
|
|
|
|
|
{
|
|
|
|
|
stats->exported++;
|
|
|
|
|
print_status_exported (node->pkt->pkt.public_key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else if (!err)
|
|
|
|
|
{
|
|
|
|
|
err = receive_seckey_from_agent (ctrl, cipherhd,
|
|
|
|
|
cleartext, &cache_nonce,
|
|
|
|
|
hexgrip, pk);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED)
|
|
|
|
|
goto leave;
|
|
|
|
|
skip_until_subkey = 1;
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
gpg: Revamp reading and writing of ring trust packets.
* g10/parse-packet.c (parse_trust): Rename to ...
(parse_ring_trust): this. Change args and implement new ring trust
packet format.
(parse): Add special ring trust packet handling.
* g10/packet.h (PKT_user_id): New fields KEYUPDATE, UPDATEURL, and
KEYSRC.
(PKT_public_key): Ditto.
(RING_TRUST_SIG, RING_TRUST_KEY, RING_TRUST_UID): New consts.
(PKT_ring_trust): New.
(struct packet_struct): Remove member RING_TRUST.
(strcu parse_packet_ctx_s): Add field SKIP_META.
(init_parse_packet): Init SKIPT_META.
* g10/free-packet.c (release_public_key_parts): Free UDPATEURL.
(free_user_id): Ditto.
* g10/mainproc.c (list_node): Remove printing of non-documented "rtv"
lines.
* g10/build-packet.c (build_packet_and_meta): New.
(do_ring_trust): New.
* g10/export.c (write_keyblock_to_output): Use build_packet_and_meta
in backup mode.
(do_export_one_keyblock): Ditto.
* g10/import.c (read_block): Add arg WITH_META. Skip ring trust
packets if that ism not set.
(import): Call read_block WITH_META in restore mode.
* g10/keydb.h (KEYSRC_UNKNOWN, KEYSRC_FILE, KEYSRC_KS, KEYSRC_PREF_KS)
(KEYSRC_WKD, KEYSRC_WKD_SD, KEYSRC_DANE): New constants. They are not
yet used, though.
* g10/keydb.c (parse_keyblock_image): Allow ring trust packets.
(build_keyblock_image): Ditto. Use build_packet_and_meta.
* g10/keyring.c (keyring_get_keyblock): Remove specila treatment of
ring trust packets.
(write_keyblock): Use build_packet_and_meta. Remove special treatment
of ring trust packets and initialization of the signature caches.
--
This patch introduced the framework to store meta data for keys and
user ids in the keyrings/keyboxes. Ring trust packets are
implementation defined and have always been used in gpg to cache the
signature verification status.
Ring trust packets are only exported with the export option "backup"
and only imported with the import option "restore".
The new code uses a cleaner way to handle the ring trust packets: When
the parser reads a ring trust packet and the previously read packet
matches the type of that ring trust packet, the information is stored
in that previously read packet (signature, user id, or primary key)
and the next packet is read immediately. Thus only the parser sees
the ring trust packets. Ring trust packets are written by using the
new function build_packet_and_meta instead of build_packet. That
function writes a ring trust packet when the needed information is
available.
As a side-effect of this patch the signature status cache works again
and "gpg --check-sigs" is thus much faster.
Signed-off-by: Werner Koch <wk@gnupg.org>
2017-03-30 09:07:02 +02:00
|
|
|
|
if ((options & EXPORT_BACKUP))
|
|
|
|
|
err = build_packet_and_meta (out, node->pkt);
|
|
|
|
|
else
|
|
|
|
|
err = build_packet (out, node->pkt);
|
2016-07-07 14:16:21 +02:00
|
|
|
|
if (node->pkt->pkttype == PKT_PUBLIC_KEY)
|
|
|
|
|
{
|
|
|
|
|
stats->exported++;
|
|
|
|
|
print_status_exported (node->pkt->pkt.public_key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
log_error ("key %s: error getting keyinfo from agent: %s"
|
|
|
|
|
" - skipped\n", keystr_with_sub (keyid, subkid),
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
skip_until_subkey = 1;
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xfree (pk->seckey_info);
|
|
|
|
|
pk->seckey_info = NULL;
|
|
|
|
|
{
|
|
|
|
|
int i;
|
|
|
|
|
for (i = pubkey_get_npkey (pk->pubkey_algo);
|
|
|
|
|
i < pubkey_get_nskey (pk->pubkey_algo); i++)
|
|
|
|
|
{
|
|
|
|
|
gcry_mpi_release (pk->pkey[i]);
|
|
|
|
|
pk->pkey[i] = NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else /* Not secret or common packets. */
|
|
|
|
|
{
|
gpg: Revamp reading and writing of ring trust packets.
* g10/parse-packet.c (parse_trust): Rename to ...
(parse_ring_trust): this. Change args and implement new ring trust
packet format.
(parse): Add special ring trust packet handling.
* g10/packet.h (PKT_user_id): New fields KEYUPDATE, UPDATEURL, and
KEYSRC.
(PKT_public_key): Ditto.
(RING_TRUST_SIG, RING_TRUST_KEY, RING_TRUST_UID): New consts.
(PKT_ring_trust): New.
(struct packet_struct): Remove member RING_TRUST.
(strcu parse_packet_ctx_s): Add field SKIP_META.
(init_parse_packet): Init SKIPT_META.
* g10/free-packet.c (release_public_key_parts): Free UDPATEURL.
(free_user_id): Ditto.
* g10/mainproc.c (list_node): Remove printing of non-documented "rtv"
lines.
* g10/build-packet.c (build_packet_and_meta): New.
(do_ring_trust): New.
* g10/export.c (write_keyblock_to_output): Use build_packet_and_meta
in backup mode.
(do_export_one_keyblock): Ditto.
* g10/import.c (read_block): Add arg WITH_META. Skip ring trust
packets if that ism not set.
(import): Call read_block WITH_META in restore mode.
* g10/keydb.h (KEYSRC_UNKNOWN, KEYSRC_FILE, KEYSRC_KS, KEYSRC_PREF_KS)
(KEYSRC_WKD, KEYSRC_WKD_SD, KEYSRC_DANE): New constants. They are not
yet used, though.
* g10/keydb.c (parse_keyblock_image): Allow ring trust packets.
(build_keyblock_image): Ditto. Use build_packet_and_meta.
* g10/keyring.c (keyring_get_keyblock): Remove specila treatment of
ring trust packets.
(write_keyblock): Use build_packet_and_meta. Remove special treatment
of ring trust packets and initialization of the signature caches.
--
This patch introduced the framework to store meta data for keys and
user ids in the keyrings/keyboxes. Ring trust packets are
implementation defined and have always been used in gpg to cache the
signature verification status.
Ring trust packets are only exported with the export option "backup"
and only imported with the import option "restore".
The new code uses a cleaner way to handle the ring trust packets: When
the parser reads a ring trust packet and the previously read packet
matches the type of that ring trust packet, the information is stored
in that previously read packet (signature, user id, or primary key)
and the next packet is read immediately. Thus only the parser sees
the ring trust packets. Ring trust packets are written by using the
new function build_packet_and_meta instead of build_packet. That
function writes a ring trust packet when the needed information is
available.
As a side-effect of this patch the signature status cache works again
and "gpg --check-sigs" is thus much faster.
Signed-off-by: Werner Koch <wk@gnupg.org>
2017-03-30 09:07:02 +02:00
|
|
|
|
if ((options & EXPORT_BACKUP))
|
|
|
|
|
err = build_packet_and_meta (out, node->pkt);
|
|
|
|
|
else
|
|
|
|
|
err = build_packet (out, node->pkt);
|
2016-07-07 14:16:21 +02:00
|
|
|
|
if (!err && node->pkt->pkttype == PKT_PUBLIC_KEY)
|
|
|
|
|
{
|
|
|
|
|
stats->exported++;
|
|
|
|
|
print_status_exported (node->pkt->pkt.public_key);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("build_packet(%d) failed: %s\n",
|
|
|
|
|
node->pkt->pkttype, gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!skip_until_subkey)
|
|
|
|
|
*any = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
release_subkey_list (subkey_list);
|
|
|
|
|
xfree (serialno);
|
|
|
|
|
xfree (hexgrip);
|
|
|
|
|
xfree (cache_nonce);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2010-10-01 20:33:53 +00:00
|
|
|
|
/* Export the keys identified by the list of strings in USERS to the
|
2016-07-07 14:16:21 +02:00
|
|
|
|
stream OUT. If SECRET is false public keys will be exported. With
|
2010-10-01 20:33:53 +00:00
|
|
|
|
secret true secret keys will be exported; in this case 1 means the
|
|
|
|
|
entire secret keyblock and 2 only the subkeys. OPTIONS are the
|
|
|
|
|
export options to apply. If KEYBLOCK_OUT is not NULL, AND the exit
|
|
|
|
|
code is zero, a pointer to the first keyblock found and exported
|
|
|
|
|
will be stored at this address; no other keyblocks are exported in
|
2015-10-07 16:55:15 +02:00
|
|
|
|
this case. The caller must free the returned keyblock. If any
|
2010-10-01 20:33:53 +00:00
|
|
|
|
key has been exported true is stored at ANY. */
|
2003-06-05 07:14:21 +00:00
|
|
|
|
static int
|
2010-10-01 20:33:53 +00:00
|
|
|
|
do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret,
|
2015-11-12 16:02:35 +01:00
|
|
|
|
kbnode_t *keyblock_out, unsigned int options,
|
|
|
|
|
export_stats_t stats, int *any)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
2010-09-06 19:57:42 +00:00
|
|
|
|
gpg_error_t err = 0;
|
|
|
|
|
PACKET pkt;
|
2016-07-07 14:16:21 +02:00
|
|
|
|
kbnode_t keyblock = NULL;
|
|
|
|
|
kbnode_t node;
|
2010-09-06 19:57:42 +00:00
|
|
|
|
size_t ndesc, descindex;
|
|
|
|
|
KEYDB_SEARCH_DESC *desc = NULL;
|
|
|
|
|
KEYDB_HANDLE kdbhd;
|
|
|
|
|
strlist_t sl;
|
2010-10-01 20:33:53 +00:00
|
|
|
|
gcry_cipher_hd_t cipherhd = NULL;
|
2015-11-12 16:02:35 +01:00
|
|
|
|
struct export_stats_s dummystats;
|
2016-07-07 17:02:58 +02:00
|
|
|
|
iobuf_t out_help = NULL;
|
2010-09-06 19:57:42 +00:00
|
|
|
|
|
2015-11-12 16:02:35 +01:00
|
|
|
|
if (!stats)
|
|
|
|
|
stats = &dummystats;
|
2010-09-06 19:57:42 +00:00
|
|
|
|
*any = 0;
|
|
|
|
|
init_packet (&pkt);
|
|
|
|
|
kdbhd = keydb_new ();
|
2015-12-03 12:18:32 +01:00
|
|
|
|
if (!kdbhd)
|
|
|
|
|
return gpg_error_from_syserror ();
|
2010-09-06 19:57:42 +00:00
|
|
|
|
|
2016-07-07 17:02:58 +02:00
|
|
|
|
/* For the PKA and DANE format open a helper iobuf and for DANE
|
|
|
|
|
* enforce some options. */
|
|
|
|
|
if ((options & (EXPORT_PKA_FORMAT | EXPORT_DANE_FORMAT)))
|
|
|
|
|
{
|
|
|
|
|
out_help = iobuf_temp ();
|
|
|
|
|
if ((options & EXPORT_DANE_FORMAT))
|
|
|
|
|
options |= EXPORT_MINIMAL | EXPORT_CLEAN;
|
|
|
|
|
}
|
2015-10-08 15:04:45 +02:00
|
|
|
|
|
2011-02-02 15:48:54 +01:00
|
|
|
|
if (!users)
|
2010-09-06 19:57:42 +00:00
|
|
|
|
{
|
|
|
|
|
ndesc = 1;
|
|
|
|
|
desc = xcalloc (ndesc, sizeof *desc);
|
|
|
|
|
desc[0].mode = KEYDB_SEARCH_MODE_FIRST;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
2010-09-06 19:57:42 +00:00
|
|
|
|
else
|
|
|
|
|
{
|
2011-02-02 15:48:54 +01:00
|
|
|
|
for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++)
|
2010-09-06 19:57:42 +00:00
|
|
|
|
;
|
|
|
|
|
desc = xmalloc ( ndesc * sizeof *desc);
|
2011-02-02 15:48:54 +01:00
|
|
|
|
|
2010-09-06 19:57:42 +00:00
|
|
|
|
for (ndesc=0, sl=users; sl; sl = sl->next)
|
|
|
|
|
{
|
2011-04-25 23:56:47 +02:00
|
|
|
|
if (!(err=classify_user_id (sl->d, desc+ndesc, 1)))
|
2010-09-06 19:57:42 +00:00
|
|
|
|
ndesc++;
|
|
|
|
|
else
|
|
|
|
|
log_error (_("key \"%s\" not found: %s\n"),
|
|
|
|
|
sl->d, gpg_strerror (err));
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 11:54:51 +01:00
|
|
|
|
keydb_disable_caching (kdbhd); /* We are looping the search. */
|
|
|
|
|
|
2010-09-06 19:57:42 +00:00
|
|
|
|
/* It would be nice to see which of the given users did actually
|
|
|
|
|
match one in the keyring. To implement this we need to have
|
|
|
|
|
a found flag for each entry in desc. To set this flag we
|
|
|
|
|
must check all those entries after a match to mark all
|
|
|
|
|
matched one - currently we stop at the first match. To do
|
|
|
|
|
this we need an extra flag to enable this feature. */
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2006-04-19 11:26:11 +00:00
|
|
|
|
#ifdef ENABLE_SELINUX_HACKS
|
2010-09-06 19:57:42 +00:00
|
|
|
|
if (secret)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("exporting secret keys not allowed\n"));
|
2010-10-01 20:33:53 +00:00
|
|
|
|
err = gpg_error (GPG_ERR_NOT_SUPPORTED);
|
2010-09-06 19:57:42 +00:00
|
|
|
|
goto leave;
|
2006-04-19 11:26:11 +00:00
|
|
|
|
}
|
|
|
|
|
#endif
|
2011-02-02 15:48:54 +01:00
|
|
|
|
|
2010-10-01 20:33:53 +00:00
|
|
|
|
/* For secret key export we need to setup a decryption context. */
|
|
|
|
|
if (secret)
|
|
|
|
|
{
|
|
|
|
|
void *kek = NULL;
|
|
|
|
|
size_t keklen;
|
|
|
|
|
|
|
|
|
|
err = agent_keywrap_key (ctrl, 1, &kek, &keklen);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("error getting the KEK: %s\n", gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2011-02-02 15:48:54 +01:00
|
|
|
|
|
2010-10-01 20:33:53 +00:00
|
|
|
|
/* Prepare a cipher context. */
|
|
|
|
|
err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128,
|
|
|
|
|
GCRY_CIPHER_MODE_AESWRAP, 0);
|
|
|
|
|
if (!err)
|
|
|
|
|
err = gcry_cipher_setkey (cipherhd, kek, keklen);
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error ("error setting up an encryption context: %s\n",
|
|
|
|
|
gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
xfree (kek);
|
|
|
|
|
kek = NULL;
|
|
|
|
|
}
|
2006-04-19 11:26:11 +00:00
|
|
|
|
|
2015-06-19 12:39:29 +02:00
|
|
|
|
for (;;)
|
2010-09-06 19:57:42 +00:00
|
|
|
|
{
|
|
|
|
|
u32 keyid[2];
|
2010-10-01 20:33:53 +00:00
|
|
|
|
PKT_public_key *pk;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2015-06-19 12:39:29 +02:00
|
|
|
|
err = keydb_search (kdbhd, desc, ndesc, &descindex);
|
2011-02-02 15:48:54 +01:00
|
|
|
|
if (!users)
|
2010-09-06 19:57:42 +00:00
|
|
|
|
desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
|
2015-06-19 12:39:29 +02:00
|
|
|
|
if (err)
|
|
|
|
|
break;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2010-09-06 19:57:42 +00:00
|
|
|
|
/* Read the keyblock. */
|
2010-10-01 20:33:53 +00:00
|
|
|
|
release_kbnode (keyblock);
|
|
|
|
|
keyblock = NULL;
|
2010-09-06 19:57:42 +00:00
|
|
|
|
err = keydb_get_keyblock (kdbhd, &keyblock);
|
2011-02-02 15:48:54 +01:00
|
|
|
|
if (err)
|
2010-09-06 19:57:42 +00:00
|
|
|
|
{
|
|
|
|
|
log_error (_("error reading keyblock: %s\n"), gpg_strerror (err));
|
|
|
|
|
goto leave;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2010-10-01 20:33:53 +00:00
|
|
|
|
node = find_kbnode (keyblock, PKT_PUBLIC_KEY);
|
|
|
|
|
if (!node)
|
2010-09-06 19:57:42 +00:00
|
|
|
|
{
|
2010-10-01 20:33:53 +00:00
|
|
|
|
log_error ("public key packet not found in keyblock - skipped\n");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2015-11-12 16:02:35 +01:00
|
|
|
|
stats->count++;
|
2014-04-15 16:40:48 +02:00
|
|
|
|
setup_main_keyids (keyblock); /* gpg_format_keydesc needs it. */
|
2010-10-01 20:33:53 +00:00
|
|
|
|
pk = node->pkt->pkt.public_key;
|
|
|
|
|
keyid_from_pk (pk, keyid);
|
2010-09-06 19:57:42 +00:00
|
|
|
|
|
2010-10-01 20:33:53 +00:00
|
|
|
|
/* If a secret key export is required we need to check whether
|
|
|
|
|
we have a secret key at all and if so create the seckey_info
|
|
|
|
|
structure. */
|
|
|
|
|
if (secret)
|
|
|
|
|
{
|
|
|
|
|
if (agent_probe_any_secret_key (ctrl, keyblock))
|
|
|
|
|
continue; /* No secret key (neither primary nor subkey). */
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2010-09-06 19:57:42 +00:00
|
|
|
|
/* No v3 keys with GNU mode 1001. */
|
2010-10-01 20:33:53 +00:00
|
|
|
|
if (secret == 2 && pk->version == 3)
|
2010-09-06 19:57:42 +00:00
|
|
|
|
{
|
2010-10-01 20:33:53 +00:00
|
|
|
|
log_info (_("key %s: PGP 2.x style key - skipped\n"),
|
|
|
|
|
keystr (keyid));
|
2010-09-06 19:57:42 +00:00
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
More cleanup of "allow to".
* README, agent/command.c, agent/keyformat.txt, common/i18n.c,
common/iobuf.c, common/keyserver.h, dirmngr/cdblib.c,
dirmngr/ldap-wrapper.c, doc/DETAILS, doc/TRANSLATE,
doc/announce-2.1.txt, doc/gpg.texi, doc/gpgsm.texi,
doc/scdaemon.texi, doc/tools.texi, doc/whats-new-in-2.1.txt,
g10/export.c, g10/getkey.c, g10/import.c, g10/keyedit.c, m4/ksba.m4,
m4/libgcrypt.m4, m4/ntbtls.m4, po/ca.po, po/cs.po, po/da.po,
po/de.po, po/el.po, po/eo.po, po/es.po, po/et.po, po/fi.po,
po/fr.po, po/gl.po, po/hu.po, po/id.po, po/it.po, po/ja.po,
po/nb.po, po/pl.po, po/pt.po, po/ro.po, po/ru.po, po/sk.po,
po/sv.po, po/tr.po, po/uk.po, po/zh_CN.po, po/zh_TW.po,
scd/app-p15.c, scd/ccid-driver.c, scd/command.c, sm/gpgsm.c,
sm/sign.c, tools/gpgconf-comp.c, tools/gpgtar.h: replace "Allow to"
with clearer text.
In standard English, the normal construction is "${XXX} allows ${YYY}
to" -- that is, the subject (${XXX}) of the sentence is allowing the
object (${YYY}) to do something. When the object is missing, the
phrasing sounds awkward, even if the object is implied by context.
There's almost always a better construction that isn't as awkward.
These changes should make the language a bit clearer.
Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2016-08-01 22:19:17 -04:00
|
|
|
|
/* The agent does not yet allow export of v3 packets. It is
|
2010-10-01 20:33:53 +00:00
|
|
|
|
actually questionable whether we should allow them at
|
|
|
|
|
all. */
|
|
|
|
|
if (pk->version == 3)
|
2010-09-06 19:57:42 +00:00
|
|
|
|
{
|
2010-10-01 20:33:53 +00:00
|
|
|
|
log_info ("key %s: PGP 2.x style key (v3) export "
|
|
|
|
|
"not yet supported - skipped\n", keystr (keyid));
|
2003-06-05 07:14:21 +00:00
|
|
|
|
continue;
|
2010-09-06 19:57:42 +00:00
|
|
|
|
}
|
2015-11-12 16:02:35 +01:00
|
|
|
|
stats->secret_count++;
|
2010-09-06 19:57:42 +00:00
|
|
|
|
}
|
2010-10-01 20:33:53 +00:00
|
|
|
|
|
|
|
|
|
/* Always do the cleaning on the public key part if requested.
|
2018-07-09 09:49:09 +02:00
|
|
|
|
* A designated revocation is never stripped, even with
|
|
|
|
|
* export-minimal set. */
|
2010-10-01 20:33:53 +00:00
|
|
|
|
if ((options & EXPORT_CLEAN))
|
2018-07-06 11:48:38 +02:00
|
|
|
|
{
|
|
|
|
|
merge_keys_and_selfsig (ctrl, keyblock);
|
|
|
|
|
clean_all_uids (ctrl, keyblock, opt.verbose,
|
|
|
|
|
(options&EXPORT_MINIMAL), NULL, NULL);
|
2018-07-09 09:49:09 +02:00
|
|
|
|
clean_all_subkeys (ctrl, keyblock, opt.verbose,
|
|
|
|
|
(options&EXPORT_MINIMAL)? KEY_CLEAN_ALL
|
|
|
|
|
/**/ : KEY_CLEAN_AUTHENCR,
|
|
|
|
|
NULL, NULL);
|
|
|
|
|
commit_kbnode (&keyblock);
|
2018-07-06 11:48:38 +02:00
|
|
|
|
}
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2016-07-01 16:50:12 +02:00
|
|
|
|
if (export_keep_uid)
|
|
|
|
|
{
|
|
|
|
|
commit_kbnode (&keyblock);
|
2017-03-31 20:03:52 +02:00
|
|
|
|
apply_keep_uid_filter (ctrl, keyblock, export_keep_uid);
|
2016-07-01 16:50:12 +02:00
|
|
|
|
commit_kbnode (&keyblock);
|
|
|
|
|
}
|
|
|
|
|
|
2016-09-05 09:51:16 +02:00
|
|
|
|
if (export_drop_subkey)
|
|
|
|
|
{
|
|
|
|
|
commit_kbnode (&keyblock);
|
2017-03-31 20:03:52 +02:00
|
|
|
|
apply_drop_subkey_filter (ctrl, keyblock, export_drop_subkey);
|
2016-09-05 09:51:16 +02:00
|
|
|
|
commit_kbnode (&keyblock);
|
|
|
|
|
}
|
|
|
|
|
|
2010-09-06 19:57:42 +00:00
|
|
|
|
/* And write it. */
|
2016-07-07 17:02:58 +02:00
|
|
|
|
err = do_export_one_keyblock (ctrl, keyblock, keyid,
|
|
|
|
|
out_help? out_help : out,
|
|
|
|
|
secret, options, stats, any,
|
2016-07-07 14:16:21 +02:00
|
|
|
|
desc, ndesc, descindex, cipherhd);
|
|
|
|
|
if (err)
|
|
|
|
|
break;
|
2006-04-19 11:26:11 +00:00
|
|
|
|
|
2010-10-01 20:33:53 +00:00
|
|
|
|
if (keyblock_out)
|
2010-09-06 19:57:42 +00:00
|
|
|
|
{
|
2010-10-01 20:33:53 +00:00
|
|
|
|
*keyblock_out = keyblock;
|
2010-09-06 19:57:42 +00:00
|
|
|
|
break;
|
|
|
|
|
}
|
2016-07-07 17:02:58 +02:00
|
|
|
|
|
|
|
|
|
if (out_help)
|
|
|
|
|
{
|
|
|
|
|
/* We want to write PKA or DANE records. OUT_HELP has the
|
|
|
|
|
* keyblock and we print a record for each uid to OUT. */
|
|
|
|
|
const void *data;
|
|
|
|
|
size_t datalen;
|
|
|
|
|
|
|
|
|
|
iobuf_flush_temp (out_help);
|
|
|
|
|
data = iobuf_get_temp_buffer (out_help);
|
|
|
|
|
datalen = iobuf_get_temp_length (out_help);
|
|
|
|
|
|
2016-07-12 15:09:18 +02:00
|
|
|
|
err = print_pka_or_dane_records (out,
|
|
|
|
|
keyblock, pk, data, datalen,
|
|
|
|
|
(options & EXPORT_PKA_FORMAT),
|
|
|
|
|
(options & EXPORT_DANE_FORMAT));
|
2016-07-07 17:02:58 +02:00
|
|
|
|
if (err)
|
2016-07-12 15:09:18 +02:00
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
iobuf_close (out_help);
|
|
|
|
|
out_help = iobuf_temp ();
|
2016-07-07 17:02:58 +02:00
|
|
|
|
}
|
|
|
|
|
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
2011-04-29 15:07:11 +02:00
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_NOT_FOUND)
|
2010-09-06 19:57:42 +00:00
|
|
|
|
err = 0;
|
|
|
|
|
|
|
|
|
|
leave:
|
2016-07-07 17:02:58 +02:00
|
|
|
|
iobuf_cancel (out_help);
|
2010-10-01 20:33:53 +00:00
|
|
|
|
gcry_cipher_close (cipherhd);
|
2010-09-06 19:57:42 +00:00
|
|
|
|
xfree(desc);
|
|
|
|
|
keydb_release (kdbhd);
|
|
|
|
|
if (err || !keyblock_out)
|
|
|
|
|
release_kbnode( keyblock );
|
|
|
|
|
if( !*any )
|
|
|
|
|
log_info(_("WARNING: nothing exported\n"));
|
|
|
|
|
return err;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
2016-01-08 17:22:32 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static gpg_error_t
|
|
|
|
|
key_to_sshblob (membuf_t *mb, const char *identifier, ...)
|
|
|
|
|
{
|
|
|
|
|
va_list arg_ptr;
|
|
|
|
|
gpg_error_t err = 0;
|
|
|
|
|
unsigned char nbuf[4];
|
|
|
|
|
unsigned char *buf;
|
|
|
|
|
size_t buflen;
|
|
|
|
|
gcry_mpi_t a;
|
|
|
|
|
|
|
|
|
|
ulongtobuf (nbuf, (ulong)strlen (identifier));
|
|
|
|
|
put_membuf (mb, nbuf, 4);
|
|
|
|
|
put_membuf_str (mb, identifier);
|
2016-01-08 20:37:25 +01:00
|
|
|
|
if (!strncmp (identifier, "ecdsa-sha2-", 11))
|
|
|
|
|
{
|
|
|
|
|
ulongtobuf (nbuf, (ulong)strlen (identifier+11));
|
|
|
|
|
put_membuf (mb, nbuf, 4);
|
|
|
|
|
put_membuf_str (mb, identifier+11);
|
|
|
|
|
}
|
2016-01-08 17:22:32 +01:00
|
|
|
|
va_start (arg_ptr, identifier);
|
|
|
|
|
while ((a = va_arg (arg_ptr, gcry_mpi_t)))
|
|
|
|
|
{
|
|
|
|
|
err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, &buf, &buflen, a);
|
|
|
|
|
if (err)
|
|
|
|
|
break;
|
|
|
|
|
if (!strcmp (identifier, "ssh-ed25519")
|
|
|
|
|
&& buflen > 5 && buf[4] == 0x40)
|
|
|
|
|
{
|
|
|
|
|
/* We need to strip our 0x40 prefix. */
|
|
|
|
|
put_membuf (mb, "\x00\x00\x00\x20", 4);
|
|
|
|
|
put_membuf (mb, buf+5, buflen-5);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
put_membuf (mb, buf, buflen);
|
|
|
|
|
gcry_free (buf);
|
|
|
|
|
}
|
|
|
|
|
va_end (arg_ptr);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Export the key identified by USERID in the SSH public key format.
|
|
|
|
|
The function exports the latest subkey with Authentication
|
|
|
|
|
capability unless the '!' suffix is used to export a specific
|
|
|
|
|
key. */
|
|
|
|
|
gpg_error_t
|
|
|
|
|
export_ssh_key (ctrl_t ctrl, const char *userid)
|
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
kbnode_t keyblock = NULL;
|
|
|
|
|
KEYDB_SEARCH_DESC desc;
|
|
|
|
|
u32 latest_date;
|
|
|
|
|
u32 curtime = make_timestamp ();
|
|
|
|
|
kbnode_t latest_key, node;
|
|
|
|
|
PKT_public_key *pk;
|
2017-04-12 08:47:23 +09:00
|
|
|
|
const char *identifier = NULL;
|
2016-01-08 17:22:32 +01:00
|
|
|
|
membuf_t mb;
|
|
|
|
|
estream_t fp = NULL;
|
|
|
|
|
struct b64state b64_state;
|
|
|
|
|
const char *fname = "-";
|
|
|
|
|
|
|
|
|
|
init_membuf (&mb, 4096);
|
|
|
|
|
|
|
|
|
|
/* We need to know whether the key has been specified using the
|
|
|
|
|
exact syntax ('!' suffix). Thus we need to run a
|
|
|
|
|
classify_user_id on our own. */
|
|
|
|
|
err = classify_user_id (userid, &desc, 1);
|
|
|
|
|
|
|
|
|
|
/* Get the public key. */
|
|
|
|
|
if (!err)
|
|
|
|
|
{
|
|
|
|
|
getkey_ctx_t getkeyctx;
|
|
|
|
|
|
|
|
|
|
err = get_pubkey_byname (ctrl, &getkeyctx, NULL, userid, &keyblock,
|
|
|
|
|
NULL,
|
|
|
|
|
0 /* Only usable keys or given exact. */,
|
|
|
|
|
1 /* No AKL lookup. */);
|
|
|
|
|
if (!err)
|
|
|
|
|
{
|
2017-03-31 20:03:52 +02:00
|
|
|
|
err = getkey_next (ctrl, getkeyctx, NULL, NULL);
|
2016-01-08 17:22:32 +01:00
|
|
|
|
if (!err)
|
|
|
|
|
err = gpg_error (GPG_ERR_AMBIGUOUS_NAME);
|
|
|
|
|
else if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY)
|
|
|
|
|
err = 0;
|
|
|
|
|
}
|
2017-03-31 20:35:28 +02:00
|
|
|
|
getkey_end (ctrl, getkeyctx);
|
2016-01-08 17:22:32 +01:00
|
|
|
|
}
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("key \"%s\" not found: %s\n"), userid, gpg_strerror (err));
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* The finish_lookup code in getkey.c does not handle auth keys,
|
|
|
|
|
thus we have to duplicate the code here to find the latest
|
|
|
|
|
subkey. However, if the key has been found using an exact match
|
|
|
|
|
('!' notation) we use that key without any further checks and
|
|
|
|
|
even allow the use of the primary key. */
|
|
|
|
|
latest_date = 0;
|
|
|
|
|
latest_key = NULL;
|
|
|
|
|
for (node = keyblock; node; node = node->next)
|
|
|
|
|
{
|
|
|
|
|
if ((node->pkt->pkttype == PKT_PUBLIC_SUBKEY
|
|
|
|
|
|| node->pkt->pkttype == PKT_PUBLIC_KEY)
|
|
|
|
|
&& node->pkt->pkt.public_key->flags.exact)
|
|
|
|
|
{
|
|
|
|
|
latest_key = node;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!latest_key)
|
|
|
|
|
{
|
|
|
|
|
for (node = keyblock; node; node = node->next)
|
|
|
|
|
{
|
|
|
|
|
if (node->pkt->pkttype != PKT_PUBLIC_SUBKEY)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
pk = node->pkt->pkt.public_key;
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("\tchecking subkey %08lX\n",
|
|
|
|
|
(ulong) keyid_from_pk (pk, NULL));
|
|
|
|
|
if (!(pk->pubkey_usage & PUBKEY_USAGE_AUTH))
|
|
|
|
|
{
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("\tsubkey not usable for authentication\n");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!pk->flags.valid)
|
|
|
|
|
{
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("\tsubkey not valid\n");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (pk->flags.revoked)
|
|
|
|
|
{
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("\tsubkey has been revoked\n");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (pk->has_expired)
|
|
|
|
|
{
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("\tsubkey has expired\n");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (pk->timestamp > curtime && !opt.ignore_valid_from)
|
|
|
|
|
{
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("\tsubkey not yet valid\n");
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("\tsubkey might be fine\n");
|
|
|
|
|
/* In case a key has a timestamp of 0 set, we make sure that it
|
|
|
|
|
is used. A better change would be to compare ">=" but that
|
|
|
|
|
might also change the selected keys and is as such a more
|
|
|
|
|
intrusive change. */
|
|
|
|
|
if (pk->timestamp > latest_date || (!pk->timestamp && !latest_date))
|
|
|
|
|
{
|
|
|
|
|
latest_date = pk->timestamp;
|
|
|
|
|
latest_key = node;
|
|
|
|
|
}
|
|
|
|
|
}
|
2017-02-14 10:55:13 +01:00
|
|
|
|
|
|
|
|
|
/* If no subkey was suitable check the primary key. */
|
|
|
|
|
if (!latest_key
|
|
|
|
|
&& (node = keyblock) && node->pkt->pkttype == PKT_PUBLIC_KEY)
|
|
|
|
|
{
|
|
|
|
|
pk = node->pkt->pkt.public_key;
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("\tchecking primary key %08lX\n",
|
|
|
|
|
(ulong) keyid_from_pk (pk, NULL));
|
|
|
|
|
if (!(pk->pubkey_usage & PUBKEY_USAGE_AUTH))
|
|
|
|
|
{
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("\tprimary key not usable for authentication\n");
|
|
|
|
|
}
|
|
|
|
|
else if (!pk->flags.valid)
|
|
|
|
|
{
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("\tprimary key not valid\n");
|
|
|
|
|
}
|
|
|
|
|
else if (pk->flags.revoked)
|
|
|
|
|
{
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("\tprimary key has been revoked\n");
|
|
|
|
|
}
|
|
|
|
|
else if (pk->has_expired)
|
|
|
|
|
{
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("\tprimary key has expired\n");
|
|
|
|
|
}
|
|
|
|
|
else if (pk->timestamp > curtime && !opt.ignore_valid_from)
|
|
|
|
|
{
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("\tprimary key not yet valid\n");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("\tprimary key is fine\n");
|
|
|
|
|
latest_date = pk->timestamp;
|
|
|
|
|
latest_key = node;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-08 17:22:32 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!latest_key)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY);
|
|
|
|
|
log_error (_("key \"%s\" not found: %s\n"), userid, gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pk = latest_key->pkt->pkt.public_key;
|
|
|
|
|
if (DBG_LOOKUP)
|
|
|
|
|
log_debug ("\tusing key %08lX\n", (ulong) keyid_from_pk (pk, NULL));
|
|
|
|
|
|
|
|
|
|
switch (pk->pubkey_algo)
|
|
|
|
|
{
|
|
|
|
|
case PUBKEY_ALGO_DSA:
|
|
|
|
|
identifier = "ssh-dss";
|
|
|
|
|
err = key_to_sshblob (&mb, identifier,
|
|
|
|
|
pk->pkey[0], pk->pkey[1], pk->pkey[2], pk->pkey[3],
|
|
|
|
|
NULL);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PUBKEY_ALGO_RSA:
|
|
|
|
|
case PUBKEY_ALGO_RSA_S:
|
|
|
|
|
identifier = "ssh-rsa";
|
|
|
|
|
err = key_to_sshblob (&mb, identifier, pk->pkey[1], pk->pkey[0], NULL);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PUBKEY_ALGO_ECDSA:
|
|
|
|
|
{
|
|
|
|
|
char *curveoid;
|
|
|
|
|
const char *curve;
|
|
|
|
|
|
|
|
|
|
curveoid = openpgp_oid_to_str (pk->pkey[0]);
|
|
|
|
|
if (!curveoid)
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
else if (!(curve = openpgp_oid_to_curve (curveoid, 0)))
|
|
|
|
|
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (!strcmp (curve, "nistp256"))
|
|
|
|
|
identifier = "ecdsa-sha2-nistp256";
|
|
|
|
|
else if (!strcmp (curve, "nistp384"))
|
|
|
|
|
identifier = "ecdsa-sha2-nistp384";
|
|
|
|
|
else if (!strcmp (curve, "nistp521"))
|
|
|
|
|
identifier = "ecdsa-sha2-nistp521";
|
|
|
|
|
|
|
|
|
|
if (!identifier)
|
|
|
|
|
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
|
|
|
|
|
else
|
|
|
|
|
err = key_to_sshblob (&mb, identifier, pk->pkey[1], NULL);
|
|
|
|
|
}
|
|
|
|
|
xfree (curveoid);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PUBKEY_ALGO_EDDSA:
|
|
|
|
|
if (!openpgp_oid_is_ed25519 (pk->pkey[0]))
|
|
|
|
|
err = gpg_error (GPG_ERR_UNKNOWN_CURVE);
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
identifier = "ssh-ed25519";
|
|
|
|
|
err = key_to_sshblob (&mb, identifier, pk->pkey[1], NULL);
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
case PUBKEY_ALGO_ELGAMAL_E:
|
|
|
|
|
case PUBKEY_ALGO_ELGAMAL:
|
|
|
|
|
err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
default:
|
|
|
|
|
err = GPG_ERR_PUBKEY_ALGO;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-12 08:47:23 +09:00
|
|
|
|
if (!identifier)
|
2016-01-08 17:22:32 +01:00
|
|
|
|
goto leave;
|
|
|
|
|
|
|
|
|
|
if (opt.outfile && *opt.outfile && strcmp (opt.outfile, "-"))
|
|
|
|
|
fp = es_fopen ((fname = opt.outfile), "w");
|
|
|
|
|
else
|
|
|
|
|
fp = es_stdout;
|
|
|
|
|
if (!fp)
|
|
|
|
|
{
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
log_error (_("error creating '%s': %s\n"), fname, gpg_strerror (err));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
es_fprintf (fp, "%s ", identifier);
|
|
|
|
|
err = b64enc_start_es (&b64_state, fp, "");
|
2017-04-12 08:47:23 +09:00
|
|
|
|
if (!err)
|
|
|
|
|
{
|
|
|
|
|
void *blob;
|
|
|
|
|
size_t bloblen;
|
2016-01-08 17:22:32 +01:00
|
|
|
|
|
2017-04-12 08:47:23 +09:00
|
|
|
|
blob = get_membuf (&mb, &bloblen);
|
|
|
|
|
if (blob)
|
|
|
|
|
{
|
|
|
|
|
err = b64enc_write (&b64_state, blob, bloblen);
|
|
|
|
|
xfree (blob);
|
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
err = b64enc_finish (&b64_state);
|
|
|
|
|
}
|
2016-01-08 17:22:32 +01:00
|
|
|
|
if (err)
|
|
|
|
|
goto leave;
|
|
|
|
|
es_fprintf (fp, " openpgp:0x%08lX\n", (ulong)keyid_from_pk (pk, NULL));
|
|
|
|
|
|
|
|
|
|
if (es_ferror (fp))
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (es_fclose (fp))
|
|
|
|
|
err = gpg_error_from_syserror ();
|
|
|
|
|
fp = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
|
log_error (_("error writing '%s': %s\n"), fname, gpg_strerror (err));
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
es_fclose (fp);
|
|
|
|
|
xfree (get_membuf (&mb, NULL));
|
|
|
|
|
release_kbnode (keyblock);
|
|
|
|
|
return err;
|
|
|
|
|
}
|