[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