include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / cryptnet / cryptnet_main.c
blob1068dd268685f8b36968df3e145f03355cf77403
1 /*
2 * Copyright (C) 2006 Maarten Lankhorst
3 * Copyright 2007 Juan Lang
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #define CERT_REVOCATION_PARA_HAS_EXTRA_FIELDS
23 #include <share.h>
24 #include <stdio.h>
25 #include <stdarg.h>
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winnt.h"
30 #include "winnls.h"
31 #include "wininet.h"
32 #include "objbase.h"
33 #include "wincrypt.h"
34 #include "initguid.h"
35 #include "knownfolders.h"
36 #include "shlobj.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(cryptnet);
42 #define IS_INTOID(x) (((ULONG_PTR)(x) >> 16) == 0)
45 /***********************************************************************
46 * DllRegisterServer (CRYPTNET.@)
48 HRESULT WINAPI DllRegisterServer(void)
50 TRACE("\n");
51 CryptRegisterDefaultOIDFunction(X509_ASN_ENCODING,
52 CRYPT_OID_VERIFY_REVOCATION_FUNC, 0, L"cryptnet.dll");
53 CryptRegisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC, "Ldap",
54 L"cryptnet.dll", "LdapProvOpenStore");
55 CryptRegisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC,
56 CERT_STORE_PROV_LDAP_W, L"cryptnet.dll", "LdapProvOpenStore");
57 return S_OK;
60 /***********************************************************************
61 * DllUnregisterServer (CRYPTNET.@)
63 HRESULT WINAPI DllUnregisterServer(void)
65 TRACE("\n");
66 CryptUnregisterDefaultOIDFunction(X509_ASN_ENCODING,
67 CRYPT_OID_VERIFY_REVOCATION_FUNC, L"cryptnet.dll");
68 CryptUnregisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC, "Ldap");
69 CryptUnregisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC,
70 CERT_STORE_PROV_LDAP_W);
71 return S_OK;
74 static const char *url_oid_to_str(LPCSTR oid)
76 if (IS_INTOID(oid))
78 static char buf[10];
80 switch (LOWORD(oid))
82 #define _x(oid) case LOWORD(oid): return #oid
83 _x(URL_OID_CERTIFICATE_ISSUER);
84 _x(URL_OID_CERTIFICATE_CRL_DIST_POINT);
85 _x(URL_OID_CTL_ISSUER);
86 _x(URL_OID_CTL_NEXT_UPDATE);
87 _x(URL_OID_CRL_ISSUER);
88 _x(URL_OID_CERTIFICATE_FRESHEST_CRL);
89 _x(URL_OID_CRL_FRESHEST_CRL);
90 _x(URL_OID_CROSS_CERT_DIST_POINT);
91 #undef _x
92 default:
93 snprintf(buf, sizeof(buf), "%d", LOWORD(oid));
94 return buf;
97 else
98 return oid;
101 typedef BOOL (WINAPI *UrlDllGetObjectUrlFunc)(LPCSTR, LPVOID, DWORD,
102 PCRYPT_URL_ARRAY, DWORD *, PCRYPT_URL_INFO, DWORD *, LPVOID);
104 static BOOL WINAPI CRYPT_GetUrlFromCertificateIssuer(LPCSTR pszUrlOid,
105 LPVOID pvPara, DWORD dwFlags, PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray,
106 PCRYPT_URL_INFO pUrlInfo, DWORD *pcbUrlInfo, LPVOID pvReserved)
108 PCCERT_CONTEXT cert = pvPara;
109 PCERT_EXTENSION ext;
110 BOOL ret = FALSE;
112 /* The only applicable flag is CRYPT_GET_URL_FROM_EXTENSION */
113 if (dwFlags && !(dwFlags & CRYPT_GET_URL_FROM_EXTENSION))
115 SetLastError(CRYPT_E_NOT_FOUND);
116 return FALSE;
118 if ((ext = CertFindExtension(szOID_AUTHORITY_INFO_ACCESS,
119 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
121 CERT_AUTHORITY_INFO_ACCESS *aia;
122 DWORD size;
124 ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_AUTHORITY_INFO_ACCESS,
125 ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
126 &aia, &size);
127 if (ret)
129 DWORD i, cUrl, bytesNeeded = sizeof(CRYPT_URL_ARRAY);
131 for (i = 0, cUrl = 0; i < aia->cAccDescr; i++)
132 if (!strcmp(aia->rgAccDescr[i].pszAccessMethod,
133 szOID_PKIX_CA_ISSUERS))
135 if (aia->rgAccDescr[i].AccessLocation.dwAltNameChoice ==
136 CERT_ALT_NAME_URL)
138 if (aia->rgAccDescr[i].AccessLocation.pwszURL)
140 cUrl++;
141 bytesNeeded += sizeof(LPWSTR) +
142 (lstrlenW(aia->rgAccDescr[i].AccessLocation.
143 pwszURL) + 1) * sizeof(WCHAR);
146 else
147 FIXME("unsupported alt name type %ld\n",
148 aia->rgAccDescr[i].AccessLocation.dwAltNameChoice);
150 if (!pcbUrlArray)
152 SetLastError(E_INVALIDARG);
153 ret = FALSE;
155 else if (!pUrlArray)
156 *pcbUrlArray = bytesNeeded;
157 else if (*pcbUrlArray < bytesNeeded)
159 SetLastError(ERROR_MORE_DATA);
160 *pcbUrlArray = bytesNeeded;
161 ret = FALSE;
163 else
165 LPWSTR nextUrl;
167 *pcbUrlArray = bytesNeeded;
168 pUrlArray->cUrl = 0;
169 pUrlArray->rgwszUrl =
170 (LPWSTR *)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY));
171 nextUrl = (LPWSTR)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY)
172 + cUrl * sizeof(LPWSTR));
173 for (i = 0; i < aia->cAccDescr; i++)
174 if (!strcmp(aia->rgAccDescr[i].pszAccessMethod,
175 szOID_PKIX_CA_ISSUERS))
177 if (aia->rgAccDescr[i].AccessLocation.dwAltNameChoice
178 == CERT_ALT_NAME_URL)
180 if (aia->rgAccDescr[i].AccessLocation.pwszURL)
182 lstrcpyW(nextUrl,
183 aia->rgAccDescr[i].AccessLocation.pwszURL);
184 pUrlArray->rgwszUrl[pUrlArray->cUrl++] =
185 nextUrl;
186 nextUrl += (lstrlenW(nextUrl) + 1);
191 if (ret)
193 if (pcbUrlInfo)
195 FIXME("url info: stub\n");
196 if (!pUrlInfo)
197 *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
198 else if (*pcbUrlInfo < sizeof(CRYPT_URL_INFO))
200 *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
201 SetLastError(ERROR_MORE_DATA);
202 ret = FALSE;
204 else
206 *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
207 memset(pUrlInfo, 0, sizeof(CRYPT_URL_INFO));
211 LocalFree(aia);
214 else
215 SetLastError(CRYPT_E_NOT_FOUND);
216 return ret;
219 static BOOL CRYPT_GetUrlFromCRLDistPointsExt(const CRYPT_DATA_BLOB *value,
220 PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray, PCRYPT_URL_INFO pUrlInfo,
221 DWORD *pcbUrlInfo)
223 BOOL ret;
224 CRL_DIST_POINTS_INFO *info;
225 DWORD size;
227 ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CRL_DIST_POINTS,
228 value->pbData, value->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size);
229 if (ret)
231 DWORD i, cUrl, bytesNeeded = sizeof(CRYPT_URL_ARRAY);
233 for (i = 0, cUrl = 0; i < info->cDistPoint; i++)
234 if (info->rgDistPoint[i].DistPointName.dwDistPointNameChoice
235 == CRL_DIST_POINT_FULL_NAME)
237 DWORD j;
238 CERT_ALT_NAME_INFO *name =
239 &info->rgDistPoint[i].DistPointName.FullName;
241 for (j = 0; j < name->cAltEntry; j++)
242 if (name->rgAltEntry[j].dwAltNameChoice ==
243 CERT_ALT_NAME_URL)
245 if (name->rgAltEntry[j].pwszURL)
247 cUrl++;
248 bytesNeeded += sizeof(LPWSTR) +
249 (lstrlenW(name->rgAltEntry[j].pwszURL) + 1)
250 * sizeof(WCHAR);
254 if (!pcbUrlArray)
256 SetLastError(E_INVALIDARG);
257 ret = FALSE;
259 else if (!pUrlArray)
260 *pcbUrlArray = bytesNeeded;
261 else if (*pcbUrlArray < bytesNeeded)
263 SetLastError(ERROR_MORE_DATA);
264 *pcbUrlArray = bytesNeeded;
265 ret = FALSE;
267 else
269 LPWSTR nextUrl;
271 *pcbUrlArray = bytesNeeded;
272 pUrlArray->cUrl = 0;
273 pUrlArray->rgwszUrl =
274 (LPWSTR *)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY));
275 nextUrl = (LPWSTR)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY)
276 + cUrl * sizeof(LPWSTR));
277 for (i = 0; i < info->cDistPoint; i++)
278 if (info->rgDistPoint[i].DistPointName.dwDistPointNameChoice
279 == CRL_DIST_POINT_FULL_NAME)
281 DWORD j;
282 CERT_ALT_NAME_INFO *name =
283 &info->rgDistPoint[i].DistPointName.FullName;
285 for (j = 0; j < name->cAltEntry; j++)
286 if (name->rgAltEntry[j].dwAltNameChoice ==
287 CERT_ALT_NAME_URL)
289 if (name->rgAltEntry[j].pwszURL)
291 lstrcpyW(nextUrl,
292 name->rgAltEntry[j].pwszURL);
293 pUrlArray->rgwszUrl[pUrlArray->cUrl++] =
294 nextUrl;
295 nextUrl +=
296 (lstrlenW(name->rgAltEntry[j].pwszURL) + 1);
301 if (ret)
303 if (pcbUrlInfo)
305 FIXME("url info: stub\n");
306 if (!pUrlInfo)
307 *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
308 else if (*pcbUrlInfo < sizeof(CRYPT_URL_INFO))
310 *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
311 SetLastError(ERROR_MORE_DATA);
312 ret = FALSE;
314 else
316 *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
317 memset(pUrlInfo, 0, sizeof(CRYPT_URL_INFO));
321 LocalFree(info);
323 return ret;
326 static BOOL WINAPI CRYPT_GetUrlFromCertificateCRLDistPoint(LPCSTR pszUrlOid,
327 LPVOID pvPara, DWORD dwFlags, PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray,
328 PCRYPT_URL_INFO pUrlInfo, DWORD *pcbUrlInfo, LPVOID pvReserved)
330 PCCERT_CONTEXT cert = pvPara;
331 PCERT_EXTENSION ext;
332 BOOL ret = FALSE;
334 /* The only applicable flag is CRYPT_GET_URL_FROM_EXTENSION */
335 if (dwFlags && !(dwFlags & CRYPT_GET_URL_FROM_EXTENSION))
337 SetLastError(CRYPT_E_NOT_FOUND);
338 return FALSE;
340 if ((ext = CertFindExtension(szOID_CRL_DIST_POINTS,
341 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
342 ret = CRYPT_GetUrlFromCRLDistPointsExt(&ext->Value, pUrlArray,
343 pcbUrlArray, pUrlInfo, pcbUrlInfo);
344 else
345 SetLastError(CRYPT_E_NOT_FOUND);
346 return ret;
349 /***********************************************************************
350 * CryptGetObjectUrl (CRYPTNET.@)
352 BOOL WINAPI CryptGetObjectUrl(LPCSTR pszUrlOid, LPVOID pvPara, DWORD dwFlags,
353 PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray, PCRYPT_URL_INFO pUrlInfo,
354 DWORD *pcbUrlInfo, LPVOID pvReserved)
356 UrlDllGetObjectUrlFunc func = NULL;
357 HCRYPTOIDFUNCADDR hFunc = NULL;
358 BOOL ret = FALSE;
360 TRACE("(%s, %p, %08lx, %p, %p, %p, %p, %p)\n", debugstr_a(pszUrlOid),
361 pvPara, dwFlags, pUrlArray, pcbUrlArray, pUrlInfo, pcbUrlInfo, pvReserved);
363 if (IS_INTOID(pszUrlOid))
365 switch (LOWORD(pszUrlOid))
367 case LOWORD(URL_OID_CERTIFICATE_ISSUER):
368 func = CRYPT_GetUrlFromCertificateIssuer;
369 break;
370 case LOWORD(URL_OID_CERTIFICATE_CRL_DIST_POINT):
371 func = CRYPT_GetUrlFromCertificateCRLDistPoint;
372 break;
373 default:
374 FIXME("unimplemented for %s\n", url_oid_to_str(pszUrlOid));
375 SetLastError(ERROR_FILE_NOT_FOUND);
378 else
380 static HCRYPTOIDFUNCSET set = NULL;
382 if (!set)
383 set = CryptInitOIDFunctionSet(URL_OID_GET_OBJECT_URL_FUNC, 0);
384 CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, pszUrlOid, 0,
385 (void **)&func, &hFunc);
387 if (func)
388 ret = func(pszUrlOid, pvPara, dwFlags, pUrlArray, pcbUrlArray,
389 pUrlInfo, pcbUrlInfo, pvReserved);
390 if (hFunc)
391 CryptFreeOIDFunctionAddress(hFunc, 0);
392 return ret;
395 /***********************************************************************
396 * CryptRetrieveObjectByUrlA (CRYPTNET.@)
398 BOOL WINAPI CryptRetrieveObjectByUrlA(LPCSTR pszURL, LPCSTR pszObjectOid,
399 DWORD dwRetrievalFlags, DWORD dwTimeout, LPVOID *ppvObject,
400 HCRYPTASYNC hAsyncRetrieve, PCRYPT_CREDENTIALS pCredentials, LPVOID pvVerify,
401 PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
403 BOOL ret = FALSE;
404 int len;
406 TRACE("(%s, %s, %08lx, %ld, %p, %p, %p, %p, %p)\n", debugstr_a(pszURL),
407 debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, ppvObject,
408 hAsyncRetrieve, pCredentials, pvVerify, pAuxInfo);
410 if (!pszURL)
412 SetLastError(ERROR_INVALID_PARAMETER);
413 return FALSE;
415 len = MultiByteToWideChar(CP_ACP, 0, pszURL, -1, NULL, 0);
416 if (len)
418 LPWSTR url = CryptMemAlloc(len * sizeof(WCHAR));
420 if (url)
422 MultiByteToWideChar(CP_ACP, 0, pszURL, -1, url, len);
423 ret = CryptRetrieveObjectByUrlW(url, pszObjectOid,
424 dwRetrievalFlags, dwTimeout, ppvObject, hAsyncRetrieve,
425 pCredentials, pvVerify, pAuxInfo);
426 CryptMemFree(url);
428 else
429 SetLastError(ERROR_OUTOFMEMORY);
431 return ret;
434 static void WINAPI CRYPT_FreeBlob(LPCSTR pszObjectOid,
435 PCRYPT_BLOB_ARRAY pObject, void *pvFreeContext)
437 DWORD i;
439 for (i = 0; i < pObject->cBlob; i++)
440 CryptMemFree(pObject->rgBlob[i].pbData);
441 CryptMemFree(pObject->rgBlob);
444 static BOOL CRYPT_GetObjectFromFile(HANDLE hFile, PCRYPT_BLOB_ARRAY pObject)
446 BOOL ret;
447 LARGE_INTEGER size;
449 if ((ret = GetFileSizeEx(hFile, &size)))
451 if (size.HighPart)
453 WARN("file too big\n");
454 SetLastError(ERROR_INVALID_DATA);
455 ret = FALSE;
457 else
459 CRYPT_DATA_BLOB blob;
461 blob.pbData = CryptMemAlloc(size.LowPart);
462 if (blob.pbData)
464 ret = ReadFile(hFile, blob.pbData, size.LowPart, &blob.cbData,
465 NULL);
466 if (ret)
468 pObject->rgBlob = CryptMemAlloc(sizeof(CRYPT_DATA_BLOB));
469 if (pObject->rgBlob)
471 pObject->cBlob = 1;
472 memcpy(pObject->rgBlob, &blob, sizeof(CRYPT_DATA_BLOB));
474 else
476 SetLastError(ERROR_OUTOFMEMORY);
477 ret = FALSE;
480 if (!ret)
481 CryptMemFree(blob.pbData);
483 else
485 SetLastError(ERROR_OUTOFMEMORY);
486 ret = FALSE;
490 return ret;
493 static BOOL CRYPT_GetObjectFromCache(LPCWSTR pszURL, PCRYPT_BLOB_ARRAY pObject,
494 PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
496 BOOL ret = FALSE;
497 INTERNET_CACHE_ENTRY_INFOW *pCacheInfo = NULL;
498 DWORD size = 0;
500 TRACE("(%s, %p, %p)\n", debugstr_w(pszURL), pObject, pAuxInfo);
502 RetrieveUrlCacheEntryFileW(pszURL, NULL, &size, 0);
503 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
504 return FALSE;
506 pCacheInfo = CryptMemAlloc(size);
507 if (!pCacheInfo)
509 SetLastError(ERROR_OUTOFMEMORY);
510 return FALSE;
513 if ((ret = RetrieveUrlCacheEntryFileW(pszURL, pCacheInfo, &size, 0)))
515 FILETIME ft;
517 GetSystemTimeAsFileTime(&ft);
518 if (CompareFileTime(&pCacheInfo->ExpireTime, &ft) >= 0)
520 HANDLE hFile = CreateFileW(pCacheInfo->lpszLocalFileName, GENERIC_READ,
521 FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
523 if (hFile != INVALID_HANDLE_VALUE)
525 if ((ret = CRYPT_GetObjectFromFile(hFile, pObject)))
527 if (pAuxInfo && pAuxInfo->cbSize >= RTL_SIZEOF_THROUGH_FIELD(CRYPT_RETRIEVE_AUX_INFO, pLastSyncTime)
528 && pAuxInfo->pLastSyncTime)
530 memcpy(pAuxInfo->pLastSyncTime,
531 &pCacheInfo->LastSyncTime,
532 sizeof(FILETIME));
535 CloseHandle(hFile);
537 else
539 DeleteUrlCacheEntryW(pszURL);
540 ret = FALSE;
543 else
545 DeleteUrlCacheEntryW(pszURL);
546 ret = FALSE;
548 UnlockUrlCacheEntryFileW(pszURL, 0);
550 CryptMemFree(pCacheInfo);
551 TRACE("returning %d\n", ret);
552 return ret;
555 /* Parses the URL, and sets components' lpszHostName and lpszUrlPath members
556 * to NULL-terminated copies of those portions of the URL (to be freed with
557 * CryptMemFree.)
559 static BOOL CRYPT_CrackUrl(LPCWSTR pszURL, URL_COMPONENTSW *components)
561 BOOL ret;
563 TRACE("(%s, %p)\n", debugstr_w(pszURL), components);
565 memset(components, 0, sizeof(*components));
566 components->dwStructSize = sizeof(*components);
567 components->lpszHostName = CryptMemAlloc(INTERNET_MAX_HOST_NAME_LENGTH * sizeof(WCHAR));
568 components->dwHostNameLength = INTERNET_MAX_HOST_NAME_LENGTH;
569 if (!components->lpszHostName)
571 SetLastError(ERROR_OUTOFMEMORY);
572 return FALSE;
574 components->lpszUrlPath = CryptMemAlloc(INTERNET_MAX_PATH_LENGTH * sizeof(WCHAR));
575 components->dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
576 if (!components->lpszUrlPath)
578 CryptMemFree(components->lpszHostName);
579 SetLastError(ERROR_OUTOFMEMORY);
580 return FALSE;
583 ret = InternetCrackUrlW(pszURL, 0, ICU_DECODE, components);
584 if (ret)
586 switch (components->nScheme)
588 case INTERNET_SCHEME_FTP:
589 if (!components->nPort)
590 components->nPort = INTERNET_DEFAULT_FTP_PORT;
591 break;
592 case INTERNET_SCHEME_HTTP:
593 if (!components->nPort)
594 components->nPort = INTERNET_DEFAULT_HTTP_PORT;
595 break;
596 default:
597 ; /* do nothing */
600 TRACE("returning %d\n", ret);
601 return ret;
604 struct InetContext
606 HANDLE event;
607 DWORD timeout;
608 DWORD error;
611 static struct InetContext *CRYPT_MakeInetContext(DWORD dwTimeout)
613 struct InetContext *context = CryptMemAlloc(sizeof(struct InetContext));
615 if (context)
617 context->event = CreateEventW(NULL, FALSE, FALSE, NULL);
618 if (!context->event)
620 CryptMemFree(context);
621 context = NULL;
623 else
625 context->timeout = dwTimeout;
626 context->error = ERROR_SUCCESS;
629 return context;
632 static BOOL CRYPT_DownloadObject(DWORD dwRetrievalFlags, HINTERNET hHttp,
633 struct InetContext *context, PCRYPT_BLOB_ARRAY pObject,
634 PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
636 CRYPT_DATA_BLOB object = { 0, NULL };
637 DWORD bytesAvailable;
638 BOOL ret;
640 do {
641 if ((ret = InternetQueryDataAvailable(hHttp, &bytesAvailable, 0, 0)))
643 if (bytesAvailable)
645 if (object.pbData)
646 object.pbData = CryptMemRealloc(object.pbData,
647 object.cbData + bytesAvailable);
648 else
649 object.pbData = CryptMemAlloc(bytesAvailable);
650 if (object.pbData)
652 INTERNET_BUFFERSA buffer = { sizeof(buffer), 0 };
654 buffer.dwBufferLength = bytesAvailable;
655 buffer.lpvBuffer = object.pbData + object.cbData;
656 if (!(ret = InternetReadFileExA(hHttp, &buffer, IRF_NO_WAIT,
657 (DWORD_PTR)context)))
659 if (GetLastError() == ERROR_IO_PENDING)
661 if (WaitForSingleObject(context->event,
662 context->timeout) == WAIT_TIMEOUT)
663 SetLastError(ERROR_TIMEOUT);
664 else if (context->error)
665 SetLastError(context->error);
666 else
667 ret = TRUE;
670 if (ret)
671 object.cbData += buffer.dwBufferLength;
673 else
675 SetLastError(ERROR_OUTOFMEMORY);
676 ret = FALSE;
680 else if (GetLastError() == ERROR_IO_PENDING)
682 if (WaitForSingleObject(context->event, context->timeout) ==
683 WAIT_TIMEOUT)
684 SetLastError(ERROR_TIMEOUT);
685 else
686 ret = TRUE;
688 } while (ret && bytesAvailable);
689 if (ret)
691 pObject->rgBlob = CryptMemAlloc(sizeof(CRYPT_DATA_BLOB));
692 if (!pObject->rgBlob)
694 CryptMemFree(object.pbData);
695 SetLastError(ERROR_OUTOFMEMORY);
696 ret = FALSE;
698 else
700 pObject->rgBlob[0].cbData = object.cbData;
701 pObject->rgBlob[0].pbData = object.pbData;
702 pObject->cBlob = 1;
705 TRACE("returning %d\n", ret);
706 return ret;
709 /* Finds the object specified by pszURL in the cache. If it's not found,
710 * creates a new cache entry for the object and writes the object to it.
711 * Sets the expiration time of the cache entry to expires.
713 static void CRYPT_CacheURL(LPCWSTR pszURL, const CRYPT_BLOB_ARRAY *pObject,
714 DWORD dwRetrievalFlags, FILETIME expires)
716 WCHAR cacheFileName[MAX_PATH];
717 HANDLE hCacheFile;
718 DWORD size = 0, entryType;
719 FILETIME ft;
721 GetUrlCacheEntryInfoW(pszURL, NULL, &size);
722 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
724 INTERNET_CACHE_ENTRY_INFOW *info = CryptMemAlloc(size);
726 if (!info)
728 ERR("out of memory\n");
729 return;
732 if (GetUrlCacheEntryInfoW(pszURL, info, &size))
734 lstrcpyW(cacheFileName, info->lpszLocalFileName);
735 /* Check if the existing cache entry is up to date. If it isn't,
736 * remove the existing cache entry, and create a new one with the
737 * new value.
739 GetSystemTimeAsFileTime(&ft);
740 if (CompareFileTime(&info->ExpireTime, &ft) < 0)
742 DeleteUrlCacheEntryW(pszURL);
744 else
746 info->ExpireTime = expires;
747 SetUrlCacheEntryInfoW(pszURL, info, CACHE_ENTRY_EXPTIME_FC);
748 CryptMemFree(info);
749 return;
752 CryptMemFree(info);
755 if (!CreateUrlCacheEntryW(pszURL, pObject->rgBlob[0].cbData, NULL, cacheFileName, 0))
756 return;
758 hCacheFile = CreateFileW(cacheFileName, GENERIC_WRITE, 0,
759 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
760 if(hCacheFile == INVALID_HANDLE_VALUE)
761 return;
763 WriteFile(hCacheFile, pObject->rgBlob[0].pbData,
764 pObject->rgBlob[0].cbData, &size, NULL);
765 CloseHandle(hCacheFile);
767 if (!(dwRetrievalFlags & CRYPT_STICKY_CACHE_RETRIEVAL))
768 entryType = NORMAL_CACHE_ENTRY;
769 else
770 entryType = STICKY_CACHE_ENTRY;
771 memset(&ft, 0, sizeof(ft));
772 CommitUrlCacheEntryW(pszURL, cacheFileName, expires, ft, entryType,
773 NULL, 0, NULL, NULL);
776 static void CALLBACK CRYPT_InetStatusCallback(HINTERNET hInt,
777 DWORD_PTR dwContext, DWORD status, void *statusInfo, DWORD statusInfoLen)
779 struct InetContext *context = (struct InetContext *)dwContext;
780 LPINTERNET_ASYNC_RESULT result;
782 switch (status)
784 case INTERNET_STATUS_REQUEST_COMPLETE:
785 result = statusInfo;
786 context->error = result->dwError;
787 SetEvent(context->event);
791 static BOOL CRYPT_Connect(const URL_COMPONENTSW *components,
792 struct InetContext *context, PCRYPT_CREDENTIALS pCredentials,
793 HINTERNET *phInt, HINTERNET *phHost)
795 BOOL ret;
797 TRACE("(%s:%d, %p, %p, %p, %p)\n", debugstr_w(components->lpszHostName),
798 components->nPort, context, pCredentials, phInt, phInt);
800 *phHost = NULL;
801 *phInt = InternetOpenW(NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL,
802 context ? INTERNET_FLAG_ASYNC : 0);
803 if (*phInt)
805 DWORD service;
807 if (context)
808 InternetSetStatusCallbackW(*phInt, CRYPT_InetStatusCallback);
809 switch (components->nScheme)
811 case INTERNET_SCHEME_FTP:
812 service = INTERNET_SERVICE_FTP;
813 break;
814 case INTERNET_SCHEME_HTTP:
815 service = INTERNET_SERVICE_HTTP;
816 break;
817 default:
818 service = 0;
820 /* FIXME: use pCredentials for username/password */
821 *phHost = InternetConnectW(*phInt, components->lpszHostName,
822 components->nPort, NULL, NULL, service, 0, (DWORD_PTR)context);
823 if (!*phHost)
825 InternetCloseHandle(*phInt);
826 *phInt = NULL;
827 ret = FALSE;
829 else
830 ret = TRUE;
832 else
833 ret = FALSE;
834 TRACE("returning %d\n", ret);
835 return ret;
838 static BOOL WINAPI FTP_RetrieveEncodedObjectW(LPCWSTR pszURL,
839 LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
840 PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
841 void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
842 PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
844 FIXME("(%s, %s, %08lx, %ld, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
845 debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject,
846 ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo);
848 pObject->cBlob = 0;
849 pObject->rgBlob = NULL;
850 *ppfnFreeObject = CRYPT_FreeBlob;
851 *ppvFreeContext = NULL;
852 return FALSE;
855 static BOOL WINAPI HTTP_RetrieveEncodedObjectW(LPCWSTR pszURL,
856 LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
857 PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
858 void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
859 PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
861 BOOL ret = FALSE;
863 TRACE("(%s, %s, %08lx, %ld, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
864 debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject,
865 ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo);
867 pObject->cBlob = 0;
868 pObject->rgBlob = NULL;
869 *ppfnFreeObject = CRYPT_FreeBlob;
870 *ppvFreeContext = NULL;
872 if (!(dwRetrievalFlags & CRYPT_WIRE_ONLY_RETRIEVAL))
873 ret = CRYPT_GetObjectFromCache(pszURL, pObject, pAuxInfo);
874 if (!ret && (!(dwRetrievalFlags & CRYPT_CACHE_ONLY_RETRIEVAL) ||
875 (dwRetrievalFlags & CRYPT_WIRE_ONLY_RETRIEVAL)))
877 URL_COMPONENTSW components;
879 if ((ret = CRYPT_CrackUrl(pszURL, &components)))
881 HINTERNET hInt, hHost;
882 struct InetContext *context = NULL;
884 if (dwTimeout)
885 context = CRYPT_MakeInetContext(dwTimeout);
886 ret = CRYPT_Connect(&components, context, pCredentials, &hInt,
887 &hHost);
888 if (ret)
890 static LPCWSTR types[] =
892 L"application/x-x509-ca-cert", L"application/x-x509-email-cert",
893 L"application/x-x509-server-cert", L"application/x-x509-user-cert",
894 L"application/x-pkcs7-certificates", L"application/pkix-crl",
895 L"application/x-pkcs7-crl", L"application/x-pkcs7-signature",
896 L"application/x-pkcs7-mime", NULL
898 HINTERNET hHttp = HttpOpenRequestW(hHost, NULL,
899 components.lpszUrlPath, NULL, NULL, types,
900 INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_UI,
901 (DWORD_PTR)context);
903 if (hHttp)
905 if (dwTimeout)
907 InternetSetOptionW(hHttp,
908 INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeout,
909 sizeof(dwTimeout));
910 InternetSetOptionW(hHttp, INTERNET_OPTION_SEND_TIMEOUT,
911 &dwTimeout, sizeof(dwTimeout));
913 ret = HttpSendRequestExW(hHttp, NULL, NULL, 0,
914 (DWORD_PTR)context);
915 if (!ret && GetLastError() == ERROR_IO_PENDING)
917 if (WaitForSingleObject(context->event,
918 context->timeout) == WAIT_TIMEOUT)
919 SetLastError(ERROR_TIMEOUT);
920 else
921 ret = TRUE;
923 if (ret &&
924 !(ret = HttpEndRequestW(hHttp, NULL, 0, (DWORD_PTR)context)) &&
925 GetLastError() == ERROR_IO_PENDING)
927 if (WaitForSingleObject(context->event,
928 context->timeout) == WAIT_TIMEOUT)
929 SetLastError(ERROR_TIMEOUT);
930 else
931 ret = TRUE;
933 if (ret)
934 ret = CRYPT_DownloadObject(dwRetrievalFlags, hHttp,
935 context, pObject, pAuxInfo);
936 if (ret && !(dwRetrievalFlags & CRYPT_DONT_CACHE_RESULT))
938 SYSTEMTIME st;
939 FILETIME ft;
940 DWORD len = sizeof(st);
942 if (HttpQueryInfoW(hHttp, HTTP_QUERY_EXPIRES | HTTP_QUERY_FLAG_SYSTEMTIME,
943 &st, &len, NULL) && SystemTimeToFileTime(&st, &ft))
944 CRYPT_CacheURL(pszURL, pObject, dwRetrievalFlags, ft);
946 InternetCloseHandle(hHttp);
948 InternetCloseHandle(hHost);
949 InternetCloseHandle(hInt);
951 if (context)
953 CloseHandle(context->event);
954 CryptMemFree(context);
956 CryptMemFree(components.lpszUrlPath);
957 CryptMemFree(components.lpszHostName);
960 TRACE("returning %d\n", ret);
961 return ret;
964 static BOOL WINAPI File_RetrieveEncodedObjectW(LPCWSTR pszURL,
965 LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
966 PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
967 void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
968 PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
970 URL_COMPONENTSW components = { sizeof(components), 0 };
971 BOOL ret;
973 TRACE("(%s, %s, %08lx, %ld, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
974 debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject,
975 ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo);
977 pObject->cBlob = 0;
978 pObject->rgBlob = NULL;
979 *ppfnFreeObject = CRYPT_FreeBlob;
980 *ppvFreeContext = NULL;
982 components.lpszUrlPath = CryptMemAlloc(INTERNET_MAX_PATH_LENGTH * sizeof(WCHAR));
983 components.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
984 if (!components.lpszUrlPath)
986 SetLastError(ERROR_OUTOFMEMORY);
987 return FALSE;
990 ret = InternetCrackUrlW(pszURL, 0, ICU_DECODE, &components);
991 if (ret)
993 LPWSTR path;
995 /* 3 == lstrlenW(L"c:") + 1 */
996 path = CryptMemAlloc((components.dwUrlPathLength + 3) * sizeof(WCHAR));
997 if (path)
999 HANDLE hFile;
1001 /* Try to create the file directly - Wine handles / in pathnames */
1002 lstrcpynW(path, components.lpszUrlPath,
1003 components.dwUrlPathLength + 1);
1004 hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
1005 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1006 if (hFile == INVALID_HANDLE_VALUE)
1008 /* Try again on the current drive */
1009 GetCurrentDirectoryW(components.dwUrlPathLength, path);
1010 if (path[1] == ':')
1012 lstrcpynW(path + 2, components.lpszUrlPath,
1013 components.dwUrlPathLength + 1);
1014 hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
1015 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1017 if (hFile == INVALID_HANDLE_VALUE)
1019 /* Try again on the Windows drive */
1020 GetWindowsDirectoryW(path, components.dwUrlPathLength);
1021 if (path[1] == ':')
1023 lstrcpynW(path + 2, components.lpszUrlPath,
1024 components.dwUrlPathLength + 1);
1025 hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
1026 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1030 if (hFile != INVALID_HANDLE_VALUE)
1032 if ((ret = CRYPT_GetObjectFromFile(hFile, pObject)))
1034 if (pAuxInfo && pAuxInfo->cbSize >= RTL_SIZEOF_THROUGH_FIELD(CRYPT_RETRIEVE_AUX_INFO, pLastSyncTime)
1035 && pAuxInfo->pLastSyncTime)
1037 GetFileTime(hFile, NULL, NULL,
1038 pAuxInfo->pLastSyncTime);
1041 CloseHandle(hFile);
1043 else
1044 ret = FALSE;
1045 CryptMemFree(path);
1047 else
1049 SetLastError(ERROR_OUTOFMEMORY);
1050 ret = FALSE;
1053 CryptMemFree(components.lpszUrlPath);
1054 return ret;
1057 typedef BOOL (WINAPI *SchemeDllRetrieveEncodedObjectW)(LPCWSTR pwszUrl,
1058 LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
1059 PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
1060 void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
1061 PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo);
1063 static BOOL CRYPT_GetRetrieveFunction(LPCWSTR pszURL,
1064 SchemeDllRetrieveEncodedObjectW *pFunc, HCRYPTOIDFUNCADDR *phFunc)
1066 URL_COMPONENTSW components = { sizeof(components), 0 };
1067 BOOL ret;
1069 TRACE("(%s, %p, %p)\n", debugstr_w(pszURL), pFunc, phFunc);
1071 *pFunc = NULL;
1072 *phFunc = 0;
1073 components.dwSchemeLength = 1;
1074 ret = InternetCrackUrlW(pszURL, 0, 0, &components);
1075 if (ret)
1077 /* Microsoft always uses CryptInitOIDFunctionSet/
1078 * CryptGetOIDFunctionAddress, but there doesn't seem to be a pressing
1079 * reason to do so for builtin schemes.
1081 switch (components.nScheme)
1083 case INTERNET_SCHEME_FTP:
1084 *pFunc = FTP_RetrieveEncodedObjectW;
1085 break;
1086 case INTERNET_SCHEME_HTTP:
1087 *pFunc = HTTP_RetrieveEncodedObjectW;
1088 break;
1089 case INTERNET_SCHEME_FILE:
1090 *pFunc = File_RetrieveEncodedObjectW;
1091 break;
1092 default:
1094 int len = WideCharToMultiByte(CP_ACP, 0, components.lpszScheme,
1095 components.dwSchemeLength, NULL, 0, NULL, NULL);
1097 if (len)
1099 LPSTR scheme = CryptMemAlloc(len);
1101 if (scheme)
1103 static HCRYPTOIDFUNCSET set = NULL;
1105 if (!set)
1106 set = CryptInitOIDFunctionSet(
1107 SCHEME_OID_RETRIEVE_ENCODED_OBJECTW_FUNC, 0);
1108 WideCharToMultiByte(CP_ACP, 0, components.lpszScheme,
1109 components.dwSchemeLength, scheme, len, NULL, NULL);
1110 ret = CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING,
1111 scheme, 0, (void **)pFunc, phFunc);
1112 CryptMemFree(scheme);
1114 else
1116 SetLastError(ERROR_OUTOFMEMORY);
1117 ret = FALSE;
1120 else
1121 ret = FALSE;
1125 TRACE("returning %d\n", ret);
1126 return ret;
1129 static BOOL WINAPI CRYPT_CreateBlob(LPCSTR pszObjectOid,
1130 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1132 DWORD size, i;
1133 CRYPT_BLOB_ARRAY *context;
1134 BOOL ret = FALSE;
1136 size = sizeof(CRYPT_BLOB_ARRAY) + pObject->cBlob * sizeof(CRYPT_DATA_BLOB);
1137 for (i = 0; i < pObject->cBlob; i++)
1138 size += pObject->rgBlob[i].cbData;
1139 context = CryptMemAlloc(size);
1140 if (context)
1142 LPBYTE nextData;
1144 context->cBlob = 0;
1145 context->rgBlob =
1146 (CRYPT_DATA_BLOB *)((LPBYTE)context + sizeof(CRYPT_BLOB_ARRAY));
1147 nextData =
1148 (LPBYTE)context->rgBlob + pObject->cBlob * sizeof(CRYPT_DATA_BLOB);
1149 for (i = 0; i < pObject->cBlob; i++)
1151 memcpy(nextData, pObject->rgBlob[i].pbData,
1152 pObject->rgBlob[i].cbData);
1153 context->rgBlob[i].pbData = nextData;
1154 context->rgBlob[i].cbData = pObject->rgBlob[i].cbData;
1155 nextData += pObject->rgBlob[i].cbData;
1156 context->cBlob++;
1158 *ppvContext = context;
1159 ret = TRUE;
1161 return ret;
1164 typedef BOOL (WINAPI *AddContextToStore)(HCERTSTORE hCertStore,
1165 const void *pContext, DWORD dwAddDisposition, const void **ppStoreContext);
1167 static BOOL decode_base64_blob( const CRYPT_DATA_BLOB *in, CRYPT_DATA_BLOB *out )
1169 BOOL ret;
1170 DWORD len = in->cbData;
1172 while (len && !in->pbData[len - 1]) len--;
1173 if (!CryptStringToBinaryA( (char *)in->pbData, len, CRYPT_STRING_BASE64_ANY,
1174 NULL, &out->cbData, NULL, NULL )) return FALSE;
1176 if (!(out->pbData = CryptMemAlloc( out->cbData ))) return FALSE;
1177 ret = CryptStringToBinaryA( (char *)in->pbData, len, CRYPT_STRING_BASE64_ANY,
1178 out->pbData, &out->cbData, NULL, NULL );
1179 if (!ret) CryptMemFree( out->pbData );
1180 return ret;
1183 static BOOL CRYPT_CreateContext(const CRYPT_BLOB_ARRAY *pObject,
1184 DWORD dwExpectedContentTypeFlags, AddContextToStore addFunc, void **ppvContext)
1186 BOOL ret = TRUE;
1187 CRYPT_DATA_BLOB blob;
1189 if (!pObject->cBlob)
1191 SetLastError(ERROR_INVALID_DATA);
1192 *ppvContext = NULL;
1193 ret = FALSE;
1195 else if (pObject->cBlob == 1)
1197 if (decode_base64_blob(&pObject->rgBlob[0], &blob))
1199 ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob,
1200 dwExpectedContentTypeFlags, CERT_QUERY_FORMAT_FLAG_BINARY, 0,
1201 NULL, NULL, NULL, NULL, NULL, (const void **)ppvContext);
1202 CryptMemFree(blob.pbData);
1204 else
1206 ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &pObject->rgBlob[0],
1207 dwExpectedContentTypeFlags, CERT_QUERY_FORMAT_FLAG_BINARY, 0,
1208 NULL, NULL, NULL, NULL, NULL, (const void **)ppvContext);
1210 if (!ret)
1212 SetLastError(CRYPT_E_NO_MATCH);
1213 ret = FALSE;
1216 else
1218 HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
1219 CERT_STORE_CREATE_NEW_FLAG, NULL);
1221 if (store)
1223 DWORD i;
1224 const void *context;
1226 for (i = 0; i < pObject->cBlob; i++)
1228 if (decode_base64_blob(&pObject->rgBlob[i], &blob))
1230 ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &blob,
1231 dwExpectedContentTypeFlags, CERT_QUERY_FORMAT_FLAG_BINARY,
1232 0, NULL, NULL, NULL, NULL, NULL, &context);
1233 CryptMemFree(blob.pbData);
1235 else
1237 ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
1238 &pObject->rgBlob[i], dwExpectedContentTypeFlags,
1239 CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL, NULL,
1240 NULL, &context);
1242 if (ret)
1244 if (!addFunc(store, context, CERT_STORE_ADD_ALWAYS, NULL))
1245 ret = FALSE;
1247 else
1249 SetLastError(CRYPT_E_NO_MATCH);
1250 ret = FALSE;
1254 else
1255 ret = FALSE;
1256 *ppvContext = store;
1258 return ret;
1261 static BOOL WINAPI CRYPT_CreateCert(LPCSTR pszObjectOid,
1262 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1264 return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CERT,
1265 (AddContextToStore)CertAddCertificateContextToStore, ppvContext);
1268 static BOOL WINAPI CRYPT_CreateCRL(LPCSTR pszObjectOid,
1269 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1271 return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CRL,
1272 (AddContextToStore)CertAddCRLContextToStore, ppvContext);
1275 static BOOL WINAPI CRYPT_CreateCTL(LPCSTR pszObjectOid,
1276 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1278 return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CTL,
1279 (AddContextToStore)CertAddCTLContextToStore, ppvContext);
1282 static BOOL WINAPI CRYPT_CreatePKCS7(LPCSTR pszObjectOid,
1283 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1285 BOOL ret;
1287 if (!pObject->cBlob)
1289 SetLastError(ERROR_INVALID_DATA);
1290 *ppvContext = NULL;
1291 ret = FALSE;
1293 else if (pObject->cBlob == 1)
1294 ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &pObject->rgBlob[0],
1295 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
1296 CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED, CERT_QUERY_FORMAT_FLAG_BINARY,
1297 0, NULL, NULL, NULL, ppvContext, NULL, NULL);
1298 else
1300 FIXME("multiple messages unimplemented\n");
1301 ret = FALSE;
1303 return ret;
1306 static BOOL WINAPI CRYPT_CreateAny(LPCSTR pszObjectOid,
1307 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1309 BOOL ret;
1311 if (!pObject->cBlob)
1313 SetLastError(ERROR_INVALID_DATA);
1314 *ppvContext = NULL;
1315 ret = FALSE;
1317 else
1319 HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
1320 CERT_STORE_CREATE_NEW_FLAG, NULL);
1322 if (store)
1324 HCERTSTORE memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
1325 CERT_STORE_CREATE_NEW_FLAG, NULL);
1327 if (memStore)
1329 CertAddStoreToCollection(store, memStore,
1330 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 0);
1331 CertCloseStore(memStore, 0);
1333 else
1335 CertCloseStore(store, 0);
1336 store = NULL;
1339 if (store)
1341 DWORD i;
1343 ret = TRUE;
1344 for (i = 0; i < pObject->cBlob; i++)
1346 DWORD contentType, expectedContentTypes =
1347 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
1348 CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED |
1349 CERT_QUERY_CONTENT_FLAG_CERT |
1350 CERT_QUERY_CONTENT_FLAG_CRL |
1351 CERT_QUERY_CONTENT_FLAG_CTL;
1352 HCERTSTORE contextStore;
1353 const void *context;
1355 if (CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
1356 &pObject->rgBlob[i], expectedContentTypes,
1357 CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, &contentType, NULL,
1358 &contextStore, NULL, &context))
1360 switch (contentType)
1362 case CERT_QUERY_CONTENT_CERT:
1363 if (!CertAddCertificateContextToStore(store,
1364 context, CERT_STORE_ADD_ALWAYS, NULL))
1365 ret = FALSE;
1366 CertFreeCertificateContext(context);
1367 break;
1368 case CERT_QUERY_CONTENT_CRL:
1369 if (!CertAddCRLContextToStore(store,
1370 context, CERT_STORE_ADD_ALWAYS, NULL))
1371 ret = FALSE;
1372 CertFreeCRLContext(context);
1373 break;
1374 case CERT_QUERY_CONTENT_CTL:
1375 if (!CertAddCTLContextToStore(store,
1376 context, CERT_STORE_ADD_ALWAYS, NULL))
1377 ret = FALSE;
1378 CertFreeCTLContext(context);
1379 break;
1380 default:
1381 CertAddStoreToCollection(store, contextStore, 0, 0);
1383 CertCloseStore(contextStore, 0);
1385 else
1386 ret = FALSE;
1389 else
1390 ret = FALSE;
1391 *ppvContext = store;
1393 return ret;
1396 typedef BOOL (WINAPI *ContextDllCreateObjectContext)(LPCSTR pszObjectOid,
1397 DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext);
1399 static BOOL CRYPT_GetCreateFunction(LPCSTR pszObjectOid,
1400 ContextDllCreateObjectContext *pFunc, HCRYPTOIDFUNCADDR *phFunc)
1402 BOOL ret = TRUE;
1404 TRACE("(%s, %p, %p)\n", debugstr_a(pszObjectOid), pFunc, phFunc);
1406 *pFunc = NULL;
1407 *phFunc = 0;
1408 if (IS_INTOID(pszObjectOid))
1410 switch (LOWORD(pszObjectOid))
1412 case 0:
1413 *pFunc = CRYPT_CreateBlob;
1414 break;
1415 case LOWORD(CONTEXT_OID_CERTIFICATE):
1416 *pFunc = CRYPT_CreateCert;
1417 break;
1418 case LOWORD(CONTEXT_OID_CRL):
1419 *pFunc = CRYPT_CreateCRL;
1420 break;
1421 case LOWORD(CONTEXT_OID_CTL):
1422 *pFunc = CRYPT_CreateCTL;
1423 break;
1424 case LOWORD(CONTEXT_OID_PKCS7):
1425 *pFunc = CRYPT_CreatePKCS7;
1426 break;
1427 case LOWORD(CONTEXT_OID_CAPI2_ANY):
1428 *pFunc = CRYPT_CreateAny;
1429 break;
1432 if (!*pFunc)
1434 static HCRYPTOIDFUNCSET set = NULL;
1436 if (!set)
1437 set = CryptInitOIDFunctionSet(
1438 CONTEXT_OID_CREATE_OBJECT_CONTEXT_FUNC, 0);
1439 ret = CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, pszObjectOid,
1440 0, (void **)pFunc, phFunc);
1442 TRACE("returning %d\n", ret);
1443 return ret;
1446 static BOOL CRYPT_GetExpiration(const void *object, const char *pszObjectOid, FILETIME *expiration)
1448 if (!IS_INTOID(pszObjectOid))
1449 return FALSE;
1451 switch (LOWORD(pszObjectOid)) {
1452 case LOWORD(CONTEXT_OID_CERTIFICATE):
1453 *expiration = ((const CERT_CONTEXT*)object)->pCertInfo->NotAfter;
1454 return TRUE;
1455 case LOWORD(CONTEXT_OID_CRL):
1456 *expiration = ((const CRL_CONTEXT*)object)->pCrlInfo->NextUpdate;
1457 return TRUE;
1458 case LOWORD(CONTEXT_OID_CTL):
1459 *expiration = ((const CTL_CONTEXT*)object)->pCtlInfo->NextUpdate;
1460 return TRUE;
1463 return FALSE;
1466 /***********************************************************************
1467 * CryptRetrieveObjectByUrlW (CRYPTNET.@)
1469 BOOL WINAPI CryptRetrieveObjectByUrlW(LPCWSTR pszURL, LPCSTR pszObjectOid,
1470 DWORD dwRetrievalFlags, DWORD dwTimeout, LPVOID *ppvObject,
1471 HCRYPTASYNC hAsyncRetrieve, PCRYPT_CREDENTIALS pCredentials, LPVOID pvVerify,
1472 PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
1474 BOOL ret;
1475 SchemeDllRetrieveEncodedObjectW retrieve;
1476 ContextDllCreateObjectContext create;
1477 HCRYPTOIDFUNCADDR hRetrieve = 0, hCreate = 0;
1479 TRACE("(%s, %s, %08lx, %ld, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
1480 debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, ppvObject,
1481 hAsyncRetrieve, pCredentials, pvVerify, pAuxInfo);
1483 if (!pszURL)
1485 SetLastError(ERROR_INVALID_PARAMETER);
1486 return FALSE;
1488 ret = CRYPT_GetRetrieveFunction(pszURL, &retrieve, &hRetrieve);
1489 if (ret)
1490 ret = CRYPT_GetCreateFunction(pszObjectOid, &create, &hCreate);
1491 if (ret)
1493 CRYPT_BLOB_ARRAY object = { 0, NULL };
1494 PFN_FREE_ENCODED_OBJECT_FUNC freeObject;
1495 void *freeContext;
1496 FILETIME expires;
1498 ret = retrieve(pszURL, pszObjectOid, dwRetrievalFlags, dwTimeout,
1499 &object, &freeObject, &freeContext, hAsyncRetrieve, pCredentials,
1500 pAuxInfo);
1501 if (ret)
1503 ret = create(pszObjectOid, dwRetrievalFlags, &object, ppvObject);
1504 if (ret && !(dwRetrievalFlags & CRYPT_DONT_CACHE_RESULT) &&
1505 CRYPT_GetExpiration(*ppvObject, pszObjectOid, &expires))
1507 CRYPT_CacheURL(pszURL, &object, dwRetrievalFlags, expires);
1509 freeObject(pszObjectOid, &object, freeContext);
1512 if (hCreate)
1513 CryptFreeOIDFunctionAddress(hCreate, 0);
1514 if (hRetrieve)
1515 CryptFreeOIDFunctionAddress(hRetrieve, 0);
1516 TRACE("returning %d\n", ret);
1517 return ret;
1520 /* Store successful revocation checks (whether the certificate was revoked or
1521 * not) in an on-disk cache. This is not because of network latency—we already
1522 * have a cache for that—but rather because parsing very large CRLs can take a
1523 * long time (at the time of writing, 20 MB CRLs have been seen in the wild and
1524 * can take several hundred milliseconds) and applications expect chain building
1525 * to be much faster.
1527 * The cache is treated as invalid once we pass the nextUpdate field of the CRL.
1528 * This isn't quite what the field is meant for (it's rather meant to specify a
1529 * later bound for the next time the CRL will be reissued, and doesn't prescribe
1530 * a date by which the CRL is invalid; see RFC 5280 § 5.1.2.5) but it's the way
1531 * it's used in practice.
1533 * The location of the cache roughly matches Windows, but the file name and
1534 * contents do not.
1537 static const char revocation_cache_signature[] = "Wine cached revocation";
1539 #define CACHED_CERT_HASH_SIZE 20
1541 static FILE *open_cached_revocation_file(const CERT_CONTEXT *cert, const CERT_REVOCATION_PARA *params,
1542 const WCHAR *mode, int sharing)
1544 BYTE hash_data[CACHED_CERT_HASH_SIZE];
1545 WCHAR path[MAX_PATH];
1546 WCHAR *appdata_path;
1547 DWORD len, i, size;
1548 HCRYPTPROV prov;
1549 HCRYPTHASH hash;
1550 HRESULT hr;
1552 if (FAILED(hr = SHGetKnownFolderPath(&FOLDERID_LocalAppDataLow, 0, NULL, &appdata_path)))
1554 ERR("Failed to get LocalAppDataLow path, hr %#lx.\n", hr);
1555 return INVALID_HANDLE_VALUE;
1558 len = swprintf(path, ARRAY_SIZE(path), L"%s\\Microsoft\\CryptnetUrlCache\\Content\\", appdata_path);
1559 CoTaskMemFree(appdata_path);
1561 if (len + CACHED_CERT_HASH_SIZE * 2 * sizeof(WCHAR) > ARRAY_SIZE(path) - 1)
1563 WARN("Hash length exceeds static buffer; not caching.\n");
1564 return INVALID_HANDLE_VALUE;
1567 CryptAcquireContextW(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
1568 CryptCreateHash(prov, CALG_SHA1, 0, 0, &hash);
1569 CryptHashData(hash, cert->pbCertEncoded, cert->cbCertEncoded, 0);
1570 if (params && params->pIssuerCert)
1572 CryptHashData(hash, (BYTE *)&params->pIssuerCert->cbCertEncoded, sizeof(params->pIssuerCert->cbCertEncoded), 0);
1573 CryptHashData(hash, params->pIssuerCert->pbCertEncoded, params->pIssuerCert->cbCertEncoded, 0);
1575 else
1577 size = 0;
1578 CryptHashData(hash, (BYTE *)&size, sizeof(size), 0);
1580 size = sizeof(hash_data);
1581 CryptGetHashParam(hash, HP_HASHVAL, hash_data, &size, 0);
1582 CryptDestroyHash(hash);
1583 CryptReleaseContext(prov, 0);
1585 SHCreateDirectoryExW(NULL, path, NULL);
1587 for (i = 0; i < CACHED_CERT_HASH_SIZE; ++i)
1589 swprintf(path + len, 3, L"%02x", hash_data[i]);
1590 len += 2;
1593 return _wfsopen(path, mode, sharing);
1596 static BOOL find_cached_revocation_status(const CERT_CONTEXT *cert, const CERT_REVOCATION_PARA *params,
1597 const FILETIME *time, CERT_REVOCATION_STATUS *status)
1599 char buffer[sizeof(revocation_cache_signature)];
1600 FILETIME update_time;
1601 FILE *file;
1602 int len;
1604 if (!(file = open_cached_revocation_file(cert, params, L"rb", _SH_DENYWR)))
1605 return FALSE;
1607 if ((len = fread(buffer, 1, sizeof(buffer), file)) != sizeof(buffer)
1608 || memcmp(buffer, revocation_cache_signature, len))
1610 ERR("Invalid cache signature.\n");
1611 fclose(file);
1612 return FALSE;
1615 if (fread(&update_time, sizeof(update_time), 1, file) != 1)
1617 ERR("Failed to read update time.\n");
1618 fclose(file);
1619 return FALSE;
1622 if (CompareFileTime(time, &update_time) > 0)
1624 TRACE("Cached revocation status is potentially out of date.\n");
1625 fclose(file);
1626 return FALSE;
1629 if (fread(&status->dwError, sizeof(status->dwError), 1, file) != 1)
1631 ERR("Failed to read error code.\n");
1632 fclose(file);
1633 return FALSE;
1636 if (status->dwError == CERT_E_REVOKED && fread(&status->dwReason, sizeof(status->dwReason), 1, file) != 1)
1638 ERR("Failed to read revocation reason.\n");
1639 fclose(file);
1640 return FALSE;
1643 TRACE("Using cached status %#lx, reason %#lx.\n", status->dwError, status->dwReason);
1644 return TRUE;
1647 static void cache_revocation_status(const CERT_CONTEXT *cert, const CERT_REVOCATION_PARA *params,
1648 const FILETIME *time, const CERT_REVOCATION_STATUS *status)
1650 FILE *file;
1652 if (!(file = open_cached_revocation_file(cert, params, L"wb", _SH_DENYRW)))
1653 return;
1654 fwrite(revocation_cache_signature, 1, sizeof(revocation_cache_signature), file);
1655 fwrite(time, sizeof(*time), 1, file);
1656 fwrite(&status->dwError, sizeof(status->dwError), 1, file);
1657 if (status->dwError == CERT_E_REVOKED)
1658 fwrite(&status->dwReason, sizeof(status->dwReason), 1, file);
1659 fclose(file);
1662 static DWORD verify_cert_revocation_with_crl_online(const CERT_CONTEXT *cert,
1663 const CRL_CONTEXT *crl, FILETIME *pTime, CERT_REVOCATION_STATUS *pRevStatus)
1665 PCRL_ENTRY entry = NULL;
1667 CertFindCertificateInCRL(cert, crl, 0, NULL, &entry);
1668 if (entry)
1669 return CRYPT_E_REVOKED;
1671 /* Since the CRL was retrieved for the cert being checked, then it's
1672 * guaranteed to be fresh, and the cert is not revoked. */
1673 return ERROR_SUCCESS;
1676 /* Try to retrieve a CRL from any one of the specified distribution points. */
1677 static const CRL_CONTEXT *retrieve_crl_from_dist_points(const CRYPT_URL_ARRAY *array,
1678 DWORD verify_flags, DWORD timeout)
1680 DWORD retrieve_flags = 0;
1681 const CRL_CONTEXT *crl;
1682 DWORD i;
1684 if (verify_flags & CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION)
1685 retrieve_flags |= CRYPT_CACHE_ONLY_RETRIEVAL;
1687 /* Yes, this is a weird algorithm, but the documentation for
1688 * CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT specifies this, and
1689 * tests seem to bear it out for CertVerifyRevocation() as well. */
1690 if (verify_flags & CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG)
1691 timeout /= 2;
1693 for (i = 0; i < array->cUrl; ++i)
1695 if (CryptRetrieveObjectByUrlW(array->rgwszUrl[i], CONTEXT_OID_CRL, retrieve_flags,
1696 timeout, (void **)&crl, NULL, NULL, NULL, NULL))
1697 return crl;
1699 /* We don't check the current time here. This may result in less
1700 * accurate timeouts, but this too seems to be true of Windows. */
1701 if ((verify_flags & CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG) && GetLastError() == ERROR_TIMEOUT)
1702 timeout /= 2;
1705 return NULL;
1708 static DWORD verify_cert_revocation_from_dist_points_ext(const CRYPT_DATA_BLOB *value, const CERT_CONTEXT *cert,
1709 FILETIME *time, DWORD flags, const CERT_REVOCATION_PARA *params, CERT_REVOCATION_STATUS *status,
1710 FILETIME *next_update)
1712 DWORD url_array_size, error;
1713 CRYPT_URL_ARRAY *url_array;
1714 const CRL_CONTEXT *crl;
1715 DWORD timeout = 0;
1717 if (!params || !params->pIssuerCert)
1719 TRACE("no issuer certificate\n");
1720 return CRYPT_E_REVOCATION_OFFLINE;
1723 if (!CRYPT_GetUrlFromCRLDistPointsExt(value, NULL, &url_array_size, NULL, NULL))
1724 return GetLastError();
1726 if (!(url_array = CryptMemAlloc(url_array_size)))
1727 return ERROR_OUTOFMEMORY;
1729 if (!CRYPT_GetUrlFromCRLDistPointsExt(value, url_array, &url_array_size, NULL, NULL))
1731 CryptMemFree(url_array);
1732 return GetLastError();
1735 if (params && params->cbSize >= RTL_SIZEOF_THROUGH_FIELD(CERT_REVOCATION_PARA, dwUrlRetrievalTimeout))
1736 timeout = params->dwUrlRetrievalTimeout;
1738 if (!(crl = retrieve_crl_from_dist_points(url_array, flags, timeout)))
1740 CryptMemFree(url_array);
1741 return CRYPT_E_REVOCATION_OFFLINE;
1744 error = verify_cert_revocation_with_crl_online(cert, crl, time, status);
1746 *next_update = crl->pCrlInfo->NextUpdate;
1748 CertFreeCRLContext(crl);
1749 CryptMemFree(url_array);
1750 return error;
1753 static void sha1_hash(const BYTE *data, DWORD datalen, BYTE *buf, DWORD *buflen)
1755 HCRYPTPROV prov;
1756 HCRYPTHASH hash;
1758 CryptAcquireContextW(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT);
1759 CryptCreateHash(prov, CALG_SHA1, 0, 0, &hash);
1760 CryptHashData(hash, data, datalen, 0);
1761 CryptGetHashParam(hash, HP_HASHVAL, buf, buflen, 0);
1763 CryptDestroyHash(hash);
1764 CryptReleaseContext(prov, 0);
1767 static BYTE *build_ocsp_request(const CERT_CONTEXT *cert, const CERT_CONTEXT *issuer_cert, DWORD *ret_size)
1769 OCSP_REQUEST_ENTRY entry;
1770 OCSP_REQUEST_INFO request;
1771 OCSP_SIGNED_REQUEST_INFO request_signed;
1772 CERT_INFO *issuer = issuer_cert->pCertInfo;
1773 BYTE issuer_name_hash[20], issuer_key_hash[20], *buf, *ret;
1774 DWORD size = 0, hash_len = sizeof(issuer_name_hash);
1776 memset(&entry, 0, sizeof(entry));
1777 entry.CertId.HashAlgorithm.pszObjId = (char *)szOID_OIWSEC_sha1;
1779 sha1_hash(issuer->Subject.pbData, issuer->Subject.cbData, issuer_name_hash, &hash_len);
1780 entry.CertId.IssuerNameHash.cbData = sizeof(issuer_name_hash);
1781 entry.CertId.IssuerNameHash.pbData = issuer_name_hash;
1783 sha1_hash(issuer->SubjectPublicKeyInfo.PublicKey.pbData, issuer->SubjectPublicKeyInfo.PublicKey.cbData,
1784 issuer_key_hash, &hash_len);
1785 entry.CertId.IssuerKeyHash.cbData = sizeof(issuer_key_hash);
1786 entry.CertId.IssuerKeyHash.pbData = issuer_key_hash;
1788 entry.CertId.SerialNumber.cbData = cert->pCertInfo->SerialNumber.cbData;
1789 entry.CertId.SerialNumber.pbData = cert->pCertInfo->SerialNumber.pbData;
1791 request.dwVersion = OCSP_REQUEST_V1;
1792 request.pRequestorName = NULL;
1793 request.cRequestEntry = 1;
1794 request.rgRequestEntry = &entry;
1795 request.cExtension = 0;
1796 request.rgExtension = NULL;
1797 if (!CryptEncodeObjectEx(X509_ASN_ENCODING, OCSP_REQUEST, &request, CRYPT_ENCODE_ALLOC_FLAG, NULL, &buf, &size))
1799 ERR("failed to encode request %#lx\n", GetLastError());
1800 return NULL;
1803 request_signed.ToBeSigned.pbData = buf;
1804 request_signed.ToBeSigned.cbData = size;
1805 request_signed.pOptionalSignatureInfo = NULL;
1806 if (!CryptEncodeObjectEx(X509_ASN_ENCODING, OCSP_SIGNED_REQUEST, &request_signed, CRYPT_ENCODE_ALLOC_FLAG, NULL,
1807 &ret, &size))
1809 ERR("failed to encode signed request %#lx\n", GetLastError());
1810 LocalFree(buf);
1811 return NULL;
1814 LocalFree(buf);
1815 *ret_size = size;
1816 return ret;
1819 static void escape_path(const WCHAR *src, DWORD src_len, WCHAR *dst, DWORD *dst_len)
1821 static const WCHAR hex[] = L"0123456789ABCDEF";
1822 WCHAR *ptr = dst;
1823 DWORD i;
1825 *dst_len = src_len;
1826 for (i = 0; i < src_len; i++)
1828 if (src[i] == '+' || src[i] == '/' || src[i] == '=')
1830 if (dst)
1832 ptr[0] = '%';
1833 ptr[1] = hex[(src[i] >> 4) & 0xf];
1834 ptr[2] = hex[src[i] & 0xf];
1835 ptr += 3;
1837 *dst_len += 2;
1839 else if (dst) *ptr++ = src[i];
1843 static WCHAR *build_request_path(const BYTE *data, DWORD data_size)
1845 WCHAR *path, *ret;
1846 DWORD path_len, ret_len;
1848 if (!CryptBinaryToStringW(data, data_size, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, NULL, &path_len)) return NULL;
1849 if (!(path = malloc(path_len * sizeof(WCHAR)))) return NULL;
1850 CryptBinaryToStringW(data, data_size, CRYPT_STRING_BASE64 | CRYPT_STRING_NOCRLF, path, &path_len);
1852 escape_path(path, path_len, NULL, &ret_len);
1853 if (!(ret = malloc((ret_len + 2) * sizeof(WCHAR))))
1855 free(path);
1856 return NULL;
1858 escape_path(path, path_len, ret + 1, &ret_len);
1859 ret[ret_len + 1] = 0;
1860 ret[0] = '/';
1862 free(path);
1863 return ret;
1866 static WCHAR *build_request_url(const WCHAR *base_url, const BYTE *data, DWORD data_size)
1868 WCHAR *path, *ret;
1869 DWORD len = 0;
1871 if (!(path = build_request_path(data, data_size))) return NULL;
1872 len = (wcslen(base_url) + wcslen(path) + 1) * sizeof(WCHAR);
1873 if (!(ret = malloc(len * sizeof(WCHAR))))
1875 free(path);
1876 return NULL;
1878 wcscpy(ret, base_url);
1879 wcscat(ret, path);
1880 free(path);
1881 TRACE("-> %s.\n", debugstr_w(ret));
1882 return ret;
1885 static DWORD map_ocsp_status(DWORD status)
1887 switch (status)
1889 case OCSP_BASIC_GOOD_CERT_STATUS: return ERROR_SUCCESS;
1890 case OCSP_BASIC_REVOKED_CERT_STATUS: return CRYPT_E_REVOKED;
1891 case OCSP_BASIC_UNKNOWN_CERT_STATUS: return CRYPT_E_REVOCATION_OFFLINE;
1892 default:
1893 FIXME("unhandled status %lu\n", status);
1894 return CRYPT_E_REVOCATION_OFFLINE;
1898 static BOOL match_cert_id(const OCSP_CERT_ID *id, const CERT_INFO *cert, const CERT_INFO *issuer)
1900 BYTE hash[20];
1901 DWORD hash_len = sizeof(hash);
1903 if (!id->HashAlgorithm.pszObjId || strcmp(id->HashAlgorithm.pszObjId, szOID_OIWSEC_sha1))
1905 FIXME("hash algorithm %s not supported\n", debugstr_a(id->HashAlgorithm.pszObjId));
1906 return FALSE;
1909 sha1_hash(issuer->Subject.pbData, issuer->Subject.cbData, hash, &hash_len);
1910 if (id->IssuerNameHash.cbData != hash_len) return FALSE;
1911 if (memcmp(id->IssuerNameHash.pbData, hash, hash_len)) return FALSE;
1913 sha1_hash(issuer->SubjectPublicKeyInfo.PublicKey.pbData,
1914 issuer->SubjectPublicKeyInfo.PublicKey.cbData, hash, &hash_len);
1915 if (id->IssuerKeyHash.cbData != hash_len) return FALSE;
1916 if (memcmp(id->IssuerKeyHash.pbData, hash, hash_len)) return FALSE;
1918 if (cert->SerialNumber.cbData != id->SerialNumber.cbData) return FALSE;
1919 return !memcmp(cert->SerialNumber.pbData, id->SerialNumber.pbData, id->SerialNumber.cbData);
1922 static DWORD check_ocsp_response_info(const CERT_INFO *cert, const CERT_INFO *issuer,
1923 const CRYPT_OBJID_BLOB *blob, DWORD *status, FILETIME *next_update)
1925 OCSP_BASIC_RESPONSE_INFO *info;
1926 DWORD size, i;
1928 memset(next_update, 0, sizeof(*next_update));
1929 if (!CryptDecodeObjectEx(X509_ASN_ENCODING, OCSP_BASIC_RESPONSE, blob->pbData, blob->cbData,
1930 CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size)) return GetLastError();
1932 FIXME("check responder id\n");
1933 for (i = 0; i < info->cResponseEntry; i++)
1935 OCSP_BASIC_RESPONSE_ENTRY *entry = &info->rgResponseEntry[i];
1936 if (match_cert_id(&entry->CertId, cert, issuer))
1938 *status = map_ocsp_status(entry->dwCertStatus);
1939 *next_update = entry->NextUpdate;
1943 LocalFree(info);
1944 return ERROR_SUCCESS;
1947 static DWORD verify_signed_ocsp_response_info(const CERT_INFO *cert, const CERT_INFO *issuer,
1948 const CRYPT_OBJID_BLOB *blob, FILETIME *next_update)
1950 OCSP_BASIC_SIGNED_RESPONSE_INFO *info;
1951 DWORD size, error, status = CRYPT_E_REVOCATION_OFFLINE;
1952 CRYPT_ALGORITHM_IDENTIFIER *alg;
1953 CRYPT_BIT_BLOB *sig;
1954 HCRYPTPROV prov = 0;
1955 HCRYPTHASH hash = 0;
1956 HCRYPTKEY key = 0;
1957 DWORD algid;
1959 if (!CryptDecodeObjectEx(X509_ASN_ENCODING, OCSP_BASIC_SIGNED_RESPONSE, blob->pbData, blob->cbData,
1960 CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size)) return GetLastError();
1962 if ((error = check_ocsp_response_info(cert, issuer, &info->ToBeSigned, &status, next_update))) goto done;
1964 alg = &info->SignatureInfo.SignatureAlgorithm;
1965 if (!alg->pszObjId || !(algid = CertOIDToAlgId(alg->pszObjId)))
1967 FIXME("unhandled signature algorithm %s\n", debugstr_a(alg->pszObjId));
1968 error = CRYPT_E_NO_REVOCATION_CHECK;
1969 goto done;
1972 if (!CryptAcquireContextW(&prov, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) goto done;
1973 if (!CryptCreateHash(prov, algid, 0, 0, &hash)) goto done;
1974 if (!CryptHashData(hash, info->ToBeSigned.pbData, info->ToBeSigned.cbData, 0)) goto done;
1976 sig = &info->SignatureInfo.Signature;
1977 if (!CryptImportPublicKeyInfoEx(prov, X509_ASN_ENCODING, (CERT_PUBLIC_KEY_INFO *)&issuer->SubjectPublicKeyInfo,
1978 0, 0, NULL, &key))
1980 error = GetLastError();
1981 TRACE("failed to import public key %#lx\n", error);
1983 else if (!CryptVerifySignatureW(hash, sig->pbData, sig->cbData, key, NULL, 0))
1985 error = GetLastError();
1986 TRACE("failed to verify signature %#lx\n", error);
1988 else error = ERROR_SUCCESS;
1990 done:
1991 CryptDestroyKey(key);
1992 CryptDestroyHash(hash);
1993 CryptReleaseContext(prov, 0);
1994 LocalFree(info);
1995 if (error) return error;
1996 return status;
1999 static DWORD handle_ocsp_response(const CERT_INFO *cert, const CERT_INFO *issuer, const BYTE *encoded,
2000 DWORD encoded_size, FILETIME *next_update)
2002 OCSP_RESPONSE_INFO *info;
2003 DWORD size, error = CRYPT_E_NO_REVOCATION_CHECK;
2005 if (!CryptDecodeObjectEx(X509_ASN_ENCODING, OCSP_RESPONSE, encoded, encoded_size, CRYPT_DECODE_ALLOC_FLAG, NULL,
2006 &info, &size)) return GetLastError();
2008 switch (info->dwStatus)
2010 case OCSP_SUCCESSFUL_RESPONSE:
2011 if (!info->pszObjId || strcmp(info->pszObjId, szOID_PKIX_OCSP_BASIC_SIGNED_RESPONSE))
2013 FIXME("unhandled response type %s\n", debugstr_a(info->pszObjId));
2014 break;
2016 error = verify_signed_ocsp_response_info(cert, issuer, &info->Value, next_update);
2017 break;
2019 default:
2020 FIXME("unhandled status %lu\n", info->dwStatus);
2021 break;
2024 LocalFree(info);
2025 return error;
2028 static DWORD verify_cert_revocation_with_ocsp(const CERT_CONTEXT *cert, const WCHAR *base_url,
2029 const CERT_REVOCATION_PARA *revpara, FILETIME *next_update)
2031 HINTERNET ses, con, req = NULL;
2032 BYTE *request_data = NULL, *response_data = NULL;
2033 DWORD size, flags, status, request_len, response_len, count, ret = CRYPT_E_REVOCATION_OFFLINE;
2034 URL_COMPONENTSW comp;
2035 WCHAR *url;
2037 if (!revpara || !revpara->pIssuerCert)
2039 TRACE("no issuer certificate\n");
2040 return CRYPT_E_REVOCATION_OFFLINE;
2042 if (!(request_data = build_ocsp_request(cert, revpara->pIssuerCert, &request_len)))
2043 return CRYPT_E_REVOCATION_OFFLINE;
2045 url = build_request_url(base_url, request_data, request_len);
2046 LocalFree(request_data);
2047 if (!url) return CRYPT_E_REVOCATION_OFFLINE;
2049 memset(&comp, 0, sizeof(comp));
2050 comp.dwStructSize = sizeof(comp);
2051 comp.dwHostNameLength = ~0u;
2052 comp.dwUrlPathLength = ~0u;
2053 if (!InternetCrackUrlW(url, 0, 0, &comp))
2055 free(url);
2056 return CRYPT_E_REVOCATION_OFFLINE;
2059 switch (comp.nScheme)
2061 case INTERNET_SCHEME_HTTP:
2062 flags = 0;
2063 break;
2064 case INTERNET_SCHEME_HTTPS:
2065 flags = INTERNET_FLAG_SECURE;
2066 break;
2067 default:
2068 FIXME("scheme %u not supported\n", comp.nScheme);
2069 free(url);
2070 return ERROR_NOT_SUPPORTED;
2073 if (!(ses = InternetOpenW(L"CryptoAPI", 0, NULL, NULL, 0))) return GetLastError();
2074 comp.lpszHostName[comp.dwHostNameLength] = 0;
2075 if (!(con = InternetConnectW(ses, comp.lpszHostName, comp.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0)))
2077 free(url);
2078 InternetCloseHandle(ses);
2079 return GetLastError();
2081 comp.lpszHostName[comp.dwHostNameLength] = '/';
2082 if (!(req = HttpOpenRequestW(con, NULL, comp.lpszUrlPath, NULL, NULL, NULL, flags, 0)) ||
2083 !HttpSendRequestW(req, NULL, 0, NULL, 0)) goto done;
2085 size = sizeof(status);
2086 if (!HttpQueryInfoW(req, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &status, &size, NULL)) goto done;
2087 if (status != HTTP_STATUS_OK)
2089 WARN("request status %lu\n", status);
2090 goto done;
2093 size = sizeof(response_len);
2094 if (!HttpQueryInfoW(req, HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_CONTENT_LENGTH, &response_len, &size, 0) ||
2095 !response_len || !(response_data = malloc(response_len)) ||
2096 !InternetReadFile(req, response_data, response_len, &count) || count != response_len) goto done;
2098 ret = handle_ocsp_response(cert->pCertInfo, revpara->pIssuerCert->pCertInfo, response_data, response_len,
2099 next_update);
2101 done:
2102 free(url);
2103 free(response_data);
2104 InternetCloseHandle(req);
2105 InternetCloseHandle(con);
2106 InternetCloseHandle(ses);
2107 return ret;
2110 static DWORD verify_cert_revocation_from_aia_ext(const CRYPT_DATA_BLOB *value, const CERT_CONTEXT *cert,
2111 FILETIME *pTime, DWORD dwFlags, CERT_REVOCATION_PARA *pRevPara, CERT_REVOCATION_STATUS *pRevStatus,
2112 FILETIME *next_update)
2114 BOOL ret;
2115 DWORD size, i, error = CRYPT_E_NO_REVOCATION_CHECK;
2116 CERT_AUTHORITY_INFO_ACCESS *aia;
2118 ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_AUTHORITY_INFO_ACCESS, value->pbData, value->cbData,
2119 CRYPT_DECODE_ALLOC_FLAG, NULL, &aia, &size);
2120 if (!ret) return GetLastError();
2122 for (i = 0; i < aia->cAccDescr; i++)
2124 if (!strcmp(aia->rgAccDescr[i].pszAccessMethod, szOID_PKIX_OCSP))
2126 if (aia->rgAccDescr[i].AccessLocation.dwAltNameChoice == CERT_ALT_NAME_URL)
2128 const WCHAR *url = aia->rgAccDescr[i].AccessLocation.pwszURL;
2129 TRACE("OCSP URL = %s\n", debugstr_w(url));
2130 error = verify_cert_revocation_with_ocsp(cert, url, pRevPara, next_update);
2132 else
2134 FIXME("unsupported AccessLocation type %lu\n", aia->rgAccDescr[i].AccessLocation.dwAltNameChoice);
2135 error = ERROR_NOT_SUPPORTED;
2137 break;
2141 LocalFree(aia);
2142 return error;
2145 static DWORD verify_cert_revocation_with_crl_offline(PCCERT_CONTEXT cert,
2146 const CRL_CONTEXT *crl, FILETIME *pTime, CERT_REVOCATION_STATUS *pRevStatus)
2148 PCRL_ENTRY entry = NULL;
2149 LONG valid;
2151 valid = CompareFileTime(pTime, &crl->pCrlInfo->ThisUpdate);
2152 if (valid <= 0)
2154 /* If this CRL is not older than the time being verified, there's no
2155 * way to know whether the certificate was revoked.
2157 TRACE("CRL not old enough\n");
2158 return CRYPT_E_REVOCATION_OFFLINE;
2161 CertFindCertificateInCRL(cert, crl, 0, NULL, &entry);
2162 if (entry)
2163 return CRYPT_E_REVOKED;
2165 /* Since the CRL was not retrieved for the cert being checked, there's no
2166 * guarantee it's fresh, so the cert *might* be okay, but it's safer not to
2167 * guess. */
2168 TRACE("certificate not found\n");
2169 return CRYPT_E_REVOCATION_OFFLINE;
2172 static DWORD verify_cert_revocation(const CERT_CONTEXT *cert, FILETIME *pTime,
2173 DWORD dwFlags, CERT_REVOCATION_PARA *pRevPara, CERT_REVOCATION_STATUS *pRevStatus)
2175 DWORD error = ERROR_SUCCESS;
2176 FILETIME next_update = {0};
2177 PCERT_EXTENSION ext;
2179 if (find_cached_revocation_status(cert, pRevPara, pTime, pRevStatus))
2181 if (pRevStatus->dwError == ERROR_SUCCESS || pRevStatus->dwError == CRYPT_E_REVOKED)
2183 TRACE("Returning cached status.\n");
2184 return pRevStatus->dwError;
2188 if ((ext = CertFindExtension(szOID_AUTHORITY_INFO_ACCESS, cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
2190 error = verify_cert_revocation_from_aia_ext(&ext->Value, cert, pTime, dwFlags, pRevPara, pRevStatus,
2191 &next_update);
2192 TRACE("verify_cert_revocation_from_aia_ext() returned %08lx\n", error);
2193 if (error == ERROR_SUCCESS || error == CRYPT_E_REVOKED) goto done;
2195 if ((ext = CertFindExtension(szOID_CRL_DIST_POINTS, cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
2197 error = verify_cert_revocation_from_dist_points_ext(&ext->Value, cert, pTime, dwFlags, pRevPara, pRevStatus,
2198 &next_update);
2199 TRACE("verify_cert_revocation_from_dist_points_ext() returned %08lx\n", error);
2200 if (error == ERROR_SUCCESS || error == CRYPT_E_REVOKED) goto done;
2202 if (!ext)
2204 if (pRevPara && pRevPara->hCrlStore && pRevPara->pIssuerCert)
2206 PCCRL_CONTEXT crl = NULL;
2207 BOOL canSignCRLs;
2209 /* If the caller told us about the issuer, make sure the issuer
2210 * can sign CRLs before looking for one.
2212 if ((ext = CertFindExtension(szOID_KEY_USAGE,
2213 pRevPara->pIssuerCert->pCertInfo->cExtension,
2214 pRevPara->pIssuerCert->pCertInfo->rgExtension)))
2216 CRYPT_BIT_BLOB usage;
2217 DWORD size = sizeof(usage);
2219 if (!CryptDecodeObjectEx(cert->dwCertEncodingType, X509_BITS,
2220 ext->Value.pbData, ext->Value.cbData,
2221 CRYPT_DECODE_NOCOPY_FLAG, NULL, &usage, &size))
2222 canSignCRLs = FALSE;
2223 else if (usage.cbData > 2)
2225 /* The key usage extension only defines 9 bits => no more
2226 * than 2 bytes are needed to encode all known usages.
2228 canSignCRLs = FALSE;
2230 else
2232 BYTE usageBits = usage.pbData[usage.cbData - 1];
2234 canSignCRLs = usageBits & CERT_CRL_SIGN_KEY_USAGE;
2237 else
2238 canSignCRLs = TRUE;
2239 if (canSignCRLs)
2241 /* If the caller was helpful enough to tell us where to find a
2242 * CRL for the cert, look for one and check it.
2244 crl = CertFindCRLInStore(pRevPara->hCrlStore,
2245 cert->dwCertEncodingType,
2246 CRL_FIND_ISSUED_BY_SIGNATURE_FLAG |
2247 CRL_FIND_ISSUED_BY_AKI_FLAG,
2248 CRL_FIND_ISSUED_BY, pRevPara->pIssuerCert, NULL);
2250 if (crl)
2252 error = verify_cert_revocation_with_crl_offline(cert, crl, pTime, pRevStatus);
2253 CertFreeCRLContext(crl);
2255 else
2257 TRACE("no CRL found\n");
2258 error = CRYPT_E_NO_REVOCATION_CHECK;
2261 else
2263 if (!pRevPara)
2264 WARN("no CERT_REVOCATION_PARA\n");
2265 else if (!pRevPara->hCrlStore)
2266 WARN("no dist points/aia extension and no CRL store\n");
2267 else if (!pRevPara->pIssuerCert)
2268 WARN("no dist points/aia extension and no issuer\n");
2269 error = CRYPT_E_NO_REVOCATION_CHECK;
2272 done:
2273 if ((next_update.dwLowDateTime || next_update.dwHighDateTime)
2274 && (error == ERROR_SUCCESS || error == CRYPT_E_REVOKED))
2276 CERT_REVOCATION_STATUS rev_status;
2278 memset(&rev_status, 0, sizeof(rev_status));
2279 rev_status.cbSize = sizeof(rev_status);
2280 rev_status.dwError = error;
2281 cache_revocation_status(cert, pRevPara, &next_update, &rev_status);
2283 return error;
2286 typedef struct _CERT_REVOCATION_PARA_NO_EXTRA_FIELDS {
2287 DWORD cbSize;
2288 PCCERT_CONTEXT pIssuerCert;
2289 DWORD cCertStore;
2290 HCERTSTORE *rgCertStore;
2291 HCERTSTORE hCrlStore;
2292 LPFILETIME pftTimeToUse;
2293 } CERT_REVOCATION_PARA_NO_EXTRA_FIELDS;
2295 typedef struct _OLD_CERT_REVOCATION_STATUS {
2296 DWORD cbSize;
2297 DWORD dwIndex;
2298 DWORD dwError;
2299 DWORD dwReason;
2300 } OLD_CERT_REVOCATION_STATUS;
2302 /***********************************************************************
2303 * CertDllVerifyRevocation (CRYPTNET.@)
2305 BOOL WINAPI CertDllVerifyRevocation(DWORD dwEncodingType, DWORD dwRevType,
2306 DWORD cContext, PVOID rgpvContext[], DWORD dwFlags,
2307 PCERT_REVOCATION_PARA pRevPara, PCERT_REVOCATION_STATUS pRevStatus)
2309 DWORD error = 0, i;
2310 FILETIME now;
2311 LPFILETIME pTime = NULL;
2313 TRACE("(%08lx, %ld, %ld, %p, %08lx, %p, %p)\n", dwEncodingType, dwRevType,
2314 cContext, rgpvContext, dwFlags, pRevPara, pRevStatus);
2316 if (pRevStatus->cbSize != sizeof(OLD_CERT_REVOCATION_STATUS) &&
2317 pRevStatus->cbSize != sizeof(CERT_REVOCATION_STATUS))
2319 SetLastError(E_INVALIDARG);
2320 return FALSE;
2322 if (!cContext)
2324 SetLastError(E_INVALIDARG);
2325 return FALSE;
2327 if (pRevPara && pRevPara->cbSize >=
2328 sizeof(CERT_REVOCATION_PARA_NO_EXTRA_FIELDS))
2329 pTime = pRevPara->pftTimeToUse;
2330 if (!pTime)
2332 GetSystemTimeAsFileTime(&now);
2333 pTime = &now;
2335 memset(&pRevStatus->dwIndex, 0, pRevStatus->cbSize - sizeof(DWORD));
2336 if (dwRevType != CERT_CONTEXT_REVOCATION_TYPE)
2337 error = CRYPT_E_NO_REVOCATION_CHECK;
2338 else
2340 for (i = 0; i < cContext; i++)
2342 if ((error = verify_cert_revocation(rgpvContext[i], pTime, dwFlags, pRevPara, pRevStatus)))
2344 pRevStatus->dwIndex = i;
2345 break;
2349 if (error)
2351 SetLastError(error);
2352 pRevStatus->dwError = error;
2354 TRACE("returning %d (%08lx)\n", !error, error);
2355 return !error;