[PATCH] wintrust: Verify image hash in WinVerifyTrust. (v2)

Vijay Kiran Kamuju infyquest at gmail.com
Thu Mar 28 08:49:20 CDT 2019


From: Mark Jansen <learn0more+wine at gmail.com>

Includes various improvements by Sebastian Lackner <sebastian at fds-team.de>.

Changes in v2:
* Do not use memory mapping (based on a patch by Mark Jansen).

From: Mark Jansen <learn0more+wine at gmail.com>
Signed-off-by: Vijay Kiran Kamuju <infyquest at gmail.com>
---
 dlls/wintrust/softpub.c       | 194 ++++++++++++++++++++++++++++++++++
 dlls/wintrust/tests/softpub.c |   8 +-
 2 files changed, 198 insertions(+), 4 deletions(-)

diff --git a/dlls/wintrust/softpub.c b/dlls/wintrust/softpub.c
index d65d2b681d..ae1300eb19 100644
--- a/dlls/wintrust/softpub.c
+++ b/dlls/wintrust/softpub.c
@@ -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
@@ -21,6 +22,7 @@
 
 #include "windef.h"
 #include "winbase.h"
+#include "winternl.h"
 #include "wintrust.h"
 #include "mssip.h"
 #include "softpub.h"
@@ -208,6 +210,195 @@ static DWORD SOFTPUB_GetMessageFromFile(CRYPT_PROVIDER_DATA *data, HANDLE file,
     return err;
 }
 
