wintrust(5/7): Implement decoding SPC links

Juan Lang juan.lang at gmail.com
Fri Aug 10 16:41:28 CDT 2007


--Juan
-------------- next part --------------
From 66fc7849cfed516ffd699b17b94f77ef83b8f48e Mon Sep 17 00:00:00 2001
From: Juan Lang <juan.lang at gmail.com>
Date: Fri, 10 Aug 2007 11:23:55 -0700
Subject: [PATCH] Implement decoding SPC links
---
 dlls/wintrust/Makefile.in |    2 
 dlls/wintrust/asn.c       |  347 ++++++++++++++++++++++++++++++++++++++++++++-
 dlls/wintrust/tests/asn.c |   12 +-
 3 files changed, 348 insertions(+), 13 deletions(-)

diff --git a/dlls/wintrust/Makefile.in b/dlls/wintrust/Makefile.in
index b6e547d..4e84922 100644
--- a/dlls/wintrust/Makefile.in
+++ b/dlls/wintrust/Makefile.in
@@ -4,7 +4,7 @@ SRCDIR    = @srcdir@
 VPATH     = @srcdir@
 MODULE    = wintrust.dll
 IMPORTLIB = libwintrust.$(IMPLIBEXT)
-IMPORTS   = crypt32 user32 advapi32 kernel32
+IMPORTS   = crypt32 user32 advapi32 kernel32 ntdll
 DELAYIMPORTS = imagehlp
 
 C_SRCS = \
diff --git a/dlls/wintrust/asn.c b/dlls/wintrust/asn.c
index 7acba64..77c42d1 100644
--- a/dlls/wintrust/asn.c
+++ b/dlls/wintrust/asn.c
@@ -18,16 +18,32 @@
  *
  */
 #include <stdarg.h>
+#include <assert.h>
 #define NONAMELESSUNION
 #include "windef.h"
 #include "winbase.h"
 #include "winerror.h"
 #include "wincrypt.h"
 #include "wintrust.h"
+#include "snmp.h"
+#include "winternl.h"
 #include "wine/debug.h"
+#include "wine/exception.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(cryptasn);
 
+#ifdef WORDS_BIGENDIAN
+
+#define hton16(x) (x)
+#define n16toh(x) (x)
+
+#else
+
+#define hton16(x) RtlUshortByteSwap(x)
+#define n16toh(x) RtlUshortByteSwap(x)
+
+#endif
+
 BOOL WINAPI WVTAsn1SpcLinkEncode(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const void *pvStructInfo, BYTE *pbEncoded,
  DWORD *pcbEncoded)
@@ -48,14 +64,337 @@ BOOL WINAPI WVTAsn1SpcPeImageDataEncode(
     return FALSE;
 }
 
