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:
parent
c7d1d34956
commit
2a31760331
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue