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.)
This commit is contained in:
Juan Lang 2008-07-23 17:42:32 -07:00 committed by Alexandre Julliard
parent c7d1d34956
commit 2a31760331
1 changed files with 211 additions and 1 deletions

View File

@ -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",