|  | /* | 
|  | * Copyright (C) 2006 Maarten Lankhorst | 
|  | * Copyright 2007 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | * | 
|  | */ | 
|  |  | 
|  | #include "config.h" | 
|  | #include "wine/port.h" | 
|  |  | 
|  | #define NONAMELESSUNION | 
|  | #define NONAMELESSSTRUCT | 
|  | #define CERT_REVOCATION_PARA_HAS_EXTRA_FIELDS | 
|  |  | 
|  | #include <stdio.h> | 
|  | #include <stdarg.h> | 
|  |  | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "winnt.h" | 
|  | #include "winnls.h" | 
|  | #include "wininet.h" | 
|  | #include "objbase.h" | 
|  | #include "wincrypt.h" | 
|  |  | 
|  | #include "wine/debug.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(cryptnet); | 
|  |  | 
|  | #define IS_INTOID(x)    (((ULONG_PTR)(x) >> 16) == 0) | 
|  |  | 
|  | BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) | 
|  | { | 
|  | TRACE("(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved); | 
|  |  | 
|  | switch (fdwReason) { | 
|  | case DLL_PROCESS_ATTACH: | 
|  | DisableThreadLibraryCalls(hinstDLL); | 
|  | break; | 
|  | case DLL_PROCESS_DETACH: | 
|  | /* Do uninitialisation here */ | 
|  | break; | 
|  | default: break; | 
|  | } | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | static const WCHAR cryptNet[] = { 'c','r','y','p','t','n','e','t','.', | 
|  | 'd','l','l',0 }; | 
|  |  | 
|  | /*********************************************************************** | 
|  | *    DllRegisterServer (CRYPTNET.@) | 
|  | */ | 
|  | HRESULT WINAPI DllRegisterServer(void) | 
|  | { | 
|  | TRACE("\n"); | 
|  | CryptRegisterDefaultOIDFunction(X509_ASN_ENCODING, | 
|  | CRYPT_OID_VERIFY_REVOCATION_FUNC, 0, cryptNet); | 
|  | CryptRegisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC, "Ldap", | 
|  | cryptNet, "LdapProvOpenStore"); | 
|  | CryptRegisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC, | 
|  | CERT_STORE_PROV_LDAP_W, cryptNet, "LdapProvOpenStore"); | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *    DllUnregisterServer (CRYPTNET.@) | 
|  | */ | 
|  | HRESULT WINAPI DllUnregisterServer(void) | 
|  | { | 
|  | TRACE("\n"); | 
|  | CryptUnregisterDefaultOIDFunction(X509_ASN_ENCODING, | 
|  | CRYPT_OID_VERIFY_REVOCATION_FUNC, cryptNet); | 
|  | CryptUnregisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC, "Ldap"); | 
|  | CryptUnregisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC, | 
|  | CERT_STORE_PROV_LDAP_W); | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static const char *url_oid_to_str(LPCSTR oid) | 
|  | { | 
|  | if (IS_INTOID(oid)) | 
|  | { | 
|  | static char buf[10]; | 
|  |  | 
|  | switch (LOWORD(oid)) | 
|  | { | 
|  | #define _x(oid) case LOWORD(oid): return #oid | 
|  | _x(URL_OID_CERTIFICATE_ISSUER); | 
|  | _x(URL_OID_CERTIFICATE_CRL_DIST_POINT); | 
|  | _x(URL_OID_CTL_ISSUER); | 
|  | _x(URL_OID_CTL_NEXT_UPDATE); | 
|  | _x(URL_OID_CRL_ISSUER); | 
|  | _x(URL_OID_CERTIFICATE_FRESHEST_CRL); | 
|  | _x(URL_OID_CRL_FRESHEST_CRL); | 
|  | _x(URL_OID_CROSS_CERT_DIST_POINT); | 
|  | #undef _x | 
|  | default: | 
|  | snprintf(buf, sizeof(buf), "%d", LOWORD(oid)); | 
|  | return buf; | 
|  | } | 
|  | } | 
|  | else | 
|  | return oid; | 
|  | } | 
|  |  | 
|  | typedef BOOL (WINAPI *UrlDllGetObjectUrlFunc)(LPCSTR, LPVOID, DWORD, | 
|  | PCRYPT_URL_ARRAY, DWORD *, PCRYPT_URL_INFO, DWORD *, LPVOID); | 
|  |  | 
|  | static BOOL WINAPI CRYPT_GetUrlFromCertificateIssuer(LPCSTR pszUrlOid, | 
|  | LPVOID pvPara, DWORD dwFlags, PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray, | 
|  | PCRYPT_URL_INFO pUrlInfo, DWORD *pcbUrlInfo, LPVOID pvReserved) | 
|  | { | 
|  | PCCERT_CONTEXT cert = pvPara; | 
|  | PCERT_EXTENSION ext; | 
|  | BOOL ret = FALSE; | 
|  |  | 
|  | /* The only applicable flag is CRYPT_GET_URL_FROM_EXTENSION */ | 
|  | if (dwFlags && !(dwFlags & CRYPT_GET_URL_FROM_EXTENSION)) | 
|  | { | 
|  | SetLastError(CRYPT_E_NOT_FOUND); | 
|  | return FALSE; | 
|  | } | 
|  | if ((ext = CertFindExtension(szOID_AUTHORITY_INFO_ACCESS, | 
|  | cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension))) | 
|  | { | 
|  | CERT_AUTHORITY_INFO_ACCESS *aia; | 
|  | DWORD size; | 
|  |  | 
|  | ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_AUTHORITY_INFO_ACCESS, | 
|  | ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, | 
|  | &aia, &size); | 
|  | if (ret) | 
|  | { | 
|  | DWORD i, cUrl, bytesNeeded = sizeof(CRYPT_URL_ARRAY); | 
|  |  | 
|  | for (i = 0, cUrl = 0; i < aia->cAccDescr; i++) | 
|  | if (!strcmp(aia->rgAccDescr[i].pszAccessMethod, | 
|  | szOID_PKIX_CA_ISSUERS)) | 
|  | { | 
|  | if (aia->rgAccDescr[i].AccessLocation.dwAltNameChoice == | 
|  | CERT_ALT_NAME_URL) | 
|  | { | 
|  | if (aia->rgAccDescr[i].AccessLocation.u.pwszURL) | 
|  | { | 
|  | cUrl++; | 
|  | bytesNeeded += sizeof(LPWSTR) + | 
|  | (lstrlenW(aia->rgAccDescr[i].AccessLocation.u. | 
|  | pwszURL) + 1) * sizeof(WCHAR); | 
|  | } | 
|  | } | 
|  | else | 
|  | FIXME("unsupported alt name type %d\n", | 
|  | aia->rgAccDescr[i].AccessLocation.dwAltNameChoice); | 
|  | } | 
|  | if (!pcbUrlArray) | 
|  | { | 
|  | SetLastError(E_INVALIDARG); | 
|  | ret = FALSE; | 
|  | } | 
|  | else if (!pUrlArray) | 
|  | *pcbUrlArray = bytesNeeded; | 
|  | else if (*pcbUrlArray < bytesNeeded) | 
|  | { | 
|  | SetLastError(ERROR_MORE_DATA); | 
|  | *pcbUrlArray = bytesNeeded; | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | LPWSTR nextUrl; | 
|  |  | 
|  | *pcbUrlArray = bytesNeeded; | 
|  | pUrlArray->cUrl = 0; | 
|  | pUrlArray->rgwszUrl = | 
|  | (LPWSTR *)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY)); | 
|  | nextUrl = (LPWSTR)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY) | 
|  | + cUrl * sizeof(LPWSTR)); | 
|  | for (i = 0; i < aia->cAccDescr; i++) | 
|  | if (!strcmp(aia->rgAccDescr[i].pszAccessMethod, | 
|  | szOID_PKIX_CA_ISSUERS)) | 
|  | { | 
|  | if (aia->rgAccDescr[i].AccessLocation.dwAltNameChoice | 
|  | == CERT_ALT_NAME_URL) | 
|  | { | 
|  | if (aia->rgAccDescr[i].AccessLocation.u.pwszURL) | 
|  | { | 
|  | lstrcpyW(nextUrl, | 
|  | aia->rgAccDescr[i].AccessLocation.u.pwszURL); | 
|  | pUrlArray->rgwszUrl[pUrlArray->cUrl++] = | 
|  | nextUrl; | 
|  | nextUrl += (lstrlenW(nextUrl) + 1); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | if (ret) | 
|  | { | 
|  | if (pcbUrlInfo) | 
|  | { | 
|  | FIXME("url info: stub\n"); | 
|  | if (!pUrlInfo) | 
|  | *pcbUrlInfo = sizeof(CRYPT_URL_INFO); | 
|  | else if (*pcbUrlInfo < sizeof(CRYPT_URL_INFO)) | 
|  | { | 
|  | *pcbUrlInfo = sizeof(CRYPT_URL_INFO); | 
|  | SetLastError(ERROR_MORE_DATA); | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | *pcbUrlInfo = sizeof(CRYPT_URL_INFO); | 
|  | memset(pUrlInfo, 0, sizeof(CRYPT_URL_INFO)); | 
|  | } | 
|  | } | 
|  | } | 
|  | LocalFree(aia); | 
|  | } | 
|  | } | 
|  | else | 
|  | SetLastError(CRYPT_E_NOT_FOUND); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_GetUrlFromCRLDistPointsExt(const CRYPT_DATA_BLOB *value, | 
|  | PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray, PCRYPT_URL_INFO pUrlInfo, | 
|  | DWORD *pcbUrlInfo) | 
|  | { | 
|  | BOOL ret; | 
|  | CRL_DIST_POINTS_INFO *info; | 
|  | DWORD size; | 
|  |  | 
|  | ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CRL_DIST_POINTS, | 
|  | value->pbData, value->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size); | 
|  | if (ret) | 
|  | { | 
|  | DWORD i, cUrl, bytesNeeded = sizeof(CRYPT_URL_ARRAY); | 
|  |  | 
|  | for (i = 0, cUrl = 0; i < info->cDistPoint; i++) | 
|  | if (info->rgDistPoint[i].DistPointName.dwDistPointNameChoice | 
|  | == CRL_DIST_POINT_FULL_NAME) | 
|  | { | 
|  | DWORD j; | 
|  | CERT_ALT_NAME_INFO *name = | 
|  | &info->rgDistPoint[i].DistPointName.u.FullName; | 
|  |  | 
|  | for (j = 0; j < name->cAltEntry; j++) | 
|  | if (name->rgAltEntry[j].dwAltNameChoice == | 
|  | CERT_ALT_NAME_URL) | 
|  | { | 
|  | if (name->rgAltEntry[j].u.pwszURL) | 
|  | { | 
|  | cUrl++; | 
|  | bytesNeeded += sizeof(LPWSTR) + | 
|  | (lstrlenW(name->rgAltEntry[j].u.pwszURL) + 1) | 
|  | * sizeof(WCHAR); | 
|  | } | 
|  | } | 
|  | } | 
|  | if (!pcbUrlArray) | 
|  | { | 
|  | SetLastError(E_INVALIDARG); | 
|  | ret = FALSE; | 
|  | } | 
|  | else if (!pUrlArray) | 
|  | *pcbUrlArray = bytesNeeded; | 
|  | else if (*pcbUrlArray < bytesNeeded) | 
|  | { | 
|  | SetLastError(ERROR_MORE_DATA); | 
|  | *pcbUrlArray = bytesNeeded; | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | LPWSTR nextUrl; | 
|  |  | 
|  | *pcbUrlArray = bytesNeeded; | 
|  | pUrlArray->cUrl = 0; | 
|  | pUrlArray->rgwszUrl = | 
|  | (LPWSTR *)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY)); | 
|  | nextUrl = (LPWSTR)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY) | 
|  | + cUrl * sizeof(LPWSTR)); | 
|  | for (i = 0; i < info->cDistPoint; i++) | 
|  | if (info->rgDistPoint[i].DistPointName.dwDistPointNameChoice | 
|  | == CRL_DIST_POINT_FULL_NAME) | 
|  | { | 
|  | DWORD j; | 
|  | CERT_ALT_NAME_INFO *name = | 
|  | &info->rgDistPoint[i].DistPointName.u.FullName; | 
|  |  | 
|  | for (j = 0; j < name->cAltEntry; j++) | 
|  | if (name->rgAltEntry[j].dwAltNameChoice == | 
|  | CERT_ALT_NAME_URL) | 
|  | { | 
|  | if (name->rgAltEntry[j].u.pwszURL) | 
|  | { | 
|  | lstrcpyW(nextUrl, | 
|  | name->rgAltEntry[j].u.pwszURL); | 
|  | pUrlArray->rgwszUrl[pUrlArray->cUrl++] = | 
|  | nextUrl; | 
|  | nextUrl += | 
|  | (lstrlenW(name->rgAltEntry[j].u.pwszURL) + 1); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | if (ret) | 
|  | { | 
|  | if (pcbUrlInfo) | 
|  | { | 
|  | FIXME("url info: stub\n"); | 
|  | if (!pUrlInfo) | 
|  | *pcbUrlInfo = sizeof(CRYPT_URL_INFO); | 
|  | else if (*pcbUrlInfo < sizeof(CRYPT_URL_INFO)) | 
|  | { | 
|  | *pcbUrlInfo = sizeof(CRYPT_URL_INFO); | 
|  | SetLastError(ERROR_MORE_DATA); | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | *pcbUrlInfo = sizeof(CRYPT_URL_INFO); | 
|  | memset(pUrlInfo, 0, sizeof(CRYPT_URL_INFO)); | 
|  | } | 
|  | } | 
|  | } | 
|  | LocalFree(info); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI CRYPT_GetUrlFromCertificateCRLDistPoint(LPCSTR pszUrlOid, | 
|  | LPVOID pvPara, DWORD dwFlags, PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray, | 
|  | PCRYPT_URL_INFO pUrlInfo, DWORD *pcbUrlInfo, LPVOID pvReserved) | 
|  | { | 
|  | PCCERT_CONTEXT cert = pvPara; | 
|  | PCERT_EXTENSION ext; | 
|  | BOOL ret = FALSE; | 
|  |  | 
|  | /* The only applicable flag is CRYPT_GET_URL_FROM_EXTENSION */ | 
|  | if (dwFlags && !(dwFlags & CRYPT_GET_URL_FROM_EXTENSION)) | 
|  | { | 
|  | SetLastError(CRYPT_E_NOT_FOUND); | 
|  | return FALSE; | 
|  | } | 
|  | if ((ext = CertFindExtension(szOID_CRL_DIST_POINTS, | 
|  | cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension))) | 
|  | ret = CRYPT_GetUrlFromCRLDistPointsExt(&ext->Value, pUrlArray, | 
|  | pcbUrlArray, pUrlInfo, pcbUrlInfo); | 
|  | else | 
|  | SetLastError(CRYPT_E_NOT_FOUND); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *    CryptGetObjectUrl (CRYPTNET.@) | 
|  | */ | 
|  | BOOL WINAPI CryptGetObjectUrl(LPCSTR pszUrlOid, LPVOID pvPara, DWORD dwFlags, | 
|  | PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray, PCRYPT_URL_INFO pUrlInfo, | 
|  | DWORD *pcbUrlInfo, LPVOID pvReserved) | 
|  | { | 
|  | UrlDllGetObjectUrlFunc func = NULL; | 
|  | HCRYPTOIDFUNCADDR hFunc = NULL; | 
|  | BOOL ret = FALSE; | 
|  |  | 
|  | TRACE("(%s, %p, %08x, %p, %p, %p, %p, %p)\n", debugstr_a(pszUrlOid), | 
|  | pvPara, dwFlags, pUrlArray, pcbUrlArray, pUrlInfo, pcbUrlInfo, pvReserved); | 
|  |  | 
|  | if (IS_INTOID(pszUrlOid)) | 
|  | { | 
|  | switch (LOWORD(pszUrlOid)) | 
|  | { | 
|  | case LOWORD(URL_OID_CERTIFICATE_ISSUER): | 
|  | func = CRYPT_GetUrlFromCertificateIssuer; | 
|  | break; | 
|  | case LOWORD(URL_OID_CERTIFICATE_CRL_DIST_POINT): | 
|  | func = CRYPT_GetUrlFromCertificateCRLDistPoint; | 
|  | break; | 
|  | default: | 
|  | FIXME("unimplemented for %s\n", url_oid_to_str(pszUrlOid)); | 
|  | SetLastError(ERROR_FILE_NOT_FOUND); | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | static HCRYPTOIDFUNCSET set = NULL; | 
|  |  | 
|  | if (!set) | 
|  | set = CryptInitOIDFunctionSet(URL_OID_GET_OBJECT_URL_FUNC, 0); | 
|  | CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, pszUrlOid, 0, | 
|  | (void **)&func, &hFunc); | 
|  | } | 
|  | if (func) | 
|  | ret = func(pszUrlOid, pvPara, dwFlags, pUrlArray, pcbUrlArray, | 
|  | pUrlInfo, pcbUrlInfo, pvReserved); | 
|  | if (hFunc) | 
|  | CryptFreeOIDFunctionAddress(hFunc, 0); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *    CryptRetrieveObjectByUrlA (CRYPTNET.@) | 
|  | */ | 
|  | BOOL WINAPI CryptRetrieveObjectByUrlA(LPCSTR pszURL, LPCSTR pszObjectOid, | 
|  | DWORD dwRetrievalFlags, DWORD dwTimeout, LPVOID *ppvObject, | 
|  | HCRYPTASYNC hAsyncRetrieve, PCRYPT_CREDENTIALS pCredentials, LPVOID pvVerify, | 
|  | PCRYPT_RETRIEVE_AUX_INFO pAuxInfo) | 
|  | { | 
|  | BOOL ret = FALSE; | 
|  | int len; | 
|  |  | 
|  | TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p)\n", debugstr_a(pszURL), | 
|  | debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, ppvObject, | 
|  | hAsyncRetrieve, pCredentials, pvVerify, pAuxInfo); | 
|  |  | 
|  | if (!pszURL) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return FALSE; | 
|  | } | 
|  | len = MultiByteToWideChar(CP_ACP, 0, pszURL, -1, NULL, 0); | 
|  | if (len) | 
|  | { | 
|  | LPWSTR url = CryptMemAlloc(len * sizeof(WCHAR)); | 
|  |  | 
|  | if (url) | 
|  | { | 
|  | MultiByteToWideChar(CP_ACP, 0, pszURL, -1, url, len); | 
|  | ret = CryptRetrieveObjectByUrlW(url, pszObjectOid, | 
|  | dwRetrievalFlags, dwTimeout, ppvObject, hAsyncRetrieve, | 
|  | pCredentials, pvVerify, pAuxInfo); | 
|  | CryptMemFree(url); | 
|  | } | 
|  | else | 
|  | SetLastError(ERROR_OUTOFMEMORY); | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static void WINAPI CRYPT_FreeBlob(LPCSTR pszObjectOid, | 
|  | PCRYPT_BLOB_ARRAY pObject, void *pvFreeContext) | 
|  | { | 
|  | DWORD i; | 
|  |  | 
|  | for (i = 0; i < pObject->cBlob; i++) | 
|  | CryptMemFree(pObject->rgBlob[i].pbData); | 
|  | CryptMemFree(pObject->rgBlob); | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_GetObjectFromFile(HANDLE hFile, PCRYPT_BLOB_ARRAY pObject) | 
|  | { | 
|  | BOOL ret; | 
|  | LARGE_INTEGER size; | 
|  |  | 
|  | if ((ret = GetFileSizeEx(hFile, &size))) | 
|  | { | 
|  | if (size.u.HighPart) | 
|  | { | 
|  | WARN("file too big\n"); | 
|  | SetLastError(ERROR_INVALID_DATA); | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | CRYPT_DATA_BLOB blob; | 
|  |  | 
|  | blob.pbData = CryptMemAlloc(size.u.LowPart); | 
|  | if (blob.pbData) | 
|  | { | 
|  | blob.cbData = size.u.LowPart; | 
|  | ret = ReadFile(hFile, blob.pbData, size.u.LowPart, &blob.cbData, | 
|  | NULL); | 
|  | if (ret) | 
|  | { | 
|  | pObject->rgBlob = CryptMemAlloc(sizeof(CRYPT_DATA_BLOB)); | 
|  | if (pObject->rgBlob) | 
|  | { | 
|  | pObject->cBlob = 1; | 
|  | memcpy(pObject->rgBlob, &blob, sizeof(CRYPT_DATA_BLOB)); | 
|  | } | 
|  | else | 
|  | { | 
|  | SetLastError(ERROR_OUTOFMEMORY); | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | if (!ret) | 
|  | CryptMemFree(blob.pbData); | 
|  | } | 
|  | else | 
|  | { | 
|  | SetLastError(ERROR_OUTOFMEMORY); | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_GetObjectFromCache(LPCWSTR pszURL, PCRYPT_BLOB_ARRAY pObject, | 
|  | PCRYPT_RETRIEVE_AUX_INFO pAuxInfo) | 
|  | { | 
|  | BOOL ret = FALSE; | 
|  | INTERNET_CACHE_ENTRY_INFOW *pCacheInfo = NULL; | 
|  | DWORD size = 0; | 
|  |  | 
|  | TRACE("(%s, %p, %p)\n", debugstr_w(pszURL), pObject, pAuxInfo); | 
|  |  | 
|  | ret = GetUrlCacheEntryInfoW(pszURL, NULL, &size); | 
|  | if (!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER) | 
|  | { | 
|  | pCacheInfo = CryptMemAlloc(size); | 
|  | if (pCacheInfo) | 
|  | ret = TRUE; | 
|  | else | 
|  | SetLastError(ERROR_OUTOFMEMORY); | 
|  | } | 
|  | if (ret && (ret = GetUrlCacheEntryInfoW(pszURL, pCacheInfo, &size))) | 
|  | { | 
|  | FILETIME ft; | 
|  |  | 
|  | GetSystemTimeAsFileTime(&ft); | 
|  | if (CompareFileTime(&pCacheInfo->ExpireTime, &ft) >= 0) | 
|  | { | 
|  | HANDLE hFile = CreateFileW(pCacheInfo->lpszLocalFileName, | 
|  | GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | 
|  |  | 
|  | if (hFile != INVALID_HANDLE_VALUE) | 
|  | { | 
|  | if ((ret = CRYPT_GetObjectFromFile(hFile, pObject))) | 
|  | { | 
|  | if (pAuxInfo && pAuxInfo->cbSize >= | 
|  | offsetof(CRYPT_RETRIEVE_AUX_INFO, | 
|  | pLastSyncTime) + sizeof(PFILETIME) && | 
|  | pAuxInfo->pLastSyncTime) | 
|  | memcpy(pAuxInfo->pLastSyncTime, | 
|  | &pCacheInfo->LastSyncTime, | 
|  | sizeof(FILETIME)); | 
|  | } | 
|  | CloseHandle(hFile); | 
|  | } | 
|  | else | 
|  | { | 
|  | DeleteUrlCacheEntryW(pszURL); | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | DeleteUrlCacheEntryW(pszURL); | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | CryptMemFree(pCacheInfo); | 
|  | TRACE("returning %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Parses the URL, and sets components' lpszHostName and lpszUrlPath members | 
|  | * to NULL-terminated copies of those portions of the URL (to be freed with | 
|  | * CryptMemFree.) | 
|  | */ | 
|  | static BOOL CRYPT_CrackUrl(LPCWSTR pszURL, URL_COMPONENTSW *components) | 
|  | { | 
|  | BOOL ret; | 
|  |  | 
|  | TRACE("(%s, %p)\n", debugstr_w(pszURL), components); | 
|  |  | 
|  | memset(components, 0, sizeof(*components)); | 
|  | components->dwStructSize = sizeof(*components); | 
|  | components->lpszHostName = CryptMemAlloc(INTERNET_MAX_HOST_NAME_LENGTH * sizeof(WCHAR)); | 
|  | components->dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH; | 
|  | if (!components->lpszHostName) | 
|  | { | 
|  | SetLastError(ERROR_OUTOFMEMORY); | 
|  | return FALSE; | 
|  | } | 
|  | components->lpszUrlPath = CryptMemAlloc(INTERNET_MAX_PATH_LENGTH * sizeof(WCHAR)); | 
|  | components->dwUrlPathLength = INTERNET_MAX_PATH_LENGTH; | 
|  | if (!components->lpszUrlPath) | 
|  | { | 
|  | CryptMemFree(components->lpszHostName); | 
|  | SetLastError(ERROR_OUTOFMEMORY); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | ret = InternetCrackUrlW(pszURL, 0, ICU_DECODE, components); | 
|  | if (ret) | 
|  | { | 
|  | switch (components->nScheme) | 
|  | { | 
|  | case INTERNET_SCHEME_FTP: | 
|  | if (!components->nPort) | 
|  | components->nPort = INTERNET_DEFAULT_FTP_PORT; | 
|  | break; | 
|  | case INTERNET_SCHEME_HTTP: | 
|  | if (!components->nPort) | 
|  | components->nPort = INTERNET_DEFAULT_HTTP_PORT; | 
|  | break; | 
|  | default: | 
|  | ; /* do nothing */ | 
|  | } | 
|  | } | 
|  | TRACE("returning %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | struct InetContext | 
|  | { | 
|  | HANDLE event; | 
|  | DWORD  timeout; | 
|  | DWORD  error; | 
|  | }; | 
|  |  | 
|  | static struct InetContext *CRYPT_MakeInetContext(DWORD dwTimeout) | 
|  | { | 
|  | struct InetContext *context = CryptMemAlloc(sizeof(struct InetContext)); | 
|  |  | 
|  | if (context) | 
|  | { | 
|  | context->event = CreateEventW(NULL, FALSE, FALSE, NULL); | 
|  | if (!context->event) | 
|  | { | 
|  | CryptMemFree(context); | 
|  | context = NULL; | 
|  | } | 
|  | else | 
|  | { | 
|  | context->timeout = dwTimeout; | 
|  | context->error = ERROR_SUCCESS; | 
|  | } | 
|  | } | 
|  | return context; | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_DownloadObject(DWORD dwRetrievalFlags, HINTERNET hHttp, | 
|  | struct InetContext *context, PCRYPT_BLOB_ARRAY pObject, | 
|  | PCRYPT_RETRIEVE_AUX_INFO pAuxInfo) | 
|  | { | 
|  | CRYPT_DATA_BLOB object = { 0, NULL }; | 
|  | DWORD bytesAvailable; | 
|  | BOOL ret; | 
|  |  | 
|  | do { | 
|  | if ((ret = InternetQueryDataAvailable(hHttp, &bytesAvailable, 0, 0))) | 
|  | { | 
|  | if (bytesAvailable) | 
|  | { | 
|  | if (object.pbData) | 
|  | object.pbData = CryptMemRealloc(object.pbData, | 
|  | object.cbData + bytesAvailable); | 
|  | else | 
|  | object.pbData = CryptMemAlloc(bytesAvailable); | 
|  | if (object.pbData) | 
|  | { | 
|  | INTERNET_BUFFERSA buffer = { sizeof(buffer), 0 }; | 
|  |  | 
|  | buffer.dwBufferLength = bytesAvailable; | 
|  | buffer.lpvBuffer = object.pbData + object.cbData; | 
|  | if (!(ret = InternetReadFileExA(hHttp, &buffer, IRF_NO_WAIT, | 
|  | (DWORD_PTR)context))) | 
|  | { | 
|  | if (GetLastError() == ERROR_IO_PENDING) | 
|  | { | 
|  | if (WaitForSingleObject(context->event, | 
|  | context->timeout) == WAIT_TIMEOUT) | 
|  | SetLastError(ERROR_TIMEOUT); | 
|  | else if (context->error) | 
|  | SetLastError(context->error); | 
|  | else | 
|  | ret = TRUE; | 
|  | } | 
|  | } | 
|  | if (ret) | 
|  | object.cbData += buffer.dwBufferLength; | 
|  | } | 
|  | else | 
|  | { | 
|  | SetLastError(ERROR_OUTOFMEMORY); | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | } | 
|  | else if (GetLastError() == ERROR_IO_PENDING) | 
|  | { | 
|  | if (WaitForSingleObject(context->event, context->timeout) == | 
|  | WAIT_TIMEOUT) | 
|  | SetLastError(ERROR_TIMEOUT); | 
|  | else | 
|  | ret = TRUE; | 
|  | } | 
|  | } while (ret && bytesAvailable); | 
|  | if (ret) | 
|  | { | 
|  | pObject->rgBlob = CryptMemAlloc(sizeof(CRYPT_DATA_BLOB)); | 
|  | if (!pObject->rgBlob) | 
|  | { | 
|  | CryptMemFree(object.pbData); | 
|  | SetLastError(ERROR_OUTOFMEMORY); | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | pObject->rgBlob[0].cbData = object.cbData; | 
|  | pObject->rgBlob[0].pbData = object.pbData; | 
|  | pObject->cBlob = 1; | 
|  | } | 
|  | } | 
|  | TRACE("returning %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* Finds the object specified by pszURL in the cache.  If it's not found, | 
|  | * creates a new cache entry for the object and writes the object to it. | 
|  | * Sets the expiration time of the cache entry to expires. | 
|  | */ | 
|  | static void CRYPT_CacheURL(LPCWSTR pszURL, const CRYPT_BLOB_ARRAY *pObject, | 
|  | DWORD dwRetrievalFlags, FILETIME expires) | 
|  | { | 
|  | WCHAR cacheFileName[MAX_PATH]; | 
|  | DWORD size = 0; | 
|  | BOOL ret, create = FALSE; | 
|  |  | 
|  | GetUrlCacheEntryInfoW(pszURL, NULL, &size); | 
|  | if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) | 
|  | { | 
|  | INTERNET_CACHE_ENTRY_INFOW *info = CryptMemAlloc(size); | 
|  |  | 
|  | if (info) | 
|  | { | 
|  | FILETIME ft; | 
|  |  | 
|  | ret = GetUrlCacheEntryInfoW(pszURL, info, &size); | 
|  | if (ret) | 
|  | lstrcpyW(cacheFileName, info->lpszLocalFileName); | 
|  | /* Check if the existing cache entry is up to date.  If it isn't, | 
|  | * remove the existing cache entry, and create a new one with the | 
|  | * new value. | 
|  | */ | 
|  | GetSystemTimeAsFileTime(&ft); | 
|  | if (CompareFileTime(&info->ExpireTime, &ft) < 0) | 
|  | { | 
|  | create = TRUE; | 
|  | DeleteUrlCacheEntryW(pszURL); | 
|  | } | 
|  | CryptMemFree(info); | 
|  | } | 
|  | else | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | ret = CreateUrlCacheEntryW(pszURL, pObject->rgBlob[0].cbData, NULL, | 
|  | cacheFileName, 0); | 
|  | create = TRUE; | 
|  | } | 
|  | if (ret) | 
|  | { | 
|  | DWORD entryType; | 
|  | FILETIME ft = { 0 }; | 
|  |  | 
|  | if (create) | 
|  | { | 
|  | HANDLE hCacheFile = CreateFileW(cacheFileName, GENERIC_WRITE, 0, | 
|  | NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); | 
|  |  | 
|  | if (hCacheFile != INVALID_HANDLE_VALUE) | 
|  | { | 
|  | DWORD bytesWritten; | 
|  |  | 
|  | WriteFile(hCacheFile, pObject->rgBlob[0].pbData, | 
|  | pObject->rgBlob[0].cbData, &bytesWritten, NULL); | 
|  | CloseHandle(hCacheFile); | 
|  | } | 
|  | else | 
|  | ret = FALSE; | 
|  | } | 
|  | if (ret) | 
|  | { | 
|  | if (!(dwRetrievalFlags & CRYPT_STICKY_CACHE_RETRIEVAL)) | 
|  | entryType = NORMAL_CACHE_ENTRY; | 
|  | else | 
|  | entryType = STICKY_CACHE_ENTRY; | 
|  | CommitUrlCacheEntryW(pszURL, cacheFileName, expires, ft, entryType, | 
|  | NULL, 0, NULL, NULL); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | static void CALLBACK CRYPT_InetStatusCallback(HINTERNET hInt, | 
|  | DWORD_PTR dwContext, DWORD status, void *statusInfo, DWORD statusInfoLen) | 
|  | { | 
|  | struct InetContext *context = (struct InetContext *)dwContext; | 
|  | LPINTERNET_ASYNC_RESULT result; | 
|  |  | 
|  | switch (status) | 
|  | { | 
|  | case INTERNET_STATUS_REQUEST_COMPLETE: | 
|  | result = statusInfo; | 
|  | context->error = result->dwError; | 
|  | SetEvent(context->event); | 
|  | } | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_Connect(const URL_COMPONENTSW *components, | 
|  | struct InetContext *context, PCRYPT_CREDENTIALS pCredentials, | 
|  | HINTERNET *phInt, HINTERNET *phHost) | 
|  | { | 
|  | BOOL ret; | 
|  |  | 
|  | TRACE("(%s:%d, %p, %p, %p, %p)\n", debugstr_w(components->lpszHostName), | 
|  | components->nPort, context, pCredentials, phInt, phInt); | 
|  |  | 
|  | *phHost = NULL; | 
|  | *phInt = InternetOpenW(NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, | 
|  | context ? INTERNET_FLAG_ASYNC : 0); | 
|  | if (*phInt) | 
|  | { | 
|  | DWORD service; | 
|  |  | 
|  | if (context) | 
|  | InternetSetStatusCallbackW(*phInt, CRYPT_InetStatusCallback); | 
|  | switch (components->nScheme) | 
|  | { | 
|  | case INTERNET_SCHEME_FTP: | 
|  | service = INTERNET_SERVICE_FTP; | 
|  | break; | 
|  | case INTERNET_SCHEME_HTTP: | 
|  | service = INTERNET_SERVICE_HTTP; | 
|  | break; | 
|  | default: | 
|  | service = 0; | 
|  | } | 
|  | /* FIXME: use pCredentials for username/password */ | 
|  | *phHost = InternetConnectW(*phInt, components->lpszHostName, | 
|  | components->nPort, NULL, NULL, service, 0, (DWORD_PTR)context); | 
|  | if (!*phHost) | 
|  | { | 
|  | InternetCloseHandle(*phInt); | 
|  | *phInt = NULL; | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | ret = TRUE; | 
|  | } | 
|  | else | 
|  | ret = FALSE; | 
|  | TRACE("returning %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI FTP_RetrieveEncodedObjectW(LPCWSTR pszURL, | 
|  | LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout, | 
|  | PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject, | 
|  | void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve, | 
|  | PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo) | 
|  | { | 
|  | FIXME("(%s, %s, %08x, %d, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL), | 
|  | debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject, | 
|  | ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo); | 
|  |  | 
|  | pObject->cBlob = 0; | 
|  | pObject->rgBlob = NULL; | 
|  | *ppfnFreeObject = CRYPT_FreeBlob; | 
|  | *ppvFreeContext = NULL; | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | static const WCHAR x509cacert[] = { 'a','p','p','l','i','c','a','t','i','o','n', | 
|  | '/','x','-','x','5','0','9','-','c','a','-','c','e','r','t',0 }; | 
|  | static const WCHAR x509emailcert[] = { 'a','p','p','l','i','c','a','t','i','o', | 
|  | 'n','/','x','-','x','5','0','9','-','e','m','a','i','l','-','c','e','r','t', | 
|  | 0 }; | 
|  | static const WCHAR x509servercert[] = { 'a','p','p','l','i','c','a','t','i','o', | 
|  | 'n','/','x','-','x','5','0','9','-','s','e','r','v','e','r','-','c','e','r', | 
|  | 't',0 }; | 
|  | static const WCHAR x509usercert[] = { 'a','p','p','l','i','c','a','t','i','o', | 
|  | 'n','/','x','-','x','5','0','9','-','u','s','e','r','-','c','e','r','t',0 }; | 
|  | static const WCHAR pkcs7cert[] = { 'a','p','p','l','i','c','a','t','i','o','n', | 
|  | '/','x','-','p','k','c','s','7','-','c','e','r','t','i','f','c','a','t','e', | 
|  | 's',0 }; | 
|  | static const WCHAR pkixCRL[] = { 'a','p','p','l','i','c','a','t','i','o','n', | 
|  | '/','p','k','i','x','-','c','r','l',0 }; | 
|  | static const WCHAR pkcs7CRL[] = { 'a','p','p','l','i','c','a','t','i','o','n', | 
|  | '/','x','-','p','k','c','s','7','-','c','r','l',0 }; | 
|  | static const WCHAR pkcs7sig[] = { 'a','p','p','l','i','c','a','t','i','o','n', | 
|  | '/','x','-','p','k','c','s','7','-','s','i','g','n','a','t','u','r','e',0 }; | 
|  | static const WCHAR pkcs7mime[] = { 'a','p','p','l','i','c','a','t','i','o','n', | 
|  | '/','x','-','p','k','c','s','7','-','m','i','m','e',0 }; | 
|  |  | 
|  | static BOOL WINAPI HTTP_RetrieveEncodedObjectW(LPCWSTR pszURL, | 
|  | LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout, | 
|  | PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject, | 
|  | void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve, | 
|  | PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo) | 
|  | { | 
|  | BOOL ret = FALSE; | 
|  |  | 
|  | TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL), | 
|  | debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject, | 
|  | ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo); | 
|  |  | 
|  | pObject->cBlob = 0; | 
|  | pObject->rgBlob = NULL; | 
|  | *ppfnFreeObject = CRYPT_FreeBlob; | 
|  | *ppvFreeContext = NULL; | 
|  |  | 
|  | if (!(dwRetrievalFlags & CRYPT_WIRE_ONLY_RETRIEVAL)) | 
|  | ret = CRYPT_GetObjectFromCache(pszURL, pObject, pAuxInfo); | 
|  | if (!ret && (!(dwRetrievalFlags & CRYPT_CACHE_ONLY_RETRIEVAL) || | 
|  | (dwRetrievalFlags & CRYPT_WIRE_ONLY_RETRIEVAL))) | 
|  | { | 
|  | URL_COMPONENTSW components; | 
|  |  | 
|  | if ((ret = CRYPT_CrackUrl(pszURL, &components))) | 
|  | { | 
|  | HINTERNET hInt, hHost; | 
|  | struct InetContext *context = NULL; | 
|  |  | 
|  | if (dwTimeout) | 
|  | context = CRYPT_MakeInetContext(dwTimeout); | 
|  | ret = CRYPT_Connect(&components, context, pCredentials, &hInt, | 
|  | &hHost); | 
|  | if (ret) | 
|  | { | 
|  | static LPCWSTR types[] = { x509cacert, x509emailcert, | 
|  | x509servercert, x509usercert, pkcs7cert, pkixCRL, pkcs7CRL, | 
|  | pkcs7sig, pkcs7mime, NULL }; | 
|  | HINTERNET hHttp = HttpOpenRequestW(hHost, NULL, | 
|  | components.lpszUrlPath, NULL, NULL, types, | 
|  | INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_UI, | 
|  | (DWORD_PTR)context); | 
|  |  | 
|  | if (hHttp) | 
|  | { | 
|  | if (dwTimeout) | 
|  | { | 
|  | InternetSetOptionW(hHttp, | 
|  | INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeout, | 
|  | sizeof(dwTimeout)); | 
|  | InternetSetOptionW(hHttp, INTERNET_OPTION_SEND_TIMEOUT, | 
|  | &dwTimeout, sizeof(dwTimeout)); | 
|  | } | 
|  | ret = HttpSendRequestExW(hHttp, NULL, NULL, 0, | 
|  | (DWORD_PTR)context); | 
|  | if (!ret && GetLastError() == ERROR_IO_PENDING) | 
|  | { | 
|  | if (WaitForSingleObject(context->event, | 
|  | context->timeout) == WAIT_TIMEOUT) | 
|  | SetLastError(ERROR_TIMEOUT); | 
|  | else | 
|  | ret = TRUE; | 
|  | } | 
|  | /* We don't set ret to TRUE in this block to avoid masking | 
|  | * an error from HttpSendRequestExW. | 
|  | */ | 
|  | if (!HttpEndRequestW(hHttp, NULL, 0, (DWORD_PTR)context) && | 
|  | GetLastError() == ERROR_IO_PENDING) | 
|  | { | 
|  | if (WaitForSingleObject(context->event, | 
|  | context->timeout) == WAIT_TIMEOUT) | 
|  | { | 
|  | SetLastError(ERROR_TIMEOUT); | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | if (ret) | 
|  | ret = CRYPT_DownloadObject(dwRetrievalFlags, hHttp, | 
|  | context, pObject, pAuxInfo); | 
|  | if (ret && !(dwRetrievalFlags & CRYPT_DONT_CACHE_RESULT)) | 
|  | { | 
|  | SYSTEMTIME st; | 
|  | DWORD len = sizeof(st); | 
|  |  | 
|  | if (HttpQueryInfoW(hHttp, | 
|  | HTTP_QUERY_EXPIRES | HTTP_QUERY_FLAG_SYSTEMTIME, &st, | 
|  | &len, NULL)) | 
|  | { | 
|  | FILETIME ft; | 
|  |  | 
|  | SystemTimeToFileTime(&st, &ft); | 
|  | CRYPT_CacheURL(pszURL, pObject, dwRetrievalFlags, | 
|  | ft); | 
|  | } | 
|  | } | 
|  | InternetCloseHandle(hHttp); | 
|  | } | 
|  | InternetCloseHandle(hHost); | 
|  | InternetCloseHandle(hInt); | 
|  | } | 
|  | if (context) | 
|  | { | 
|  | CloseHandle(context->event); | 
|  | CryptMemFree(context); | 
|  | } | 
|  | CryptMemFree(components.lpszUrlPath); | 
|  | CryptMemFree(components.lpszHostName); | 
|  | } | 
|  | } | 
|  | TRACE("returning %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI File_RetrieveEncodedObjectW(LPCWSTR pszURL, | 
|  | LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout, | 
|  | PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject, | 
|  | void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve, | 
|  | PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo) | 
|  | { | 
|  | URL_COMPONENTSW components = { sizeof(components), 0 }; | 
|  | BOOL ret; | 
|  |  | 
|  | TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL), | 
|  | debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject, | 
|  | ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo); | 
|  |  | 
|  | pObject->cBlob = 0; | 
|  | pObject->rgBlob = NULL; | 
|  | *ppfnFreeObject = CRYPT_FreeBlob; | 
|  | *ppvFreeContext = NULL; | 
|  |  | 
|  | components.lpszUrlPath = CryptMemAlloc(INTERNET_MAX_PATH_LENGTH * sizeof(WCHAR)); | 
|  | components.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH; | 
|  | if (!components.lpszUrlPath) | 
|  | { | 
|  | SetLastError(ERROR_OUTOFMEMORY); | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | ret = InternetCrackUrlW(pszURL, 0, ICU_DECODE, &components); | 
|  | if (ret) | 
|  | { | 
|  | LPWSTR path; | 
|  |  | 
|  | /* 3 == lstrlenW(L"c:") + 1 */ | 
|  | path = CryptMemAlloc((components.dwUrlPathLength + 3) * sizeof(WCHAR)); | 
|  | if (path) | 
|  | { | 
|  | HANDLE hFile; | 
|  |  | 
|  | /* Try to create the file directly - Wine handles / in pathnames */ | 
|  | lstrcpynW(path, components.lpszUrlPath, | 
|  | components.dwUrlPathLength + 1); | 
|  | hFile = CreateFileW(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, | 
|  | FILE_ATTRIBUTE_NORMAL, NULL); | 
|  | if (hFile == INVALID_HANDLE_VALUE) | 
|  | { | 
|  | /* Try again on the current drive */ | 
|  | GetCurrentDirectoryW(components.dwUrlPathLength, path); | 
|  | if (path[1] == ':') | 
|  | { | 
|  | lstrcpynW(path + 2, components.lpszUrlPath, | 
|  | components.dwUrlPathLength + 1); | 
|  | hFile = CreateFileW(path, GENERIC_READ, 0, NULL, | 
|  | OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | 
|  | } | 
|  | if (hFile == INVALID_HANDLE_VALUE) | 
|  | { | 
|  | /* Try again on the Windows drive */ | 
|  | GetWindowsDirectoryW(path, components.dwUrlPathLength); | 
|  | if (path[1] == ':') | 
|  | { | 
|  | lstrcpynW(path + 2, components.lpszUrlPath, | 
|  | components.dwUrlPathLength + 1); | 
|  | hFile = CreateFileW(path, GENERIC_READ, 0, NULL, | 
|  | OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); | 
|  | } | 
|  | } | 
|  | } | 
|  | if (hFile != INVALID_HANDLE_VALUE) | 
|  | { | 
|  | if ((ret = CRYPT_GetObjectFromFile(hFile, pObject))) | 
|  | { | 
|  | if (pAuxInfo && pAuxInfo->cbSize >= | 
|  | offsetof(CRYPT_RETRIEVE_AUX_INFO, | 
|  | pLastSyncTime) + sizeof(PFILETIME) && | 
|  | pAuxInfo->pLastSyncTime) | 
|  | GetFileTime(hFile, NULL, NULL, | 
|  | pAuxInfo->pLastSyncTime); | 
|  | } | 
|  | CloseHandle(hFile); | 
|  | } | 
|  | else | 
|  | ret = FALSE; | 
|  | CryptMemFree(path); | 
|  | } | 
|  | else | 
|  | { | 
|  | SetLastError(ERROR_OUTOFMEMORY); | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | CryptMemFree(components.lpszUrlPath); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | typedef BOOL (WINAPI *SchemeDllRetrieveEncodedObjectW)(LPCWSTR pwszUrl, | 
|  | LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout, | 
|  | PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject, | 
|  | void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve, | 
|  | PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo); | 
|  |  | 
|  | static BOOL CRYPT_GetRetrieveFunction(LPCWSTR pszURL, | 
|  | SchemeDllRetrieveEncodedObjectW *pFunc, HCRYPTOIDFUNCADDR *phFunc) | 
|  | { | 
|  | URL_COMPONENTSW components = { sizeof(components), 0 }; | 
|  | BOOL ret; | 
|  |  | 
|  | TRACE("(%s, %p, %p)\n", debugstr_w(pszURL), pFunc, phFunc); | 
|  |  | 
|  | *pFunc = NULL; | 
|  | *phFunc = 0; | 
|  | components.dwSchemeLength = 1; | 
|  | ret = InternetCrackUrlW(pszURL, 0, 0, &components); | 
|  | if (ret) | 
|  | { | 
|  | /* Microsoft always uses CryptInitOIDFunctionSet/ | 
|  | * CryptGetOIDFunctionAddress, but there doesn't seem to be a pressing | 
|  | * reason to do so for builtin schemes. | 
|  | */ | 
|  | switch (components.nScheme) | 
|  | { | 
|  | case INTERNET_SCHEME_FTP: | 
|  | *pFunc = FTP_RetrieveEncodedObjectW; | 
|  | break; | 
|  | case INTERNET_SCHEME_HTTP: | 
|  | *pFunc = HTTP_RetrieveEncodedObjectW; | 
|  | break; | 
|  | case INTERNET_SCHEME_FILE: | 
|  | *pFunc = File_RetrieveEncodedObjectW; | 
|  | break; | 
|  | default: | 
|  | { | 
|  | int len = WideCharToMultiByte(CP_ACP, 0, components.lpszScheme, | 
|  | components.dwSchemeLength, NULL, 0, NULL, NULL); | 
|  |  | 
|  | if (len) | 
|  | { | 
|  | LPSTR scheme = CryptMemAlloc(len); | 
|  |  | 
|  | if (scheme) | 
|  | { | 
|  | static HCRYPTOIDFUNCSET set = NULL; | 
|  |  | 
|  | if (!set) | 
|  | set = CryptInitOIDFunctionSet( | 
|  | SCHEME_OID_RETRIEVE_ENCODED_OBJECTW_FUNC, 0); | 
|  | WideCharToMultiByte(CP_ACP, 0, components.lpszScheme, | 
|  | components.dwSchemeLength, scheme, len, NULL, NULL); | 
|  | ret = CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, | 
|  | scheme, 0, (void **)pFunc, phFunc); | 
|  | CryptMemFree(scheme); | 
|  | } | 
|  | else | 
|  | { | 
|  | SetLastError(ERROR_OUTOFMEMORY); | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | else | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | } | 
|  | TRACE("returning %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI CRYPT_CreateBlob(LPCSTR pszObjectOid, | 
|  | DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext) | 
|  | { | 
|  | DWORD size, i; | 
|  | CRYPT_BLOB_ARRAY *context; | 
|  | BOOL ret = FALSE; | 
|  |  | 
|  | size = sizeof(CRYPT_BLOB_ARRAY) + pObject->cBlob * sizeof(CRYPT_DATA_BLOB); | 
|  | for (i = 0; i < pObject->cBlob; i++) | 
|  | size += pObject->rgBlob[i].cbData; | 
|  | context = CryptMemAlloc(size); | 
|  | if (context) | 
|  | { | 
|  | LPBYTE nextData; | 
|  |  | 
|  | context->cBlob = 0; | 
|  | context->rgBlob = | 
|  | (CRYPT_DATA_BLOB *)((LPBYTE)context + sizeof(CRYPT_BLOB_ARRAY)); | 
|  | nextData = | 
|  | (LPBYTE)context->rgBlob + pObject->cBlob * sizeof(CRYPT_DATA_BLOB); | 
|  | for (i = 0; i < pObject->cBlob; i++) | 
|  | { | 
|  | memcpy(nextData, pObject->rgBlob[i].pbData, | 
|  | pObject->rgBlob[i].cbData); | 
|  | context->rgBlob[i].pbData = nextData; | 
|  | context->rgBlob[i].cbData = pObject->rgBlob[i].cbData; | 
|  | nextData += pObject->rgBlob[i].cbData; | 
|  | context->cBlob++; | 
|  | } | 
|  | *ppvContext = context; | 
|  | ret = TRUE; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | typedef BOOL (WINAPI *AddContextToStore)(HCERTSTORE hCertStore, | 
|  | const void *pContext, DWORD dwAddDisposition, const void **ppStoreContext); | 
|  |  | 
|  | static BOOL CRYPT_CreateContext(const CRYPT_BLOB_ARRAY *pObject, | 
|  | DWORD dwExpectedContentTypeFlags, AddContextToStore addFunc, void **ppvContext) | 
|  | { | 
|  | BOOL ret = TRUE; | 
|  |  | 
|  | if (!pObject->cBlob) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_DATA); | 
|  | *ppvContext = NULL; | 
|  | ret = FALSE; | 
|  | } | 
|  | else if (pObject->cBlob == 1) | 
|  | { | 
|  | if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &pObject->rgBlob[0], | 
|  | dwExpectedContentTypeFlags, CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, | 
|  | NULL, NULL, NULL, NULL, (const void **)ppvContext)) | 
|  | { | 
|  | SetLastError(CRYPT_E_NO_MATCH); | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, | 
|  | CERT_STORE_CREATE_NEW_FLAG, NULL); | 
|  |  | 
|  | if (store) | 
|  | { | 
|  | DWORD i; | 
|  | const void *context; | 
|  |  | 
|  | for (i = 0; i < pObject->cBlob; i++) | 
|  | { | 
|  | if (CryptQueryObject(CERT_QUERY_OBJECT_BLOB, | 
|  | &pObject->rgBlob[i], dwExpectedContentTypeFlags, | 
|  | CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL, NULL, | 
|  | NULL, &context)) | 
|  | { | 
|  | if (!addFunc(store, context, CERT_STORE_ADD_ALWAYS, NULL)) | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | SetLastError(CRYPT_E_NO_MATCH); | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | } | 
|  | else | 
|  | ret = FALSE; | 
|  | *ppvContext = store; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI CRYPT_CreateCert(LPCSTR pszObjectOid, | 
|  | DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext) | 
|  | { | 
|  | return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CERT, | 
|  | (AddContextToStore)CertAddCertificateContextToStore, ppvContext); | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI CRYPT_CreateCRL(LPCSTR pszObjectOid, | 
|  | DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext) | 
|  | { | 
|  | return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CRL, | 
|  | (AddContextToStore)CertAddCRLContextToStore, ppvContext); | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI CRYPT_CreateCTL(LPCSTR pszObjectOid, | 
|  | DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext) | 
|  | { | 
|  | return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CTL, | 
|  | (AddContextToStore)CertAddCTLContextToStore, ppvContext); | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI CRYPT_CreatePKCS7(LPCSTR pszObjectOid, | 
|  | DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext) | 
|  | { | 
|  | BOOL ret; | 
|  |  | 
|  | if (!pObject->cBlob) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_DATA); | 
|  | *ppvContext = NULL; | 
|  | ret = FALSE; | 
|  | } | 
|  | else if (pObject->cBlob == 1) | 
|  | ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &pObject->rgBlob[0], | 
|  | CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED | | 
|  | CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED, CERT_QUERY_FORMAT_FLAG_BINARY, | 
|  | 0, NULL, NULL, NULL, ppvContext, NULL, NULL); | 
|  | else | 
|  | { | 
|  | FIXME("multiple messages unimplemented\n"); | 
|  | ret = FALSE; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static BOOL WINAPI CRYPT_CreateAny(LPCSTR pszObjectOid, | 
|  | DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext) | 
|  | { | 
|  | BOOL ret; | 
|  |  | 
|  | if (!pObject->cBlob) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_DATA); | 
|  | *ppvContext = NULL; | 
|  | ret = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0, | 
|  | CERT_STORE_CREATE_NEW_FLAG, NULL); | 
|  |  | 
|  | if (store) | 
|  | { | 
|  | HCERTSTORE memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, | 
|  | CERT_STORE_CREATE_NEW_FLAG, NULL); | 
|  |  | 
|  | if (memStore) | 
|  | { | 
|  | CertAddStoreToCollection(store, memStore, | 
|  | CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 0); | 
|  | CertCloseStore(memStore, 0); | 
|  | } | 
|  | else | 
|  | { | 
|  | CertCloseStore(store, 0); | 
|  | store = NULL; | 
|  | } | 
|  | } | 
|  | if (store) | 
|  | { | 
|  | DWORD i; | 
|  |  | 
|  | ret = TRUE; | 
|  | for (i = 0; i < pObject->cBlob; i++) | 
|  | { | 
|  | DWORD contentType, expectedContentTypes = | 
|  | CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED | | 
|  | CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED | | 
|  | CERT_QUERY_CONTENT_FLAG_CERT | | 
|  | CERT_QUERY_CONTENT_FLAG_CRL | | 
|  | CERT_QUERY_CONTENT_FLAG_CTL; | 
|  | HCERTSTORE contextStore; | 
|  | const void *context; | 
|  |  | 
|  | if (CryptQueryObject(CERT_QUERY_OBJECT_BLOB, | 
|  | &pObject->rgBlob[i], expectedContentTypes, | 
|  | CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, &contentType, NULL, | 
|  | &contextStore, NULL, &context)) | 
|  | { | 
|  | switch (contentType) | 
|  | { | 
|  | case CERT_QUERY_CONTENT_CERT: | 
|  | if (!CertAddCertificateContextToStore(store, | 
|  | context, CERT_STORE_ADD_ALWAYS, NULL)) | 
|  | ret = FALSE; | 
|  | CertFreeCertificateContext(context); | 
|  | break; | 
|  | case CERT_QUERY_CONTENT_CRL: | 
|  | if (!CertAddCRLContextToStore(store, | 
|  | context, CERT_STORE_ADD_ALWAYS, NULL)) | 
|  | ret = FALSE; | 
|  | CertFreeCRLContext(context); | 
|  | break; | 
|  | case CERT_QUERY_CONTENT_CTL: | 
|  | if (!CertAddCTLContextToStore(store, | 
|  | context, CERT_STORE_ADD_ALWAYS, NULL)) | 
|  | ret = FALSE; | 
|  | CertFreeCTLContext(context); | 
|  | break; | 
|  | default: | 
|  | CertAddStoreToCollection(store, contextStore, 0, 0); | 
|  | } | 
|  | CertCloseStore(contextStore, 0); | 
|  | } | 
|  | else | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | else | 
|  | ret = FALSE; | 
|  | *ppvContext = store; | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | typedef BOOL (WINAPI *ContextDllCreateObjectContext)(LPCSTR pszObjectOid, | 
|  | DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext); | 
|  |  | 
|  | static BOOL CRYPT_GetCreateFunction(LPCSTR pszObjectOid, | 
|  | ContextDllCreateObjectContext *pFunc, HCRYPTOIDFUNCADDR *phFunc) | 
|  | { | 
|  | BOOL ret = TRUE; | 
|  |  | 
|  | TRACE("(%s, %p, %p)\n", debugstr_a(pszObjectOid), pFunc, phFunc); | 
|  |  | 
|  | *pFunc = NULL; | 
|  | *phFunc = 0; | 
|  | if (IS_INTOID(pszObjectOid)) | 
|  | { | 
|  | switch (LOWORD(pszObjectOid)) | 
|  | { | 
|  | case 0: | 
|  | *pFunc = CRYPT_CreateBlob; | 
|  | break; | 
|  | case LOWORD(CONTEXT_OID_CERTIFICATE): | 
|  | *pFunc = CRYPT_CreateCert; | 
|  | break; | 
|  | case LOWORD(CONTEXT_OID_CRL): | 
|  | *pFunc = CRYPT_CreateCRL; | 
|  | break; | 
|  | case LOWORD(CONTEXT_OID_CTL): | 
|  | *pFunc = CRYPT_CreateCTL; | 
|  | break; | 
|  | case LOWORD(CONTEXT_OID_PKCS7): | 
|  | *pFunc = CRYPT_CreatePKCS7; | 
|  | break; | 
|  | case LOWORD(CONTEXT_OID_CAPI2_ANY): | 
|  | *pFunc = CRYPT_CreateAny; | 
|  | break; | 
|  | } | 
|  | } | 
|  | if (!*pFunc) | 
|  | { | 
|  | static HCRYPTOIDFUNCSET set = NULL; | 
|  |  | 
|  | if (!set) | 
|  | set = CryptInitOIDFunctionSet( | 
|  | CONTEXT_OID_CREATE_OBJECT_CONTEXT_FUNC, 0); | 
|  | ret = CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, pszObjectOid, | 
|  | 0, (void **)pFunc, phFunc); | 
|  | } | 
|  | TRACE("returning %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | typedef BOOL (*get_object_expiration_func)(const void *pvContext, | 
|  | FILETIME *expiration); | 
|  |  | 
|  | static BOOL CRYPT_GetExpirationFromCert(const void *pvObject, FILETIME *expiration) | 
|  | { | 
|  | PCCERT_CONTEXT cert = pvObject; | 
|  |  | 
|  | *expiration = cert->pCertInfo->NotAfter; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_GetExpirationFromCRL(const void *pvObject, FILETIME *expiration) | 
|  | { | 
|  | PCCRL_CONTEXT cert = pvObject; | 
|  |  | 
|  | *expiration = cert->pCrlInfo->NextUpdate; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_GetExpirationFromCTL(const void *pvObject, FILETIME *expiration) | 
|  | { | 
|  | PCCTL_CONTEXT cert = pvObject; | 
|  |  | 
|  | *expiration = cert->pCtlInfo->NextUpdate; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | static BOOL CRYPT_GetExpirationFunction(LPCSTR pszObjectOid, | 
|  | get_object_expiration_func *getExpiration) | 
|  | { | 
|  | BOOL ret; | 
|  |  | 
|  | if (IS_INTOID(pszObjectOid)) | 
|  | { | 
|  | switch (LOWORD(pszObjectOid)) | 
|  | { | 
|  | case LOWORD(CONTEXT_OID_CERTIFICATE): | 
|  | *getExpiration = CRYPT_GetExpirationFromCert; | 
|  | ret = TRUE; | 
|  | break; | 
|  | case LOWORD(CONTEXT_OID_CRL): | 
|  | *getExpiration = CRYPT_GetExpirationFromCRL; | 
|  | ret = TRUE; | 
|  | break; | 
|  | case LOWORD(CONTEXT_OID_CTL): | 
|  | *getExpiration = CRYPT_GetExpirationFromCTL; | 
|  | ret = TRUE; | 
|  | break; | 
|  | default: | 
|  | ret = FALSE; | 
|  | } | 
|  | } | 
|  | else | 
|  | ret = FALSE; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /*********************************************************************** | 
|  | *    CryptRetrieveObjectByUrlW (CRYPTNET.@) | 
|  | */ | 
|  | BOOL WINAPI CryptRetrieveObjectByUrlW(LPCWSTR pszURL, LPCSTR pszObjectOid, | 
|  | DWORD dwRetrievalFlags, DWORD dwTimeout, LPVOID *ppvObject, | 
|  | HCRYPTASYNC hAsyncRetrieve, PCRYPT_CREDENTIALS pCredentials, LPVOID pvVerify, | 
|  | PCRYPT_RETRIEVE_AUX_INFO pAuxInfo) | 
|  | { | 
|  | BOOL ret; | 
|  | SchemeDllRetrieveEncodedObjectW retrieve; | 
|  | ContextDllCreateObjectContext create; | 
|  | HCRYPTOIDFUNCADDR hRetrieve = 0, hCreate = 0; | 
|  |  | 
|  | TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL), | 
|  | debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, ppvObject, | 
|  | hAsyncRetrieve, pCredentials, pvVerify, pAuxInfo); | 
|  |  | 
|  | if (!pszURL) | 
|  | { | 
|  | SetLastError(ERROR_INVALID_PARAMETER); | 
|  | return FALSE; | 
|  | } | 
|  | ret = CRYPT_GetRetrieveFunction(pszURL, &retrieve, &hRetrieve); | 
|  | if (ret) | 
|  | ret = CRYPT_GetCreateFunction(pszObjectOid, &create, &hCreate); | 
|  | if (ret) | 
|  | { | 
|  | CRYPT_BLOB_ARRAY object = { 0, NULL }; | 
|  | PFN_FREE_ENCODED_OBJECT_FUNC freeObject; | 
|  | void *freeContext; | 
|  |  | 
|  | ret = retrieve(pszURL, pszObjectOid, dwRetrievalFlags, dwTimeout, | 
|  | &object, &freeObject, &freeContext, hAsyncRetrieve, pCredentials, | 
|  | pAuxInfo); | 
|  | if (ret) | 
|  | { | 
|  | get_object_expiration_func getExpiration; | 
|  |  | 
|  | ret = create(pszObjectOid, dwRetrievalFlags, &object, ppvObject); | 
|  | if (ret && !(dwRetrievalFlags & CRYPT_DONT_CACHE_RESULT) && | 
|  | CRYPT_GetExpirationFunction(pszObjectOid, &getExpiration)) | 
|  | { | 
|  | FILETIME expires; | 
|  |  | 
|  | if (getExpiration(*ppvObject, &expires)) | 
|  | CRYPT_CacheURL(pszURL, &object, dwRetrievalFlags, expires); | 
|  | } | 
|  | freeObject(pszObjectOid, &object, freeContext); | 
|  | } | 
|  | } | 
|  | if (hCreate) | 
|  | CryptFreeOIDFunctionAddress(hCreate, 0); | 
|  | if (hRetrieve) | 
|  | CryptFreeOIDFunctionAddress(hRetrieve, 0); | 
|  | TRACE("returning %d\n", ret); | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | static DWORD verify_cert_revocation_with_crl_online(PCCERT_CONTEXT cert, | 
|  | PCCRL_CONTEXT crl, DWORD index, FILETIME *pTime, | 
|  | PCERT_REVOCATION_STATUS pRevStatus) | 
|  | { | 
|  | DWORD error; | 
|  | PCRL_ENTRY entry = NULL; | 
|  |  | 
|  | CertFindCertificateInCRL(cert, crl, 0, NULL, &entry); | 
|  | if (entry) | 
|  | { | 
|  | error = CRYPT_E_REVOKED; | 
|  | pRevStatus->dwIndex = index; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Since the CRL was retrieved for the cert being checked, then it's | 
|  | * guaranteed to be fresh, and the cert is not revoked. | 
|  | */ | 
|  | error = ERROR_SUCCESS; | 
|  | } | 
|  | return error; | 
|  | } | 
|  |  | 
|  | static DWORD verify_cert_revocation_from_dist_points_ext( | 
|  | const CRYPT_DATA_BLOB *value, PCCERT_CONTEXT cert, DWORD index, | 
|  | FILETIME *pTime, DWORD dwFlags, const CERT_REVOCATION_PARA *pRevPara, | 
|  | PCERT_REVOCATION_STATUS pRevStatus) | 
|  | { | 
|  | DWORD error = ERROR_SUCCESS, cbUrlArray; | 
|  |  | 
|  | if (CRYPT_GetUrlFromCRLDistPointsExt(value, NULL, &cbUrlArray, NULL, NULL)) | 
|  | { | 
|  | CRYPT_URL_ARRAY *urlArray = CryptMemAlloc(cbUrlArray); | 
|  |  | 
|  | if (urlArray) | 
|  | { | 
|  | DWORD j, retrievalFlags = 0, startTime, endTime, timeout; | 
|  | BOOL ret; | 
|  |  | 
|  | ret = CRYPT_GetUrlFromCRLDistPointsExt(value, urlArray, | 
|  | &cbUrlArray, NULL, NULL); | 
|  | if (dwFlags & CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION) | 
|  | retrievalFlags |= CRYPT_CACHE_ONLY_RETRIEVAL; | 
|  | if (dwFlags & CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG && | 
|  | pRevPara && pRevPara->cbSize >= offsetof(CERT_REVOCATION_PARA, | 
|  | dwUrlRetrievalTimeout) + sizeof(DWORD)) | 
|  | { | 
|  | startTime = GetTickCount(); | 
|  | endTime = startTime + pRevPara->dwUrlRetrievalTimeout; | 
|  | timeout = pRevPara->dwUrlRetrievalTimeout; | 
|  | } | 
|  | else | 
|  | endTime = timeout = 0; | 
|  | if (!ret) | 
|  | error = GetLastError(); | 
|  | for (j = 0; !error && j < urlArray->cUrl; j++) | 
|  | { | 
|  | PCCRL_CONTEXT crl; | 
|  |  | 
|  | ret = CryptRetrieveObjectByUrlW(urlArray->rgwszUrl[j], | 
|  | CONTEXT_OID_CRL, retrievalFlags, timeout, (void **)&crl, | 
|  | NULL, NULL, NULL, NULL); | 
|  | if (ret) | 
|  | { | 
|  | error = verify_cert_revocation_with_crl_online(cert, crl, | 
|  | index, pTime, pRevStatus); | 
|  | if (!error && timeout) | 
|  | { | 
|  | DWORD time = GetTickCount(); | 
|  |  | 
|  | if ((int)(endTime - time) <= 0) | 
|  | { | 
|  | error = ERROR_TIMEOUT; | 
|  | pRevStatus->dwIndex = index; | 
|  | } | 
|  | else | 
|  | timeout = endTime - time; | 
|  | } | 
|  | CertFreeCRLContext(crl); | 
|  | } | 
|  | else | 
|  | error = CRYPT_E_REVOCATION_OFFLINE; | 
|  | } | 
|  | CryptMemFree(urlArray); | 
|  | } | 
|  | else | 
|  | { | 
|  | error = ERROR_OUTOFMEMORY; | 
|  | pRevStatus->dwIndex = index; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | error = GetLastError(); | 
|  | pRevStatus->dwIndex = index; | 
|  | } | 
|  | return error; | 
|  | } | 
|  |  | 
|  | static DWORD verify_cert_revocation_from_aia_ext( | 
|  | const CRYPT_DATA_BLOB *value, PCCERT_CONTEXT cert, DWORD index, | 
|  | FILETIME *pTime, DWORD dwFlags, PCERT_REVOCATION_PARA pRevPara, | 
|  | PCERT_REVOCATION_STATUS pRevStatus) | 
|  | { | 
|  | BOOL ret; | 
|  | DWORD error, size; | 
|  | CERT_AUTHORITY_INFO_ACCESS *aia; | 
|  |  | 
|  | ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_AUTHORITY_INFO_ACCESS, | 
|  | value->pbData, value->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &aia, &size); | 
|  | if (ret) | 
|  | { | 
|  | DWORD i; | 
|  |  | 
|  | for (i = 0; i < aia->cAccDescr; i++) | 
|  | if (!strcmp(aia->rgAccDescr[i].pszAccessMethod, | 
|  | szOID_PKIX_OCSP)) | 
|  | { | 
|  | if (aia->rgAccDescr[i].AccessLocation.dwAltNameChoice == | 
|  | CERT_ALT_NAME_URL) | 
|  | FIXME("OCSP URL = %s\n", | 
|  | debugstr_w(aia->rgAccDescr[i].AccessLocation.u.pwszURL)); | 
|  | else | 
|  | FIXME("unsupported AccessLocation type %d\n", | 
|  | aia->rgAccDescr[i].AccessLocation.dwAltNameChoice); | 
|  | } | 
|  | LocalFree(aia); | 
|  | /* FIXME: lie and pretend OCSP validated the cert */ | 
|  | error = ERROR_SUCCESS; | 
|  | } | 
|  | else | 
|  | error = GetLastError(); | 
|  | return error; | 
|  | } | 
|  |  | 
|  | static DWORD verify_cert_revocation_with_crl_offline(PCCERT_CONTEXT cert, | 
|  | PCCRL_CONTEXT crl, DWORD index, FILETIME *pTime, | 
|  | PCERT_REVOCATION_STATUS pRevStatus) | 
|  | { | 
|  | DWORD error; | 
|  | LONG valid; | 
|  |  | 
|  | valid = CompareFileTime(pTime, &crl->pCrlInfo->ThisUpdate); | 
|  | if (valid <= 0) | 
|  | { | 
|  | /* If this CRL is not older than the time being verified, there's no | 
|  | * way to know whether the certificate was revoked. | 
|  | */ | 
|  | TRACE("CRL not old enough\n"); | 
|  | error = CRYPT_E_REVOCATION_OFFLINE; | 
|  | } | 
|  | else | 
|  | { | 
|  | PCRL_ENTRY entry = NULL; | 
|  |  | 
|  | CertFindCertificateInCRL(cert, crl, 0, NULL, &entry); | 
|  | if (entry) | 
|  | { | 
|  | error = CRYPT_E_REVOKED; | 
|  | pRevStatus->dwIndex = index; | 
|  | } | 
|  | else | 
|  | { | 
|  | /* Since the CRL was not retrieved for the cert being checked, | 
|  | * there's no guarantee it's fresh, so the cert *might* be okay, | 
|  | * but it's safer not to guess. | 
|  | */ | 
|  | TRACE("certificate not found\n"); | 
|  | error = CRYPT_E_REVOCATION_OFFLINE; | 
|  | } | 
|  | } | 
|  | return error; | 
|  | } | 
|  |  | 
|  | static DWORD verify_cert_revocation(PCCERT_CONTEXT cert, DWORD index, | 
|  | FILETIME *pTime, DWORD dwFlags, PCERT_REVOCATION_PARA pRevPara, | 
|  | PCERT_REVOCATION_STATUS pRevStatus) | 
|  | { | 
|  | DWORD error = ERROR_SUCCESS; | 
|  | PCERT_EXTENSION ext; | 
|  |  | 
|  | if ((ext = CertFindExtension(szOID_CRL_DIST_POINTS, | 
|  | cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension))) | 
|  | error = verify_cert_revocation_from_dist_points_ext(&ext->Value, cert, | 
|  | index, pTime, dwFlags, pRevPara, pRevStatus); | 
|  | else if ((ext = CertFindExtension(szOID_AUTHORITY_INFO_ACCESS, | 
|  | cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension))) | 
|  | error = verify_cert_revocation_from_aia_ext(&ext->Value, cert, | 
|  | index, pTime, dwFlags, pRevPara, pRevStatus); | 
|  | else | 
|  | { | 
|  | if (pRevPara && pRevPara->hCrlStore && pRevPara->pIssuerCert) | 
|  | { | 
|  | PCCRL_CONTEXT crl = NULL; | 
|  | BOOL canSignCRLs; | 
|  |  | 
|  | /* If the caller told us about the issuer, make sure the issuer | 
|  | * can sign CRLs before looking for one. | 
|  | */ | 
|  | if ((ext = CertFindExtension(szOID_KEY_USAGE, | 
|  | pRevPara->pIssuerCert->pCertInfo->cExtension, | 
|  | pRevPara->pIssuerCert->pCertInfo->rgExtension))) | 
|  | { | 
|  | CRYPT_BIT_BLOB usage; | 
|  | DWORD size = sizeof(usage); | 
|  |  | 
|  | if (!CryptDecodeObjectEx(cert->dwCertEncodingType, X509_BITS, | 
|  | ext->Value.pbData, ext->Value.cbData, | 
|  | CRYPT_DECODE_NOCOPY_FLAG, NULL, &usage, &size)) | 
|  | canSignCRLs = FALSE; | 
|  | else if (usage.cbData > 2) | 
|  | { | 
|  | /* The key usage extension only defines 9 bits => no more | 
|  | * than 2 bytes are needed to encode all known usages. | 
|  | */ | 
|  | canSignCRLs = FALSE; | 
|  | } | 
|  | else | 
|  | { | 
|  | BYTE usageBits = usage.pbData[usage.cbData - 1]; | 
|  |  | 
|  | canSignCRLs = usageBits & CERT_CRL_SIGN_KEY_USAGE; | 
|  | } | 
|  | } | 
|  | else | 
|  | canSignCRLs = TRUE; | 
|  | if (canSignCRLs) | 
|  | { | 
|  | /* If the caller was helpful enough to tell us where to find a | 
|  | * CRL for the cert, look for one and check it. | 
|  | */ | 
|  | crl = CertFindCRLInStore(pRevPara->hCrlStore, | 
|  | cert->dwCertEncodingType, | 
|  | CRL_FIND_ISSUED_BY_SIGNATURE_FLAG | | 
|  | CRL_FIND_ISSUED_BY_AKI_FLAG, | 
|  | CRL_FIND_ISSUED_BY, pRevPara->pIssuerCert, NULL); | 
|  | } | 
|  | if (crl) | 
|  | { | 
|  | error = verify_cert_revocation_with_crl_offline(cert, crl, | 
|  | index, pTime, pRevStatus); | 
|  | CertFreeCRLContext(crl); | 
|  | } | 
|  | else | 
|  | { | 
|  | TRACE("no CRL found\n"); | 
|  | error = CRYPT_E_NO_REVOCATION_CHECK; | 
|  | pRevStatus->dwIndex = index; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | if (!pRevPara) | 
|  | WARN("no CERT_REVOCATION_PARA\n"); | 
|  | else if (!pRevPara->hCrlStore) | 
|  | WARN("no dist points/aia extension and no CRL store\n"); | 
|  | else if (!pRevPara->pIssuerCert) | 
|  | WARN("no dist points/aia extension and no issuer\n"); | 
|  | error = CRYPT_E_NO_REVOCATION_CHECK; | 
|  | pRevStatus->dwIndex = index; | 
|  | } | 
|  | } | 
|  | return error; | 
|  | } | 
|  |  | 
|  | typedef struct _CERT_REVOCATION_PARA_NO_EXTRA_FIELDS { | 
|  | DWORD                     cbSize; | 
|  | PCCERT_CONTEXT            pIssuerCert; | 
|  | DWORD                     cCertStore; | 
|  | HCERTSTORE               *rgCertStore; | 
|  | HCERTSTORE                hCrlStore; | 
|  | LPFILETIME                pftTimeToUse; | 
|  | } CERT_REVOCATION_PARA_NO_EXTRA_FIELDS, *PCERT_REVOCATION_PARA_NO_EXTRA_FIELDS; | 
|  |  | 
|  | typedef struct _OLD_CERT_REVOCATION_STATUS { | 
|  | DWORD cbSize; | 
|  | DWORD dwIndex; | 
|  | DWORD dwError; | 
|  | DWORD dwReason; | 
|  | } OLD_CERT_REVOCATION_STATUS, *POLD_CERT_REVOCATION_STATUS; | 
|  |  | 
|  | /*********************************************************************** | 
|  | *    CertDllVerifyRevocation (CRYPTNET.@) | 
|  | */ | 
|  | BOOL WINAPI CertDllVerifyRevocation(DWORD dwEncodingType, DWORD dwRevType, | 
|  | DWORD cContext, PVOID rgpvContext[], DWORD dwFlags, | 
|  | PCERT_REVOCATION_PARA pRevPara, PCERT_REVOCATION_STATUS pRevStatus) | 
|  | { | 
|  | DWORD error = 0, i; | 
|  | FILETIME now; | 
|  | LPFILETIME pTime = NULL; | 
|  |  | 
|  | TRACE("(%08x, %d, %d, %p, %08x, %p, %p)\n", dwEncodingType, dwRevType, | 
|  | cContext, rgpvContext, dwFlags, pRevPara, pRevStatus); | 
|  |  | 
|  | if (pRevStatus->cbSize != sizeof(OLD_CERT_REVOCATION_STATUS) && | 
|  | pRevStatus->cbSize != sizeof(CERT_REVOCATION_STATUS)) | 
|  | { | 
|  | SetLastError(E_INVALIDARG); | 
|  | return FALSE; | 
|  | } | 
|  | if (!cContext) | 
|  | { | 
|  | SetLastError(E_INVALIDARG); | 
|  | return FALSE; | 
|  | } | 
|  | if (pRevPara && pRevPara->cbSize >= | 
|  | sizeof(CERT_REVOCATION_PARA_NO_EXTRA_FIELDS)) | 
|  | pTime = pRevPara->pftTimeToUse; | 
|  | if (!pTime) | 
|  | { | 
|  | GetSystemTimeAsFileTime(&now); | 
|  | pTime = &now; | 
|  | } | 
|  | memset(&pRevStatus->dwIndex, 0, pRevStatus->cbSize - sizeof(DWORD)); | 
|  | if (dwRevType != CERT_CONTEXT_REVOCATION_TYPE) | 
|  | error = CRYPT_E_NO_REVOCATION_CHECK; | 
|  | else | 
|  | { | 
|  | for (i = 0; !error && i < cContext; i++) | 
|  | error = verify_cert_revocation(rgpvContext[i], i, pTime, dwFlags, | 
|  | pRevPara, pRevStatus); | 
|  | } | 
|  | if (error) | 
|  | { | 
|  | SetLastError(error); | 
|  | pRevStatus->dwError = error; | 
|  | } | 
|  | TRACE("returning %d (%08x)\n", !error, error); | 
|  | return !error; | 
|  | } |