Read autocrypt header for autoimport
authorAndre Heinecke <aheinecke@gnupg.org>
Thu, 21 Feb 2019 12:09:50 +0000 (13:09 +0100)
committerAndre Heinecke <aheinecke@gnupg.org>
Thu, 21 Feb 2019 12:16:20 +0000 (13:16 +0100)
* src/common_indep.h (autocrypt_s): Data transfer struct.
* src/keycache.cpp (KeyCache::import_pgp_key_data): Fix and
add more debugging.
* src/mail.cpp (Mail::preProcessMessage_m): Check for autocrypt
headers even on non crypto mails.
(Mail::decryptVerify_o): Parse autocrypt header for crypto mails.
* src/mapihelp.cpp (get_message_content_type_cb): Renamed
to parse_headers_cb.
(ac_get_value): New helper.
(prepare_key_data): New helper.
(mapi_get_header_info): General function for additional header info.
* src/mapihelp.h: Update accordingly.
* src/parsecontroller.cpp (handle_autocrypt_info): New helper.
(ParseController::parse): Handle autocrypt info on demand.
* src/parsecontroller.h: Update accordingly.

--
Ideally we want to do the import in the parse step as it is
then in the background thread. But for unencrypted mails we
have to do in in the pre processing because afterwards we no
longer look at the unencrypted mail.

mapi_get_header_info is intended as a new one time parse function
to get some more info from the headers. For now it only supports
autocrypt header.

src/common_indep.h
src/keycache.cpp
src/mail.cpp
src/mapihelp.cpp
src/mapihelp.h
src/parsecontroller.cpp
src/parsecontroller.h

index 0a71914..c8df4d3 100644 (file)
@@ -323,6 +323,16 @@ char *generate_boundary (char *buffer);
 
 #ifdef __cplusplus
 }
+
+#include <string>
+#include <gpgme++/data.h>
+struct autocrypt_s
+{
+  bool exists;
+  std::string pref;
+  std::string addr;
+  GpgME::Data data;
+};
 #endif
 
 #endif // COMMON_INDEP_H
index 9d6f5d2..75d8058 100644 (file)
@@ -1468,9 +1468,34 @@ KeyCache::import_pgp_key_data (const GpgME::Data &data)
         }
       log_debug ("Importing keys: %s", ss.str().c_str());
     }
-  const auto result = ctx->importKeys(keys);
+  const auto result = ctx->importKeys(data);
 
-  log_debug ("%s:%s: Import result from attached key err: %s",
-             SRCNAME, __func__, result.error ().asString ());
+  if ((opt.enable_debug & DBG_DATA))
+    {
+      std::stringstream ss;
+      ss << result;
+      log_debug ("%s:%s: Import result: %s details:\n %s",
+                 SRCNAME, __func__, result.error ().asString (),
+                 ss.str().c_str());
+      if (result.error())
+        {
+          GpgME::Data out;
+          if (ctx->getAuditLog(out, GpgME::Context::DiagnosticAuditLog))
+            {
+              log_error ("%s:%s: Failed to get diagnostics",
+                         SRCNAME, __func__);
+            }
+          else
+            {
+              log_debug ("%s:%s: Diagnostics: \n%s\n",
+                         SRCNAME, __func__, out.toString().c_str());
+            }
+        }
+    }
+  else
+    {
+      log_debug ("%s:%s: Import result: %s",
+                 SRCNAME, __func__, result.error ().asString ());
+    }
   TRETURN !result.error();
 }
index fe4a106..45e252e 100644 (file)
@@ -310,6 +310,19 @@ Mail::preProcessMessage_m ()
 
   if (m_type == MSGTYPE_UNKNOWN)
     {
+      /* For unknown messages we still need to check for autocrypt
+         headers. If the mails are crypto messages the autocrypt
+         stuff is handled in the parsecontroller. */
+      autocrypt_s ac;
+      if (mapi_get_header_info (message, ac))
+        {
+          if (ac.exists)
+            {
+              log_debug ("%s:%s: Importing autocrypt header from unencrypted "
+                         "mail.", SRCNAME, __func__);
+              KeyCache::import_pgp_key_data (ac.data);
+            }
+        }
       gpgol_release (message);
       TRETURN 0;
     }
