diff --git a/dlls/wintrust/softpub.c b/dlls/wintrust/softpub.c index c5eb503a9d9..f78ed9e9f73 100644 --- a/dlls/wintrust/softpub.c +++ b/dlls/wintrust/softpub.c @@ -19,6 +19,7 @@ #include "windef.h" #include "winbase.h" #include "wintrust.h" +#include "mssip.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(wintrust); @@ -35,3 +36,266 @@ HRESULT WINAPI SoftpubInitialize(CRYPT_PROVIDER_DATA *data) TRACE("returning %08x\n", ret); return ret; } + +/* Assumes data->pWintrustData->pFile exists. Makes sure a file handle is + * open for the file. + */ +static BOOL SOFTPUB_OpenFile(CRYPT_PROVIDER_DATA *data) +{ + BOOL ret = TRUE; + + /* PSDK implies that all values should be initialized to NULL, so callers + * typically have hFile as NULL rather than INVALID_HANDLE_VALUE. Check + * for both. + */ + if (!data->pWintrustData->pFile->hFile || + data->pWintrustData->pFile->hFile == INVALID_HANDLE_VALUE) + { + data->pWintrustData->pFile->hFile = + CreateFileW(data->pWintrustData->pFile->pcwszFilePath, GENERIC_READ, + FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + if (data->pWintrustData->pFile->hFile != INVALID_HANDLE_VALUE) + data->fOpenedFile = TRUE; + else + ret = FALSE; + } + TRACE("returning %d\n", ret); + return ret; +} + +/* Assumes data->pWintrustData->pFile exists. Sets data->pPDSip->gSubject to + * the file's subject GUID. + */ +static BOOL SOFTPUB_GetFileSubject(CRYPT_PROVIDER_DATA *data) +{ + BOOL ret; + + if (!data->pWintrustData->pFile->pgKnownSubject) + { + ret = CryptSIPRetrieveSubjectGuid( + data->pWintrustData->pFile->pcwszFilePath, + data->pWintrustData->pFile->hFile, + &data->pPDSip->gSubject); + } + else + { + memcpy(&data->pPDSip->gSubject, + data->pWintrustData->pFile->pgKnownSubject, sizeof(GUID)); + ret = TRUE; + } + TRACE("returning %d\n", ret); + return ret; +} + +/* Assumes data->pPDSip exists, and its gSubject member set. + * Allocates data->pPDSip->pSip and loads it, if possible. + */ +static BOOL SOFTPUB_GetSIP(CRYPT_PROVIDER_DATA *data) +{ + BOOL ret; + + data->pPDSip->pSip = data->psPfns->pfnAlloc(sizeof(SIP_DISPATCH_INFO)); + if (data->pPDSip->pSip) + ret = CryptSIPLoad(&data->pPDSip->gSubject, 0, data->pPDSip->pSip); + else + { + SetLastError(ERROR_OUTOFMEMORY); + ret = FALSE; + } + TRACE("returning %d\n", ret); + return ret; +} + +/* Assumes data->pPDSip has been loaded, and data->pPDSip->pSip allocated. + * Calls data->pPDSip->pSip->pfGet to construct data->hMsg. + */ +static BOOL SOFTPUB_GetMessageFromFile(CRYPT_PROVIDER_DATA *data) +{ + BOOL ret; + LPBYTE buf = NULL; + DWORD size = 0; + + data->pPDSip->psSipSubjectInfo = + data->psPfns->pfnAlloc(sizeof(SIP_SUBJECTINFO)); + if (!data->pPDSip->psSipSubjectInfo) + { + SetLastError(ERROR_OUTOFMEMORY); + return FALSE; + } + + data->pPDSip->psSipSubjectInfo->cbSize = sizeof(SIP_SUBJECTINFO); + data->pPDSip->psSipSubjectInfo->pgSubjectType = &data->pPDSip->gSubject; + data->pPDSip->psSipSubjectInfo->hFile = data->pWintrustData->pFile->hFile; + data->pPDSip->psSipSubjectInfo->pwsFileName = + data->pWintrustData->pFile->pcwszFilePath; + data->pPDSip->psSipSubjectInfo->hProv = data->hProv; + ret = data->pPDSip->pSip->pfGet(data->pPDSip->psSipSubjectInfo, + &data->dwEncoding, 0, &size, 0); + if (!ret) + { + SetLastError(TRUST_E_NOSIGNATURE); + return FALSE; + } + + buf = data->psPfns->pfnAlloc(size); + if (!buf) + { + SetLastError(ERROR_OUTOFMEMORY); + return FALSE; + } + + ret = data->pPDSip->pSip->pfGet(data->pPDSip->psSipSubjectInfo, + &data->dwEncoding, 0, &size, buf); + if (ret) + { + data->hMsg = CryptMsgOpenToDecode(data->dwEncoding, 0, 0, data->hProv, + NULL, NULL); + if (data->hMsg) + ret = CryptMsgUpdate(data->hMsg, buf, size, TRUE); + } + + data->psPfns->pfnFree(buf); + TRACE("returning %d\n", ret); + return ret; +} + +static BOOL SOFTPUB_CreateStoreFromMessage(CRYPT_PROVIDER_DATA *data) +{ + BOOL ret = FALSE; + HCERTSTORE store; + + store = CertOpenStore(CERT_STORE_PROV_MSG, data->dwEncoding, + data->hProv, CERT_STORE_NO_CRYPT_RELEASE_FLAG, data->hMsg); + if (store) + { + data->pahStores = data->psPfns->pfnAlloc(sizeof(HCERTSTORE)); + if (data->pahStores) + { + data->chStores = 1; + data->pahStores[0] = CertDuplicateStore(store); + CertCloseStore(store, 0); + ret = TRUE; + } + else + SetLastError(ERROR_OUTOFMEMORY); + } + TRACE("returning %d\n", ret); + return ret; +} + +static DWORD SOFTPUB_DecodeInnerContent(CRYPT_PROVIDER_DATA *data) +{ + BOOL ret; + DWORD size; + LPBYTE buf = NULL; + + ret = CryptMsgGetParam(data->hMsg, CMSG_INNER_CONTENT_TYPE_PARAM, 0, NULL, + &size); + if (!ret) + goto error; + buf = data->psPfns->pfnAlloc(size); + if (!buf) + { + SetLastError(ERROR_OUTOFMEMORY); + ret = FALSE; + goto error; + } + ret = CryptMsgGetParam(data->hMsg, CMSG_INNER_CONTENT_TYPE_PARAM, 0, buf, + &size); + if (!ret) + goto error; + if (!strcmp((LPCSTR)buf, SPC_INDIRECT_DATA_OBJID)) + { + data->psPfns->pfnFree(buf); + buf = NULL; + ret = CryptMsgGetParam(data->hMsg, CMSG_CONTENT_PARAM, 0, NULL, &size); + if (!ret) + goto error; + buf = data->psPfns->pfnAlloc(size); + if (!buf) + { + SetLastError(ERROR_OUTOFMEMORY); + ret = FALSE; + goto error; + } + ret = CryptMsgGetParam(data->hMsg, CMSG_CONTENT_PARAM, 0, buf, &size); + if (!ret) + goto error; + ret = CryptDecodeObject(data->dwEncoding, + SPC_INDIRECT_DATA_CONTENT_STRUCT, buf, size, 0, NULL, &size); + if (!ret) + goto error; + data->pPDSip->psIndirectData = data->psPfns->pfnAlloc(size); + if (!data->pPDSip->psIndirectData) + { + SetLastError(ERROR_OUTOFMEMORY); + ret = FALSE; + goto error; + } + ret = CryptDecodeObject(data->dwEncoding, + SPC_INDIRECT_DATA_CONTENT_STRUCT, buf, size, 0, + data->pPDSip->psIndirectData, &size); + } + else + { + FIXME("unimplemented for OID %s\n", (LPCSTR)buf); + SetLastError(TRUST_E_SUBJECT_FORM_UNKNOWN); + ret = FALSE; + } + +error: + TRACE("returning %d\n", ret); + return ret; +} + +HRESULT WINAPI SoftpubLoadMessage(CRYPT_PROVIDER_DATA *data) +{ + BOOL ret; + + TRACE("(%p)\n", data); + + if (!data->padwTrustStepErrors) + return S_FALSE; + + switch (data->pWintrustData->dwUnionChoice) + { + case WTD_CHOICE_CERT: + /* Do nothing!? See the tests */ + ret = TRUE; + break; + case WTD_CHOICE_FILE: + if (!data->pWintrustData->pFile) + { + SetLastError(ERROR_INVALID_PARAMETER); + ret = FALSE; + goto error; + } + ret = SOFTPUB_OpenFile(data); + if (!ret) + goto error; + ret = SOFTPUB_GetFileSubject(data); + if (!ret) + goto error; + ret = SOFTPUB_GetSIP(data); + if (!ret) + goto error; + ret = SOFTPUB_GetMessageFromFile(data); + if (!ret) + goto error; + ret = SOFTPUB_CreateStoreFromMessage(data); + if (!ret) + goto error; + ret = SOFTPUB_DecodeInnerContent(data); + break; + default: + FIXME("unimplemented for %d\n", data->pWintrustData->dwUnionChoice); + SetLastError(ERROR_INVALID_PARAMETER); + ret = FALSE; + } + +error: + if (!ret) + data->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] = + GetLastError(); + return ret ? S_OK : S_FALSE; +} diff --git a/dlls/wintrust/tests/softpub.c b/dlls/wintrust/tests/softpub.c index 3186379f6e8..5df430b6e51 100644 --- a/dlls/wintrust/tests/softpub.c +++ b/dlls/wintrust/tests/softpub.c @@ -115,6 +115,95 @@ static void testInitialize(SAFE_PROVIDER_FUNCTIONS *funcs, GUID *actionID) } } +static const BYTE v1CertWithPubKey[] = { +0x30,0x81,0x95,0x02,0x01,0x01,0x30,0x02,0x06,0x00,0x30,0x15,0x31,0x13,0x30, +0x11,0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75,0x61,0x6e,0x20,0x4c,0x61, +0x6e,0x67,0x00,0x30,0x22,0x18,0x0f,0x31,0x36,0x30,0x31,0x30,0x31,0x30,0x31, +0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x18,0x0f,0x31,0x36,0x30,0x31,0x30,0x31, +0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5a,0x30,0x15,0x31,0x13,0x30,0x11, +0x06,0x03,0x55,0x04,0x03,0x13,0x0a,0x4a,0x75,0x61,0x6e,0x20,0x4c,0x61,0x6e, +0x67,0x00,0x30,0x22,0x30,0x0d,0x06,0x09,0x2a,0x86,0x48,0x86,0xf7,0x0d,0x01, +0x01,0x01,0x05,0x00,0x03,0x11,0x00,0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07, +0x08,0x09,0x0a,0x0b,0x0c,0x0d,0x0e,0x0f,0xa3,0x16,0x30,0x14,0x30,0x12,0x06, +0x03,0x55,0x1d,0x13,0x01,0x01,0xff,0x04,0x08,0x30,0x06,0x01,0x01,0xff,0x02, +0x01,0x01 }; + +static void testObjTrust(SAFE_PROVIDER_FUNCTIONS *funcs, GUID *actionID) +{ + HRESULT ret; + CRYPT_PROVIDER_DATA data = { 0 }; + WINTRUST_DATA wintrust_data = { 0 }; + WINTRUST_CERT_INFO certInfo = { sizeof(WINTRUST_CERT_INFO), 0 }; + WINTRUST_FILE_INFO fileInfo = { sizeof(WINTRUST_FILE_INFO), 0 }; + + if (!funcs->pfnObjectTrust) + { + skip("missing pfnObjectTrust\n"); + return; + } + + /* Crashes + ret = funcs->pfnObjectTrust(NULL); + */ + data.pWintrustData = &wintrust_data; + data.padwTrustStepErrors = + funcs->pfnAlloc(TRUSTERROR_MAX_STEPS * sizeof(DWORD)); + if (data.padwTrustStepErrors) + { + static const WCHAR notepad[] = { '\\','n','o','t','e','p','a','d','.', + 'e','x','e',0 }; + WCHAR notepadPath[MAX_PATH]; + PROVDATA_SIP provDataSIP = { 0 }; + static const GUID unknown = { 0xC689AAB8, 0x8E78, 0x11D0, { 0x8C,0x47, + 0x00,0xC0,0x4F,0xC2,0x95,0xEE } }; + + ret = funcs->pfnObjectTrust(&data); + ok(ret == S_FALSE, "Expected S_FALSE, got %08x\n", ret); + ok(data.padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] == + ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %08x\n", + data.padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV]); + wintrust_data.pCert = &certInfo; + wintrust_data.dwUnionChoice = WTD_CHOICE_CERT; + ret = funcs->pfnObjectTrust(&data); + ok(ret == S_OK, "Expected S_OK, got %08x\n", ret); + certInfo.psCertContext = (PCERT_CONTEXT)CertCreateCertificateContext( + X509_ASN_ENCODING, v1CertWithPubKey, sizeof(v1CertWithPubKey)); + ret = funcs->pfnObjectTrust(&data); + ok(ret == S_OK, "Expected S_OK, got %08x\n", ret); + CertFreeCertificateContext(certInfo.psCertContext); + certInfo.psCertContext = NULL; + wintrust_data.dwUnionChoice = WTD_CHOICE_FILE; + wintrust_data.pFile = NULL; + ret = funcs->pfnObjectTrust(&data); + ok(ret == S_FALSE, "Expected S_FALSE, got %08x\n", ret); + ok(data.padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] == + ERROR_INVALID_PARAMETER, + "Expected ERROR_INVALID_PARAMETER, got %08x\n", + data.padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV]); + wintrust_data.pFile = &fileInfo; + /* Crashes + ret = funcs->pfnObjectTrust(&data); + */ + GetWindowsDirectoryW(notepadPath, MAX_PATH); + lstrcatW(notepadPath, notepad); + fileInfo.pcwszFilePath = notepadPath; + /* pfnObjectTrust now crashes unless both pPDSip and psPfns are set */ + data.pPDSip = &provDataSIP; + data.psPfns = (CRYPT_PROVIDER_FUNCTIONS *)funcs; + ret = funcs->pfnObjectTrust(&data); + ok(ret == S_FALSE, "Expected S_FALSE, got %08x\n", ret); + ok(data.padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV] == + TRUST_E_NOSIGNATURE, "Expected TRUST_E_NOSIGNATURE, got %08x\n", + data.padwTrustStepErrors[TRUSTERROR_STEP_FINAL_OBJPROV]); + ok(!memcmp(&provDataSIP.gSubject, &unknown, sizeof(unknown)), + "Unexpected subject GUID\n"); + ok(provDataSIP.pSip != NULL, "Expected a SIP\n"); + ok(provDataSIP.psSipSubjectInfo != NULL, "Expected a subject info\n"); + funcs->pfnFree(data.padwTrustStepErrors); + } +} + START_TEST(softpub) { static GUID generic_verify_v2 = WINTRUST_ACTION_GENERIC_VERIFY_V2; @@ -128,5 +217,6 @@ START_TEST(softpub) else { testInitialize(&funcs, &generic_verify_v2); + testObjTrust(&funcs, &generic_verify_v2); } } diff --git a/dlls/wintrust/wintrust.spec b/dlls/wintrust/wintrust.spec index 71f104cca1f..fe9df8a8612 100644 --- a/dlls/wintrust/wintrust.spec +++ b/dlls/wintrust/wintrust.spec @@ -66,7 +66,7 @@ @ stub SoftpubFreeDefUsageCallData @ stdcall SoftpubInitialize(ptr) @ stub SoftpubLoadDefUsageCallData -@ stub SoftpubLoadMessage +@ stdcall SoftpubLoadMessage(ptr) @ stub SoftpubLoadSignature @ stub TrustDecode @ stub TrustFindIssuerCertificate