diff options
Diffstat (limited to 'agent')
-rw-r--r-- | agent/ChangeLog | 13 | ||||
-rw-r--r-- | agent/agent.h | 6 | ||||
-rw-r--r-- | agent/cache.c | 36 | ||||
-rw-r--r-- | agent/call-pinentry.c | 6 | ||||
-rw-r--r-- | agent/command.c | 45 | ||||
-rw-r--r-- | agent/cvt-openpgp.c | 32 | ||||
-rw-r--r-- | agent/cvt-openpgp.h | 1 | ||||
-rw-r--r-- | agent/genkey.c | 45 |
8 files changed, 152 insertions, 32 deletions
diff --git a/agent/ChangeLog b/agent/ChangeLog index e584005ff..5d726eab3 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,16 @@ +2010-09-01 Werner Koch <[email protected]> + + * 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 <[email protected]> * pksign.c (do_encode_dsa): Fix sign problem. diff --git a/agent/agent.h b/agent/agent.h index 57078ab40..cb06faca3 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -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); diff --git a/agent/cache.c b/agent/cache.c index 10f9ef65a..0a2dd00cb 100644 --- a/agent/cache.c +++ b/agent/cache.c @@ -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) diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c index d00fdf6fc..6ab845a0c 100644 --- a/agent/call-pinentry.c +++ b/agent/call-pinentry.c @@ -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 diff --git a/agent/command.c b/agent/command.c index 4b847e503..965d24d84 100644 --- a/agent/command.c +++ b/agent/command.c @@ -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); diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c index a392518ba..87f62042a 100644 --- a/agent/cvt-openpgp.c +++ b/agent/cvt-openpgp.c @@ -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) { diff --git a/agent/cvt-openpgp.h b/agent/cvt-openpgp.h index 17b1a6ead..8dafbc454 100644 --- a/agent/cvt-openpgp.h +++ b/agent/cvt-openpgp.h @@ -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); diff --git a/agent/genkey.c b/agent/genkey.c index b064c98ed..60cc3416f 100644 --- a/agent/genkey.c +++ b/agent/genkey.c @@ -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); |