Index: dll/win32/wintrust/softpub.c =================================================================== --- dll/win32/wintrust/softpub.c (revision 70788) +++ dll/win32/wintrust/softpub.c (working copy) @@ -1,5 +1,6 @@ /* * Copyright 2007 Juan Lang + * Copyright 2016 Mark Jansen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -199,6 +200,139 @@ return err; } +#define BUFSIZE 1024 +#define OFFSET_DOS_E_LFANEW offsetof(IMAGE_DOS_HEADER, e_lfanew) +#define OFFSET_NT_CHECKSUM offsetof(IMAGE_NT_HEADERS, OptionalHeader.CheckSum) +#define NT_CHECKSUM_SIZE sizeof(DWORD) +#define OFFSET_NT_DATADIR4 offsetof(IMAGE_NT_HEADERS, OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]) +#define NT_DATADIR4_SIZE sizeof(IMAGE_DATA_DIRECTORY) + + +/* See https://www.cs.auckland.ac.nz/~pgut001/pubs/authenticode.txt for details about the hashing */ + +static BOOL SOFTPUB_HashPEFile(HANDLE hFile, HCRYPTHASH hHash) +{ + DWORD e_lfanew = 0; + DWORD cbRead = 0, vaSecurityEntry = 0, BytesLeft; + BYTE rgbFile[BUFSIZE]; + BOOL bResult; + + SetFilePointer(hFile, OFFSET_DOS_E_LFANEW, NULL, FILE_BEGIN); + bResult = ReadFile(hFile, &e_lfanew, sizeof(e_lfanew), &cbRead, NULL); + if (!bResult || cbRead != sizeof(e_lfanew)) + return FALSE; + SetFilePointer(hFile, 0, NULL, FILE_BEGIN); + + /* Do not overflow */ + BytesLeft = e_lfanew + OFFSET_NT_CHECKSUM; + if (BytesLeft > BUFSIZE) + return FALSE; + + /* Read until checksum + hash */ + bResult = ReadFile(hFile, rgbFile, BytesLeft, &cbRead, NULL); + if (!bResult || (BytesLeft != cbRead) || !CryptHashData(hHash, rgbFile, cbRead, 0)) + return FALSE; + + /* Skip checksum */ + bResult = ReadFile(hFile, rgbFile, sizeof(DWORD), &cbRead, NULL); + if (!bResult || cbRead != sizeof(DWORD)) + return FALSE; + + /* Again, make sure not to overflow */ + BytesLeft = (e_lfanew + OFFSET_NT_DATADIR4) - (e_lfanew + OFFSET_NT_CHECKSUM + NT_CHECKSUM_SIZE); + if (BytesLeft > BUFSIZE) + return FALSE; + + /* Read until the IMAGE_DIRECTORY_ENTRY_SECURITY + hash */ + bResult = ReadFile(hFile, rgbFile, BytesLeft, &cbRead, NULL); + if (!bResult || (BytesLeft != cbRead) || !CryptHashData(hHash, rgbFile, cbRead, 0)) + return FALSE; + + /* Read the va of the security entry */ + bResult = ReadFile(hFile, &vaSecurityEntry, sizeof(vaSecurityEntry), &cbRead, NULL); + if (!bResult || cbRead != sizeof(vaSecurityEntry)) + return FALSE; + + /* Skip length of security entry */ + bResult = ReadFile(hFile, rgbFile, sizeof(DWORD), &cbRead, NULL); + if (!bResult || cbRead != sizeof(DWORD)) + return FALSE; + + /* Calculate the remainder (We need to skip the security entry at the end of the file) */ + BytesLeft = vaSecurityEntry - (e_lfanew + OFFSET_NT_DATADIR4 + NT_DATADIR4_SIZE); + + /* Read + hash the rest */ + while (bResult && BytesLeft) + { + cbRead = min(BUFSIZE, BytesLeft); + bResult = ReadFile(hFile, rgbFile, cbRead, &cbRead, NULL); + if (0 == cbRead || !bResult) + return FALSE; + BytesLeft -= cbRead; + if (!CryptHashData(hHash, rgbFile, cbRead, 0)) + { + return FALSE; + } + } + return TRUE; +} + +static DWORD SOFTPUB_VerifyImageHash(CRYPT_PROVIDER_DATA *data, HANDLE file) +{ + DWORD err = TRUST_E_SYSTEM_ERROR; + SPC_INDIRECT_DATA_CONTENT* indirect = (SPC_INDIRECT_DATA_CONTENT*)data->u.pPDSip->psIndirectData; + HCRYPTPROV hProv = data->hProv; + BOOL ReleaseProv = FALSE; + ALG_ID algID; + HCRYPTHASH hHash; + DWORD cbHash, cbHashSize = sizeof(cbHash); + BYTE* rgbHash; + + /* Are we a PE file? */ + if (!HIWORD(indirect->Data.pszObjId) || strcmp(indirect->Data.pszObjId, SPC_PE_IMAGE_DATA_OBJID)) + return ERROR_SUCCESS; + + algID = CertOIDToAlgId(indirect->DigestAlgorithm.pszObjId); + + if (!hProv) + { + ReleaseProv = TRUE; + if (!CryptAcquireContextW(&hProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) + return GetLastError(); + } + + if (!CryptCreateHash(hProv, algID, 0, 0, &hHash)) + { + if (ReleaseProv) + CryptReleaseContext(hProv, 0); + return GetLastError(); + } + + if (SOFTPUB_HashPEFile(file, hHash)) + { + /* If we got this far, we could read the hash from the PE file, + so assume all other failures are a bad digest */ + err = TRUST_E_BAD_DIGEST; + + if (CryptGetHashParam(hHash, HP_HASHSIZE, (BYTE*)&cbHash, &cbHashSize, 0)) + { + rgbHash = data->psPfns->pfnAlloc(cbHash); + if (CryptGetHashParam(hHash, HP_HASHVAL, rgbHash, &cbHash, 0) && cbHash == indirect->Digest.cbData) + { + if (!memcmp(rgbHash, indirect->Digest.pbData, cbHash)) + err = S_OK; + } + data->psPfns->pfnFree(rgbHash); + } + } + CryptDestroyHash(hHash); + if (ReleaseProv) + CryptReleaseContext(hProv, 0); + + return err; +} + + static DWORD SOFTPUB_CreateStoreFromMessage(CRYPT_PROVIDER_DATA *data) { DWORD err = ERROR_SUCCESS; @@ -362,6 +496,9 @@ if (err) goto error; err = SOFTPUB_DecodeInnerContent(data); + if (err) + goto error; + err = SOFTPUB_VerifyImageHash(data, data->pWintrustData->u.pFile->hFile); error: if (err && data->fOpenedFile && data->pWintrustData->u.pFile)