+/* Gets the number of length bytes from the given (leading) length byte */
+#define GET_LEN_BYTES(b) ((b) <= 0x7f ? 1 : 1 + ((b) & 0x7f))
+
+/* Helper function to get the encoded length of the data starting at pbEncoded,
+ * where pbEncoded[0] is the tag.  If the data are too short to contain a
+ * length or if the length is too large for cbEncoded, sets an appropriate
+ * error code and returns FALSE.
+ */
+static BOOL CRYPT_GetLen(const BYTE *pbEncoded, DWORD cbEncoded, DWORD *len)
+{
+    BOOL ret;
+
+    if (cbEncoded <= 1)
+    {
+        SetLastError(CRYPT_E_ASN1_CORRUPT);
+        ret = FALSE;
+    }
+    else if (pbEncoded[1] <= 0x7f)
+    {
+        if (pbEncoded[1] + 1 > cbEncoded)
+        {
+            SetLastError(CRYPT_E_ASN1_EOD);
+            ret = FALSE;
+        }
+        else
+        {
+            *len = pbEncoded[1];
+            ret = TRUE;
+        }
+    }
+    else if (pbEncoded[1] == 0x80)
+    {
+        FIXME("unimplemented for indefinite-length encoding\n");
+        SetLastError(CRYPT_E_ASN1_CORRUPT);
+        ret = FALSE;
+    }
+    else
+    {
+        BYTE lenLen = GET_LEN_BYTES(pbEncoded[1]);
+
+        if (lenLen > sizeof(DWORD) + 1)
+        {
+            SetLastError(CRYPT_E_ASN1_LARGE);
+            ret = FALSE;
+        }
+        else if (lenLen + 2 > cbEncoded)
+        {
+            SetLastError(CRYPT_E_ASN1_CORRUPT);
+            ret = FALSE;
+        }
+        else
+        {
+            DWORD out = 0;
+
+            pbEncoded += 2;
+            while (--lenLen)
+            {
+                out <<= 8;
+                out |= *pbEncoded++;
+            }
+            if (out + lenLen + 1 > cbEncoded)
+            {
+                SetLastError(CRYPT_E_ASN1_EOD);
+                ret = FALSE;
+            }
+            else
+            {
+                *len = out;
+                ret = TRUE;
+            }
+        }
+    }
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeOctets(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret;
+    DWORD bytesNeeded, dataLen;
+
+    TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo);
+
+    if (!cbEncoded)
+    {
+        SetLastError(CRYPT_E_ASN1_CORRUPT);
+        ret = FALSE;
+    }
+    else if (pbEncoded[0] != ASN_OCTETSTRING)
+    {
+        SetLastError(CRYPT_E_ASN1_BADTAG);
+        ret = FALSE;
+    }
+    else if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+    {
+        if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
+            bytesNeeded = sizeof(CRYPT_DATA_BLOB);
+        else
+            bytesNeeded = dataLen + sizeof(CRYPT_DATA_BLOB);
+        if (!pvStructInfo)
+            *pcbStructInfo = bytesNeeded;
+        else if (*pcbStructInfo < bytesNeeded)
+        {
+            SetLastError(ERROR_MORE_DATA);
+            *pcbStructInfo = bytesNeeded;
+            ret = FALSE;
+        }
+        else
+        {
+            CRYPT_DATA_BLOB *blob;
+            BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+
+            blob = (CRYPT_DATA_BLOB *)pvStructInfo;
+            blob->cbData = dataLen;
+            if (dwFlags & CRYPT_DECODE_NOCOPY_FLAG)
+                blob->pbData = (BYTE *)pbEncoded + 1 + lenBytes;
+            else
+            {
+                assert(blob->pbData);
+                if (blob->cbData)
+                    memcpy(blob->pbData, pbEncoded + 1 + lenBytes,
+                     blob->cbData);
+            }
+        }
+    }
+    return ret;
+}
+
+static BOOL WINAPI CRYPT_AsnDecodeSPCLinkInternal(DWORD dwCertEncodingType,
+ LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
+ void *pvStructInfo, DWORD *pcbStructInfo)
+{
+    BOOL ret = FALSE;
+    DWORD bytesNeeded = sizeof(SPC_LINK), dataLen;
+
+    TRACE("%p, %d, %08x, %p, %d\n", pbEncoded, cbEncoded, dwFlags,
+     pvStructInfo, *pcbStructInfo);
+
+    if ((ret = CRYPT_GetLen(pbEncoded, cbEncoded, &dataLen)))
+    {
+        BYTE lenBytes = GET_LEN_BYTES(pbEncoded[1]);
+        DWORD realDataLen;
+
+        switch (pbEncoded[0])
+        {
+        case ASN_CONTEXT:
+            bytesNeeded += (dataLen + 1) * sizeof(WCHAR);
+            if (!pvStructInfo)
+                *pcbStructInfo = bytesNeeded;
+            else if (*pcbStructInfo < bytesNeeded)
+            {
+                *pcbStructInfo = bytesNeeded;
+                SetLastError(ERROR_MORE_DATA);
+                ret = FALSE;
+            }
+            else
+            {
+                PSPC_LINK link = (PSPC_LINK)pvStructInfo;
+                DWORD i;
+
+                link->dwLinkChoice = SPC_URL_LINK_CHOICE;
+                for (i = 0; i < dataLen; i++)
+                    link->u.pwszUrl[i] =
+                     *(pbEncoded + 1 + lenBytes + i);
+                link->u.pwszUrl[i] = '\0';
+                TRACE("returning url %s\n", debugstr_w(link->u.pwszUrl));
+            }
+            break;
+        case ASN_CONSTRUCTOR | ASN_CONTEXT | 1:
+        {
+            CRYPT_DATA_BLOB classId;
+            DWORD size = sizeof(classId);
+
+            if ((ret = CRYPT_AsnDecodeOctets(dwCertEncodingType, NULL,
+             pbEncoded + 1 + lenBytes, cbEncoded - 1 - lenBytes,
+             CRYPT_DECODE_NOCOPY_FLAG, &classId, &size)))
+            {
+                if (classId.cbData != sizeof(SPC_UUID))
+                {
+                    SetLastError(CRYPT_E_BAD_ENCODE);
+                    ret = FALSE;
+                }
+                else
+                {
+                    CRYPT_DATA_BLOB data;
+
+                    /* The tag length for the classId must be 1 since the
+                     * length is correct.
+                     */
+                    size = sizeof(data);
+                    if ((ret = CRYPT_AsnDecodeOctets(dwCertEncodingType, NULL,
+                     pbEncoded + 3 + lenBytes + classId.cbData,
+                     cbEncoded - 3 - lenBytes - classId.cbData,
+                     CRYPT_DECODE_NOCOPY_FLAG, &data, &size)))
+                    {
+                        bytesNeeded += data.cbData;
+                        if (!pvStructInfo)
+                            *pcbStructInfo = bytesNeeded;
+                        else if (*pcbStructInfo < bytesNeeded)
+                        {
+                            *pcbStructInfo = bytesNeeded;
+                            SetLastError(ERROR_MORE_DATA);
+                            ret = FALSE;
+                        }
+                        else
+                        {
+                            PSPC_LINK link = (PSPC_LINK)pvStructInfo;
+
+                            link->dwLinkChoice = SPC_MONIKER_LINK_CHOICE;
+                            /* pwszFile pointer was set by caller, copy it
+                             * before overwriting it
+                             */
+                            link->u.Moniker.SerializedData.pbData =
+                             (BYTE *)link->u.pwszFile;
+                            memcpy(&link->u.Moniker.ClassId, classId.pbData,
+                             classId.cbData);
+                            memcpy(link->u.Moniker.SerializedData.pbData,
+                             data.pbData, data.cbData);
+                            link->u.Moniker.SerializedData.cbData = data.cbData;
+                        }
+                    }
+                }
+            }
+            break;
+        }
+        case ASN_CONSTRUCTOR | ASN_CONTEXT | 2:
+            if (dataLen && pbEncoded[1 + lenBytes] != ASN_CONTEXT)
+                SetLastError(CRYPT_E_ASN1_BADTAG);
+            else if ((ret = CRYPT_GetLen(pbEncoded + 1 + lenBytes, dataLen,
+             &realDataLen)))
+            {
+                BYTE realLenBytes = GET_LEN_BYTES(pbEncoded[2 + lenBytes]);
+
+                bytesNeeded += realDataLen + sizeof(WCHAR);
+                if (!pvStructInfo)
+                    *pcbStructInfo = bytesNeeded;
+                else if (*pcbStructInfo < bytesNeeded)
+                {
+                    *pcbStructInfo = bytesNeeded;
+                    SetLastError(ERROR_MORE_DATA);
+                    ret = FALSE;
+                }
+                else
+                {
+                    PSPC_LINK link = (PSPC_LINK)pvStructInfo;
+                    DWORD i;
+                    const BYTE *ptr = pbEncoded + 2 + lenBytes + realLenBytes;
+
+                    link->dwLinkChoice = SPC_FILE_LINK_CHOICE;
+                    for (i = 0; i < dataLen / sizeof(WCHAR); i++)
+                        link->u.pwszFile[i] =
+                         hton16(*(WORD *)(ptr + i * sizeof(WCHAR)));
+                    link->u.pwszFile[realDataLen / sizeof(WCHAR)] = '\0';
+                    TRACE("returning file %s\n", debugstr_w(link->u.pwszFile));
+                }
+            }
+            else
+            {
+                bytesNeeded += sizeof(WCHAR);
+                if (!pvStructInfo)
+                    *pcbStructInfo = bytesNeeded;
+                else if (*pcbStructInfo < bytesNeeded)
+                {
+                    *pcbStructInfo = bytesNeeded;
+                    SetLastError(ERROR_MORE_DATA);
+                    ret = FALSE;
+                }
+                else
+                {
+                    PSPC_LINK link = (PSPC_LINK)pvStructInfo;
+
+                    link->dwLinkChoice = SPC_FILE_LINK_CHOICE;
+                    link->u.pwszFile[0] = '\0';
+                    ret = TRUE;
+                }
+            }
+            break;
+        default:
+            SetLastError(CRYPT_E_ASN1_BADTAG);
+        }
+    }
+    TRACE("returning %d\n", ret);
+    return ret;
+}
+
 BOOL WINAPI WVTAsn1SpcLinkDecode(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
+    {
+        DWORD bytesNeeded;
+
+        ret = CRYPT_AsnDecodeSPCLinkInternal(dwCertEncodingType,
+         lpszStructType, pbEncoded, cbEncoded, dwFlags, NULL, &bytesNeeded);
+        if (ret)
+        {
+            if (!pvStructInfo)
+                *pcbStructInfo = bytesNeeded;
+            else if (*pcbStructInfo < bytesNeeded)
+            {
+                *pcbStructInfo = bytesNeeded;
+                SetLastError(ERROR_MORE_DATA);
+                ret = FALSE;
+            }
+            else
+            {
+                SPC_LINK *link = (SPC_LINK *)pvStructInfo;
+
+                link->u.pwszFile =
+                 (LPWSTR)((BYTE *)pvStructInfo + sizeof(SPC_LINK));
+                ret = CRYPT_AsnDecodeSPCLinkInternal(dwCertEncodingType,
+                 lpszStructType, pbEncoded, cbEncoded, dwFlags, pvStructInfo,
+                 pcbStructInfo);
+            }
+        }
+    }
+    __EXCEPT_PAGE_FAULT
+    {
+        SetLastError(STATUS_ACCESS_VIOLATION);
+    }
+    __ENDTRY
+    TRACE("returning %d\n", ret);
+    return ret;
 }
 
 BOOL WINAPI WVTAsn1SpcPeImageDataDecode(DWORD dwCertEncodingType,
diff --git a/dlls/wintrust/tests/asn.c b/dlls/wintrust/tests/asn.c
index da83b5f..01e6bd0 100644
--- a/dlls/wintrust/tests/asn.c
+++ b/dlls/wintrust/tests/asn.c
@@ -147,7 +147,6 @@ static void test_decodeSPCLink(void)
     ret = CryptDecodeObjectEx(X509_ASN_ENCODING, SPC_LINK_STRUCT,
      emptyURLSPCLink, sizeof(emptyURLSPCLink), CRYPT_DECODE_ALLOC_FLAG, NULL,
      (BYTE *)&buf, &size);
-    todo_wine
     ok(ret, "CryptDecodeObjectEx failed: %08x\n", GetLastError());
     if (ret)
     {
@@ -160,7 +159,6 @@ static void test_decodeSPCLink(void)
     ret = CryptDecodeObjectEx(X509_ASN_ENCODING, SPC_LINK_STRUCT,
      urlSPCLink, sizeof(urlSPCLink), CRYPT_DECODE_ALLOC_FLAG, NULL,
      (BYTE *)&buf, &size);
-    todo_wine
     ok(ret, "CryptDecodeObjectEx failed: %08x\n", GetLastError());
     if (ret)
     {
@@ -173,7 +171,6 @@ static void test_decodeSPCLink(void)
     ret = CryptDecodeObjectEx(X509_ASN_ENCODING, SPC_LINK_STRUCT,
      fileSPCLink, sizeof(fileSPCLink), CRYPT_DECODE_ALLOC_FLAG, NULL,
      (BYTE *)&buf, &size);
-    todo_wine
     ok(ret, "CryptDecodeObjectEx failed: %08x\n", GetLastError());
     if (ret)
     {
@@ -186,7 +183,6 @@ static void test_decodeSPCLink(void)
     ret = CryptDecodeObjectEx(X509_ASN_ENCODING, SPC_LINK_STRUCT,
      emptyMonikerSPCLink, sizeof(emptyMonikerSPCLink), CRYPT_DECODE_ALLOC_FLAG,
      NULL, (BYTE *)&buf, &size);
-    todo_wine
     ok(ret, "CryptDecodeObjectEx failed: %08x\n", GetLastError());
     if (ret)
     {
@@ -195,14 +191,15 @@ static void test_decodeSPCLink(void)
         link = (SPC_LINK *)buf;
         ok(link->dwLinkChoice == SPC_MONIKER_LINK_CHOICE,
          "Expected SPC_MONIKER_LINK_CHOICE, got %d\n", link->dwLinkChoice);
-        ok(!memcmp(&link->Moniker, &emptyMoniker, sizeof(emptyMoniker)),
-         "Unexpected value\n");
+        ok(!memcmp(&link->Moniker.ClassId, &emptyMoniker.ClassId,
+         sizeof(emptyMoniker.ClassId)), "Unexpected value\n");
+        ok(link->Moniker.SerializedData.cbData == 0,
+         "Expected no serialized data\n");
         LocalFree(buf);
     }
     ret = CryptDecodeObjectEx(X509_ASN_ENCODING, SPC_LINK_STRUCT,
      monikerSPCLink, sizeof(monikerSPCLink), CRYPT_DECODE_ALLOC_FLAG, NULL,
      (BYTE *)&buf, &size);
-    todo_wine
     ok(ret, "CryptDecodeObjectEx failed: %08x\n", GetLastError());
     if (ret)
     {
@@ -224,7 +221,6 @@ static void test_decodeSPCLink(void)
     ret = CryptDecodeObjectEx(X509_ASN_ENCODING, SPC_LINK_STRUCT,
      badMonikerSPCLink, sizeof(badMonikerSPCLink), CRYPT_DECODE_ALLOC_FLAG,
      NULL, (BYTE *)&buf, &size);
-    todo_wine
     ok(!ret && GetLastError() == CRYPT_E_BAD_ENCODE,
      "Expected CRYPT_E_BAD_ENCODE, got %08x\n", GetLastError());
 }
-- 
1.4.1


More information about the wine-patches mailing list