aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--agent/agent.h1
-rw-r--r--agent/cache.c29
-rw-r--r--agent/findkey.c42
-rw-r--r--g10/keygen.c2
-rw-r--r--g10/main.h2
-rw-r--r--g10/revoke.c14
6 files changed, 81 insertions, 9 deletions
diff --git a/agent/agent.h b/agent/agent.h
index 4ed8c7fe6..a420baed9 100644
--- a/agent/agent.h
+++ b/agent/agent.h
@@ -364,6 +364,7 @@ void agent_flush_cache (void);
int agent_put_cache (const char *key, cache_mode_t cache_mode,
const char *data, int ttl);
char *agent_get_cache (const char *key, cache_mode_t cache_mode);
+void agent_store_cache_hit (const char *key);
/*-- pksign.c --*/
diff --git a/agent/cache.c b/agent/cache.c
index d4deaeb8c..49402e434 100644
--- a/agent/cache.c
+++ b/agent/cache.c
@@ -65,6 +65,9 @@ struct cache_item_s {
/* The cache himself. */
static ITEM thecache;
+/* NULL or the last cache key stored by agent_store_cache_hit. */
+static char *last_stored_cache_key;
+
/* This function must be called once to initialize this module. It
has to be done before a second thread is spawned. */
@@ -388,12 +391,24 @@ agent_get_cache (const char *key, cache_mode_t cache_mode)
ITEM r;
char *value = NULL;
int res;
+ int last_stored = 0;
if (cache_mode == CACHE_MODE_IGNORE)
return NULL;
+ if (!key)
+ {
+ key = last_stored_cache_key;
+ if (!key)
+ return NULL;
+ last_stored = 1;
+ }
+
+
if (DBG_CACHE)
- log_debug ("agent_get_cache '%s' (mode %d) ...\n", key, cache_mode);
+ log_debug ("agent_get_cache '%s' (mode %d)%s ...\n",
+ key, cache_mode,
+ last_stored? " (stored cache key)":"");
housekeeping ();
for (r=thecache; r; r = r->next)
@@ -404,6 +419,7 @@ agent_get_cache (const char *key, cache_mode_t cache_mode)
|| r->cache_mode == cache_mode)
&& !strcmp (r->key, key))
{
+ /* Note: To avoid races KEY may not be accessed anymore below. */
r->accessed = gnupg_get_time ();
if (DBG_CACHE)
log_debug ("... hit\n");
@@ -442,3 +458,14 @@ agent_get_cache (const char *key, cache_mode_t cache_mode)
return NULL;
}
+
+
+/* Store the key for the last successful cache hit. That value is
+ used by agent_get_cache if the requested KEY is given as NULL.
+ NULL may be used to remove that key. */
+void
+agent_store_cache_hit (const char *key)
+{
+ xfree (last_stored_cache_key);
+ last_stored_cache_key = key? xtrystrdup (key) : NULL;
+}
diff --git a/agent/findkey.c b/agent/findkey.c
index 5ff263ef4..fbe303116 100644
--- a/agent/findkey.c
+++ b/agent/findkey.c
@@ -372,6 +372,8 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
rc = agent_unprotect (ctrl, *keybuf, pw, NULL, &result, &resultlen);
if (!rc)
{
+ if (cache_mode == CACHE_MODE_NORMAL)
+ agent_store_cache_hit (hexgrip);
if (r_passphrase)
*r_passphrase = pw;
else
@@ -383,6 +385,45 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
xfree (pw);
rc = 0;
}
+ else if (cache_mode == CACHE_MODE_NORMAL)
+ {
+ /* The standard use of GPG keys is to have a signing and an
+ encryption subkey. Commonly both use the same
+ passphrase. We try to help the user to enter the
+ passphrase only once by silently trying the last
+ correctly entered passphrase. Checking one additional
+ passphrase should be acceptable; despite the S2K
+ introduced delays. The assumed workflow is:
+
+ 1. Read encrypted message in a MUA and thus enter a
+ passphrase for the encryption subkey.
+
+ 2. Reply to that mail with an encrypted and signed
+ mail, thus entering the passphrase for the signing
+ subkey.
+
+ We can often avoid the passphrase entry in the second
+ step. We do this only in normal mode, so not to
+ interfere with unrelated cache entries. */
+ pw = agent_get_cache (NULL, cache_mode);
+ if (pw)
+ {
+ rc = agent_unprotect (ctrl, *keybuf, pw, NULL,
+ &result, &resultlen);
+ if (!rc)
+ {
+ if (r_passphrase)
+ *r_passphrase = pw;
+ else
+ xfree (pw);
+ xfree (*keybuf);
+ *keybuf = result;
+ return 0;
+ }
+ xfree (pw);
+ rc = 0;
+ }
+ }
/* If the pinentry is currently in use, we wait up to 60 seconds
for it to close and check the cache again. This solves a common
@@ -460,6 +501,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
{
agent_put_cache (hexgrip, cache_mode, pi->pin,
lookup_ttl? lookup_ttl (hexgrip) : 0);
+ agent_store_cache_hit (hexgrip);
if (r_passphrase && *pi->pin)
*r_passphrase = xtrystrdup (pi->pin);
}
diff --git a/g10/keygen.c b/g10/keygen.c
index 92337bb75..4ae34bf72 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -4115,7 +4115,7 @@ do_generate_keypair (struct para_data_s *para,
update_ownertrust (pk, ((get_ownertrust (pk) & ~TRUST_MASK)
| TRUST_ULTIMATE ));
- gen_standard_revoke (pk);
+ gen_standard_revoke (pk, cache_nonce);
if (!opt.batch)
{
diff --git a/g10/main.h b/g10/main.h
index 4eb1b5f31..44c447866 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -333,7 +333,7 @@ int enarmor_file( const char *fname );
/*-- revoke.c --*/
struct revocation_reason_info;
-int gen_standard_revoke (PKT_public_key *psk);
+int gen_standard_revoke (PKT_public_key *psk, const char *cache_nonce);
int gen_revoke( const char *uname );
int gen_desig_revoke( const char *uname, strlist_t locusr);
int revocation_reason_build_cb( PKT_signature *sig, void *opaque );
diff --git a/g10/revoke.c b/g10/revoke.c
index 67f62e5cc..019c62c0a 100644
--- a/g10/revoke.c
+++ b/g10/revoke.c
@@ -443,7 +443,8 @@ create_revocation (const char *filename,
struct revocation_reason_info *reason,
PKT_public_key *psk,
kbnode_t keyblock,
- const char *leadintext, int suffix)
+ const char *leadintext, int suffix,
+ const char *cache_nonce)
{
int rc;
iobuf_t out = NULL;
@@ -466,7 +467,7 @@ create_revocation (const char *filename,
rc = make_keysig_packet (&sig, psk, NULL, NULL, psk, 0x20, 0,
opt.force_v4_certs? 4:0,
0, 0,
- revocation_reason_build_cb, reason, NULL);
+ revocation_reason_build_cb, reason, cache_nonce);
if (rc)
{
log_error (_("make_keysig_packet failed: %s\n"), g10_errstr (rc));
@@ -511,9 +512,10 @@ create_revocation (const char *filename,
by gpg's interactive key generation function. The certificate is
stored at a dedicated place in a slightly modified form to avoid an
accidental import. PSK is the primary key; a corresponding secret
- key must be available. */
+ key must be available. CACHE_NONCE is optional but can be used to
+ help gpg-agent to avoid an extra passphrase prompt. */
int
-gen_standard_revoke (PKT_public_key *psk)
+gen_standard_revoke (PKT_public_key *psk, const char *cache_nonce)
{
int rc;
estream_t memfp;
@@ -573,7 +575,7 @@ gen_standard_revoke (PKT_public_key *psk)
reason.code = 0x00; /* No particular reason. */
reason.desc = NULL;
- rc = create_revocation (fname, &reason, psk, NULL, leadin, 3);
+ rc = create_revocation (fname, &reason, psk, NULL, leadin, 3, cache_nonce);
xfree (leadin);
xfree (fname);
@@ -662,7 +664,7 @@ gen_revoke (const char *uname)
if (!opt.armor)
tty_printf (_("ASCII armored output forced.\n"));
- rc = create_revocation (NULL, reason, psk, keyblock, NULL, 0);
+ rc = create_revocation (NULL, reason, psk, keyblock, NULL, 0, NULL);
if (rc)
goto leave;