Implement direct trust handling
authorAndre Heinecke <aheinecke@intevation.de>
Fri, 16 Dec 2016 14:53:28 +0000 (15:53 +0100)
committerAndre Heinecke <aheinecke@intevation.de>
Fri, 16 Dec 2016 14:55:38 +0000 (15:55 +0100)
* src/mail.cpp (level_4_check): Do the actual check for direct
trust.
(Mail::get_crypto_details): Handle level 4 subcases with different
details.
* src/parsecontroller.cpp (ParseController::parse): Fetch
ultimately trusted keys if a sig was fully or ultimately valid.
(ParseController::get_ultimate_keys): New Helper function
to cache the ultimately valid keys.
* src/parsecontroller.h: Update accordingly.

src/mail.cpp
src/parsecontroller.cpp
src/parsecontroller.h

index 42222f6..feb0ca4 100644 (file)
@@ -1388,28 +1388,81 @@ Mail::set_uuid()
   return 0;
 }
 
-/* Returns -1 if the mail was signed by a uid with ownertrust
-   ultimate and to which we have the secret key.
-   This basically means "mail was signed by yourself"
+/* Returns 1 if the userid is ultimately trusted.
 
-   Returns the number of the signature fromt the uid that belongs
-   made it ultimately or fully trusted if the mail was signed by some
-   ultimate key for which we don't have the secret key.
-   This is direct trust or CA Style PGP.
+   Returns 1 if the userid is fully trusted but has
+   a signature by a key for which we have a secret
+   and which is ultimately trusted. (Direct trust)
 
    0 otherwise */
 static int
 level_4_check (const UserID &uid)
 {
-  if (uid.validity() == UserID::Validity::Ultimate)
+  if (uid.isNull())
     {
-      /* TODO look for the signature that caused this
-         to be ultimate. And check if it is our own. */
-      return -1;
+      return 0;
     }
-  else if (uid.validity() == UserID::Validity::Full)
+  if (uid.validity () == UserID::Validity::Ultimate)
     {
-      return 1;
+      return 2;
+    }
+  if (uid.validity () == UserID::Validity::Full)
+    {
+      for (const auto sig: uid.signatures ())
+        {
+          const char *sigID = sig.signerKeyID ();
+          if (sig.isNull() || !sigID)
+            {
+              /* should not happen */
+              TRACEPOINT;
+              continue;
+            }
+          /* Direct trust information is not available
+             through gnupg so we cached the keys with ultimate
+             trust during parsing and now see if we find a direct
+             trust path.*/
+          for (const auto secKey: ParseController::get_ultimate_keys ())
+            {
+              /* Check that the Key id of the key matches */
+              const char *secKeyID = secKey.keyID ();
+              if (!secKeyID || strcmp (secKeyID, sigID))
+                {
+                  continue;
+                }
+              /* Check that the userID of the signature is the ultimately
+                 trusted one. */
+              const char *signed_uid_str = uid.id ();
+              if (!signed_uid_str)
+                {
+                  /* should not happen */
+                  TRACEPOINT;
+                  continue;
+                }
+              for (const auto signer_uid: secKey.userIDs ())
+                {
+                  if (signer_uid.validity() != UserID::Validity::Ultimate)
+                    {
+                      continue;
+                    }
+                  const char *signer_uid_str = uid.id ();
+                  if (!signer_uid_str)
+                    {
+                      /* should not happen */
+                      TRACEPOINT;
+                      continue;
+                    }
+                  if (!strcmp(signer_uid_str, signed_uid_str))
+                    {
+                      /* We have a match */
+                      log_debug ("%s:%s: classified %s as ultimate because "
+                                 "it was signed by uid %s of key %s",
+                                 SRCNAME, __func__, signed_uid_str, signer_uid_str,
+                                 secKeyID);
+                      return 1;
+                    }
+                }
+            }
+        }
     }
   return 0;
 }
@@ -1484,17 +1537,16 @@ Mail::get_crypto_details()
       /* level 4 check for direct trust */
       int four_check = level_4_check (m_uid);
 
-      if (four_check == -1)
+      if (four_check == 1 && m_sig.key().hasSecret ())
         {
           message = _("You signed this message.");
         }
-      else if (four_check >= 0)
+      else if (four_check == 1)
+        {
+          message = _("The sender is allowed to manage your mail trust.");
+        }
+      else if (four_check == 2)
         {
-          /* TODO
-            And you certified the identity of the sender.
-            And <uid with ultimate trust> certified the identity
-            of the sender.
-          */
           message = _("The senders identity was certified by yourself.");
         }
       else
index 2752fad..60e03a7 100644 (file)
@@ -320,6 +320,13 @@ ParseController::parse()
   for (const auto sig: m_verify_result.signatures())
     {
       sig.key(true, true);
+      if (sig.validity() == Signature::Validity::Full ||
+          sig.validity() == Signature::Validity::Ultimate)
+        {
+          /* Ensure that we have the keys with ultimate
+             trust cached for the ui. */
+          get_ultimate_keys ();
+        }
     }
 
   if (opt.enable_debug)
@@ -401,3 +408,63 @@ ParseController::get_attachments() const
       return std::vector<std::shared_ptr<Attachment> >();
     }
 }
+
+GPGRT_LOCK_DEFINE(keylist_lock);
+/* static */
+std::vector<Key>
+ParseController::get_ultimate_keys()
+{
+  static bool s_keys_listed;
+  static std::vector<Key> s_ultimate_keys;
+  gpgrt_lock_lock (&keylist_lock);
+  if (s_keys_listed)
+    {
+      gpgrt_lock_unlock (&keylist_lock);
+      return s_ultimate_keys;
+    }
+  log_debug ("%s:%s: Starting keylisting.",
+             SRCNAME, __func__);
+  auto ctx = Context::createForProtocol (OpenPGP);
+  if (!ctx)
+    {
+      /* Maybe PGP broken and not S/MIME */
+      log_error ("%s:%s: broken installation no ctx.",
+                 SRCNAME, __func__);
+      gpgrt_lock_unlock (&keylist_lock);
+      return s_ultimate_keys;
+    }
+  ctx->setKeyListMode (KeyListMode::Local);
+  Error err;
+  if ((err = ctx->startKeyListing ()))
+    {
+      log_error ("%s:%s: Failed to start keylisting err: %i: %s",
+                 SRCNAME, __func__, err.code (), err.asString());
+      delete ctx;
+      gpgrt_lock_unlock (&keylist_lock);
+      return s_ultimate_keys;
+    }
+  while (!err)
+    {
+      const auto key = ctx->nextKey(err);
+      if (key.isNull() || err)
+        {
+          break;
+        }
+      for (const auto uid: key.userIDs())
+        {
+          if (uid.validity() == UserID::Validity::Ultimate)
+            {
+              s_ultimate_keys.push_back (key);
+              log_debug ("adding ultimate uid: %s", uid.id());
+              break;
+            }
+        }
+    }
+  delete ctx;
+  log_debug ("%s:%s: keylisting done.",
+             SRCNAME, __func__);
+
+  s_keys_listed = true;
+  gpgrt_lock_unlock (&keylist_lock);
+  return s_ultimate_keys;
+}
index 19d622f..ec45982 100644 (file)
@@ -61,6 +61,13 @@ 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();