[svn] GpgOL - r220 - trunk/src

svn author wk cvs at cvs.gnupg.org
Mon Feb 11 11:36:21 CET 2008


Author: wk
Date: 2008-02-11 11:36:20 +0100 (Mon, 11 Feb 2008)
New Revision: 220

Modified:
   trunk/src/ChangeLog
   trunk/src/common.c
   trunk/src/common.h
   trunk/src/mapihelp.cpp
Log:
workaround for CryptoEx wrongly marking signed messages as encrypted.


Modified: trunk/src/ChangeLog
===================================================================
--- trunk/src/ChangeLog	2008-02-08 12:15:08 UTC (rev 219)
+++ trunk/src/ChangeLog	2008-02-11 10:36:20 UTC (rev 220)
@@ -1,3 +1,12 @@
+2008-02-11  Werner Koch  <wk at g10code.com>
+
+	* common.h (tlvinfo_t): New.
+	* common.c (parse_tlv): New.  Based on code from libksba.
+	* mapihelp.cpp (has_smime_filename): New.
+	(is_really_cms_encrypted): New.
+	(mapi_change_message_class): Use this here to work around a
+	CryptoEx bug.
+
 2008-02-08  Werner Koch  <wk at g10code.com>
 
 	* mapihelp.cpp (mapi_change_message_class): Improve detecion of

Modified: trunk/src/common.c
===================================================================
--- trunk/src/common.c	2008-02-08 12:15:08 UTC (rev 219)
+++ trunk/src/common.c	2008-02-11 10:36:20 UTC (rev 220)
@@ -1,5 +1,5 @@
 /* common.c - Common routines used by GpgOL
- *	Copyright (C) 2005, 2007 g10 Code GmbH
+ *	Copyright (C) 2005, 2007, 2008 g10 Code GmbH
  *
  * This file is part of GpgOL.
  *
@@ -788,3 +788,85 @@
   CloseHandle (pi.hProcess);
   return 0;
 }
+
+
+
+
+/* Simple but pretty complete ASN.1 BER parser.  Parse the data at the
+   address of BUFFER with a length given at the address of SIZE.  On
+   success return 0 and update BUFFER and SIZE to point to the value.
+   Do not update them on error.  The information about the object are
+   stored in the caller allocated TI structure.  */
+int
+parse_tlv (char const **buffer, size_t *size, tlvinfo_t *ti)
+{
+  int c;
+  unsigned long tag;
+  const unsigned char *buf = (const unsigned char *)(*buffer);
+  size_t length = *size;
+
+  ti->cls = 0;
+  ti->tag = 0;
+  ti->is_cons = 0;
+  ti->is_ndef = 0;
+  ti->length = 0;
+  ti->nhdr = 0;
+
+  if (!length)
+    return -1;
+  c = *buf++; length--; ++ti->nhdr;
+
+  ti->cls = (c & 0xc0) >> 6;
+  ti->is_cons = !!(c & 0x20);
+  tag = c & 0x1f;
+
+  if (tag == 0x1f)
+    {
+      tag = 0;
+      do
+        {
+          tag <<= 7;
+          if (!length)
+            return -1;
+          c = *buf++; length--; ++ti->nhdr;
+          tag |= c & 0x7f;
+        }
+      while (c & 0x80);
+    }
+  ti->tag = tag;
+
+  if (!length)
+    return -1;
+  c = *buf++; length--; ++ti->nhdr;
+
+  if ( !(c & 0x80) )
+    ti->length = c;
+  else if (c == 0x80)
+    ti->is_ndef = 1;
+  else if (c == 0xff)
+    return -1;
+  else
+    {
+      unsigned long len = 0;
+      int count = (c & 0x7f);
+
+      if (count > sizeof (len) || count > sizeof (size_t))
+        return -1;
+
+      for (; count; count--)
+        {
+          len <<= 8;
+          if (!length)
+            return -1;
+          c = *buf++; length--; ++ti->nhdr;
+          len |= c & 0xff;
+        }
+      ti->length = len;
+    }
+  
+  *buffer = buf;
+  *size = length;
+  return 0;
+}
+
+

