1
0
Fork 0
mirror of git://git.gnupg.org/gnupg.git synced 2025-07-02 22:46:30 +02:00

Use passphrase caching for import and genkey.

This commit is contained in:
Werner Koch 2010-09-01 09:48:35 +00:00
parent e3d8bb0244
commit 9a9b3da58f
13 changed files with 240 additions and 58 deletions

View file

@ -1,3 +1,16 @@
2010-09-01 Werner Koch <wk@g10code.com>
* call-pinentry.c (start_pinentry): Disable pinentry logging.
* command.c (cmd_import_key, cmd_genkey): Add CACHE handling.
* cvt-openpgp.c (convert_openpgp): Add arg CACHE_NONCE and try the
cached nonce first.
* genkey.c (agent_genkey): Add arg CACHE_NONCE.
* cache.c (agent_get_cache): Require user and impgen cache modes
to match the requested mode.
(agent_put_cache): Ditto.
* agent.h (CACHE_MODE_IMPGEN): New.
2010-08-31 Werner Koch <wk@g10code.com>
* pksign.c (do_encode_dsa): Fix sign problem.

View file

@ -193,7 +193,9 @@ typedef enum
CACHE_MODE_ANY, /* Any mode except ignore matches. */
CACHE_MODE_NORMAL, /* Normal cache (gpg-agent). */
CACHE_MODE_USER, /* GET_PASSPHRASE related cache. */
CACHE_MODE_SSH /* SSH related cache. */
CACHE_MODE_SSH, /* SSH related cache. */
CACHE_MODE_IMPGEN /* Used for import and genkey. This is a
non-predictable nonce. */
}
cache_mode_t;
@ -286,7 +288,7 @@ int agent_pkdecrypt (ctrl_t ctrl, const char *desc_text,
int check_passphrase_constraints (ctrl_t ctrl, const char *pw, int silent);
gpg_error_t agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
char **r_passphrase);
int agent_genkey (ctrl_t ctrl,
int agent_genkey (ctrl_t ctrl, const char *cache_nonce,
const char *keyparam, size_t keyparmlen, membuf_t *outbuf);
int agent_protect_and_store (ctrl_t ctrl, gcry_sexp_t s_skey);

View file

@ -1,5 +1,5 @@
/* cache.c - keep a cache of passphrases
* Copyright (C) 2002 Free Software Foundation, Inc.
* Copyright (C) 2002, 2010 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -131,9 +131,9 @@ housekeeping (void)
{
if (r->lockcount)
{
log_error ("can't remove unused cache entry `%s' due to"
log_error ("can't remove unused cache entry `%s' (mode %d) due to"
" lockcount=%d\n",
r->key, r->lockcount);
r->key, r->cache_mode, r->lockcount);
r->accessed += 60*10; /* next error message in 10 minutes */
rprev = r;
r = r->next;
@ -142,7 +142,8 @@ housekeeping (void)
{
ITEM r2 = r->next;
if (DBG_CACHE)
log_debug (" removed `%s' (slot not used for 30m)\n", r->key);
log_debug (" removed `%s' (mode %d) (slot not used for 30m)\n",
r->key, r->cache_mode);
xfree (r);
if (!rprev)
thecache = r2;
@ -203,8 +204,8 @@ agent_put_cache (const char *key, cache_mode_t cache_mode,
ITEM r;
if (DBG_CACHE)
log_debug ("agent_put_cache `%s' requested ttl=%d mode=%d\n",
key, ttl, cache_mode);
log_debug ("agent_put_cache `%s' (mode %d) requested ttl=%d\n",
key, cache_mode, ttl);
housekeeping ();
if (!ttl)
@ -220,7 +221,11 @@ agent_put_cache (const char *key, cache_mode_t cache_mode,
for (r=thecache; r; r = r->next)
{
if (!r->lockcount && !strcmp (r->key, key))
if (!r->lockcount
&& ((cache_mode != CACHE_MODE_USER
&& cache_mode != CACHE_MODE_IMPGEN)
|| r->cache_mode == cache_mode)
&& !strcmp (r->key, key))
break;
}
if (r)
@ -269,7 +274,8 @@ agent_put_cache (const char *key, cache_mode_t cache_mode,
/* Try to find an item in the cache. Note that we currently don't
make use of CACHE_MODE. */
make use of CACHE_MODE except for CACHE_MODE_IMPGEN and
CACHE_MODE_USER. */
const char *
agent_get_cache (const char *key, cache_mode_t cache_mode, void **cache_id)
{
@ -279,7 +285,7 @@ agent_get_cache (const char *key, cache_mode_t cache_mode, void **cache_id)
return NULL;
if (DBG_CACHE)
log_debug ("agent_get_cache `%s'...\n", key);
log_debug ("agent_get_cache `%s' (mode %d) ...\n", key, cache_mode);
housekeeping ();
/* first try to find one with no locks - this is an updated cache
@ -287,7 +293,11 @@ agent_get_cache (const char *key, cache_mode_t cache_mode, void **cache_id)
lockcount. */
for (r=thecache; r; r = r->next)
{
if (!r->lockcount && r->pw && !strcmp (r->key, key))
if (!r->lockcount && r->pw
&& ((cache_mode != CACHE_MODE_USER
&& cache_mode != CACHE_MODE_IMPGEN)
|| r->cache_mode == cache_mode)
&& !strcmp (r->key, key))
{
/* put_cache does only put strings into the cache, so we
don't need the lengths */
@ -302,7 +312,11 @@ agent_get_cache (const char *key, cache_mode_t cache_mode, void **cache_id)
/* again, but this time get even one with a lockcount set */
for (r=thecache; r; r = r->next)
{
if (r->pw && !strcmp (r->key, key))
if (r->pw
&& ((cache_mode != CACHE_MODE_USER
&& cache_mode != CACHE_MODE_IMPGEN)
|| r->cache_mode == cache_mode)
&& !strcmp (r->key, key))
{
r->accessed = gnupg_get_time ();
if (DBG_CACHE)

View file

@ -316,6 +316,12 @@ start_pinentry (ctrl_t ctrl)
log_error ("can't allocate assuan context: %s\n", gpg_strerror (rc));
return rc;
}
/* We don't want to log the pinentry communication to make the logs
easier to read. We might want to add a new debug option to enable
pinentry logging. */
#ifdef ASSUAN_NO_LOGGING
assuan_set_flag (ctx, ASSUAN_NO_LOGGING, 1);
#endif
/* Connect to the pinentry and perform initial handshaking. Note
that atfork is used to change the environment for pinentry. We

View file

@ -766,7 +766,7 @@ cmd_pkdecrypt (assuan_context_t ctx, char *line)
static const char hlp_genkey[] =
"GENKEY\n"
"GENKEY [<cache_nonce>]\n"
"\n"
"Generate a new key, store the secret part and return the public\n"
"part. Here is an example transaction:\n"
@ -787,8 +787,15 @@ cmd_genkey (assuan_context_t ctx, char *line)
unsigned char *value;
size_t valuelen;
membuf_t outbuf;
(void)line;
char *cache_nonce = NULL;
char *p;
p = line;
for (p=line; *p && *p != ' ' && *p != '\t'; p++)
;
*p = '\0';
if (*line)
cache_nonce = xtrystrdup (line);
/* First inquire the parameters */
rc = assuan_inquire (ctx, "KEYPARAM", &value, &valuelen, MAXLEN_KEYPARAM);
@ -797,12 +804,13 @@ cmd_genkey (assuan_context_t ctx, char *line)
init_membuf (&outbuf, 512);
rc = agent_genkey (ctrl, (char*)value, valuelen, &outbuf);
rc = agent_genkey (ctrl, cache_nonce, (char*)value, valuelen, &outbuf);
xfree (value);
if (rc)
clear_outbuf (&outbuf);
else
rc = write_and_clear_outbuf (ctx, &outbuf);
xfree (cache_nonce);
return leave_cmd (ctx, rc);
}
@ -1463,7 +1471,7 @@ cmd_keywrap_key (assuan_context_t ctx, char *line)
static const char hlp_import_key[] =
"IMPORT_KEY\n"
"IMPORT_KEY [<cache_nonce>]\n"
"\n"
"Import a secret key into the key store. The key is expected to be\n"
"encrypted using the current session's key wrapping key (cf. command\n"
@ -1485,15 +1493,22 @@ cmd_import_key (assuan_context_t ctx, char *line)
size_t finalkeylen;
unsigned char grip[20];
gcry_sexp_t openpgp_sexp = NULL;
char *cache_nonce = NULL;
char *p;
(void)line;
if (!ctrl->server_local->import_key)
{
err = gpg_error (GPG_ERR_MISSING_KEY);
goto leave;
}
p = line;
for (p=line; *p && *p != ' ' && *p != '\t'; p++)
;
*p = '\0';
if (*line)
cache_nonce = xtrystrdup (line);
assuan_begin_confidential (ctx);
err = assuan_inquire (ctx, "KEYDATA",
&wrappedkey, &wrappedkeylen, MAXLEN_KEYDATA);
@ -1567,13 +1582,26 @@ cmd_import_key (assuan_context_t ctx, char *line)
key import. */
err = convert_openpgp (ctrl, openpgp_sexp, grip,
ctrl->server_local->keydesc,
ctrl->server_local->keydesc, cache_nonce,
&key, &passphrase);
if (err)
goto leave;
realkeylen = gcry_sexp_canon_len (key, keylen, NULL, &err);
if (!realkeylen)
goto leave; /* Invalid canonical encoded S-expression. */
if (passphrase)
{
if (!cache_nonce)
{
char buf[12];
gcry_create_nonce (buf, 12);
cache_nonce = bin2hex (buf, 12, NULL);
}
if (cache_nonce
&& !agent_put_cache (cache_nonce, CACHE_MODE_IMPGEN,
passphrase, 120 /*seconds*/))
assuan_write_status (ctx, "CACHE_NONCE", cache_nonce);
}
}
else
{
@ -1604,6 +1632,7 @@ cmd_import_key (assuan_context_t ctx, char *line)
xfree (key);
gcry_cipher_close (cipherhd);
xfree (wrappedkey);
xfree (cache_nonce);
xfree (ctrl->server_local->keydesc);
ctrl->server_local->keydesc = NULL;
return leave_cmd (ctx, err);

View file

@ -497,7 +497,8 @@ try_do_unprotect_cb (struct pin_entry_info_s *pi)
gpg_error_t err;
struct try_do_unprotect_arg_s *arg = pi->check_cb_arg;
err = do_unprotect (pi->pin, arg->is_v4? 4:3,
err = do_unprotect (pi->pin,
arg->is_v4? 4:3,
arg->pubkey_algo, arg->is_protected,
arg->skey, arg->skeysize,
arg->protect_algo, arg->iv, arg->ivlen,
@ -507,15 +508,16 @@ try_do_unprotect_cb (struct pin_entry_info_s *pi)
/* SKEY may be modified now, thus we need to re-compute SKEYIDX. */
for (arg->skeyidx = 0; (arg->skeyidx < arg->skeysize
&& arg->skey[arg->skeyidx]); arg->skeyidx++)
;
;
return err;
}
/* Convert an OpenPGP transfer key into our internal format. Before
asking for a passphrase we check whether the key already exists in
our key storage. S_PGP is the OpenPGP key in transfer format. On
success R_KEY will receive a canonical encoded S-expression with
our key storage. S_PGP is the OpenPGP key in transfer format. If
CACHE_NONCE is given the passphrase will be looked up in the cache.
On success R_KEY will receive a canonical encoded S-expression with
the unprotected key in our internal format; the caller needs to
release that memory. The passphrase used to decrypt the OpenPGP
key will be returned at R_PASSPHRASE; the caller must release this
@ -525,6 +527,7 @@ try_do_unprotect_cb (struct pin_entry_info_s *pi)
gpg_error_t
convert_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
unsigned char *grip, const char *prompt,
const char *cache_nonce,
unsigned char **r_key, char **r_passphrase)
{
gpg_error_t err;
@ -759,7 +762,26 @@ convert_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
pi_arg.skeysize = DIM (skey);
pi_arg.skeyidx = skeyidx;
pi_arg.r_key = &s_skey;
err = agent_askpin (ctrl, prompt, NULL, NULL, pi);
err = gpg_error (GPG_ERR_BAD_PASSPHRASE);
if (cache_nonce)
{
void *cache_marker = NULL;
const char *cache_value;
cache_value = agent_get_cache (cache_nonce, CACHE_MODE_IMPGEN,
&cache_marker);
if (cache_value)
{
if (strlen (cache_value) < pi->max_length)
strcpy (pi->pin, cache_value);
agent_unlock_cache_entry (&cache_marker);
}
if (*pi->pin)
err = try_do_unprotect_cb (pi);
}
if (gpg_err_code (err) == GPG_ERR_BAD_PASSPHRASE)
err = agent_askpin (ctrl, prompt, NULL, NULL, pi);
skeyidx = pi_arg.skeyidx;
if (!err)
{

View file

@ -21,6 +21,7 @@
gpg_error_t convert_openpgp (ctrl_t ctrl, gcry_sexp_t s_pgp,
unsigned char *grip, const char *prompt,
const char *cache_nonce,
unsigned char **r_key, char **r_passphrase);

View file

@ -351,9 +351,11 @@ agent_ask_new_passphrase (ctrl_t ctrl, const char *prompt,
/* Generate a new keypair according to the parameters given in
KEYPARAM */
KEYPARAM. If CACHE_NONCE is given first try to lookup a passphrase
using the cache nonce. */
int
agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
agent_genkey (ctrl_t ctrl, const char *cache_nonce,
const char *keyparam, size_t keyparamlen,
membuf_t *outbuf)
{
gcry_sexp_t s_keyparam, s_key, s_private, s_public;
@ -370,10 +372,28 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
}
/* Get the passphrase now, cause key generation may take a while. */
rc = agent_ask_new_passphrase (ctrl,
_("Please enter the passphrase to%0A"
"to protect your new key"),
&passphrase);
if (cache_nonce)
{
void *cache_marker = NULL;
const char *cache_value;
cache_value = agent_get_cache (cache_nonce, CACHE_MODE_IMPGEN,
&cache_marker);
if (cache_value)
{
passphrase = xtrymalloc_secure (strlen (cache_value)+1);
if (passphrase)
strcpy (passphrase, cache_value);
agent_unlock_cache_entry (&cache_marker);
}
}
if (passphrase)
rc = 0;
else
rc = agent_ask_new_passphrase (ctrl,
_("Please enter the passphrase to%0A"
"to protect your new key"),
&passphrase);
if (rc)
return rc;
@ -410,6 +430,19 @@ agent_genkey (ctrl_t ctrl, const char *keyparam, size_t keyparamlen,
if (DBG_CRYPTO)
log_debug ("storing private key\n");
rc = store_key (s_private, passphrase, 0);
if (!rc)
{
if (!cache_nonce)
{
char tmpbuf[12];
gcry_create_nonce (tmpbuf, 12);
cache_nonce = bin2hex (tmpbuf, 12, NULL);
}
if (cache_nonce
&& !agent_put_cache (cache_nonce, CACHE_MODE_IMPGEN,
passphrase, 900 /*seconds*/))
agent_write_status (ctrl, "CACHE_NONCE", cache_nonce, NULL);
}
xfree (passphrase);
passphrase = NULL;
gcry_sexp_release (s_private);