@@ -1159,6 +1172,32 @@ Mail::decryptVerify_o ()
 
   m_parser = std::shared_ptr <ParseController> (new ParseController (cipherstream, m_type));
   m_parser->setSender(GpgME::UserID::addrSpecFromString(getSender_o ().c_str()));
+
+  if (opt.autoimport)
+    {
+      /* Handle autocrypt header. As we want to have the import
+         of the header in the same thread as the parser we leave it
+         to the parser. */
+      auto message = MAKE_SHARED (get_oom_message (m_mailitem));
+      if (!message)
+        {
+          /* Hmmm */
+          STRANGEPOINT;
+        }
+      else
+        {
+          autocrypt_s ainfo;
+          if (!mapi_get_header_info ((LPMESSAGE)message.get(), ainfo))
+            {
+              STRANGEPOINT;
+            }
+          else if (ainfo.exists)
+            {
+              m_parser->setAutocryptInfo (ainfo);
+            }
+        }
+    }
+
   log_data ("%s:%s: Parser for \"%s\" is %p",
                    SRCNAME, __func__, getSubject_o ().c_str(), m_parser.get());
   gpgol_release (cipherstream);
index 9f4499e..8d2cc01 100644 (file)
@@ -3094,7 +3094,7 @@ set_gpgol_draft_info_flags (LPMESSAGE message, int flags)
 
 /* Helper for mapi_get_msg_content_type() */
 static int
