Populate keycache on startup
authorAndre Heinecke <aheinecke@intevation.de>
Thu, 25 Oct 2018 12:03:36 +0000 (14:03 +0200)
committerAndre Heinecke <aheinecke@intevation.de>
Thu, 25 Oct 2018 12:03:36 +0000 (14:03 +0200)
* src/gpgoladdin.cpp (GpgolAddin::OnStartupComplete): Populate
keycache.
* src/keycache.cpp (KeyCache::populate, do_populate)
(do_populate_protocol): New.
(KeyCache::getUltimateKeys): New.
* src/mail.cpp (level_4_check): Use getUltimateKeys from cache.
* src/parsecontroller.cpp, src/parsecontroller.h (get_ultimate_keys):
Removed.

--
This moves the ultimate keys cache into the keycache where it
belongs and should fix problems not finding signing keys when
sign always is set. It should also speed up the first verify
operation as keylistings are no longer required.

GnuPG-Bug-Id: T4218

src/gpgoladdin.cpp
src/keycache.cpp
src/keycache.h
src/mail.cpp
src/parsecontroller.cpp
src/parsecontroller.h

index aca9943..be3bdac 100644 (file)
@@ -49,6 +49,7 @@
 #include "cpphelp.h"
 #include "dispcache.h"
 #include "categorymanager.h"
+#include "keycache.h"
 
 #include <gpg-error.h>
 #include <list>
@@ -524,6 +525,8 @@ GpgolAddin::OnStartupComplete (SAFEARRAY** custom)
 
   CloseHandle (CreateThread (NULL, 0, init_gpgme_config, nullptr, 0,
                              NULL));
+
+  KeyCache::instance ()->populate ();
   return S_OK;
 }
 
index e1bb17f..1371930 100644 (file)
@@ -215,6 +215,65 @@ do_import (LPVOID arg)
   TRETURN 0;
 }
 
+static void
+do_populate_protocol (GpgME::Protocol proto, bool secret)
+{
+  log_debug ("%s:%s: Starting keylisting for proto %s",
+             SRCNAME, __func__, to_cstr (proto));
+  auto ctx = GpgME::Context::create (proto);
+  if (!ctx)
+    {
+      /* Maybe PGP broken and not S/MIME */
+      log_error ("%s:%s: broken installation no ctx.",
+                 SRCNAME, __func__);
+      TRETURN;
+    }
+
+  ctx->setKeyListMode (GpgME::KeyListMode::Local);
+  GpgME::Error err;
+
+   if ((err = ctx->startKeyListing ((const char*)nullptr, secret)))
+    {
+      log_error ("%s:%s: Failed to start keylisting err: %i: %s",
+                 SRCNAME, __func__, err.code (), err.asString());
+      TRETURN;
+    }
+
+  while (!err)
+    {
+      const auto key = ctx->nextKey(err);
+      if (err || key.isNull())
+        {
+          TRACEPOINT;
+          break;
+        }
+      KeyCache::instance()->onUpdateJobDone (key.primaryFingerprint(),
+                                             key);
+
+    }
+  TRETURN;
+}
+
+static DWORD WINAPI
+do_populate (LPVOID)
+{
+  TSTART;
+
+  log_debug ("%s:%s: Populating keycache",
+             SRCNAME, __func__);
+  do_populate_protocol (GpgME::OpenPGP, false);
+  do_populate_protocol (GpgME::OpenPGP, true);
+  if (opt.enable_smime)
+    {
+      do_populate_protocol (GpgME::CMS, false);
+      do_populate_protocol (GpgME::CMS, true);
+    }
+  log_debug ("%s:%s: Keycache populated",
+             SRCNAME, __func__);
+
+  TRETURN 0;
+}
+
 
 class KeyCache::Private
 {
@@ -262,7 +321,8 @@ public:
     TRETURN;
   }
 
-  void setPgpKeySecret(const std::string &mbox, const GpgME::Key &key)
+  void setPgpKeySecret(const std::string &mbox, const GpgME::Key &key,
+                       bool insert = true)
   {
     TSTART;
     gpgol_lock (&keycache_lock);
@@ -276,12 +336,16 @@ public:
       {
         it->second = key;
       }
-    insertOrUpdateInFprMap (key);
+    if (insert)
+      {
+        insertOrUpdateInFprMap (key);
+      }
     gpgol_unlock (&keycache_lock);
     TRETURN;
   }
 
