/* * 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 #include #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; }