Modified: trunk/src/common.h
===================================================================
--- trunk/src/common.h	2008-02-08 12:15:08 UTC (rev 219)
+++ trunk/src/common.h	2008-02-11 10:36:20 UTC (rev 220)
@@ -1,6 +1,6 @@
 /* common.h - Common declarations for GpgOL
  *	Copyright (C) 2004 Timo Schulz
- *	Copyright (C) 2005, 2006, 2007 g10 Code GmbH
+ *	Copyright (C) 2005, 2006, 2007, 2008 g10 Code GmbH
  *
  * This file is part of GpgOL.
  *
@@ -151,6 +151,27 @@
 #define DBG_FILTER_EXTRA   16 
 #define DBG_MEMORY         32
 
+
+/* Type and constants used with parse_tlv.  */
+struct tlvinfo_s
+{
+  int cls;            /* The class of the tag.  */             
+  int tag;            /* The tag.  */           
+  int is_cons;        /* True if it is a constructed object.  */
+  int is_ndef;        /* True if the object has an indefinite length.  */
+  size_t length;      /* The length of the value.  */
+  size_t nhdr;        /* The number of octets in the header (tag,length). */
+};
+typedef struct tlvinfo_s tlvinfo_t;
+#define MY_ASN_CLASS_UNIVERSAL   0
+#define MY_ASN_CLASS_APPLICATION 1
+#define MY_ASN_CLASS_CONTEXT     2
+#define MY_ASN_CLASS_PRIVATE     3
+#define MY_ASN_TAG_OBJECT_ID     6
+#define MY_ASN_TAG_SEQUENCE     16
+
+
+
 /*-- common.c --*/
 void set_global_hinstance (HINSTANCE hinst);
 void center_window (HWND childwnd, HWND style);
@@ -171,7 +192,9 @@
 
 int gpgol_spawn_detached (const char *cmdline);
 
+int parse_tlv (char const **buffer, size_t *size, tlvinfo_t *ti);
 
+
 /*-- recipient-dialog.c --*/
 unsigned int recipient_dialog_box (gpgme_key_t **ret_rset);
 unsigned int recipient_dialog_box2 (gpgme_key_t *fnd, char **unknown,

Modified: trunk/src/mapihelp.cpp
===================================================================
--- trunk/src/mapihelp.cpp	2008-02-08 12:15:08 UTC (rev 219)
+++ trunk/src/mapihelp.cpp	2008-02-11 10:36:20 UTC (rev 220)
@@ -43,6 +43,12 @@
                         } while (0)
 
 
+static int get_attach_method (LPATTACH obj);
+static int has_smime_filename (LPATTACH obj);
+
+
+
+
 /* Print a MAPI property to the log stream. */
 void
 log_mapi_property (LPMESSAGE message, ULONG prop, const char *propname)
@@ -483,7 +489,134 @@
 }
 
 
