| /* |
| * 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.@) |
| */ |
| HCATINFO 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 (HCATINFO) 0xdeeeaaadL; |
| } |
| |
| /*********************************************************************** |
| * 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; |
| } |
| |
| static BOOL WINTRUST_GetSignedMsgFromCatFile(SIP_SUBJECTINFO *pSubjectInfo, |
| DWORD *pdwEncodingType, DWORD dwIndex, DWORD *pcbSignedDataMsg, |
| BYTE *pbSignedDataMsg) |
| { |
| BOOL ret; |
| |
| TRACE("(%p %p %d %p %p)\n", pSubjectInfo, pdwEncodingType, dwIndex, |
| pcbSignedDataMsg, pbSignedDataMsg); |
| |
| if (!pbSignedDataMsg) |
| { |
| *pcbSignedDataMsg = GetFileSize(pSubjectInfo->hFile, NULL); |
| ret = TRUE; |
| } |
| else |
| { |
| DWORD len = GetFileSize(pSubjectInfo->hFile, NULL); |
| |
| if (*pcbSignedDataMsg < len) |
| { |
| *pcbSignedDataMsg = len; |
| SetLastError(ERROR_INSUFFICIENT_BUFFER); |
| ret = FALSE; |
| } |
| else |
| { |
| ret = ReadFile(pSubjectInfo->hFile, pbSignedDataMsg, len, |
| pcbSignedDataMsg, NULL); |
| if (ret) |
| *pdwEncodingType = X509_ASN_ENCODING | PKCS_7_ASN_ENCODING; |
| } |
| } |
| return ret; |
| } |
| |
| /*********************************************************************** |
| * 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 } }; |
| static const GUID catGUID = { 0xDE351A43, 0x8E59, 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 if (!memcmp(pSubjectInfo->pgSubjectType, &catGUID, sizeof(catGUID))) |
| ret = WINTRUST_GetSignedMsgFromCatFile(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; |
| } |