From 2a317603318d3560b4fee54b20ca961cdcf6ea38 Mon Sep 17 00:00:00 2001 From: Juan Lang Date: Wed, 23 Jul 2008 17:42:32 -0700 Subject: [PATCH] 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 file changed, 211 insertions(+), 1 deletion(-) diff --git a/dlls/wintrust/crypt.c b/dlls/wintrust/crypt.c index b1b77718914..e2129dffc60 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 - +#include #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",