g10: Eliminate the redundant function get_keyblock_byfprint.
[gnupg.git] / agent / cache.c
index 19f2502..49402e4 100644 (file)
@@ -24,7 +24,7 @@
 #include <string.h>
 #include <time.h>
 #include <assert.h>
-#include <pth.h>
+#include <npth.h>
 
 #include "agent.h"
 
@@ -33,9 +33,9 @@
 
 /* A mutex used to protect the encryption.  This is required because
    we use one context to do all encryption and decryption.  */
-static pth_mutex_t encryption_lock;
+static npth_mutex_t encryption_lock;
 /* The encryption context.  This is the only place where the
-   encryption key for all cached entries is available.  It would nice
+   encryption key for all cached entries is available.  It would be nice
    to keep this (or just the key) in some hardware device, for example
    a TPM.  Libgcrypt could be extended to provide such a service.
    With the current scheme it is easy to retrieve the cached entries
@@ -65,21 +65,54 @@ 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.  */
 void
 initialize_module_cache (void)
 {
-  static int initialized;
+  int err;
+
+  err = npth_mutex_init (&encryption_lock, NULL);
+
+  if (err)
+    log_fatal ("error initializing cache module: %s\n", strerror (err));
+}
+
+
+void
+deinitialize_module_cache (void)
+{
+  gcry_cipher_close (encryption_handle);
+  encryption_handle = NULL;
+}
+
+
+/* We do the encryption init on the fly.  We can't do it in the module
+   init code because that is run before we listen for connections and
+   in case we are started on demand by gpg etc. it will only wait for
+   a few seconds to decide whether the agent may now accept
+   connections.  Thus we should get into listen state as soon as
+   possible.  */
+static gpg_error_t
+init_encryption (void)
+{
   gpg_error_t err;
   void *key;
+  int res;
+
+  if (encryption_handle)
+    return 0; /* Shortcut - Already initialized.  */
+
+  res = npth_mutex_lock (&encryption_lock);
+  if (res)
+    log_fatal ("failed to acquire cache encryption mutex: %s\n", strerror (res));
 
-  if (!pth_mutex_init (&encryption_lock))
-    err = gpg_error_from_syserror ();
-  else
-    err = gcry_cipher_open (&encryption_handle, GCRY_CIPHER_AES128,
-                            GCRY_CIPHER_MODE_AESWRAP, GCRY_CIPHER_SECURE);
+  err = gcry_cipher_open (&encryption_handle, GCRY_CIPHER_AES128,
+                          GCRY_CIPHER_MODE_AESWRAP, GCRY_CIPHER_SECURE);
   if (!err)
     {
       key = gcry_random_bytes (ENCRYPTION_KEYSIZE, GCRY_STRONG_RANDOM);
@@ -90,22 +123,25 @@ initialize_module_cache (void)
           err = gcry_cipher_setkey (encryption_handle, key, ENCRYPTION_KEYSIZE);
           xfree (key);
         }
+      if (err)
+        {
+          gcry_cipher_close (encryption_handle);
+          encryption_handle = NULL;
+        }
     }
   if (err)
-    log_fatal ("error initializing cache encryption context: %s\n",
-             gpg_strerror (err));
-  initialized = 1;
-}
+    log_error ("error initializing cache encryption context: %s\n",
+               gpg_strerror (err));
 
+  res = npth_mutex_unlock (&encryption_lock);
+  if (res)
+    log_fatal ("failed to release cache encryption mutex: %s\n", strerror (res));
 
-void
-deinitialize_module_cache (void)
-{
-  gcry_cipher_close (encryption_handle);
-  encryption_handle = NULL;
+  return err? gpg_error (GPG_ERR_NOT_INITIALIZED) : 0;
 }
 
 
+
 static void
 release_data (struct secret_data_s *data)
 {
@@ -119,11 +155,13 @@ new_data (const char *string, struct secret_data_s **r_data)
   struct secret_data_s *d, *d_enc;
   size_t length;
   int total;
-  
+  int res;
+
   *r_data = NULL;
 
-  if (!encryption_handle)
-    return gpg_error (GPG_ERR_NOT_INITIALIZED);
+  err = init_encryption ();
+  if (err)
+    return err;
 
   length = strlen (string) + 1;
 
@@ -148,13 +186,17 @@ new_data (const char *string, struct secret_data_s **r_data)
     }
 
   d_enc->totallen = total;
-  if (!pth_mutex_acquire (&encryption_lock, 0, NULL))
-    log_fatal ("failed to acquire cache encryption mutex\n");
+  res = npth_mutex_lock (&encryption_lock);
+  if (res)
+    log_fatal ("failed to acquire cache encryption mutex: %s\n",
+               strerror (res));
+
   err = gcry_cipher_encrypt (encryption_handle, d_enc->data, total,
                              d->data, total - 8);
   xfree (d);
