Juan Lang : wintrust: Implement CryptSIPGetSignedDataMsg for cabinet files.

Alexandre Julliard julliard at winehq.org
Thu Jul 24 07:14:50 CDT 2008


Module: wine
Branch: master
Commit: 2a317603318d3560b4fee54b20ca961cdcf6ea38
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=2a317603318d3560b4fee54b20ca961cdcf6ea38

Author: Juan Lang <juan.lang at gmail.com>
Date:   Wed Jul 23 17:42:32 2008 -0700

wintrust: Implement CryptSIPGetSignedDataMsg for cabinet files.

Fixes a regression in installing the DirectX 9 runtime.
Based on code from cabinet.dll's fdi.c, thus the healthy dose of
attributions.  Unfortunately there's no public API that'll get this
info for me (that I know of.)

---

 dlls/wintrust/crypt.c |  212 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 211 insertions(+), 1 deletions(-)

diff --git a/dlls/wintrust/crypt.c b/dlls/wintrust/crypt.c
index b1b7771..e2129df 100644
--- a/dlls/wintrust/crypt.c
+++ b/dlls/wintrust/crypt.c
@@ -2,6 +2,10 @@
  * WinTrust Cryptography functions
  *
  * Copyright 2006 James Hawkins
+ * Copyright 2000-2002 Stuart Caie
+ * Copyright 2002 Patrik Stridvall
+ * Copyright 2003 Greg Turner
+ * Copyright 2008 Juan Lang
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
@@ -19,7 +23,7 @@
  */
 
 #include <stdarg.h>
-
+#include <stdio.h>
 #include "windef.h"
 #include "winbase.h"
 #include "wintrust.h"
@@ -267,6 +271,207 @@ error:
     return ret;
 }
 