-  void setSmimeKeySecret(const std::string &mbox, const GpgME::Key &key)
+  void setSmimeKeySecret(const std::string &mbox, const GpgME::Key &key,
+                         bool insert = true)
   {
     TSTART;
     gpgol_lock (&keycache_lock);
@@ -295,7 +359,10 @@ public:
       {
         it->second = key;
       }
-    insertOrUpdateInFprMap (key);
+    if (insert)
+      {
+        insertOrUpdateInFprMap (key);
+      }
     gpgol_unlock (&keycache_lock);
     TRETURN;
   }
@@ -546,8 +613,6 @@ public:
 
       auto it = m_fpr_map.find (primaryFpr);
 
-      log_debug ("%s:%s \"%s\" updated.",
-                 SRCNAME, __func__, anonstr (primaryFpr));
       if (it == m_fpr_map.end ())
         {
           m_fpr_map.insert (std::make_pair (primaryFpr, key));
@@ -556,6 +621,51 @@ public:
           TRETURN;
         }
 
+      for (const auto &uid: key.userIDs())
+        {
+          if (key.isBad() || uid.isBad())
+            {
+              continue;
+            }
+          /* Update ultimate keys map */
+          if (uid.validity() == GpgME::UserID::Validity::Ultimate &&
+              uid.id())
+            {
+              const char *fpr = key.primaryFingerprint();
+              if (!fpr)
+                {
+                  STRANGEPOINT;
+                  continue;
+                }
+              TRACEPOINT;
+              m_ultimate_keys.erase (std::remove_if (m_ultimate_keys.begin(),
+                                     m_ultimate_keys.end(),
+                                     [fpr] (const GpgME::Key &ult)
+                {
+                  return ult.primaryFingerprint() && !strcmp (fpr, ult.primaryFingerprint());
+                }), m_ultimate_keys.end());
+              TRACEPOINT;
+              m_ultimate_keys.push_back (key);
+            }
+
+          /* Update skey maps */
+          if (key.hasSecret ())
+            {
+              if (key.protocol () == GpgME::OpenPGP)
+                {
+                  setPgpKeySecret (uid.addrSpec(), key, false);
+                }
+              else if (key.protocol () == GpgME::CMS)
+                {
+                  setSmimeKeySecret (uid.addrSpec(), key, false);
+                }
+              else
+                {
+                  STRANGEPOINT;
+                }
+            }
+        }
+
       if (it->second.hasSecret () && !key.hasSecret())
         {
           log_debug ("%s:%s Lost secret info on update. Merging.",
@@ -715,8 +825,6 @@ public:
 
       if (it == m_update_jobs.end())
         {
-          log_debug ("%s:%s Update for \"%s\" already finished.",
-                     SRCNAME, __func__, anonstr (fpr));
           gpgol_unlock (&update_lock);
           TRETURN;
         }
@@ -793,6 +901,18 @@ public:
       TRETURN;
     }
 
+  void populate ()
+    {
+      TSTART;
+      gpgrt_lock_lock (&keycache_lock);
+      m_ultimate_keys.clear ();
+      gpgrt_lock_unlock (&keycache_lock);
+      CloseHandle (CreateThread (nullptr, 0, do_populate,
+                                 nullptr, 0,
+                                 nullptr));
+      TRETURN;
+    }
+
   std::unordered_map<std::string, GpgME::Key> m_pgp_key_map;
   std::unordered_map<std::string, GpgME::Key> m_smime_key_map;
   std::unordered_map<std::string, GpgME::Key> m_pgp_skey_map;
@@ -801,6 +921,7 @@ public:
   std::unordered_map<std::string, std::string> m_sub_fpr_map;
   std::unordered_map<std::string, std::vector<std::string> >
     m_addr_book_overrides;
+  std::vector<GpgME::Key> m_ultimate_keys;
   std::set<std::string> m_update_jobs;
   std::set<std::string> m_import_jobs;
 };
@@ -1277,3 +1398,18 @@ KeyCache::getOverrides (const std::string &mbox)
 {
   return d->getPGPOverrides (mbox.c_str ());
 }
+
+void
+KeyCache::populate ()
+{
+  return d->populate ();
+}
+
+std::vector<GpgME::Key>
+KeyCache::getUltimateKeys ()
+{
+  gpgrt_lock_lock (&fpr_map_lock);
+  const auto ret = d->m_ultimate_keys;
+  gpgrt_lock_unlock (&fpr_map_lock);
+  return ret;
+}
index cc5ab77..0a90575 100644 (file)
@@ -124,6 +124,11 @@ public:
     /* Get optional overrides for an address. */
     std::vector<GpgME::Key> getOverrides (const std::string &mbox);
 
+    /* Populate the fingerprint and secret key maps */
+    void populate ();
+
+    /* Get a vector of ultimately trusted keys. */
+    std::vector<GpgME::Key> getUltimateKeys ();
 
     // Internal for thread
     void setSmimeKey(const std::string &mbox, const GpgME::Key &key);