+/* Check whether the message is really a CMS encrypted message.  This
+   function is required due to a bug in CryptoEx which sometimes
+   assignes the *.CexEnc message class to signed messages and only
+   updates the message class after accessing them.  Thus in old stores
+   there may be a lot of *.CexEnc message which are actually just
+   signed.  We check here whether such a message is really encrypted
+   by looking at the object identifier inside the CMS data.  Returns
+   true if the message is really encrypted.  */
+static int
+is_really_cms_encrypted (LPMESSAGE message)
+{    
+  HRESULT hr;
+  SizedSPropTagArray (1L, propAttNum) = { 1L, {PR_ATTACH_NUM} };
+  LPMAPITABLE mapitable;
+  LPSRowSet   mapirows;
+  unsigned int pos, n_attach;
+  int is_encrypted = 0;
+  LPATTACH att = NULL;
+  LPSTREAM stream = NULL;
+  char buffer[24];  /* 24 bytes are more than enough to peek at.
+                       Cf. ksba_cms_identify() from the libksba
+                       package.  */
+  const char *p;
+  ULONG nread;
+  size_t n;
+  tlvinfo_t ti;
 
+  hr = message->GetAttachmentTable (0, &mapitable);
+  if (FAILED (hr))
+    {
+      log_debug ("%s:%s: GetAttachmentTable failed: hr=%#lx",
+                 SRCNAME, __func__, hr);
+      return 0;
+    }
+      
+  hr = HrQueryAllRows (mapitable, (LPSPropTagArray)&propAttNum,
+                       NULL, NULL, 0, &mapirows);
+  if (FAILED (hr))
+    {
+      log_debug ("%s:%s: HrQueryAllRows failed: hr=%#lx",
+                 SRCNAME, __func__, hr);
+      mapitable->Release ();
+      return 0;
+    }
+  n_attach = mapirows->cRows > 0? mapirows->cRows : 0;
+  if (n_attach != 1)
+    {
+      FreeProws (mapirows);
+      mapitable->Release ();
+      log_debug ("%s:%s: not just one attachments", SRCNAME, __func__);
+      return 0;
+    }
+  pos = 0;
+
+  if (mapirows->aRow[pos].cValues < 1)
+    {
+      log_error ("%s:%s: invalid row at pos %d", SRCNAME, __func__, pos);
+      goto leave;
+    }
+  if (mapirows->aRow[pos].lpProps[0].ulPropTag != PR_ATTACH_NUM)
+    {
+      log_error ("%s:%s: invalid prop at pos %d", SRCNAME, __func__, pos);
+      goto leave;
+    }
+  hr = message->OpenAttach (mapirows->aRow[pos].lpProps[0].Value.l,
+                            NULL, MAPI_BEST_ACCESS, &att);	
+  if (FAILED (hr))
+    {
+      log_error ("%s:%s: can't open attachment %d (%ld): hr=%#lx",
+                 SRCNAME, __func__, pos, 
+                 mapirows->aRow[pos].lpProps[0].Value.l, hr);
+      goto leave;
+    }
+  if (!has_smime_filename (att))
+    goto leave;
+  if (get_attach_method (att) != ATTACH_BY_VALUE)
+    goto leave;
+  
+  hr = att->OpenProperty (PR_ATTACH_DATA_BIN, &IID_IStream, 
+                          0, 0, (LPUNKNOWN*) &stream);
+  if (FAILED (hr))
+    {
+      log_error ("%s:%s: can't open data stream of attachment: hr=%#lx",
+                 SRCNAME, __func__, hr);
+      goto leave;
+    }
+
+  hr = stream->Read (buffer, sizeof buffer, &nread);
+  if ( hr != S_OK )
+    {
+      log_error ("%s:%s: Read failed: hr=%#lx", SRCNAME, __func__, hr);
+      goto leave;
+    }
+  if (nread < sizeof buffer)
+    {
+      log_error ("%s:%s: not enough bytes returned\n", SRCNAME, __func__);
+      goto leave;
+    }
+
+  p = buffer;
+  n = nread;
+  if (parse_tlv (&p, &n, &ti))
+    goto leave;
+  if (!(ti.cls == MY_ASN_CLASS_UNIVERSAL && ti.tag == MY_ASN_TAG_SEQUENCE
+        && ti.is_cons) )
+    goto leave;
+  if (parse_tlv (&p, &n, &ti))
+    goto leave;
+  if (!(ti.cls == MY_ASN_CLASS_UNIVERSAL && ti.tag == MY_ASN_TAG_OBJECT_ID
+        && !ti.is_cons && ti.length) || ti.length > n)
+    goto leave;
+  /* Now is this enveloped data (1.2.840.113549.1.7.3)?  */
+  if (ti.length == 9 && !memcmp (p, "\x2A\x86\x48\x86\xF7\x0D\x01\x07\x03", 9))
+    is_encrypted = 1;
+
+
+ leave:
+  if (stream)
+    stream->Release ();
+  if (att)
+    att->Release ();
+  FreeProws (mapirows);
+  mapitable->Release ();
+  return !!is_encrypted;
+}
+
+
+
 /* This function checks whether MESSAGE requires processing by us and
    adjusts the message class to our own.  By passing true for
    SYNC_OVERRIDE the actual MAPI message class will be updated to our
@@ -647,7 +780,12 @@
               log_debug ("%s:%s: message has no content type", 
                          SRCNAME, __func__);
               if (cexenc)
-                newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueEncrypted");
+                {
+                  if (is_really_cms_encrypted (message))
+                    newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueEncrypted");
+                  else
+                    newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueSigned");
+                }
             }
           else
             {
@@ -687,8 +825,13 @@
                   newvalue = get_msgcls_from_pgp_lines (message);
                 }
 
-              if (!newvalue && cexenc)
-                newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueEncrypted");
+              if (!newvalue)
+                {
+                  if (is_really_cms_encrypted (message))
+                    newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueEncrypted");
+                  else
+                    newvalue = xstrdup ("IPM.Note.GpgOL.OpaqueSigned");
+                }
 
               xfree (smtype);
               xfree (proto);
@@ -1366,7 +1509,7 @@
 
 
 
-/* Return an attachment as a malloced buffer; The size of the buffer
+/* Return an attachment as a malloced buffer.  The size of the buffer
    will be stored at R_NBYTES.  Returns NULL on failure. */
 char *
 mapi_get_attach (LPMESSAGE message, mapi_attach_item_t *item, size_t *r_nbytes)
@@ -1865,7 +2008,34 @@
   return yes;
 }
 
+/* Helper to check whether the file name of OBJ is "smime.p7m".
+   Returns on true if so.  */
+static int
+has_smime_filename (LPATTACH obj)
+{
+  HRESULT hr;
+  LPSPropValue propval;
+  int yes = 0;
 
+  hr = HrGetOneProp ((LPMAPIPROP)obj, PR_ATTACH_FILENAME, &propval);
+  if (FAILED(hr))
+    return 0;
+
+  if ( PROP_TYPE (propval->ulPropTag) == PT_UNICODE)
+    {
+      if (!wcscmp (propval->Value.lpszW, L"smime.p7m"))
+        yes = 1;
+    }
+  else if ( PROP_TYPE (propval->ulPropTag) == PT_STRING8)
+    {
+      if (!strcmp (propval->Value.lpszA, "smime.p7m"))
+        yes = 1;
+    }
+  MAPIFreeBuffer (propval);
+  return yes;
+}
+
+
 /* Return the content of the body attachment of MESSAGE.  The body
    attachment is a hidden attachment created by us for later display.
    If R_NBYTES is not NULL the number of bytes in the returned buffer




More information about the Gnupg-commits mailing list