From 30f1b027c012f8022c67185832fa1aada26c396a Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 1 Feb 2002 11:39:06 +0000 Subject: [PATCH] * cache.c: Add a few debug outputs. * protect.c (agent_private_key_type): New. * agent.h: Add PRIVATE_KEY_ enums. * findkey.c (agent_key_from_file): Use it to decide whether we have to unprotect a key. (unprotect): Cache the passphrase. * findkey.c (agent_key_from_file,agent_key_available): The key files do now require a ".key" suffix to make a script's life easier. * genkey.c (store_key): Ditto. --- agent/ChangeLog | 15 ++++++++++++ agent/agent.h | 8 ++++++ agent/cache.c | 15 ++++++++++++ agent/findkey.c | 65 ++++++++++++++++++++++++++++++++++++++----------- agent/genkey.c | 4 +-- agent/protect.c | 27 ++++++++++++++++++++ 6 files changed, 118 insertions(+), 16 deletions(-) diff --git a/agent/ChangeLog b/agent/ChangeLog index 21f496628..83ac2b33c 100644 --- a/agent/ChangeLog +++ b/agent/ChangeLog @@ -1,3 +1,18 @@ +2002-02-01 Werner Koch + + * cache.c: Add a few debug outputs. + + * protect.c (agent_private_key_type): New. + * agent.h: Add PRIVATE_KEY_ enums. + * findkey.c (agent_key_from_file): Use it to decide whether we + have to unprotect a key. + (unprotect): Cache the passphrase. + + * findkey.c (agent_key_from_file,agent_key_available): The key + files do now require a ".key" suffix to make a script's life + easier. + * genkey.c (store_key): Ditto. + 2002-01-31 Werner Koch * genkey.c (store_key): Protect the key. diff --git a/agent/agent.h b/agent/agent.h index 955dd1ce2..0b88d9a2c 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -83,6 +83,13 @@ struct pin_entry_info_s { }; +enum { + PRIVATE_KEY_UNKNOWN = 0, + PRIVATE_KEY_CLEAR = 1, + PRIVATE_KEY_PROTECTED = 2, + PRIVATE_KEY_SHADOWED = 3 +}; + /*-- gpg-agent.c --*/ void agent_exit (int rc); /* also implemented in other tools */ @@ -125,6 +132,7 @@ int agent_protect (const unsigned char *plainkey, const char *passphrase, unsigned char **result, size_t *resultlen); int agent_unprotect (const unsigned char *protectedkey, const char *passphrase, unsigned char **result, size_t *resultlen); +int agent_private_key_type (const unsigned char *privatekey); /*-- trustlist.c --*/ diff --git a/agent/cache.c b/agent/cache.c index aa7a21b16..480eb277c 100644 --- a/agent/cache.c +++ b/agent/cache.c @@ -89,6 +89,9 @@ housekeeping (void) { if (r->pw && r->accessed + r->ttl < current) { + if (DBG_CACHE) + log_debug (" expired `%s' (%ds after last access)\n", + r->key, r->ttl); release_data (r->pw); r->pw = NULL; r->accessed = current; @@ -101,6 +104,8 @@ housekeeping (void) { if (r->pw && r->created + 60*60 < current) { + if (DBG_CACHE) + log_debug (" expired `%s' (1h after creation)\n", r->key); release_data (r->pw); r->pw = NULL; r->accessed = current; @@ -114,6 +119,8 @@ housekeeping (void) if (!r->pw && r->accessed + 60*30 < current) { ITEM r2 = r->next; + if (DBG_CACHE) + log_debug (" removed `%s' (slot not used for 30m)\n", r->key); xfree (r); if (!rprev) thecache = r2; @@ -140,6 +147,8 @@ agent_put_cache (const char *key, const char *data, int ttl) { ITEM r; + if (DBG_CACHE) + log_debug ("agent_put_cache `%s'\n", key); housekeeping (); if (ttl < 1) @@ -198,6 +207,8 @@ agent_get_cache (const char *key) ITEM r; int count = 0; + if (DBG_CACHE) + log_debug ("agent_get_cache `%s'...\n", key); housekeeping (); /* FIXME: Returning pointers is not thread safe - add a referencense @@ -209,9 +220,13 @@ agent_get_cache (const char *key) /* put_cache does only put strings into the cache, so we don't need the lengths */ r->accessed = time (NULL); + if (DBG_CACHE) + log_debug ("... hit\n"); return r->pw->data; } } + if (DBG_CACHE) + log_debug ("... miss\n"); return NULL; } diff --git a/agent/findkey.c b/agent/findkey.c index 097903340..250852c23 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -31,16 +31,36 @@ #include "agent.h" static int -unprotect (unsigned char **keybuf) +unprotect (unsigned char **keybuf, const unsigned char *grip) { struct pin_entry_info_s *pi; - int rc; + int rc, i; unsigned char *result; size_t resultlen; int tries = 0; + char hexgrip[40+1]; + + for (i=0; i < 20; i++) + sprintf (hexgrip+2*i, "%02X", grip[i]); + hexgrip[40] = 0; - /* fixme: check whether the key needs unprotection */ - + /* first try to get it from the cache - if there is none or we can't + unprotect it, we fall back to ask the user */ + { + const char *pw = agent_get_cache (hexgrip); + if (pw) + { + rc = agent_unprotect (*keybuf, pw, &result, &resultlen); + if (!rc) + { + xfree (*keybuf); + *keybuf = result; + return 0; + } + rc = 0; + } + } + pi = gcry_calloc_secure (1, sizeof (*pi) + 100); pi->max_length = 100; pi->min_digits = 0; /* we want a real passphrase */ @@ -55,6 +75,7 @@ unprotect (unsigned char **keybuf) rc = agent_unprotect (*keybuf, pi->pin, &result, &resultlen); if (!rc) { + agent_put_cache (hexgrip, pi->pin, 0); xfree (*keybuf); *keybuf = result; xfree (pi); @@ -82,13 +103,13 @@ agent_key_from_file (const unsigned char *grip) unsigned char *buf; size_t len, buflen, erroff; GCRY_SEXP s_skey; - char hexgrip[41]; + char hexgrip[40+4+1]; for (i=0; i < 20; i++) sprintf (hexgrip+2*i, "%02X", grip[i]); - hexgrip[40] = 0; + strcpy (hexgrip+40, ".key"); - fname = make_filename (opt.homedir, "private-keys-v1.d", hexgrip, NULL ); + fname = make_filename (opt.homedir, "private-keys-v1.d", hexgrip, NULL); fp = fopen (fname, "rb"); if (!fp) { @@ -138,16 +159,32 @@ agent_key_from_file (const unsigned char *grip) assert (len); gcry_sexp_release (s_skey); - rc = unprotect (&buf); + switch (agent_private_key_type (buf)) + { + case PRIVATE_KEY_CLEAR: + break; /* no unprotection needed */ + case PRIVATE_KEY_PROTECTED: + rc = unprotect (&buf, grip); + if (rc) + log_error ("failed to unprotect the secret key: %s\n", + gnupg_strerror (rc)); + break; + case PRIVATE_KEY_SHADOWED: + log_error ("shadowed private keys are not yet supported\n"); + rc = GNUPG_Not_Implemented; + break; + default: + log_error ("invalid private key format\n"); + rc = GNUPG_Bad_Secret_Key; + break; + } if (rc) { - log_error ("failed to unprotect the secret key: %s\n", - gcry_strerror (rc)); xfree (buf); return NULL; } - /* arggg FIXME: does scna support secure memory? */ + /* arggg FIXME: does scan support secure memory? */ rc = gcry_sexp_sscan (&s_skey, &erroff, buf, gcry_sexp_canon_len (buf, 0, NULL, NULL)); xfree (buf); @@ -168,13 +205,13 @@ agent_key_available (const unsigned char *grip) { int i; char *fname; - char hexgrip[41]; + char hexgrip[40+4+1]; for (i=0; i < 20; i++) sprintf (hexgrip+2*i, "%02X", grip[i]); - hexgrip[40] = 0; + strcpy (hexgrip+40, ".key"); - fname = make_filename (opt.homedir, "private-keys-v1.d", hexgrip, NULL ); + fname = make_filename (opt.homedir, "private-keys-v1.d", hexgrip, NULL); i = !access (fname, R_OK)? 0 : -1; xfree (fname); return i; diff --git a/agent/genkey.c b/agent/genkey.c index 2119bbb44..1aca33b83 100644 --- a/agent/genkey.c +++ b/agent/genkey.c @@ -40,7 +40,7 @@ store_key (GCRY_SEXP private, const char *passphrase) char *buf; size_t len; unsigned char grip[20]; - char hexgrip[41]; + char hexgrip[40+4+1]; if ( !gcry_pk_get_keygrip (private, grip) ) { @@ -49,7 +49,7 @@ store_key (GCRY_SEXP private, const char *passphrase) } for (i=0; i < 20; i++) sprintf (hexgrip+2*i, "%02X", grip[i]); - hexgrip[40] = 0; + strcpy (hexgrip+40, ".key"); fname = make_filename (opt.homedir, "private-keys-v1.d", hexgrip, NULL); if (!access (fname, F_OK)) diff --git a/agent/protect.c b/agent/protect.c index 6b95dabfa..115a94563 100644 --- a/agent/protect.c +++ b/agent/protect.c @@ -776,6 +776,33 @@ agent_unprotect (const unsigned char *protectedkey, const char *passphrase, return 0; } +/* Check the type of the private key, this is one of the constants: + PRIVATE_KEY_UNKNOWN if we can't figure out the type (this is the + value 0), PRIVATE_KEY_CLEAR for an unprotected private key. + PRIVATE_KEY_PROTECTED for an protected private key or + PRIVATE_KEY_SHADOWED for a sub key where the secret parts are store + elsewhere. */ +int +agent_private_key_type (const unsigned char *privatekey) +{ + const unsigned char *s; + size_t n; + + s = privatekey; + if (*s != '(') + return PRIVATE_KEY_UNKNOWN; + s++; + n = snext (&s); + if (!n) + return PRIVATE_KEY_UNKNOWN; + if (smatch (&s, n, "protected-private-key")) + return PRIVATE_KEY_PROTECTED; + if (smatch (&s, n, "shadowed-private-key")) + return PRIVATE_KEY_SHADOWED; + if (smatch (&s, n, "private-key")) + return PRIVATE_KEY_CLEAR; + return PRIVATE_KEY_UNKNOWN; +}