+/* See https://www.cs.auckland.ac.nz/~pgut001/pubs/authenticode.txt
+ * for details about the hashing.
+ */
+static BOOL SOFTPUB_HashPEFile(HANDLE file, HCRYPTHASH hash)
+{
+    DWORD pos, checksum, security_dir;
+    IMAGE_DOS_HEADER dos_header;
+    union
+    {
+        IMAGE_NT_HEADERS32 nt32;
+        IMAGE_NT_HEADERS64 nt64;
+    } nt_header;
+    IMAGE_DATA_DIRECTORY secdir;
+    LARGE_INTEGER file_size;
+    DWORD bytes_read;
+    BYTE buffer[1024];
+    BOOL ret;
+
+    if (!GetFileSizeEx(file, &file_size))
+        return FALSE;
+
+    SetFilePointer(file, 0, NULL, FILE_BEGIN);
+    ret = ReadFile(file, &dos_header, sizeof(dos_header), &bytes_read, NULL);
+    if (!ret || bytes_read != sizeof(dos_header))
+        return FALSE;
+
+    if (dos_header.e_magic != IMAGE_DOS_SIGNATURE)
+    {
+        ERR("Unrecognized IMAGE_DOS_HEADER magic %04x\n", dos_header.e_magic);
+        return FALSE;
+    }
+    if (dos_header.e_lfanew >= 256 * 1024 * 1024) /* see RtlImageNtHeaderEx */
+        return FALSE;
+    if (dos_header.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS, OptionalHeader.MajorLinkerVersion) > file_size.QuadPart)
+        return FALSE;
+
+    SetFilePointer(file, dos_header.e_lfanew, NULL, FILE_BEGIN);
+    ret = ReadFile(file, &nt_header, sizeof(nt_header), &bytes_read, NULL);
+    if (!ret || bytes_read < FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader.Magic) +
+                             sizeof(nt_header.nt32.OptionalHeader.Magic))
+        return FALSE;
+
+    if (nt_header.nt32.Signature != IMAGE_NT_SIGNATURE)
+    {
+        ERR("Unrecognized IMAGE_NT_HEADERS signature %08x\n", nt_header.nt32.Signature);
+        return FALSE;
+    }
+
+    if (nt_header.nt32.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR32_MAGIC)
+    {
+        if (bytes_read < sizeof(nt_header.nt32))
+            return FALSE;
+
+        checksum     = dos_header.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader.CheckSum);
+        security_dir = dos_header.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS32, OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]);
+        secdir       = nt_header.nt32.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
+    }
+    else if (nt_header.nt32.OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
+    {
+        if (bytes_read < sizeof(nt_header.nt64))
+            return FALSE;
+
+        checksum     = dos_header.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader.CheckSum);
+        security_dir = dos_header.e_lfanew + FIELD_OFFSET(IMAGE_NT_HEADERS64, OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY]);
+        secdir       = nt_header.nt64.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY];
+    }
+    else
+    {
+        ERR("Unrecognized OptionalHeader magic %04x\n", nt_header.nt32.OptionalHeader.Magic);
+        return FALSE;
+    }
+
+    if (secdir.VirtualAddress < security_dir + sizeof(IMAGE_DATA_DIRECTORY))
+        return FALSE;
+    if (secdir.VirtualAddress > file_size.QuadPart)
+        return FALSE;
+    if (secdir.VirtualAddress + secdir.Size != file_size.QuadPart)
+        return FALSE;
+
+    /* Hash until checksum. */
+    SetFilePointer(file, 0, NULL, FILE_BEGIN);
+    for (pos = 0; pos < checksum; pos += bytes_read)
+    {
+        ret = ReadFile(file, buffer, min(sizeof(buffer), checksum - pos), &bytes_read, NULL);
+        if (!ret || !bytes_read)
+            return FALSE;
+        if (!CryptHashData(hash, buffer, bytes_read, 0))
+            return FALSE;
+    }
+
+    /* Hash until the DataDirectory[IMAGE_DIRECTORY_ENTRY_SECURITY] entry. */
+    checksum += sizeof(DWORD);
+    SetFilePointer(file, checksum, NULL, FILE_BEGIN);
+    for (pos = checksum; pos < security_dir; pos += bytes_read)
+    {
+        ret = ReadFile(file, buffer, min(sizeof(buffer), security_dir - pos), &bytes_read, NULL);
+        if (!ret || !bytes_read)
+            return FALSE;
+        if (!CryptHashData(hash, buffer, bytes_read, 0))
+            return FALSE;
+    }
+
+    /* Hash until the end of the file. */
+    security_dir += sizeof(IMAGE_DATA_DIRECTORY);
+    SetFilePointer(file, security_dir, NULL, FILE_BEGIN);
+    for (pos = security_dir; pos < secdir.VirtualAddress; pos += bytes_read)
+    {
+        ret = ReadFile(file, buffer, min(sizeof(buffer), secdir.VirtualAddress - pos), &bytes_read, NULL);
+        if (!ret || !bytes_read)
+            return FALSE;
+        if (!CryptHashData(hash, buffer, bytes_read, 0))
+            return FALSE;
+    }
+
+    return TRUE;
+}
+
+static DWORD SOFTPUB_VerifyImageHash(CRYPT_PROVIDER_DATA *data, HANDLE file)
+{
+    SPC_INDIRECT_DATA_CONTENT *indirect = (SPC_INDIRECT_DATA_CONTENT *)data->u.pPDSip->psIndirectData;
+    DWORD err, hash_size, length;
+    BYTE *hash_data;
+    BOOL release_prov = FALSE;
+    HCRYPTPROV prov = data->hProv;
+    HCRYPTHASH hash = 0;
+    ALG_ID algID;
+
+    if (((ULONG_PTR)indirect->Data.pszObjId >> 16) == 0 ||
+        strcmp(indirect->Data.pszObjId, SPC_PE_IMAGE_DATA_OBJID))
+    {
+        FIXME("Cannot verify hash for pszObjId=%s\n", debugstr_a(indirect->Data.pszObjId));
+        return ERROR_SUCCESS;
+    }
+
+    if (!(algID = CertOIDToAlgId(indirect->DigestAlgorithm.pszObjId)))
+        return TRUST_E_SYSTEM_ERROR; /* FIXME */
+
+    if (!prov)
+    {
+        if (!CryptAcquireContextW(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+            return GetLastError();
+        release_prov = TRUE;
+    }
+
+    if (!CryptCreateHash(prov, algID, 0, 0, &hash))
+    {
+        err = GetLastError();
+        goto done;
+    }
+
+    if (!SOFTPUB_HashPEFile(file, hash))
+    {
+        err = TRUST_E_NOSIGNATURE;
+        goto done;
+    }
+
+    length = sizeof(hash_size);
+    if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *)&hash_size, &length, 0))
+    {
+        err = GetLastError();
+        goto done;
+    }
+
+    if (!(hash_data = data->psPfns->pfnAlloc(hash_size)))
+    {
+        err = ERROR_OUTOFMEMORY;
+        goto done;
+    }
+
+    if (!CryptGetHashParam(hash, HP_HASHVAL, hash_data, &hash_size, 0))
+    {
+        err = GetLastError();
+        data->psPfns->pfnFree(hash_data);
+        goto done;
+    }
+
+    err = (hash_size == indirect->Digest.cbData &&
+           !memcmp(hash_data, indirect->Digest.pbData, hash_size)) ? S_OK : TRUST_E_BAD_DIGEST;
+    data->psPfns->pfnFree(hash_data);
+
+done:
+    if (hash)
+        CryptDestroyHash(hash);
+    if (release_prov)
+        CryptReleaseContext(prov, 0);
+    return err;
+}
+
+
 static DWORD SOFTPUB_CreateStoreFromMessage(CRYPT_PROVIDER_DATA *data)
 {
     DWORD err = ERROR_SUCCESS;
@@ -371,6 +562,9 @@ static DWORD SOFTPUB_LoadFileMessage(CRYPT_PROVIDER_DATA *data)
     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)
