| /* |
| * Copyright 2002 Mike McCormack for CodeWeavers |
| * Copyright 2005 Juan Lang |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| #include <stdarg.h> |
| #include <stdio.h> |
| #include "windef.h" |
| #include "winbase.h" |
| #include "wincrypt.h" |
| #include "winreg.h" |
| #include "snmp.h" |
| #include "wine/debug.h" |
| |
| /* a few asn.1 tags we need */ |
| #define ASN_UTCTIME (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x17) |
| #define ASN_GENERALTIME (ASN_UNIVERSAL | ASN_PRIMITIVE | 0x18) |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(crypt); |
| |
| static const WCHAR szDllName[] = { 'D','l','l',0 }; |
| |
| static char *CRYPT_GetKeyName(DWORD dwEncodingType, LPCSTR pszFuncName, |
| LPCSTR pszOID) |
| { |
| static const char szEncodingTypeFmt[] = |
| "Software\\Microsoft\\Cryptography\\OID\\EncodingType %ld\\%s\\%s"; |
| UINT len; |
| char numericOID[7]; /* enough for "#65535" */ |
| const char *oid; |
| LPSTR szKey; |
| |
| /* MSDN says the encoding type is a mask, but it isn't treated that way. |
| * (E.g., if dwEncodingType were 3, the key names "EncodingType 1" and |
| * "EncodingType 2" would be expected if it were a mask. Instead native |
| * stores values in "EncodingType 3". |
| */ |
| if (!HIWORD(pszOID)) |
| { |
| snprintf(numericOID, sizeof(numericOID), "#%d", (int)pszOID); |
| oid = numericOID; |
| } |
| else |
| oid = pszOID; |
| |
| /* This is enough: the lengths of the two string parameters are explicitly |
| * counted, and we need up to five additional characters for the encoding |
| * type. These are covered by the "%d", "%s", and "%s" characters in the |
| * format specifier that are removed by sprintf. |
| */ |
| len = sizeof(szEncodingTypeFmt) + lstrlenA(pszFuncName) + lstrlenA(oid); |
| szKey = HeapAlloc(GetProcessHeap(), 0, len); |
| if (szKey) |
| sprintf(szKey, szEncodingTypeFmt, dwEncodingType, pszFuncName, oid); |
| return szKey; |
| } |
| |
| BOOL WINAPI CryptRegisterOIDFunction(DWORD dwEncodingType, LPCSTR pszFuncName, |
| LPCSTR pszOID, LPCWSTR pwszDll, LPCSTR pszOverrideFuncName) |
| { |
| LONG r; |
| HKEY hKey; |
| LPSTR szKey; |
| |
| TRACE("%lx %s %s %s %s\n", dwEncodingType, pszFuncName, pszOID, |
| debugstr_w(pwszDll), pszOverrideFuncName); |
| |
| /* This only registers functions for encoding certs, not messages */ |
| if (!GET_CERT_ENCODING_TYPE(dwEncodingType)) |
| return TRUE; |
| |
| /* Native does nothing pwszDll is NULL */ |
| if (!pwszDll) |
| return TRUE; |
| |
| /* I'm not matching MS bug for bug here, because I doubt any app depends on |
| * it: |
| * - native "succeeds" if pszFuncName is NULL, but the nonsensical entry |
| * it creates would never be used |
| * - native returns an HRESULT rather than a Win32 error if pszOID is NULL. |
| * Instead I disallow both of these with ERROR_INVALID_PARAMETER. |
| */ |
| if (!pszFuncName || !pszOID) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| |
| szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID); |
| TRACE("Key name is %s\n", debugstr_a(szKey)); |
| |
| if (!szKey) |
| return FALSE; |
| |
| r = RegCreateKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey); |
| HeapFree(GetProcessHeap(), 0, szKey); |
| if(r != ERROR_SUCCESS) |
| return FALSE; |
| |
| /* write the values */ |
| if (pszOverrideFuncName) |
| RegSetValueExA(hKey, "FuncName", 0, REG_SZ, pszOverrideFuncName, |
| lstrlenA(pszOverrideFuncName) + 1); |
| RegSetValueExW(hKey, szDllName, 0, REG_SZ, (const BYTE*) pwszDll, |
| (lstrlenW(pwszDll) + 1) * sizeof (WCHAR)); |
| |
| RegCloseKey(hKey); |
| return TRUE; |
| } |
| |
| BOOL WINAPI CryptUnregisterOIDFunction(DWORD dwEncodingType, LPCSTR pszFuncName, |
| LPCSTR pszOID) |
| { |
| LPSTR szKey; |
| LONG rc; |
| |
| TRACE("%lx %s %s\n", dwEncodingType, pszFuncName, pszOID); |
| |
| if (!GET_CERT_ENCODING_TYPE(dwEncodingType)) |
| return TRUE; |
| |
| if (!pszFuncName || !pszOID) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| |
| szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID); |
| rc = RegDeleteKeyA(HKEY_LOCAL_MACHINE, szKey); |
| HeapFree(GetProcessHeap(), 0, szKey); |
| if (rc) |
| SetLastError(rc); |
| return rc ? FALSE : TRUE; |
| } |
| |
| BOOL WINAPI CryptGetOIDFunctionValue(DWORD dwEncodingType, LPCSTR pszFuncName, |
| LPCSTR pszOID, LPCWSTR pwszValueName, DWORD *pdwValueType, BYTE *pbValueData, |
| DWORD *pcbValueData) |
| { |
| LPSTR szKey; |
| LONG rc; |
| HKEY hKey; |
| |
| TRACE("%lx %s %s %s %p %p %p\n", dwEncodingType, debugstr_a(pszFuncName), |
| debugstr_a(pszOID), debugstr_w(pwszValueName), pdwValueType, pbValueData, |
| pcbValueData); |
| |
| if (!GET_CERT_ENCODING_TYPE(dwEncodingType)) |
| return TRUE; |
| |
| if (!pszFuncName || !pszOID || !pwszValueName) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| |
| szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID); |
| rc = RegOpenKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey); |
| HeapFree(GetProcessHeap(), 0, szKey); |
| if (rc) |
| SetLastError(rc); |
| else |
| { |
| rc = RegQueryValueExW(hKey, pwszValueName, NULL, pdwValueType, |
| pbValueData, pcbValueData); |
| if (rc) |
| SetLastError(rc); |
| RegCloseKey(hKey); |
| } |
| return rc ? FALSE : TRUE; |
| } |
| |
| BOOL WINAPI CryptSetOIDFunctionValue(DWORD dwEncodingType, LPCSTR pszFuncName, |
| LPCSTR pszOID, LPCWSTR pwszValueName, DWORD dwValueType, |
| const BYTE *pbValueData, DWORD cbValueData) |
| { |
| LPSTR szKey; |
| LONG rc; |
| HKEY hKey; |
| |
| TRACE("%lx %s %s %s %ld %p %ld\n", dwEncodingType, debugstr_a(pszFuncName), |
| debugstr_a(pszOID), debugstr_w(pwszValueName), dwValueType, pbValueData, |
| cbValueData); |
| |
| if (!GET_CERT_ENCODING_TYPE(dwEncodingType)) |
| return TRUE; |
| |
| if (!pszFuncName || !pszOID || !pwszValueName) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| |
| szKey = CRYPT_GetKeyName(dwEncodingType, pszFuncName, pszOID); |
| rc = RegOpenKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey); |
| HeapFree(GetProcessHeap(), 0, szKey); |
| if (rc) |
| SetLastError(rc); |
| else |
| { |
| rc = RegSetValueExW(hKey, pwszValueName, 0, dwValueType, pbValueData, |
| cbValueData); |
| if (rc) |
| SetLastError(rc); |
| RegCloseKey(hKey); |
| } |
| return rc ? FALSE : TRUE; |
| } |
| |
| /* Gets the registered function named szFuncName for dwCertEncodingType and |
| * lpszStructType, or NULL if one could not be found. *lib will be set to the |
| * handle of the module it's in, or NULL if no module was loaded. If the |
| * return value is NULL, *lib will also be NULL, to simplify error handling. |
| */ |
| static void *CRYPT_GetFunc(DWORD dwCertEncodingType, LPCSTR lpszStructType, |
| LPCSTR szFuncName, HMODULE *lib) |
| { |
| void *ret = NULL; |
| char *szKey = CRYPT_GetKeyName(dwCertEncodingType, szFuncName, |
| lpszStructType); |
| const char *funcName; |
| long r; |
| HKEY hKey; |
| DWORD type, size = 0; |
| |
| *lib = NULL; |
| r = RegOpenKeyA(HKEY_LOCAL_MACHINE, szKey, &hKey); |
| HeapFree(GetProcessHeap(), 0, szKey); |
| if(r != ERROR_SUCCESS) |
| return NULL; |
| |
| RegQueryValueExA(hKey, "FuncName", NULL, &type, NULL, &size); |
| if (GetLastError() == ERROR_MORE_DATA && type == REG_SZ) |
| { |
| funcName = HeapAlloc(GetProcessHeap(), 0, size); |
| RegQueryValueExA(hKey, "FuncName", NULL, &type, (LPBYTE)funcName, |
| &size); |
| } |
| else |
| funcName = szFuncName; |
| RegQueryValueExW(hKey, szDllName, NULL, &type, NULL, &size); |
| if (GetLastError() == ERROR_MORE_DATA && type == REG_SZ) |
| { |
| LPWSTR dllName = HeapAlloc(GetProcessHeap(), 0, size); |
| |
| RegQueryValueExW(hKey, szDllName, NULL, &type, (LPBYTE)dllName, |
| &size); |
| *lib = LoadLibraryW(dllName); |
| if (*lib) |
| { |
| ret = GetProcAddress(*lib, funcName); |
| if (!ret) |
| { |
| /* Unload the library, the caller doesn't want to unload it |
| * when the return value is NULL. |
| */ |
| FreeLibrary(*lib); |
| *lib = NULL; |
| } |
| } |
| HeapFree(GetProcessHeap(), 0, dllName); |
| } |
| if (funcName != szFuncName) |
| HeapFree(GetProcessHeap(), 0, (char *)funcName); |
| return ret; |
| } |
| |
| typedef BOOL (WINAPI *CryptEncodeObjectFunc)(DWORD, LPCSTR, const void *, |
| BYTE *, DWORD *); |
| |
| BOOL WINAPI CryptEncodeObject(DWORD dwCertEncodingType, LPCSTR lpszStructType, |
| const void *pvStructInfo, BYTE *pbEncoded, DWORD *pcbEncoded) |
| { |
| BOOL ret = FALSE; |
| HMODULE lib; |
| CryptEncodeObjectFunc pCryptEncodeObject; |
| |
| TRACE("(0x%08lx, %s, %p, %p, %p)\n", |
| dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) : |
| "(integer value)", pvStructInfo, pbEncoded, pcbEncoded); |
| |
| if (!pbEncoded && !pcbEncoded) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| |
| /* Try registered DLL first.. */ |
| pCryptEncodeObject = |
| (CryptEncodeObjectFunc)CRYPT_GetFunc(dwCertEncodingType, |
| lpszStructType, "CryptEncodeObject", &lib); |
| if (pCryptEncodeObject) |
| { |
| ret = pCryptEncodeObject(dwCertEncodingType, lpszStructType, |
| pvStructInfo, pbEncoded, pcbEncoded); |
| FreeLibrary(lib); |
| } |
| else |
| { |
| /* If not, use CryptEncodeObjectEx */ |
| ret = CryptEncodeObjectEx(dwCertEncodingType, lpszStructType, |
| pvStructInfo, 0, NULL, pbEncoded, pcbEncoded); |
| } |
| return ret; |
| } |
| |
| static BOOL CRYPT_EncodeEnsureSpace(DWORD dwFlags, |
| PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded, |
| DWORD bytesNeeded) |
| { |
| if (!pbEncoded || (!(dwFlags & CRYPT_ENCODE_ALLOC_FLAG) && |
| bytesNeeded > *pcbEncoded)) |
| { |
| *pcbEncoded = bytesNeeded; |
| SetLastError(ERROR_MORE_DATA); |
| return FALSE; |
| } |
| if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) |
| { |
| if (pEncodePara && pEncodePara->pfnAlloc) |
| *(BYTE **)pbEncoded = pEncodePara->pfnAlloc(bytesNeeded); |
| else |
| *(BYTE **)pbEncoded = LocalAlloc(0, bytesNeeded); |
| if (!*(BYTE **)pbEncoded) |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| static BOOL WINAPI CRYPT_AsnEncodeInt(DWORD dwCertEncodingType, |
| LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, |
| PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) |
| { |
| INT val, i; |
| BYTE significantBytes, padByte = 0, bytesNeeded; |
| BOOL neg = FALSE, pad = FALSE; |
| |
| if (!pvStructInfo) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| |
| memcpy(&val, pvStructInfo, sizeof(val)); |
| /* Count the number of significant bytes. Temporarily swap sign for |
| * negatives so I count the minimum number of bytes. |
| */ |
| if (val < 0) |
| { |
| neg = TRUE; |
| val = -val; |
| } |
| for (significantBytes = sizeof(val); !(val & 0xff000000); |
| val <<= 8, significantBytes--) |
| ; |
| if (neg) |
| { |
| val = -val; |
| if ((val & 0xff000000) < 0x80000000) |
| { |
| padByte = 0xff; |
| pad = TRUE; |
| } |
| } |
| else if ((val & 0xff000000) > 0x7f000000) |
| { |
| padByte = 0; |
| pad = TRUE; |
| } |
| bytesNeeded = 2 + significantBytes; |
| if (pad) |
| bytesNeeded++; |
| if (!CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, pcbEncoded, |
| bytesNeeded)) |
| return FALSE; |
| if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) |
| pbEncoded = *(BYTE **)pbEncoded; |
| *pbEncoded++ = ASN_INTEGER; |
| if (pad) |
| { |
| *pbEncoded++ = significantBytes + 1; |
| *pbEncoded++ = padByte; |
| } |
| else |
| *pbEncoded++ = significantBytes; |
| for (i = 0; i < significantBytes; i++, val <<= 8) |
| *(pbEncoded + i) = (BYTE)((val & 0xff000000) >> 24); |
| return TRUE; |
| } |
| |
| static BOOL WINAPI CRYPT_AsnEncodeUtcTime(DWORD dwCertEncodingType, |
| LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, |
| PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) |
| { |
| SYSTEMTIME sysTime; |
| /* sorry, magic number: enough for tag, len, YYMMDDHHMMSSZ\0. I use a |
| * temporary buffer because the output buffer is not NULL-terminated. |
| */ |
| char buf[16]; |
| static const DWORD bytesNeeded = sizeof(buf) - 1; |
| |
| if (!pvStructInfo) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| /* Sanity check the year, this is a two-digit year format */ |
| if (!FileTimeToSystemTime((const FILETIME *)pvStructInfo, &sysTime)) |
| return FALSE; |
| if (sysTime.wYear < 1950 || sysTime.wYear > 2050) |
| { |
| SetLastError(CRYPT_E_BAD_ENCODE); |
| return FALSE; |
| } |
| if (!CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, pcbEncoded, |
| bytesNeeded)) |
| return FALSE; |
| if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) |
| pbEncoded = *(BYTE **)pbEncoded; |
| buf[0] = ASN_UTCTIME; |
| buf[1] = bytesNeeded - 2; |
| snprintf(buf + 2, sizeof(buf) - 2, "%02d%02d%02d%02d%02d%02dZ", |
| sysTime.wYear >= 2000 ? sysTime.wYear - 2000 : sysTime.wYear - 1900, |
| sysTime.wDay, sysTime.wMonth, sysTime.wHour, sysTime.wMinute, |
| sysTime.wSecond); |
| memcpy(pbEncoded, buf, bytesNeeded); |
| return TRUE; |
| } |
| |
| static BOOL WINAPI CRYPT_AsnEncodeChoiceOfTime(DWORD dwCertEncodingType, |
| LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags, |
| PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded) |
| { |
| SYSTEMTIME sysTime; |
| /* sorry, magic number: enough for tag, len, YYYYMMDDHHMMSSZ\0. I use a |
| * temporary buffer because the output buffer is not NULL-terminated. |
| */ |
| char buf[18]; |
| static const DWORD bytesNeeded = sizeof(buf) - 1; |
| |
| if (!pvStructInfo) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| /* Check the year, if it's in the UTCTime range call that encode func */ |
| if (!FileTimeToSystemTime((const FILETIME *)pvStructInfo, &sysTime)) |
| return FALSE; |
| if (sysTime.wYear >= 1950 && sysTime.wYear <= 2050) |
| return CRYPT_AsnEncodeUtcTime(dwCertEncodingType, lpszStructType, |
| pvStructInfo, dwFlags, pEncodePara, pbEncoded, pcbEncoded); |
| if (!CRYPT_EncodeEnsureSpace(dwFlags, pEncodePara, pbEncoded, pcbEncoded, |
| bytesNeeded)) |
| return FALSE; |
| if (dwFlags & CRYPT_ENCODE_ALLOC_FLAG) |
| pbEncoded = *(BYTE **)pbEncoded; |
| buf[0] = ASN_GENERALTIME; |
| buf[1] = bytesNeeded - 2; |
| snprintf(buf + 2, sizeof(buf) - 2, "%04d%02d%02d%02d%02d%02dZ", |
| sysTime.wYear, sysTime.wDay, sysTime.wMonth, sysTime.wHour, |
| sysTime.wMinute, sysTime.wSecond); |
| memcpy(pbEncoded, buf, bytesNeeded); |
| return TRUE; |
| } |
| |
| typedef BOOL (WINAPI *CryptEncodeObjectExFunc)(DWORD, LPCSTR, const void *, |
| DWORD, PCRYPT_ENCODE_PARA, BYTE *, DWORD *); |
| |
| BOOL WINAPI CryptEncodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType, |
| const void *pvStructInfo, DWORD dwFlags, PCRYPT_ENCODE_PARA pEncodePara, |
| BYTE *pbEncoded, DWORD *pcbEncoded) |
| { |
| BOOL ret = FALSE; |
| HMODULE lib = NULL; |
| CryptEncodeObjectExFunc encodeFunc = NULL; |
| |
| TRACE("(0x%08lx, %s, %p, 0x%08lx, %p, %p, %p): semi-stub\n", |
| dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) : |
| "(integer value)", pvStructInfo, dwFlags, pEncodePara, pbEncoded, |
| pcbEncoded); |
| |
| if (!pbEncoded && !pcbEncoded) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING |
| && (dwCertEncodingType & CMSG_ENCODING_TYPE_MASK) != PKCS_7_ASN_ENCODING) |
| { |
| SetLastError(ERROR_FILE_NOT_FOUND); |
| return FALSE; |
| } |
| |
| if (!HIWORD(lpszStructType)) |
| { |
| switch (LOWORD(lpszStructType)) |
| { |
| case (WORD)X509_INTEGER: |
| encodeFunc = CRYPT_AsnEncodeInt; |
| break; |
| case (WORD)X509_CHOICE_OF_TIME: |
| encodeFunc = CRYPT_AsnEncodeChoiceOfTime; |
| break; |
| case (WORD)PKCS_UTC_TIME: |
| encodeFunc = CRYPT_AsnEncodeUtcTime; |
| break; |
| default: |
| FIXME("%d: unimplemented\n", LOWORD(lpszStructType)); |
| } |
| } |
| else if (!strcmp(lpszStructType, szOID_RSA_signingTime)) |
| encodeFunc = CRYPT_AsnEncodeUtcTime; |
| if (!encodeFunc) |
| encodeFunc = (CryptEncodeObjectExFunc)CRYPT_GetFunc(dwCertEncodingType, |
| lpszStructType, "CryptEncodeObjectEx", &lib); |
| if (encodeFunc) |
| ret = encodeFunc(dwCertEncodingType, lpszStructType, pvStructInfo, |
| dwFlags, pEncodePara, pbEncoded, pcbEncoded); |
| else |
| SetLastError(ERROR_FILE_NOT_FOUND); |
| if (lib) |
| FreeLibrary(lib); |
| return ret; |
| } |
| |
| typedef BOOL (WINAPI *CryptDecodeObjectFunc)(DWORD, LPCSTR, const BYTE *, |
| DWORD, DWORD, void *, DWORD *); |
| |
| BOOL WINAPI CryptDecodeObject(DWORD dwCertEncodingType, LPCSTR lpszStructType, |
| const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, |
| DWORD *pcbStructInfo) |
| { |
| BOOL ret = FALSE; |
| HMODULE lib; |
| CryptDecodeObjectFunc pCryptDecodeObject; |
| |
| TRACE("(0x%08lx, %s, %p, %ld, 0x%08lx, %p, %p)\n", |
| dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) : |
| "(integer value)", pbEncoded, cbEncoded, dwFlags, pvStructInfo, |
| pcbStructInfo); |
| |
| if (!pvStructInfo && !pcbStructInfo) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| |
| /* Try registered DLL first.. */ |
| pCryptDecodeObject = |
| (CryptDecodeObjectFunc)CRYPT_GetFunc(dwCertEncodingType, |
| lpszStructType, "CryptDecodeObject", &lib); |
| if (pCryptDecodeObject) |
| { |
| ret = pCryptDecodeObject(dwCertEncodingType, lpszStructType, |
| pbEncoded, cbEncoded, dwFlags, pvStructInfo, pcbStructInfo); |
| FreeLibrary(lib); |
| } |
| else |
| { |
| /* If not, use CryptDecodeObjectEx */ |
| ret = CryptDecodeObjectEx(dwCertEncodingType, lpszStructType, pbEncoded, |
| cbEncoded, dwFlags, NULL, pvStructInfo, pcbStructInfo); |
| } |
| return ret; |
| } |
| |
| static BOOL CRYPT_DecodeEnsureSpace(DWORD dwFlags, |
| PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo, |
| DWORD bytesNeeded) |
| { |
| if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) |
| { |
| if (pDecodePara && pDecodePara->pfnAlloc) |
| *(BYTE **)pvStructInfo = pDecodePara->pfnAlloc(bytesNeeded); |
| else |
| *(BYTE **)pvStructInfo = LocalAlloc(0, bytesNeeded); |
| if (!*(BYTE **)pvStructInfo) |
| return FALSE; |
| } |
| else if (*pcbStructInfo < bytesNeeded) |
| { |
| *pcbStructInfo = bytesNeeded; |
| SetLastError(ERROR_MORE_DATA); |
| return FALSE; |
| } |
| return TRUE; |
| } |
| |
| static BOOL WINAPI CRYPT_AsnDecodeInt(DWORD dwCertEncodingType, |
| LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, |
| PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) |
| { |
| int val, i; |
| |
| if (!pbEncoded || !cbEncoded) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| if (!pvStructInfo) |
| { |
| *pcbStructInfo = sizeof(int); |
| SetLastError(ERROR_MORE_DATA); |
| return FALSE; |
| } |
| if (pbEncoded[0] != ASN_INTEGER) |
| { |
| SetLastError(CRYPT_E_ASN1_BADTAG); |
| return FALSE; |
| } |
| if (pbEncoded[1] == 0) |
| { |
| SetLastError(CRYPT_E_ASN1_CORRUPT); |
| return FALSE; |
| } |
| if (pbEncoded[1] > sizeof(int)) |
| { |
| SetLastError(CRYPT_E_ASN1_LARGE); |
| return FALSE; |
| } |
| if (pbEncoded[2] & 0x80) |
| { |
| /* initialize to a negative value to sign-extend */ |
| val = -1; |
| } |
| else |
| val = 0; |
| for (i = 0; i < pbEncoded[1]; i++) |
| { |
| val <<= 8; |
| val |= pbEncoded[2 + i]; |
| } |
| if (!CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo, |
| pcbStructInfo, sizeof(int))) |
| return FALSE; |
| if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) |
| pvStructInfo = *(BYTE **)pvStructInfo; |
| *pcbStructInfo = sizeof(int); |
| memcpy(pvStructInfo, &val, sizeof(int)); |
| return TRUE; |
| } |
| |
| #define CRYPT_TIME_GET_DIGITS(pbEncoded, len, numDigits, word) \ |
| do { \ |
| BYTE i; \ |
| \ |
| (word) = 0; \ |
| for (i = 0; (len) > 0 && i < (numDigits); i++, (len)--) \ |
| { \ |
| if (!isdigit(*(pbEncoded))) \ |
| { \ |
| SetLastError(CRYPT_E_ASN1_CORRUPT); \ |
| return FALSE; \ |
| } \ |
| (word) *= 10; \ |
| (word) += *(pbEncoded)++ - '0'; \ |
| } \ |
| } while (0) |
| |
| static BOOL WINAPI CRYPT_AsnDecodeUtcTime(DWORD dwCertEncodingType, |
| LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, |
| PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) |
| { |
| SYSTEMTIME sysTime = { 0 }; |
| BYTE len; |
| |
| if (!pbEncoded || !cbEncoded) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| if (!pvStructInfo) |
| { |
| *pcbStructInfo = sizeof(FILETIME); |
| SetLastError(ERROR_MORE_DATA); |
| return FALSE; |
| } |
| if (pbEncoded[0] != ASN_UTCTIME) |
| { |
| SetLastError(CRYPT_E_ASN1_BADTAG); |
| return FALSE; |
| } |
| len = pbEncoded[1]; |
| /* FIXME: magic # */ |
| if (len < 10) |
| { |
| SetLastError(CRYPT_E_ASN1_CORRUPT); |
| return FALSE; |
| } |
| pbEncoded += 2; |
| CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wYear); |
| if (sysTime.wYear >= 50) |
| sysTime.wYear += 1900; |
| else |
| sysTime.wYear += 2000; |
| CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMonth); |
| CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wDay); |
| CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wHour); |
| CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMinute); |
| if (len > 0) |
| { |
| if (len >= 2 && isdigit(*pbEncoded) && isdigit(*(pbEncoded + 1))) |
| CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wSecond); |
| else if (isdigit(*pbEncoded)) |
| CRYPT_TIME_GET_DIGITS(pbEncoded, len, 1, sysTime.wSecond); |
| if (len > 0) |
| { |
| /* FIXME: get timezone, for now assuming UTC (no adjustment) */ |
| } |
| } |
| |
| if (!CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo, |
| pcbStructInfo, sizeof(FILETIME))) |
| return FALSE; |
| if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) |
| pvStructInfo = *(BYTE **)pvStructInfo; |
| *pcbStructInfo = sizeof(FILETIME); |
| return SystemTimeToFileTime(&sysTime, (FILETIME *)pvStructInfo); |
| } |
| |
| static BOOL WINAPI CRYPT_AsnDecodeChoiceOfTime(DWORD dwCertEncodingType, |
| LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, |
| PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) |
| { |
| SYSTEMTIME sysTime = { 0 }; |
| BYTE len; |
| |
| if (!pbEncoded || !cbEncoded) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| if (!pvStructInfo) |
| { |
| *pcbStructInfo = sizeof(FILETIME); |
| SetLastError(ERROR_MORE_DATA); |
| return FALSE; |
| } |
| if (pbEncoded[0] == ASN_UTCTIME) |
| return CRYPT_AsnDecodeUtcTime(dwCertEncodingType, lpszStructType, |
| pbEncoded, cbEncoded, dwFlags, pDecodePara, pvStructInfo, |
| pcbStructInfo); |
| if (pbEncoded[0] != ASN_GENERALTIME) |
| { |
| SetLastError(CRYPT_E_ASN1_BADTAG); |
| return FALSE; |
| } |
| len = pbEncoded[1]; |
| /* FIXME: magic # */ |
| if (len < 10) |
| { |
| SetLastError(CRYPT_E_ASN1_CORRUPT); |
| return FALSE; |
| } |
| pbEncoded += 2; |
| CRYPT_TIME_GET_DIGITS(pbEncoded, len, 4, sysTime.wYear); |
| CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMonth); |
| CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wDay); |
| CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wHour); |
| if (len > 0) |
| { |
| CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wMinute); |
| if (len > 0) |
| CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, sysTime.wSecond); |
| if (len > 0 && (*pbEncoded == '.' || *pbEncoded == ',')) |
| { |
| BYTE digits; |
| |
| pbEncoded++; |
| len--; |
| digits = min(len, 3); /* workaround macro weirdness */ |
| CRYPT_TIME_GET_DIGITS(pbEncoded, len, digits, |
| sysTime.wMilliseconds); |
| } |
| if (len >= 5 && (*pbEncoded == '+' || *pbEncoded == '-')) |
| { |
| WORD hours, minutes; |
| BYTE sign = *pbEncoded++; |
| |
| len--; |
| CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, hours); |
| if (hours >= 24) |
| return CRYPT_E_ASN1_CORRUPT; |
| CRYPT_TIME_GET_DIGITS(pbEncoded, len, 2, minutes); |
| if (minutes >= 60) |
| return CRYPT_E_ASN1_CORRUPT; |
| if (sign == '+') |
| { |
| sysTime.wHour += hours; |
| sysTime.wMinute += minutes; |
| } |
| else |
| { |
| if (hours > sysTime.wHour) |
| { |
| sysTime.wDay--; |
| sysTime.wHour = 24 - (hours - sysTime.wHour); |
| } |
| else |
| sysTime.wHour -= hours; |
| if (minutes > sysTime.wMinute) |
| { |
| sysTime.wHour--; |
| sysTime.wMinute = 60 - (minutes - sysTime.wMinute); |
| } |
| else |
| sysTime.wMinute -= minutes; |
| } |
| } |
| } |
| |
| if (!CRYPT_DecodeEnsureSpace(dwFlags, pDecodePara, pvStructInfo, |
| pcbStructInfo, sizeof(FILETIME))) |
| return FALSE; |
| if (dwFlags & CRYPT_DECODE_ALLOC_FLAG) |
| pvStructInfo = *(BYTE **)pvStructInfo; |
| *pcbStructInfo = sizeof(FILETIME); |
| return SystemTimeToFileTime(&sysTime, (FILETIME *)pvStructInfo); |
| } |
| |
| typedef BOOL (WINAPI *CryptDecodeObjectExFunc)(DWORD, LPCSTR, const BYTE *, |
| DWORD, DWORD, PCRYPT_DECODE_PARA, void *, DWORD *); |
| |
| BOOL WINAPI CryptDecodeObjectEx(DWORD dwCertEncodingType, LPCSTR lpszStructType, |
| const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags, |
| PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo) |
| { |
| BOOL ret = FALSE; |
| HMODULE lib = NULL; |
| CryptDecodeObjectExFunc decodeFunc = NULL; |
| |
| TRACE("(0x%08lx, %s, %p, %ld, 0x%08lx, %p, %p, %p): semi-stub\n", |
| dwCertEncodingType, HIWORD(lpszStructType) ? debugstr_a(lpszStructType) : |
| "(integer value)", pbEncoded, cbEncoded, dwFlags, pDecodePara, |
| pvStructInfo, pcbStructInfo); |
| |
| if (!pvStructInfo && !pcbStructInfo) |
| { |
| SetLastError(ERROR_INVALID_PARAMETER); |
| return FALSE; |
| } |
| if ((dwCertEncodingType & CERT_ENCODING_TYPE_MASK) != X509_ASN_ENCODING |
| && (dwCertEncodingType & CMSG_ENCODING_TYPE_MASK) != PKCS_7_ASN_ENCODING) |
| { |
| SetLastError(ERROR_FILE_NOT_FOUND); |
| return FALSE; |
| } |
| |
| if (!HIWORD(lpszStructType)) |
| { |
| switch (LOWORD(lpszStructType)) |
| { |
| case (WORD)X509_INTEGER: |
| decodeFunc = CRYPT_AsnDecodeInt; |
| break; |
| case (WORD)X509_CHOICE_OF_TIME: |
| decodeFunc = CRYPT_AsnDecodeChoiceOfTime; |
| break; |
| case (WORD)PKCS_UTC_TIME: |
| decodeFunc = CRYPT_AsnDecodeUtcTime; |
| break; |
| default: |
| FIXME("%d: unimplemented\n", LOWORD(lpszStructType)); |
| } |
| } |
| else if (!strcmp(lpszStructType, szOID_RSA_signingTime)) |
| decodeFunc = CRYPT_AsnDecodeUtcTime; |
| if (!decodeFunc) |
| decodeFunc = (CryptDecodeObjectExFunc)CRYPT_GetFunc(dwCertEncodingType, |
| lpszStructType, "CryptDecodeObjectEx", &lib); |
| if (decodeFunc) |
| ret = decodeFunc(dwCertEncodingType, lpszStructType, pbEncoded, |
| cbEncoded, dwFlags, pDecodePara, pvStructInfo, pcbStructInfo); |
| else |
| SetLastError(ERROR_FILE_NOT_FOUND); |
| if (lib) |
| FreeLibrary(lib); |
| return ret; |
| } |