index 134ea9d..9305057 100644 (file)
@@ -2497,7 +2497,7 @@ level_4_check (const UserID &uid)
     }
   if (uid.validity () == UserID::Validity::Full)
     {
-      const auto ultimate_keys = ParseController::get_ultimate_keys ();
+      const auto ultimate_keys = KeyCache::instance()->getUltimateKeys ();
       for (const auto sig: uid.signatures ())
         {
           const char *sigID = sig.signerKeyID ();
index 7dfade8..826b597 100644 (file)
@@ -493,25 +493,12 @@ ParseController::parse()
   bool has_valid_encrypted_checksum = false;
   /* Ensure that the Keys for the signatures are available
      and if it has a valid encrypted checksum. */
-  bool ultimate_keys_queried = false;
   for (const auto sig: m_verify_result.signatures())
     {
       TRACEPOINT;
       has_valid_encrypted_checksum = is_valid_chksum (sig);
 
       KeyCache::instance ()->update (sig.fingerprint (), protocol);
-
-      if (!ultimate_keys_queried &&
-          (sig.validity() == Signature::Validity::Full ||
-          sig.validity() == Signature::Validity::Ultimate))
-        {
-          /* Ensure that we have the keys with ultimate
-             trust cached for the ui. */
-
-          // TODO this is something for the keycache
-          get_ultimate_keys ();
-          ultimate_keys_queried = true;
-        }
       TRACEPOINT;
     }
 
@@ -628,76 +615,3 @@ ParseController::get_attachments() const
       TRETURN std::vector<std::shared_ptr<Attachment> >();
     }
 }
-
-GPGRT_LOCK_DEFINE(keylist_lock);
-/* static */
-std::vector<Key>
-ParseController::get_ultimate_keys()
-{
-  TSTART;
-  static bool s_keys_listed;
-  static std::vector<Key> s_ultimate_keys;
-  gpgol_lock (&keylist_lock);
-  if (s_keys_listed)
-    {
-      gpgol_unlock (&keylist_lock);
-      TRETURN s_ultimate_keys;
-    }
-  log_debug ("%s:%s: Starting keylisting.",
-             SRCNAME, __func__);
-  auto ctx = std::unique_ptr<Context> (Context::createForProtocol (OpenPGP));
-  if (!ctx)
-    {
-      /* Maybe PGP broken and not S/MIME */
-      log_error ("%s:%s: broken installation no ctx.",
-                 SRCNAME, __func__);
-      gpgol_unlock (&keylist_lock);
-      TRETURN s_ultimate_keys;
-    }
-  ctx->setKeyListMode (KeyListMode::Local);
-  Error err;
-  TRACEPOINT;
-  if ((err = ctx->startKeyListing ()))
-    {
-      log_error ("%s:%s: Failed to start keylisting err: %i: %s",
-                 SRCNAME, __func__, err.code (), err.asString());
-      gpgol_unlock (&keylist_lock);
-      TRETURN s_ultimate_keys;
-    }
-  TRACEPOINT;
-  while (!err)
-    {
-      const auto key = ctx->nextKey(err);
-      if (err || key.isNull())
-        {
-          TRACEPOINT;
-          break;
-        }
-      if (key.isInvalid ())
-        {
-          log_debug ("%s:%s: skipping invalid key.",
-                     SRCNAME, __func__);
-          continue;
-        }
-      for (const auto uid: key.userIDs())
-        {
-          if (uid.validity() == UserID::Validity::Ultimate &&
-              uid.id())
-            {
-              s_ultimate_keys.push_back (key);
-              log_debug ("%s:%s: Adding ultimate uid.",
-                         SRCNAME, __func__);
-              log_data ("%s:%s: Added uid %s.",
-                        SRCNAME, __func__, uid.id());
-              break;
-            }
-        }
-    }
-  TRACEPOINT;
-  log_debug ("%s:%s: keylisting done.",
-             SRCNAME, __func__);
-
-  s_keys_listed = true;
-  gpgol_unlock (&keylist_lock);
-  TRETURN s_ultimate_keys;
-}
index 863b94c..44465b8 100644 (file)
@@ -62,13 +62,6 @@ public:
 
   ~ParseController();
 
-  /* Get a list of ultimately keys where at least one
-     userid has ultimate trust. This list
-     will be initialized only once when the first signature
-     is validity full or ultimate is encountered. */
-  static std::vector<GpgME::Key> get_ultimate_keys();
-
-
   /** Main entry point. After execution getters will become
   valid. */
   void parse();