diff --git a/dlls/wintrust/tests/softpub.c b/dlls/wintrust/tests/softpub.c
index 5d6d62c9e9..914f04d7c0 100644
--- a/dlls/wintrust/tests/softpub.c
+++ b/dlls/wintrust/tests/softpub.c
@@ -1152,7 +1152,7 @@ static void test_wintrust_digest(void)
         {
             {{ SelfSignedFile32, sizeof(SelfSignedFile32) },
              { Dummy, sizeof(Dummy) }},
-            { TRUST_E_NOSIGNATURE, TRUE }, { TRUST_E_NOSIGNATURE, TRUE }
+            { TRUST_E_NOSIGNATURE, FALSE }, { TRUST_E_NOSIGNATURE, FALSE }
         },
         {
             {{ Dummy, sizeof(Dummy) },
@@ -1163,7 +1163,7 @@ static void test_wintrust_digest(void)
             {{ SelfSignedFile32, 19 },
              { Dummy, sizeof(Dummy) },
              { SelfSignedFile32 + 19 + sizeof(Dummy), sizeof(SelfSignedFile32) - 19 - sizeof(Dummy) }},
-            { TRUST_E_BAD_DIGEST, TRUE }, { TRUST_E_NOSIGNATURE, TRUE }
+            { TRUST_E_BAD_DIGEST, FALSE }, { TRUST_E_NOSIGNATURE, TRUE }
         },
         {
             {{ SelfSignedFile32, sizeof(IMAGE_DOS_HEADER) }},
@@ -1182,7 +1182,7 @@ static void test_wintrust_digest(void)
         {
             {{ SelfSignedFile64, sizeof(SelfSignedFile64) },
              { Dummy, sizeof(Dummy) }},
-            { TRUST_E_NOSIGNATURE, TRUE }, { TRUST_E_NOSIGNATURE, TRUE }
+            { TRUST_E_NOSIGNATURE, FALSE }, { TRUST_E_NOSIGNATURE, FALSE }
         },
         {
             {{ Dummy, sizeof(Dummy) },
@@ -1193,7 +1193,7 @@ static void test_wintrust_digest(void)
             {{ SelfSignedFile64, 19 },
              { Dummy, sizeof(Dummy) },
              { SelfSignedFile64 + 19 + sizeof(Dummy), sizeof(SelfSignedFile64) - 19 - sizeof(Dummy) }},
-            { TRUST_E_BAD_DIGEST, TRUE }, { TRUST_E_NOSIGNATURE, TRUE }
+            { TRUST_E_BAD_DIGEST, FALSE }, { TRUST_E_NOSIGNATURE, TRUE }
         },
         {
             {{ SelfSignedFile64, sizeof(IMAGE_DOS_HEADER) }},
-- 
2.21.0




More information about the wine-devel mailing list