-  if (!pth_mutex_release (&encryption_lock))
-    log_fatal ("failed to release cache encryption mutex\n");
+  res = npth_mutex_unlock (&encryption_lock);
+  if (res)
+    log_fatal ("failed to release cache encryption mutex: %s\n", strerror (res));
   if (err)
     {
       xfree (d_enc);
@@ -179,7 +221,7 @@ housekeeping (void)
       if (r->pw && r->ttl >= 0 && r->accessed + r->ttl < current)
         {
           if (DBG_CACHE)
-            log_debug ("  expired `%s' (%ds after last access)\n",
+            log_debug ("  expired '%s' (%ds after last access)\n",
                        r->key, r->ttl);
           release_data (r->pw);
           r->pw = NULL;
@@ -192,7 +234,7 @@ housekeeping (void)
   for (r=thecache; r; r = r->next)
     {
       unsigned long maxttl;
-      
+
       switch (r->cache_mode)
         {
         case CACHE_MODE_SSH: maxttl = opt.max_cache_ttl_ssh; break;
@@ -201,7 +243,7 @@ housekeeping (void)
       if (r->pw && r->created + maxttl < current)
         {
           if (DBG_CACHE)
-            log_debug ("  expired `%s' (%lus after creation)\n",
+            log_debug ("  expired '%s' (%lus after creation)\n",
                        r->key, opt.max_cache_ttl);
           release_data (r->pw);
           r->pw = NULL;
@@ -217,7 +259,7 @@ housekeeping (void)
         {
           ITEM r2 = r->next;
           if (DBG_CACHE)
-            log_debug ("  removed `%s' (mode %d) (slot not used for 30m)\n",
+            log_debug ("  removed '%s' (mode %d) (slot not used for 30m)\n",
                        r->key, r->cache_mode);
           xfree (r);
           if (!rprev)
@@ -248,7 +290,7 @@ agent_flush_cache (void)
       if (r->pw)
         {
           if (DBG_CACHE)
-            log_debug ("  flushing `%s'\n", r->key);
+            log_debug ("  flushing '%s'\n", r->key);
           release_data (r->pw);
           r->pw = NULL;
           r->accessed = 0;
@@ -272,7 +314,7 @@ agent_put_cache (const char *key, cache_mode_t cache_mode,
   ITEM r;
 
   if (DBG_CACHE)
-    log_debug ("agent_put_cache `%s' (mode %d) requested ttl=%d\n",
+    log_debug ("agent_put_cache '%s' (mode %d) requested ttl=%d\n",
                key, cache_mode, ttl);
   housekeeping ();
 
@@ -284,7 +326,7 @@ agent_put_cache (const char *key, cache_mode_t cache_mode,
         default: ttl = opt.def_cache_ttl; break;
         }
     }
-  if (!ttl || cache_mode == CACHE_MODE_IGNORE)
+  if ((!ttl && data) || cache_mode == CACHE_MODE_IGNORE)
     return 0;
 
   for (r=thecache; r; r = r->next)
@@ -304,7 +346,7 @@ agent_put_cache (const char *key, cache_mode_t cache_mode,
         }
       if (data)
         {
-          r->created = r->accessed = gnupg_get_time (); 
+          r->created = r->accessed = gnupg_get_time ();
           r->ttl = ttl;
           r->cache_mode = cache_mode;
           err = new_data (data, &r->pw);
@@ -320,7 +362,7 @@ agent_put_cache (const char *key, cache_mode_t cache_mode,
       else
         {
           strcpy (r->key, key);
-          r->created = r->accessed = gnupg_get_time (); 
+          r->created = r->accessed = gnupg_get_time ();
           r->ttl = ttl;
           r->cache_mode = cache_mode;
           err = new_data (data, &r->pw);
@@ -348,12 +390,25 @@ agent_get_cache (const char *key, cache_mode_t cache_mode)
   gpg_error_t err;
   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)
@@ -364,30 +419,35 @@ 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");
           if (r->pw->totallen < 32)
             err = gpg_error (GPG_ERR_INV_LENGTH);
-          else if (!encryption_handle)
-            err = gpg_error (GPG_ERR_NOT_INITIALIZED);
+          else if ((err = init_encryption ()))
+            ;
           else if (!(value = xtrymalloc_secure (r->pw->totallen - 8)))
             err = gpg_error_from_syserror ();
           else
             {
-              if (!pth_mutex_acquire (&encryption_lock, 0, NULL))
-                log_fatal ("failed to acquire cache encryption mutex\n");
+              res = npth_mutex_lock (&encryption_lock);
+              if (res)
+                log_fatal ("failed to acquire cache encryption mutex: %s\n",
+                          strerror (res));
               err = gcry_cipher_decrypt (encryption_handle,
                                          value, r->pw->totallen - 8,
                                          r->pw->data, r->pw->totallen);
-              if (!pth_mutex_release (&encryption_lock))
-                log_fatal ("failed to release cache encryption mutex\n");
+              res = npth_mutex_unlock (&encryption_lock);
+              if (res)
+                log_fatal ("failed to release cache encryption mutex: %s\n",
+                          strerror (res));
             }
           if (err)
             {
               xfree (value);
               value = NULL;
-              log_error ("retrieving cache entry `%s' failed: %s\n",
+              log_error ("retrieving cache entry '%s' failed: %s\n",
                          key, gpg_strerror (err));
             }
           return value;
@@ -399,3 +459,13 @@ 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;
+}