wintrust(1/4): Implement SPC PE image decoding
Juan Lang
juan.lang at gmail.com
Mon Aug 13 19:38:02 CDT 2007
--Juan
-------------- next part --------------
From 88e21c6a14098525cb0275cdbe1929474e397170 Mon Sep 17 00:00:00 2001
From: Juan Lang <juan.lang at gmail.com>
Date: Mon, 13 Aug 2007 14:34:46 -0700
Subject: [PATCH] Implement SPC PE image decoding
---
dlls/wintrust/asn.c | 381 +++++++++++++++++++++++++++++++++++++++++++++
dlls/wintrust/tests/asn.c | 5 -
2 files changed, 377 insertions(+), 9 deletions(-)
diff --git a/dlls/wintrust/asn.c b/dlls/wintrust/asn.c
index 7ae2544..f60fba8 100644
--- a/dlls/wintrust/asn.c
+++ b/dlls/wintrust/asn.c
@@ -44,6 +44,8 @@ #define n16toh(x) RtlUshortByteSwap(x)
#endif
+#define ASN_BITSTRING (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x03)
+
static BOOL CRYPT_EncodeLen(DWORD len, BYTE *pbEncoded, DWORD *pcbEncoded)
{
DWORD bytesNeeded, significantBytes = 0;
@@ -615,12 +617,383 @@ BOOL WINAPI WVTAsn1SpcLinkDecode(DWORD d
return ret;
}
+typedef BOOL (WINAPI *CryptDecodeObjectFunc)(DWORD, LPCSTR, const BYTE *,
+ DWORD, DWORD, void *, DWORD *);
+
+/* tag:
+ * The expected tag of the item. If tag is 0, decodeFunc is called
+ * regardless of the tag value seen.
+ * offset:
+ * A sequence is decoded into a struct. The offset member is the
+ * offset of this item within that struct.
+ * decodeFunc:
+ * The decoder function to use. If this is NULL, then the member isn't
+ * decoded, but minSize space is reserved for it.
+ * minSize:
+ * The minimum amount of space occupied after decoding. You must set this.
+ * optional:
+ * If true, and the tag doesn't match the expected tag for this item,
+ * or the decodeFunc fails with CRYPT_E_ASN1_BADTAG, then minSize space is
+ * filled with 0 for this member.
+ * hasPointer, pointerOffset:
+ * If the item has dynamic data, set hasPointer to TRUE, pointerOffset to
+ * the offset within the struct of the data pointer (or to the
+ * first data pointer, if more than one exist).
+ * size:
+ * Used by CRYPT_AsnDecodeSequence, not for your use.
+ */
+struct AsnDecodeSequenceItem
+{
+ BYTE tag;
+ DWORD offset;
+ CryptDecodeObjectFunc decodeFunc;
+ DWORD minSize;
+ BOOL optional;
+ BOOL hasPointer;
+ DWORD pointerOffset;
+ DWORD size;
+};
+
+/* Decodes the items in a sequence, where the items are described in items,
+ * the encoded data are in pbEncoded with length cbEncoded. Decodes into
+ * pvStructInfo. nextData is a pointer to the memory location at which the
+ * first decoded item with a dynamic pointer should point.
+ * Upon decoding, *cbDecoded is the total number of bytes decoded.
+ */
+static BOOL CRYPT_AsnDecodeSequenceItems(DWORD dwCertEncodingType,
+ struct AsnDecodeSequenceItem items[], DWORD cItem, const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, BYTE *nextData,
+ DWORD *cbDecoded)
+{
+ BOOL ret;
+ DWORD i, decoded = 0;
+ const BYTE *ptr = pbEncoded;
+
+ TRACE("%p, %d, %p, %d, %08x, %p, %p, %p\n", items, cItem, pbEncoded,
+ cbEncoded, dwFlags, pvStructInfo, nextData, cbDecoded);
+
+ for (i = 0, ret = TRUE; ret && i < cItem; i++)
+ {
+ if (cbEncoded - (ptr - pbEncoded) != 0)
+ {
+ DWORD nextItemLen;
+
+ if ((ret = CRYPT_GetLen(ptr, cbEncoded - (ptr - pbEncoded),
+ &nextItemLen)))
+ {
+ BYTE nextItemLenBytes = GET_LEN_BYTES(ptr[1]);
+
+ if (ptr[0] == items[i].tag || !items[i].tag)
+ {
+ if (nextData && pvStructInfo && items[i].hasPointer)
+ {
+ TRACE("Setting next pointer to %p\n",
+ nextData);
+ *(BYTE **)((BYTE *)pvStructInfo +
+ items[i].pointerOffset) = nextData;
+ }
+ if (items[i].decodeFunc)
+ {
+ if (pvStructInfo)
+ TRACE("decoding item %d\n", i);
+ else
+ TRACE("sizing item %d\n", i);
+ ret = items[i].decodeFunc(dwCertEncodingType,
+ NULL, ptr, 1 + nextItemLenBytes + nextItemLen,
+ dwFlags & ~CRYPT_DECODE_ALLOC_FLAG,
+ pvStructInfo ? (BYTE *)pvStructInfo + items[i].offset
+ : NULL, &items[i].size);
+ if (ret)
+ {
+ /* Account for alignment padding */
+ if (items[i].size % sizeof(DWORD))
+ items[i].size += sizeof(DWORD) -
+ items[i].size % sizeof(DWORD);
+ TRACE("item %d size: %d\n", i, items[i].size);
+ if (nextData && items[i].hasPointer &&
+ items[i].size > items[i].minSize)
+ nextData += items[i].size - items[i].minSize;
+ ptr += 1 + nextItemLenBytes + nextItemLen;
+ decoded += 1 + nextItemLenBytes + nextItemLen;
+ TRACE("item %d: decoded %d bytes\n", i,
+ 1 + nextItemLenBytes + nextItemLen);
+ }
+ else if (items[i].optional &&
+ GetLastError() == CRYPT_E_ASN1_BADTAG)
+ {
+ TRACE("skipping optional item %d\n", i);
+ items[i].size = items[i].minSize;
+ SetLastError(NOERROR);
+ ret = TRUE;
+ }
+ else
+ TRACE("item %d failed: %08x\n", i,
+ GetLastError());
+ }
+ else
+ {
+ TRACE("item %d: decoded %d bytes\n", i,
+ 1 + nextItemLenBytes + nextItemLen);
+ ptr += 1 + nextItemLenBytes + nextItemLen;
+ decoded += 1 + nextItemLenBytes + nextItemLen;
+ items[i].size = items[i].minSize;
+ }
+ }
+ else if (items[i].optional)
+ {
+ TRACE("skipping optional item %d\n", i);
+ items[i].size = items[i].minSize;
+ }
+ else
+ {
+ TRACE("item %d: tag %02x doesn't match expected %02x\n",
+ i, ptr[0], items[i].tag);
+ SetLastError(CRYPT_E_ASN1_BADTAG);
+ ret = FALSE;
+ }
+ }
+ }
+ else if (items[i].optional)
+ {
+ TRACE("missing optional item %d, skipping\n", i);
+ items[i].size = items[i].minSize;
+ }
+ else
+ {
+ TRACE("not enough bytes for item %d, failing\n", i);
+ SetLastError(CRYPT_E_ASN1_CORRUPT);
+ ret = FALSE;
+ }
+ }
+ if (ret)
+ *cbDecoded = decoded;
+ TRACE("returning %d\n", ret);
+ return ret;
+}
+
+/* This decodes an arbitrary sequence into a contiguous block of memory
+ * (basically, a struct.) Each element being decoded is described by a struct
+ * AsnDecodeSequenceItem, see above.
+ * startingPointer is an optional pointer to the first place where dynamic
+ * data will be stored. If you know the starting offset, you may pass it
+ * here. Otherwise, pass NULL, and one will be inferred from the items.
+ */
+static BOOL CRYPT_AsnDecodeSequence(DWORD dwCertEncodingType,
+ struct AsnDecodeSequenceItem items[], DWORD cItem, const BYTE *pbEncoded,
+ DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
+ void *startingPointer)
+{
+ BOOL ret;
+
+ TRACE("%p, %d, %p, %d, %08x, %p, %d, %p\n", items, cItem, pbEncoded,
+ cbEncoded, dwFlags, pvStructInfo, *pcbStructInfo, startingPointer);
+
+ if (pbEncoded[0] == ASN_SEQUENCE)
+ {
+ DWORD dataLen;
+
+ if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+ {
+ DWORD lenBytes = GET_LEN_BYTES(pbEncoded[1]), cbDecoded;
+ const BYTE *ptr = pbEncoded + 1 + lenBytes;
+
+ cbEncoded -= 1 + lenBytes;
+ if (cbEncoded < dataLen)
+ {
+ TRACE("dataLen %d exceeds cbEncoded %d, failing\n", dataLen,
+ cbEncoded);
+ SetLastError(CRYPT_E_ASN1_CORRUPT);
+ ret = FALSE;
+ }
+ else
+ ret = CRYPT_AsnDecodeSequenceItems(dwFlags, items, cItem, ptr,
+ cbEncoded, dwFlags, NULL, NULL, &cbDecoded);
+ if (ret && cbDecoded != dataLen)
+ {
+ TRACE("expected %d decoded, got %d, failing\n", dataLen,
+ cbDecoded);
+ SetLastError(CRYPT_E_ASN1_CORRUPT);
+ ret = FALSE;
+ }
+ if (ret)
+ {
+ DWORD i, bytesNeeded = 0, structSize = 0;
+
+ for (i = 0; i < cItem; i++)
+ {
+ bytesNeeded += items[i].size;
+ structSize += items[i].minSize;
+ }
+ if (!pvStructInfo)
+ *pcbStructInfo = bytesNeeded;
+ else if (*pcbStructInfo < bytesNeeded)
+ {
+ SetLastError(ERROR_MORE_DATA);
+ *pcbStructInfo = bytesNeeded;
+ ret = FALSE;
+ }
+ else
+ {
+ BYTE *nextData;
+
+ *pcbStructInfo = bytesNeeded;
+ if (startingPointer)
+ nextData = (BYTE *)startingPointer;
+ else
+ nextData = (BYTE *)pvStructInfo + structSize;
+ memset(pvStructInfo, 0, structSize);
+ ret = CRYPT_AsnDecodeSequenceItems(dwFlags, items, cItem,
+ ptr, cbEncoded, dwFlags, pvStructInfo, nextData,
+ &cbDecoded);
+ }
+ }
+ }
+ }
+ else
+ {
+ SetLastError(CRYPT_E_ASN1_BADTAG);
+ ret = FALSE;
+ }
+ TRACE("returning %d (%08x)\n", ret, GetLastError());
+ return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeBitsInternal(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ void *pvStructInfo, DWORD *pcbStructInfo)
+{
+ BOOL ret;
+
+ TRACE("(%p, %d, 0x%08x, %p, %d)\n", pbEncoded, cbEncoded, dwFlags,
+ pvStructInfo, *pcbStructInfo);
+
+ if (pbEncoded[0] == ASN_BITSTRING)
+ {
+ DWORD bytesNeeded, dataLen;
+
+ if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+ {
+ if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
+ bytesNeeded = sizeof(CRYPT_BIT_BLOB);
+ else
+ bytesNeeded = dataLen - 1 + sizeof(CRYPT_BIT_BLOB);
+ if (!pvStructInfo)
+ *pcbStructInfo = bytesNeeded;
+ else if (*pcbStructInfo < bytesNeeded)
+ {
+ *pcbStructInfo = bytesNeeded;
+ SetLastError(ERROR_MORE_DATA);
+ ret = FALSE;
+ }
+ else
+ {
+ CRYPT_BIT_BLOB *blob;
+
+ blob = (CRYPT_BIT_BLOB *)pvStructInfo;
+ blob->cbData = dataLen - 1;
+ blob->cUnusedBits = *(pbEncoded + 1 +
+ GET_LEN_BYTES(pbEncoded[1]));
+ if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
+ {
+ blob->pbData = (BYTE *)pbEncoded + 2 +
+ GET_LEN_BYTES(pbEncoded[1]);
+ }
+ else
+ {
+ assert(blob->pbData);
+ if (blob->cbData)
+ {
+ BYTE mask = 0xff << blob->cUnusedBits;
+
+ memcpy(blob->pbData, pbEncoded + 2 +
+ GET_LEN_BYTES(pbEncoded[1]), blob->cbData);
+ blob->pbData[blob->cbData - 1] &= mask;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ SetLastError(CRYPT_E_ASN1_BADTAG);
+ ret = FALSE;
+ }
+ TRACE("returning %d (%08x)\n", ret, GetLastError());
+ return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeSPCLinkPointer(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ void *pvStructInfo, DWORD *pcbStructInfo)
+{
+ BOOL ret = FALSE;
+ DWORD dataLen;
+
+ if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+ {
+ BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+ DWORD size;
+ SPC_LINK **pLink = (SPC_LINK **)pvStructInfo;
+
+ ret = CRYPT_AsnDecodeSPCLinkInternal(dwCertEncodingType, lpszStructType,
+ pbEncoded + 1 + lenBytes, dataLen, dwFlags, NULL, &size);
+ if (ret)
+ {
+ if (!pvStructInfo)
+ *pcbStructInfo = size + sizeof(PSPC_LINK);
+ else if (*pcbStructInfo < size + sizeof(PSPC_LINK))
+ {
+ *pcbStructInfo = size + sizeof(PSPC_LINK);
+ SetLastError(ERROR_MORE_DATA);
+ ret = FALSE;
+ }
+ else
+ {
+ *pcbStructInfo = size + sizeof(PSPC_LINK);
+ /* Set imageData's pointer if necessary */
+ if (size > sizeof(SPC_LINK))
+ {
+ (*pLink)->u.pwszUrl =
+ (LPWSTR)((BYTE *)*pLink + sizeof(SPC_LINK));
+ }
+ ret = CRYPT_AsnDecodeSPCLinkInternal(dwCertEncodingType,
+ lpszStructType, pbEncoded + 1 + lenBytes, dataLen, dwFlags,
+ *pLink, pcbStructInfo);
+ }
+ }
+ }
+ return ret;
+}
+
BOOL WINAPI WVTAsn1SpcPeImageDataDecode(DWORD dwCertEncodingType,
LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
void *pvStructInfo, DWORD *pcbStructInfo)
{
- FIXME("(0x%08x, %s, %p, %d, 0x%08x, %p, %p)\n", dwCertEncodingType,
- debugstr_a(lpszStructType), pbEncoded, cbEncoded, dwFlags,
- pvStructInfo, pcbStructInfo);
- return FALSE;
+ BOOL ret = FALSE;
+
+ TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+ pvStructInfo, *pcbStructInfo);
+
+ __TRY
+ {
+ struct AsnDecodeSequenceItem items[] = {
+ { ASN_BITSTRING, offsetof(SPC_PE_IMAGE_DATA, Flags),
+ CRYPT_AsnDecodeBitsInternal, sizeof(CRYPT_BIT_BLOB), TRUE, TRUE,
+ offsetof(SPC_PE_IMAGE_DATA, Flags.pbData), 0 },
+ { ASN_CONSTRUCTOR | ASN_CONTEXT, offsetof(SPC_PE_IMAGE_DATA, pFile),
+ CRYPT_AsnDecodeSPCLinkPointer, sizeof(PSPC_LINK), TRUE, TRUE,
+ offsetof(SPC_PE_IMAGE_DATA, pFile), 0 },
+ };
+
+ ret = CRYPT_AsnDecodeSequence(dwCertEncodingType, items,
+ sizeof(items) / sizeof(items[0]), pbEncoded, cbEncoded, dwFlags,
+ pvStructInfo, pcbStructInfo, NULL);
+ }
+ __EXCEPT_PAGE_FAULT
+ {
+ SetLastError(STATUS_ACCESS_VIOLATION);
+ }
+ __ENDTRY
+ TRACE("returning %d\n", ret);
+ return ret;
}
diff --git a/dlls/wintrust/tests/asn.c b/dlls/wintrust/tests/asn.c
index 11b0584..b47f790 100644
--- a/dlls/wintrust/tests/asn.c
+++ b/dlls/wintrust/tests/asn.c
@@ -342,7 +342,6 @@ static void test_decodeSPCPEImage(void)
ret = CryptDecodeObjectEx(X509_ASN_ENCODING, SPC_PE_IMAGE_DATA_STRUCT,
emptySequence, sizeof(emptySequence),
CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
- todo_wine
ok(ret, "CryptDecodeObjectEx failed: %08x\n", GetLastError());
if (ret)
{
@@ -355,7 +354,6 @@ static void test_decodeSPCPEImage(void)
ret = CryptDecodeObjectEx(X509_ASN_ENCODING, SPC_PE_IMAGE_DATA_STRUCT,
onlyFlagsPEImage, sizeof(onlyFlagsPEImage),
CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
- todo_wine
ok(ret, "CryptDecodeObjectEx failed: %08x\n", GetLastError());
if (ret)
{
@@ -371,7 +369,6 @@ static void test_decodeSPCPEImage(void)
ret = CryptDecodeObjectEx(X509_ASN_ENCODING, SPC_PE_IMAGE_DATA_STRUCT,
onlyEmptyFilePEImage, sizeof(onlyEmptyFilePEImage),
CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
- todo_wine
ok(ret, "CryptDecodeObjectEx failed: %08x\n", GetLastError());
if (ret)
{
@@ -392,7 +389,6 @@ static void test_decodeSPCPEImage(void)
ret = CryptDecodeObjectEx(X509_ASN_ENCODING, SPC_PE_IMAGE_DATA_STRUCT,
flagsAndEmptyFilePEImage, sizeof(flagsAndEmptyFilePEImage),
CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
- todo_wine
ok(ret, "CryptDecodeObjectEx failed: %08x\n", GetLastError());
if (ret)
{
@@ -416,7 +412,6 @@ static void test_decodeSPCPEImage(void)
ret = CryptDecodeObjectEx(X509_ASN_ENCODING, SPC_PE_IMAGE_DATA_STRUCT,
flagsAndFilePEImage, sizeof(flagsAndFilePEImage),
CRYPT_DECODE_ALLOC_FLAG, NULL, (BYTE *)&buf, &size);
- todo_wine
ok(ret, "CryptDecodeObjectEx failed: %08x\n", GetLastError());
if (ret)
{
--
1.4.1
More information about the wine-patches
mailing list