540 lines
17 KiB
C
540 lines
17 KiB
C
/*
|
|
* 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
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "wintrust.h"
|
|
#include "mscat.h"
|
|
#include "mssip.h"
|
|
#include "imagehlp.h"
|
|
|
|
#include "wine/debug.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(wintrust);
|
|
|
|
/***********************************************************************
|
|
* CryptCATAdminAcquireContext (WINTRUST.@)
|
|
*
|
|
* Get a catalog administrator context handle.
|
|
*
|
|
* PARAMS
|
|
* catAdmin [O] Pointer to the context handle.
|
|
* sysSystem [I] Pointer to a GUID for the needed subsystem.
|
|
* dwFlags [I] Reserved.
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE. catAdmin contains the context handle.
|
|
* Failure: FALSE.
|
|
*
|
|
*/
|
|
BOOL WINAPI CryptCATAdminAcquireContext(HCATADMIN* catAdmin,
|
|
const GUID *sysSystem, DWORD dwFlags )
|
|
{
|
|
FIXME("%p %s %x\n", catAdmin, debugstr_guid(sysSystem), dwFlags);
|
|
|
|
if (!catAdmin)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
*catAdmin = (HCATADMIN)0xdeadbeef;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CryptCATAdminAddCatalog (WINTRUST.@)
|
|
*/
|
|
BOOL WINAPI CryptCATAdminAddCatalog(HCATADMIN catAdmin, PWSTR catalogFile,
|
|
PWSTR selectBaseName, DWORD flags)
|
|
{
|
|
FIXME("%p %s %s %d\n", catAdmin, debugstr_w(catalogFile),
|
|
debugstr_w(selectBaseName), flags);
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CryptCATAdminCalcHashFromFileHandle (WINTRUST.@)
|
|
*/
|
|
BOOL WINAPI CryptCATAdminCalcHashFromFileHandle(HANDLE hFile, DWORD* pcbHash,
|
|
BYTE* pbHash, DWORD dwFlags )
|
|
{
|
|
FIXME("%p %p %p %x\n", hFile, pcbHash, pbHash, dwFlags);
|
|
|
|
if (pbHash && pcbHash) memset(pbHash, 0, *pcbHash);
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CryptCATAdminEnumCatalogFromHash (WINTRUST.@)
|
|
*/
|
|
HCATINFO WINAPI CryptCATAdminEnumCatalogFromHash(HCATADMIN hCatAdmin,
|
|
BYTE* pbHash,
|
|
DWORD cbHash,
|
|
DWORD dwFlags,
|
|
HCATINFO* phPrevCatInfo )
|
|
{
|
|
FIXME("%p %p %d %d %p\n", hCatAdmin, pbHash, cbHash, dwFlags, phPrevCatInfo);
|
|
return NULL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CryptCATAdminReleaseCatalogContext (WINTRUST.@)
|
|
*
|
|
* Release a catalog context handle.
|
|
*
|
|
* PARAMS
|
|
* hCatAdmin [I] Context handle.
|
|
* hCatInfo [I] Catalog handle.
|
|
* dwFlags [I] Reserved.
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE.
|
|
* Failure: FAIL.
|
|
*
|
|
*/
|
|
BOOL WINAPI CryptCATAdminReleaseCatalogContext(HCATADMIN hCatAdmin,
|
|
HCATINFO hCatInfo,
|
|
DWORD dwFlags)
|
|
{
|
|
FIXME("%p %p %x\n", hCatAdmin, hCatInfo, dwFlags);
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CryptCATAdminReleaseContext (WINTRUST.@)
|
|
*
|
|
* Release a catalog administrator context handle.
|
|
*
|
|
* PARAMS
|
|
* catAdmin [I] Context handle.
|
|
* dwFlags [I] Reserved.
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE.
|
|
* Failure: FAIL.
|
|
*
|
|
*/
|
|
BOOL WINAPI CryptCATAdminReleaseContext(HCATADMIN hCatAdmin, DWORD dwFlags )
|
|
{
|
|
FIXME("%p %x\n", hCatAdmin, dwFlags);
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CryptCATAdminRemoveCatalog (WINTRUST.@)
|
|
*
|
|
* Remove a catalog file.
|
|
*
|
|
* PARAMS
|
|
* catAdmin [I] Context handle.
|
|
* pwszCatalogFile [I] Catalog file.
|
|
* dwFlags [I] Reserved.
|
|
*
|
|
* RETURNS
|
|
* Success: TRUE.
|
|
* Failure: FALSE.
|
|
*
|
|
*/
|
|
BOOL WINAPI CryptCATAdminRemoveCatalog(HCATADMIN hCatAdmin, LPCWSTR pwszCatalogFile, DWORD dwFlags)
|
|
{
|
|
FIXME("%p %s %x\n", hCatAdmin, debugstr_w(pwszCatalogFile), dwFlags);
|
|
return DeleteFileW(pwszCatalogFile);
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CryptCATClose (WINTRUST.@)
|
|
*/
|
|
BOOL WINAPI CryptCATClose(HANDLE hCatalog)
|
|
{
|
|
FIXME("(%p) stub\n", hCatalog);
|
|
return TRUE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CryptCATEnumerateMember (WINTRUST.@)
|
|
*/
|
|
CRYPTCATMEMBER *WINAPI CryptCATEnumerateMember(HANDLE hCatalog, CRYPTCATMEMBER* pPrevMember)
|
|
{
|
|
FIXME("(%p, %p) stub\n", hCatalog, pPrevMember);
|
|
return NULL;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CryptCATOpen (WINTRUST.@)
|
|
*/
|
|
HANDLE WINAPI CryptCATOpen(LPWSTR pwszFileName, DWORD fdwOpenFlags, HCRYPTPROV hProv,
|
|
DWORD dwPublicVersion, DWORD dwEncodingType)
|
|
{
|
|
FIXME("(%s, %d, %ld, %d, %d) stub\n", debugstr_w(pwszFileName), fdwOpenFlags,
|
|
hProv, dwPublicVersion, dwEncodingType);
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CryptSIPCreateIndirectData (WINTRUST.@)
|
|
*/
|
|
BOOL WINAPI CryptSIPCreateIndirectData(SIP_SUBJECTINFO* pSubjectInfo, DWORD* pcbIndirectData,
|
|
SIP_INDIRECT_DATA* pIndirectData)
|
|
{
|
|
FIXME("(%p %p %p) stub\n", pSubjectInfo, pcbIndirectData, pIndirectData);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static BOOL WINTRUST_GetSignedMsgFromPEFile(SIP_SUBJECTINFO *pSubjectInfo,
|
|
DWORD *pdwEncodingType, DWORD dwIndex, DWORD *pcbSignedDataMsg,
|
|
BYTE *pbSignedDataMsg)
|
|
{
|
|
BOOL ret;
|
|
WIN_CERTIFICATE *pCert = NULL;
|
|
|
|
TRACE("(%p %p %d %p %p)\n", pSubjectInfo, pdwEncodingType, dwIndex,
|
|
pcbSignedDataMsg, pbSignedDataMsg);
|
|
|
|
if (!pbSignedDataMsg)
|
|
{
|
|
WIN_CERTIFICATE cert;
|
|
|
|
/* app hasn't passed buffer, just get the length */
|
|
ret = ImageGetCertificateHeader(pSubjectInfo->hFile, dwIndex, &cert);
|
|
if (ret)
|
|
*pcbSignedDataMsg = cert.dwLength;
|
|
}
|
|
else
|
|
{
|
|
DWORD len = 0;
|
|
|
|
ret = ImageGetCertificateData(pSubjectInfo->hFile, dwIndex, NULL, &len);
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
|
|
goto error;
|
|
pCert = HeapAlloc(GetProcessHeap(), 0, len);
|
|
if (!pCert)
|
|
{
|
|
ret = FALSE;
|
|
goto error;
|
|
}
|
|
ret = ImageGetCertificateData(pSubjectInfo->hFile, dwIndex, pCert,
|
|
&len);
|
|
if (!ret)
|
|
goto error;
|
|
if (*pcbSignedDataMsg < pCert->dwLength)
|
|
{
|
|
*pcbSignedDataMsg = pCert->dwLength;
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
ret = FALSE;
|
|
}
|
|
else
|
|
{
|
|
memcpy(pbSignedDataMsg, pCert->bCertificate, pCert->dwLength);
|
|
switch (pCert->wCertificateType)
|
|
{
|
|
case WIN_CERT_TYPE_X509:
|
|
*pdwEncodingType = X509_ASN_ENCODING;
|
|
break;
|
|
case WIN_CERT_TYPE_PKCS_SIGNED_DATA:
|
|
*pdwEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING;
|
|
break;
|
|
default:
|
|
FIXME("don't know what to do for encoding type %d\n",
|
|
pCert->wCertificateType);
|
|
*pdwEncodingType = 0;
|
|
}
|
|
}
|
|
}
|
|
error:
|
|
HeapFree(GetProcessHeap(), 0, pCert);
|
|
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.@)
|
|
*/
|
|
BOOL WINAPI CryptSIPGetSignedDataMsg(SIP_SUBJECTINFO* pSubjectInfo, DWORD* pdwEncodingType,
|
|
DWORD dwIndex, DWORD* pcbSignedDataMsg, BYTE* pbSignedDataMsg)
|
|
{
|
|
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,
|
|
pcbSignedDataMsg, pbSignedDataMsg);
|
|
|
|
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",
|
|
debugstr_guid(pSubjectInfo->pgSubjectType));
|
|
ret = FALSE;
|
|
}
|
|
|
|
TRACE("returning %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CryptSIPPutSignedDataMsg (WINTRUST.@)
|
|
*/
|
|
BOOL WINAPI CryptSIPPutSignedDataMsg(SIP_SUBJECTINFO* pSubjectInfo, DWORD pdwEncodingType,
|
|
DWORD* pdwIndex, DWORD cbSignedDataMsg, BYTE* pbSignedDataMsg)
|
|
{
|
|
FIXME("(%p %d %p %d %p) stub\n", pSubjectInfo, pdwEncodingType, pdwIndex,
|
|
cbSignedDataMsg, pbSignedDataMsg);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CryptSIPRemoveSignedDataMsg (WINTRUST.@)
|
|
*/
|
|
BOOL WINAPI CryptSIPRemoveSignedDataMsg(SIP_SUBJECTINFO* pSubjectInfo,
|
|
DWORD dwIndex)
|
|
{
|
|
FIXME("(%p %d) stub\n", pSubjectInfo, dwIndex);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/***********************************************************************
|
|
* CryptSIPVerifyIndirectData (WINTRUST.@)
|
|
*/
|
|
BOOL WINAPI CryptSIPVerifyIndirectData(SIP_SUBJECTINFO* pSubjectInfo,
|
|
SIP_INDIRECT_DATA* pIndirectData)
|
|
{
|
|
FIXME("(%p %p) stub\n", pSubjectInfo, pIndirectData);
|
|
|
|
return FALSE;
|
|
}
|