+/* structure offsets */
+#define cfhead_Signature         (0x00)
+#define cfhead_CabinetSize       (0x08)
+#define cfhead_MinorVersion      (0x18)
+#define cfhead_MajorVersion      (0x19)
+#define cfhead_Flags             (0x1E)
+#define cfhead_SIZEOF            (0x24)
+#define cfheadext_HeaderReserved (0x00)
+#define cfheadext_SIZEOF         (0x04)
+#define cfsigninfo_CertOffset    (0x04)
+#define cfsigninfo_CertSize      (0x08)
+#define cfsigninfo_SIZEOF        (0x0C)
+
+/* flags */
+#define cfheadRESERVE_PRESENT          (0x0004)
+
+/* endian-neutral reading of little-endian data */
+#define EndGetI32(a)  ((((a)[3])<<24)|(((a)[2])<<16)|(((a)[1])<<8)|((a)[0]))
+#define EndGetI16(a)  ((((a)[1])<<8)|((a)[0]))
+
+/* For documentation purposes only:  this is the structure in the reserved
+ * area of a signed cabinet file.  The cert offset indicates where in the
+ * cabinet file the signature resides, and the count indicates its size.
+ */
+typedef struct _CAB_SIGNINFO
+{
+    WORD unk0; /* always 0? */
+    WORD unk1; /* always 0x0010? */
+    DWORD dwCertOffset;
+    DWORD cbCertBlock;
+} CAB_SIGNINFO, *PCAB_SIGNINFO;
+
+static BOOL WINTRUST_GetSignedMsgFromCabFile(SIP_SUBJECTINFO *pSubjectInfo,
+ DWORD *pdwEncodingType, DWORD dwIndex, DWORD *pcbSignedDataMsg,
+ BYTE *pbSignedDataMsg)
+{
+    int header_resv;
+    LONG base_offset, cabsize;
+    USHORT flags;
+    BYTE buf[64];
+    DWORD cert_offset, cert_size, dwRead;
+
+    TRACE("(%p %p %d %p %p)\n", pSubjectInfo, pdwEncodingType, dwIndex,
+          pcbSignedDataMsg, pbSignedDataMsg);
+
+    /*
+     * FIXME: I just noticed that I am memorizing the initial file pointer
+     * offset and restoring it before reading in the rest of the header
+     * information in the cabinet.  Perhaps that's correct -- that is, perhaps
+     * this API is supposed to support "streaming" cabinets which are embedded
+     * in other files, or cabinets which begin at file offsets other than zero.
+     * Otherwise, I should instead go to the absolute beginning of the file.
+     * (Either way, the semantics of wine's FDICopy require me to leave the
+     * file pointer where it is afterwards -- If Windows does not do so, we
+     * ought to duplicate the native behavior in the FDIIsCabinet API, not here.
+     *
+     * So, the answer lies in Windows; will native cabinet.dll recognize a
+     * cabinet "file" embedded in another file?  Note that cabextract.c does
+     * support this, which implies that Microsoft's might.  I haven't tried it
+     * yet so I don't know.  ATM, most of wine's FDI cabinet routines (except
+     * this one) would not work in this way.  To fix it, we could just make the
+     * various references to absolute file positions in the code relative to an
+     * initial "beginning" offset.  Because the FDICopy API doesn't take a
+     * file-handle like this one, we would therein need to search through the
+     * file for the beginning of the cabinet (as we also do in cabextract.c).
+     * Note that this limits us to a maximum of one cabinet per. file: the first.
+     *
+     * So, in summary: either the code below is wrong, or the rest of fdi.c is
+     * wrong... I cannot imagine that both are correct ;)  One of these flaws
+     * should be fixed after determining the behavior on Windows.   We ought
+     * to check both FDIIsCabinet and FDICopy for the right behavior.
+     *
+     * -gmt
+     */
+
+    /* get basic offset & size info */
+    base_offset = SetFilePointer(pSubjectInfo->hFile, 0L, NULL, SEEK_CUR);
+
+    if (SetFilePointer(pSubjectInfo->hFile, 0, NULL, SEEK_END) == -1)
+    {
+        TRACE("seek error\n");
+        return FALSE;
+    }
+
+    cabsize = SetFilePointer(pSubjectInfo->hFile, 0L, NULL, SEEK_CUR);
+    if ((cabsize == -1) || (base_offset == -1) ||
+     (SetFilePointer(pSubjectInfo->hFile, base_offset, NULL, SEEK_SET) == -1))
+    {
+        TRACE("seek error\n");
+        return FALSE;
+    }
+
+    /* read in the CFHEADER */
+    if (!ReadFile(pSubjectInfo->hFile, buf, cfhead_SIZEOF, &dwRead, NULL) ||
+     dwRead != cfhead_SIZEOF)
+    {
+        TRACE("reading header failed\n");
+        return FALSE;
+    }
+
+    /* check basic MSCF signature */
+    if (EndGetI32(buf+cfhead_Signature) != 0x4643534d)
+    {
+        WARN("cabinet signature not present\n");
+        return FALSE;
+    }
+
+    /* Ignore the number of folders and files and the set and cabinet IDs */
+
+    /* check the header revision */
+    if ((buf[cfhead_MajorVersion] > 1) ||
+        (buf[cfhead_MajorVersion] == 1 && buf[cfhead_MinorVersion] > 3))
+    {
+        WARN("cabinet format version > 1.3\n");
+        return FALSE;
+    }
+
+    /* pull the flags out */
+    flags = EndGetI16(buf+cfhead_Flags);
+
+    if (!(flags & cfheadRESERVE_PRESENT))
+    {
+        TRACE("no header present, not signed\n");
+        return FALSE;
+    }
+
+    if (!ReadFile(pSubjectInfo->hFile, buf, cfheadext_SIZEOF, &dwRead, NULL) ||
+     dwRead != cfheadext_SIZEOF)
+    {
+        ERR("bunk reserve-sizes?\n");
+        return FALSE;
+    }
+
+    header_resv = EndGetI16(buf+cfheadext_HeaderReserved);
+    if (!header_resv)
+    {
+        TRACE("no header_resv, not signed\n");
+        return FALSE;
+    }
+    else if (header_resv < cfsigninfo_SIZEOF)
+    {
+        TRACE("header_resv too small, not signed\n");
+        return FALSE;
+    }
+
+    if (header_resv > 60000)
+    {
+        WARN("WARNING; header reserved space > 60000\n");
+    }
+
+    if (!ReadFile(pSubjectInfo->hFile, buf, cfsigninfo_SIZEOF, &dwRead, NULL) ||
+     dwRead != cfsigninfo_SIZEOF)
+    {
+        ERR("couldn't read reserve\n");
+        return FALSE;
+    }
+
+    cert_offset = EndGetI32(buf+cfsigninfo_CertOffset);
+    TRACE("cert_offset: %d\n", cert_offset);
+    cert_size = EndGetI32(buf+cfsigninfo_CertSize);
+    TRACE("cert_size: %d\n", cert_size);
+
+    /* The redundant checks are to avoid wraparound */
+    if (cert_offset > cabsize || cert_size > cabsize ||
+     cert_offset + cert_size > cabsize)
+    {
+        WARN("offset beyond file, not attempting to read\n");
+        return FALSE;
+    }
+
+    SetFilePointer(pSubjectInfo->hFile, base_offset, NULL, SEEK_SET);
+    if (!pbSignedDataMsg)
+    {
+        *pcbSignedDataMsg = cert_size;
+        return TRUE;
+    }
+    if (*pcbSignedDataMsg < cert_size)
+    {
+        *pcbSignedDataMsg = cert_size;
+        SetLastError(ERROR_INSUFFICIENT_BUFFER);
+        return FALSE;
+    }
+    if (SetFilePointer(pSubjectInfo->hFile, cert_offset, NULL, SEEK_SET) == -1)
+    {
+        ERR("couldn't seek to cert location\n");
+        return FALSE;
+    }
+    if (!ReadFile(pSubjectInfo->hFile, pbSignedDataMsg, cert_size, &dwRead,
+     NULL) || dwRead != cert_size)
+    {
+        ERR("couldn't read cert\n");
+        return FALSE;
+    }
+    /* The encoding of the files I've seen appears to be in ASN.1
+     * format, and there isn't a field indicating the type, so assume it
+     * always is.
+     */
+    *pdwEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
+    return TRUE;
+}
+
 /***********************************************************************
  *      CryptSIPGetSignedDataMsg  (WINTRUST.@)
  */
@@ -275,6 +480,8 @@ BOOL WINAPI CryptSIPGetSignedDataMsg(SIP_SUBJECTINFO* pSubjectInfo, DWORD* pdwEn
 {
     static const GUID unknown = { 0xC689AAB8, 0x8E78, 0x11D0, { 0x8C,0x47,
      0x00,0xC0,0x4F,0xC2,0x95,0xEE } };
+    static const GUID cabGUID = { 0xC689AABA, 0x8E78, 0x11D0, { 0x8C,0x47,
+     0x00,0xC0,0x4F,0xC2,0x95,0xEE } };
     BOOL ret;
 
     TRACE("(%p %p %d %p %p)\n", pSubjectInfo, pdwEncodingType, dwIndex,
@@ -283,6 +490,9 @@ BOOL WINAPI CryptSIPGetSignedDataMsg(SIP_SUBJECTINFO* pSubjectInfo, DWORD* pdwEn
     if (!memcmp(pSubjectInfo->pgSubjectType, &unknown, sizeof(unknown)))
         ret = WINTRUST_GetSignedMsgFromPEFile(pSubjectInfo, pdwEncodingType,
          dwIndex, pcbSignedDataMsg, pbSignedDataMsg);
+    else if (!memcmp(pSubjectInfo->pgSubjectType, &cabGUID, sizeof(cabGUID)))
+        ret = WINTRUST_GetSignedMsgFromCabFile(pSubjectInfo, pdwEncodingType,
+         dwIndex, pcbSignedDataMsg, pbSignedDataMsg);
     else
     {
         FIXME("unimplemented for subject type %s\n",




More information about the wine-cvs mailing list