-get_message_content_type_cb (void *dummy_arg,
+parse_headers_cb (void *dummy_arg,
                              rfc822parse_event_t event, rfc822parse_t msg)
 {
   TSTART;
@@ -3133,7 +3133,7 @@ mapi_get_message_content_type (LPMESSAGE message,
     *r_smtype = NULL;
 
   /* Read the headers into an rfc822 object. */
-  msg = rfc822parse_open (get_message_content_type_cb, NULL);
+  msg = rfc822parse_open (parse_headers_cb, NULL);
   if (!msg)
     {
       log_error ("%s:%s: rfc822parse_open failed",
@@ -3559,3 +3559,129 @@ mapi_mark_or_create_moss_attach (LPMESSAGE message, msgtype_t msgtype)
    mapi_release_attach_table (table);
    TRETURN 0; /* No original attachment - this should not happen.  */
 }
+
+static std::string
+ac_get_value (const char *header, const char *what)
+{
+  TSTART;
+  if (!header || !what)
+    {
+      STRANGEPOINT;
+      TRETURN std::string();
+    }
+
+  const char *s = strstr (header, what);
+  if (!s)
+    {
+      log_debug ("%s:%s: could not find %s in autocrypt header",
+                 SRCNAME, __func__, what);
+      TRETURN std::string();
+    }
+  /* As we found it we can be sure that this is not out
+     of bounds. */
+  s += strlen (what);
+
+  if (*s != '=')
+    {
+      log_debug ("%s:%s: No equal sign after %s in autocrypt header %s",
+                 SRCNAME, __func__, what, s);
+      TRETURN std::string();
+    }
+
+  /* Move over the = sign. */
+  s++;
+
+  /* Find the sep */
+  const char *s2 = strchr (s, ';');
+  if (!s2)
+    {
+      /* No seperator found. Assume the rest is the value */
+      TRETURN s;
+    }
+
+  /* From the equal to the ; is our value. */
+  TRETURN std::string (s, s2 - s);
+}
+
+static GpgME::Data
+prepare_key_data (const std::string &d)
+{
+  TSTART;
+  if (d.empty())
+    {
+      STRANGEPOINT;
+      TRETURN GpgME::Data();
+    }
+
+  /* Prepare the keydata */
+  b64_state_t base64;     /* The state of the Base-64 decoder.  */
+  b64_init (&base64);
+
+  /* strdup and not xstrdup as we want GpgME to take over */
+  char *b64decoded = strdup (d.c_str());
+  size_t len = b64_decode (&base64, b64decoded, strlen(b64decoded));
+
+  if (!len)
+    {
+      log_error ("%s:%s: Invalid base64 in %s", SRCNAME, __func__,
+                 b64decoded);
+      xfree (b64decoded);
+      TRETURN GpgME::Data();
+    }
+
+  auto data = GpgME::Data (b64decoded, len, false /* take ownership */);
+
+  TRETURN data;
+}
+
+bool
+mapi_get_header_info (LPMESSAGE message,
+                      autocrypt_s &r_autocrypt)
+{
+  TSTART;
+  rfc822parse_t msg;
+
+  /* Read the headers into an rfc822 object. */
+  msg = rfc822parse_open (parse_headers_cb, NULL);
+  if (!msg)
+    {
+      log_error ("%s:%s: rfc822parse_open failed",
+                 SRCNAME, __func__);
+      TRETURN false;
+    }
+
+  const std::string hdrStr = mapi_get_header (message);
+  if (hdrStr.empty())
+    {
+
+      log_error ("%s:%s: failed to get headers",
+                 SRCNAME, __func__);
+      rfc822parse_close (msg);
+      TRETURN false;
+    }
+
+  size_t length;
+  const char *header_lines = hdrStr.c_str();
+  const char *s;
+  while ((s = strchr (header_lines, '\n')))
+    {
+      length = (s - header_lines);
+      if (length && s[-1] == '\r')
+        length--;
+
+      rfc822parse_insert (msg, (const unsigned char*)header_lines, length);
+      header_lines = s+1;
+    }
+
+  const char *ac_field = rfc822parse_get_field (msg, "Autocrypt", -1, 0);
+  if (ac_field)
+    {
+      r_autocrypt.exists = true;
+      r_autocrypt.addr = ac_get_value (ac_field, "addr");
+      r_autocrypt.data = prepare_key_data (ac_get_value (ac_field, "keydata"));
+      r_autocrypt.pref = ac_get_value (ac_field, "prefer-encrypt");
+    }
+
+  rfc822parse_close (msg);
+  TRETURN true;
+}
index 53a7a77..be42711 100644 (file)
@@ -117,9 +117,20 @@ int mapi_body_to_attachment (LPMESSAGE message);
 
 /* Get malloced uid of a message */
 char * mapi_get_uid (LPMESSAGE message);
+
 #ifdef __cplusplus
 }
 #include <string>
+
+/* Parse the headers for additional useful fields.
+
+  r_autocrypt_info: Information about the autocrypt header.
+
+  A return value of false indicates an error. Check the
+  individual info fields "exists" values to check if
+  the header exists in the message */
+bool mapi_get_header_info (LPMESSAGE message,
+                           autocrypt_s &r_autocrypt_info);
 std::string mapi_get_header (LPMESSAGE message);
 #endif
 #endif /*MAPIHELP_H*/
index 309e587..0a97fdb 100644 (file)
@@ -285,6 +285,28 @@ ParseController::setSender(const std::string &sender)
   TRETURN;
 }
 
+static void
+handle_autocrypt_info (const autocrypt_s &info)
+{
+  TSTART;
+  if (info.pref.size() && info.pref != "mutual")
+    {
+      log_debug ("%s:%s: Autocrypt preference is %s which is unhandled.",
+                 SRCNAME, __func__, info.pref.c_str());
+      TRETURN;
+    }
+
+#ifndef BUILD_TESTS
+  if (!KeyCache::import_pgp_key_data (info.data))
+    {
+      log_error ("%s:%s: Failed to import", SRCNAME, __func__);
+    }
+#endif
+  /* No need to free b64decoded as gpgme_data_release handles it */
+
+  TRETURN;
+}
+
 static bool
 is_valid_chksum(const GpgME::Signature &sig)
 {
@@ -328,6 +350,11 @@ ParseController::parse()
 
   auto inputType = input.type ();
 
+  if (m_autocrypt_info.exists)
+    {
+      handle_autocrypt_info (m_autocrypt_info);
+    }
+
   if (inputType == Data::Type::PGPSigned)
     {
       verify = true;
index 44465b8..d88ff19 100644 (file)
@@ -98,6 +98,9 @@ public:
   bool shouldBlockHtml() const
   { return m_block_html; }
 
+  void setAutocryptInfo (const autocrypt_s & info)
+  { m_autocrypt_info = info; }
+
 private:
   /* State variables */
   MimeDataProvider *m_inputprovider;
@@ -108,6 +111,7 @@ private:
   GpgME::VerificationResult m_verify_result;
   std::string m_sender;
   bool m_block_html;
+  autocrypt_s m_autocrypt_info; /* Autocrypt info about the mail */
 };
 
 #endif /* PARSECONTROLLER_H */