cryptnet: Delete existing cache entries when out of date.
[wine] / dlls / cryptnet / cryptnet_main.c
1 /*
2  * Copyright (C) 2006 Maarten Lankhorst
3  * Copyright 2007 Juan Lang
4  *
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.
9  *
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.
14  *
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
18  *
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #define NONAMELESSUNION
25 #define NONAMELESSSTRUCT
26 #define CERT_REVOCATION_PARA_HAS_EXTRA_FIELDS
27
28 #include <stdio.h>
29 #include <stdarg.h>
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winnt.h"
34 #include "winnls.h"
35 #include "wininet.h"
36 #include "objbase.h"
37 #include "wincrypt.h"
38
39 #include "wine/debug.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(cryptnet);
42
43 #define IS_INTOID(x)    (((ULONG_PTR)(x) >> 16) == 0)
44
45 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
46 {
47    TRACE("(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
48
49    switch (fdwReason) {
50       case DLL_PROCESS_ATTACH:
51          DisableThreadLibraryCalls(hinstDLL);
52          break;
53       case DLL_PROCESS_DETACH:
54          /* Do uninitialisation here */
55          break;
56       default: break;
57    }
58    return TRUE;
59 }
60
61 static const WCHAR cryptNet[] = { 'c','r','y','p','t','n','e','t','.',
62    'd','l','l',0 };
63 static const WCHAR ldapProvOpenStore[] = { 'L','d','a','p','P','r','o','v',
64    'O','p','e','S','t','o','r','e',0 };
65
66 /***********************************************************************
67  *    DllRegisterServer (CRYPTNET.@)
68  */
69 HRESULT WINAPI DllRegisterServer(void)
70 {
71    TRACE("\n");
72    CryptRegisterDefaultOIDFunction(X509_ASN_ENCODING,
73     CRYPT_OID_VERIFY_REVOCATION_FUNC, 0, cryptNet);
74    CryptRegisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC, "Ldap",
75     cryptNet, "LdapProvOpenStore");
76    CryptRegisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC,
77     CERT_STORE_PROV_LDAP_W, cryptNet, "LdapProvOpenStore");
78    return S_OK;
79 }
80
81 /***********************************************************************
82  *    DllUnregisterServer (CRYPTNET.@)
83  */
84 HRESULT WINAPI DllUnregisterServer(void)
85 {
86    TRACE("\n");
87    CryptUnregisterDefaultOIDFunction(X509_ASN_ENCODING,
88     CRYPT_OID_VERIFY_REVOCATION_FUNC, cryptNet);
89    CryptUnregisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC, "Ldap");
90    CryptUnregisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC,
91     CERT_STORE_PROV_LDAP_W);
92    return S_OK;
93 }
94
95 static const char *url_oid_to_str(LPCSTR oid)
96 {
97     if (IS_INTOID(oid))
98     {
99         static char buf[10];
100
101         switch (LOWORD(oid))
102         {
103 #define _x(oid) case LOWORD(oid): return #oid
104         _x(URL_OID_CERTIFICATE_ISSUER);
105         _x(URL_OID_CERTIFICATE_CRL_DIST_POINT);
106         _x(URL_OID_CTL_ISSUER);
107         _x(URL_OID_CTL_NEXT_UPDATE);
108         _x(URL_OID_CRL_ISSUER);
109         _x(URL_OID_CERTIFICATE_FRESHEST_CRL);
110         _x(URL_OID_CRL_FRESHEST_CRL);
111         _x(URL_OID_CROSS_CERT_DIST_POINT);
112 #undef _x
113         default:
114             snprintf(buf, sizeof(buf), "%d", LOWORD(oid));
115             return buf;
116         }
117     }
118     else
119         return oid;
120 }
121
122 typedef BOOL (WINAPI *UrlDllGetObjectUrlFunc)(LPCSTR, LPVOID, DWORD,
123  PCRYPT_URL_ARRAY, DWORD *, PCRYPT_URL_INFO, DWORD *, LPVOID);
124
125 static BOOL WINAPI CRYPT_GetUrlFromCertificateIssuer(LPCSTR pszUrlOid,
126  LPVOID pvPara, DWORD dwFlags, PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray,
127  PCRYPT_URL_INFO pUrlInfo, DWORD *pcbUrlInfo, LPVOID pvReserved)
128 {
129     PCCERT_CONTEXT cert = pvPara;
130     PCERT_EXTENSION ext;
131     BOOL ret = FALSE;
132
133     /* The only applicable flag is CRYPT_GET_URL_FROM_EXTENSION */
134     if (dwFlags && !(dwFlags & CRYPT_GET_URL_FROM_EXTENSION))
135     {
136         SetLastError(CRYPT_E_NOT_FOUND);
137         return FALSE;
138     }
139     if ((ext = CertFindExtension(szOID_AUTHORITY_INFO_ACCESS,
140      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
141     {
142         CERT_AUTHORITY_INFO_ACCESS *aia;
143         DWORD size;
144
145         ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_AUTHORITY_INFO_ACCESS,
146          ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
147          &aia, &size);
148         if (ret)
149         {
150             DWORD i, cUrl, bytesNeeded = sizeof(CRYPT_URL_ARRAY);
151
152             for (i = 0, cUrl = 0; i < aia->cAccDescr; i++)
153                 if (!strcmp(aia->rgAccDescr[i].pszAccessMethod,
154                  szOID_PKIX_CA_ISSUERS))
155                 {
156                     if (aia->rgAccDescr[i].AccessLocation.dwAltNameChoice ==
157                      CERT_ALT_NAME_URL)
158                     {
159                         if (aia->rgAccDescr[i].AccessLocation.u.pwszURL)
160                         {
161                             cUrl++;
162                             bytesNeeded += sizeof(LPWSTR) +
163                              (lstrlenW(aia->rgAccDescr[i].AccessLocation.u.
164                              pwszURL) + 1) * sizeof(WCHAR);
165                         }
166                     }
167                     else
168                         FIXME("unsupported alt name type %d\n",
169                          aia->rgAccDescr[i].AccessLocation.dwAltNameChoice);
170                 }
171             if (!pcbUrlArray)
172             {
173                 SetLastError(E_INVALIDARG);
174                 ret = FALSE;
175             }
176             else if (!pUrlArray)
177                 *pcbUrlArray = bytesNeeded;
178             else if (*pcbUrlArray < bytesNeeded)
179             {
180                 SetLastError(ERROR_MORE_DATA);
181                 *pcbUrlArray = bytesNeeded;
182                 ret = FALSE;
183             }
184             else
185             {
186                 LPWSTR nextUrl;
187
188                 *pcbUrlArray = bytesNeeded;
189                 pUrlArray->cUrl = 0;
190                 pUrlArray->rgwszUrl =
191                  (LPWSTR *)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY));
192                 nextUrl = (LPWSTR)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY)
193                  + cUrl * sizeof(LPWSTR));
194                 for (i = 0; i < aia->cAccDescr; i++)
195                     if (!strcmp(aia->rgAccDescr[i].pszAccessMethod,
196                      szOID_PKIX_CA_ISSUERS))
197                     {
198                         if (aia->rgAccDescr[i].AccessLocation.dwAltNameChoice
199                          == CERT_ALT_NAME_URL)
200                         {
201                             if (aia->rgAccDescr[i].AccessLocation.u.pwszURL)
202                             {
203                                 lstrcpyW(nextUrl,
204                                  aia->rgAccDescr[i].AccessLocation.u.pwszURL);
205                                 pUrlArray->rgwszUrl[pUrlArray->cUrl++] =
206                                  nextUrl;
207                                 nextUrl += (lstrlenW(nextUrl) + 1);
208                             }
209                         }
210                     }
211             }
212             if (ret)
213             {
214                 if (pcbUrlInfo)
215                 {
216                     FIXME("url info: stub\n");
217                     if (!pUrlInfo)
218                         *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
219                     else if (*pcbUrlInfo < sizeof(CRYPT_URL_INFO))
220                     {
221                         *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
222                         SetLastError(ERROR_MORE_DATA);
223                         ret = FALSE;
224                     }
225                     else
226                     {
227                         *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
228                         memset(pUrlInfo, 0, sizeof(CRYPT_URL_INFO));
229                     }
230                 }
231             }
232             LocalFree(aia);
233         }
234     }
235     else
236         SetLastError(CRYPT_E_NOT_FOUND);
237     return ret;
238 }
239
240 static BOOL CRYPT_GetUrlFromCRLDistPointsExt(const CRYPT_DATA_BLOB *value,
241  PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray, PCRYPT_URL_INFO pUrlInfo,
242  DWORD *pcbUrlInfo)
243 {
244     BOOL ret;
245     CRL_DIST_POINTS_INFO *info;
246     DWORD size;
247
248     ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CRL_DIST_POINTS,
249      value->pbData, value->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size);
250     if (ret)
251     {
252         DWORD i, cUrl, bytesNeeded = sizeof(CRYPT_URL_ARRAY);
253
254         for (i = 0, cUrl = 0; i < info->cDistPoint; i++)
255             if (info->rgDistPoint[i].DistPointName.dwDistPointNameChoice
256              == CRL_DIST_POINT_FULL_NAME)
257             {
258                 DWORD j;
259                 CERT_ALT_NAME_INFO *name =
260                  &info->rgDistPoint[i].DistPointName.u.FullName;
261
262                 for (j = 0; j < name->cAltEntry; j++)
263                     if (name->rgAltEntry[j].dwAltNameChoice ==
264                      CERT_ALT_NAME_URL)
265                     {
266                         if (name->rgAltEntry[j].u.pwszURL)
267                         {
268                             cUrl++;
269                             bytesNeeded += sizeof(LPWSTR) +
270                              (lstrlenW(name->rgAltEntry[j].u.pwszURL) + 1)
271                              * sizeof(WCHAR);
272                         }
273                     }
274             }
275         if (!pcbUrlArray)
276         {
277             SetLastError(E_INVALIDARG);
278             ret = FALSE;
279         }
280         else if (!pUrlArray)
281             *pcbUrlArray = bytesNeeded;
282         else if (*pcbUrlArray < bytesNeeded)
283         {
284             SetLastError(ERROR_MORE_DATA);
285             *pcbUrlArray = bytesNeeded;
286             ret = FALSE;
287         }
288         else
289         {
290             LPWSTR nextUrl;
291
292             *pcbUrlArray = bytesNeeded;
293             pUrlArray->cUrl = 0;
294             pUrlArray->rgwszUrl =
295              (LPWSTR *)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY));
296             nextUrl = (LPWSTR)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY)
297              + cUrl * sizeof(LPWSTR));
298             for (i = 0; i < info->cDistPoint; i++)
299                 if (info->rgDistPoint[i].DistPointName.dwDistPointNameChoice
300                  == CRL_DIST_POINT_FULL_NAME)
301                 {
302                     DWORD j;
303                     CERT_ALT_NAME_INFO *name =
304                      &info->rgDistPoint[i].DistPointName.u.FullName;
305
306                     for (j = 0; j < name->cAltEntry; j++)
307                         if (name->rgAltEntry[j].dwAltNameChoice ==
308                          CERT_ALT_NAME_URL)
309                         {
310                             if (name->rgAltEntry[j].u.pwszURL)
311                             {
312                                 lstrcpyW(nextUrl,
313                                  name->rgAltEntry[j].u.pwszURL);
314                                 pUrlArray->rgwszUrl[pUrlArray->cUrl++] =
315                                  nextUrl;
316                                 nextUrl +=
317                                  (lstrlenW(name->rgAltEntry[j].u.pwszURL) + 1);
318                             }
319                         }
320                 }
321         }
322         if (ret)
323         {
324             if (pcbUrlInfo)
325             {
326                 FIXME("url info: stub\n");
327                 if (!pUrlInfo)
328                     *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
329                 else if (*pcbUrlInfo < sizeof(CRYPT_URL_INFO))
330                 {
331                     *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
332                     SetLastError(ERROR_MORE_DATA);
333                     ret = FALSE;
334                 }
335                 else
336                 {
337                     *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
338                     memset(pUrlInfo, 0, sizeof(CRYPT_URL_INFO));
339                 }
340             }
341         }
342         LocalFree(info);
343     }
344     return ret;
345 }
346
347 static BOOL WINAPI CRYPT_GetUrlFromCertificateCRLDistPoint(LPCSTR pszUrlOid,
348  LPVOID pvPara, DWORD dwFlags, PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray,
349  PCRYPT_URL_INFO pUrlInfo, DWORD *pcbUrlInfo, LPVOID pvReserved)
350 {
351     PCCERT_CONTEXT cert = pvPara;
352     PCERT_EXTENSION ext;
353     BOOL ret = FALSE;
354
355     /* The only applicable flag is CRYPT_GET_URL_FROM_EXTENSION */
356     if (dwFlags && !(dwFlags & CRYPT_GET_URL_FROM_EXTENSION))
357     {
358         SetLastError(CRYPT_E_NOT_FOUND);
359         return FALSE;
360     }
361     if ((ext = CertFindExtension(szOID_CRL_DIST_POINTS,
362      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
363         ret = CRYPT_GetUrlFromCRLDistPointsExt(&ext->Value, pUrlArray,
364          pcbUrlArray, pUrlInfo, pcbUrlInfo);
365     else
366         SetLastError(CRYPT_E_NOT_FOUND);
367     return ret;
368 }
369
370 /***********************************************************************
371  *    CryptGetObjectUrl (CRYPTNET.@)
372  */
373 BOOL WINAPI CryptGetObjectUrl(LPCSTR pszUrlOid, LPVOID pvPara, DWORD dwFlags,
374  PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray, PCRYPT_URL_INFO pUrlInfo,
375  DWORD *pcbUrlInfo, LPVOID pvReserved)
376 {
377     UrlDllGetObjectUrlFunc func = NULL;
378     HCRYPTOIDFUNCADDR hFunc = NULL;
379     BOOL ret = FALSE;
380
381     TRACE("(%s, %p, %08x, %p, %p, %p, %p, %p)\n", debugstr_a(pszUrlOid),
382      pvPara, dwFlags, pUrlArray, pcbUrlArray, pUrlInfo, pcbUrlInfo, pvReserved);
383
384     if (IS_INTOID(pszUrlOid))
385     {
386         switch (LOWORD(pszUrlOid))
387         {
388         case LOWORD(URL_OID_CERTIFICATE_ISSUER):
389             func = CRYPT_GetUrlFromCertificateIssuer;
390             break;
391         case LOWORD(URL_OID_CERTIFICATE_CRL_DIST_POINT):
392             func = CRYPT_GetUrlFromCertificateCRLDistPoint;
393             break;
394         default:
395             FIXME("unimplemented for %s\n", url_oid_to_str(pszUrlOid));
396             SetLastError(ERROR_FILE_NOT_FOUND);
397         }
398     }
399     else
400     {
401         static HCRYPTOIDFUNCSET set = NULL;
402
403         if (!set)
404             set = CryptInitOIDFunctionSet(URL_OID_GET_OBJECT_URL_FUNC, 0);
405         CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, pszUrlOid, 0,
406          (void **)&func, &hFunc);
407     }
408     if (func)
409         ret = func(pszUrlOid, pvPara, dwFlags, pUrlArray, pcbUrlArray,
410          pUrlInfo, pcbUrlInfo, pvReserved);
411     if (hFunc)
412         CryptFreeOIDFunctionAddress(hFunc, 0);
413     return ret;
414 }
415
416 /***********************************************************************
417  *    CryptRetrieveObjectByUrlA (CRYPTNET.@)
418  */
419 BOOL WINAPI CryptRetrieveObjectByUrlA(LPCSTR pszURL, LPCSTR pszObjectOid,
420  DWORD dwRetrievalFlags, DWORD dwTimeout, LPVOID *ppvObject,
421  HCRYPTASYNC hAsyncRetrieve, PCRYPT_CREDENTIALS pCredentials, LPVOID pvVerify,
422  PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
423 {
424     BOOL ret = FALSE;
425     int len;
426
427     TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p)\n", debugstr_a(pszURL),
428      debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, ppvObject,
429      hAsyncRetrieve, pCredentials, pvVerify, pAuxInfo);
430
431     if (!pszURL)
432     {
433         SetLastError(ERROR_INVALID_PARAMETER);
434         return FALSE;
435     }
436     len = MultiByteToWideChar(CP_ACP, 0, pszURL, -1, NULL, 0);
437     if (len)
438     {
439         LPWSTR url = CryptMemAlloc(len * sizeof(WCHAR));
440
441         if (url)
442         {
443             MultiByteToWideChar(CP_ACP, 0, pszURL, -1, url, len);
444             ret = CryptRetrieveObjectByUrlW(url, pszObjectOid,
445              dwRetrievalFlags, dwTimeout, ppvObject, hAsyncRetrieve,
446              pCredentials, pvVerify, pAuxInfo);
447             CryptMemFree(url);
448         }
449         else
450             SetLastError(ERROR_OUTOFMEMORY);
451     }
452     return ret;
453 }
454
455 static void WINAPI CRYPT_FreeBlob(LPCSTR pszObjectOid,
456  PCRYPT_BLOB_ARRAY pObject, void *pvFreeContext)
457 {
458     DWORD i;
459
460     for (i = 0; i < pObject->cBlob; i++)
461         CryptMemFree(pObject->rgBlob[i].pbData);
462     CryptMemFree(pObject->rgBlob);
463 }
464
465 static BOOL CRYPT_GetObjectFromFile(HANDLE hFile, PCRYPT_BLOB_ARRAY pObject)
466 {
467     BOOL ret;
468     LARGE_INTEGER size;
469
470     if ((ret = GetFileSizeEx(hFile, &size)))
471     {
472         if (size.u.HighPart)
473         {
474             WARN("file too big\n");
475             SetLastError(ERROR_INVALID_DATA);
476             ret = FALSE;
477         }
478         else
479         {
480             CRYPT_DATA_BLOB blob;
481
482             blob.pbData = CryptMemAlloc(size.u.LowPart);
483             if (blob.pbData)
484             {
485                 blob.cbData = size.u.LowPart;
486                 ret = ReadFile(hFile, blob.pbData, size.u.LowPart, &blob.cbData,
487                  NULL);
488                 if (ret)
489                 {
490                     pObject->rgBlob = CryptMemAlloc(sizeof(CRYPT_DATA_BLOB));
491                     if (pObject->rgBlob)
492                     {
493                         pObject->cBlob = 1;
494                         memcpy(pObject->rgBlob, &blob, sizeof(CRYPT_DATA_BLOB));
495                     }
496                     else
497                     {
498                         SetLastError(ERROR_OUTOFMEMORY);
499                         ret = FALSE;
500                     }
501                 }
502                 if (!ret)
503                     CryptMemFree(blob.pbData);
504             }
505             else
506             {
507                 SetLastError(ERROR_OUTOFMEMORY);
508                 ret = FALSE;
509             }
510         }
511     }
512     return ret;
513 }
514
515 static BOOL CRYPT_GetObjectFromCache(LPCWSTR pszURL, PCRYPT_BLOB_ARRAY pObject,
516  PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
517 {
518     BOOL ret = FALSE;
519     INTERNET_CACHE_ENTRY_INFOW *pCacheInfo = NULL;
520     DWORD size = 0;
521
522     TRACE("(%s, %p, %p)\n", debugstr_w(pszURL), pObject, pAuxInfo);
523
524     ret = GetUrlCacheEntryInfoW(pszURL, NULL, &size);
525     if (!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
526     {
527         pCacheInfo = CryptMemAlloc(size);
528         if (pCacheInfo)
529             ret = TRUE;
530         else
531             SetLastError(ERROR_OUTOFMEMORY);
532     }
533     if (ret && (ret = GetUrlCacheEntryInfoW(pszURL, pCacheInfo, &size)))
534     {
535         FILETIME ft;
536
537         GetSystemTimeAsFileTime(&ft);
538         if (CompareFileTime(&pCacheInfo->ExpireTime, &ft) >= 0)
539         {
540             HANDLE hFile = CreateFileW(pCacheInfo->lpszLocalFileName,
541              GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
542
543             if (hFile != INVALID_HANDLE_VALUE)
544             {
545                 if ((ret = CRYPT_GetObjectFromFile(hFile, pObject)))
546                 {
547                     if (pAuxInfo && pAuxInfo->cbSize >=
548                      offsetof(CRYPT_RETRIEVE_AUX_INFO,
549                      pLastSyncTime) + sizeof(PFILETIME) &&
550                      pAuxInfo->pLastSyncTime)
551                         memcpy(pAuxInfo->pLastSyncTime,
552                          &pCacheInfo->LastSyncTime,
553                          sizeof(FILETIME));
554                 }
555                 CloseHandle(hFile);
556             }
557             else
558             {
559                 DeleteUrlCacheEntryW(pszURL);
560                 ret = FALSE;
561             }
562         }
563         else
564         {
565             DeleteUrlCacheEntryW(pszURL);
566             ret = FALSE;
567         }
568     }
569     CryptMemFree(pCacheInfo);
570     TRACE("returning %d\n", ret);
571     return ret;
572 }
573
574 /* Parses the URL, and sets components's lpszHostName and lpszUrlPath members
575  * to NULL-terminated copies of those portions of the URL (to be freed with
576  * CryptMemFree.)
577  */
578 static BOOL CRYPT_CrackUrl(LPCWSTR pszURL, URL_COMPONENTSW *components)
579 {
580     BOOL ret;
581
582     TRACE("(%s, %p)\n", debugstr_w(pszURL), components);
583
584     memset(components, 0, sizeof(*components));
585     components->dwStructSize = sizeof(*components);
586     components->lpszHostName = CryptMemAlloc(MAX_PATH * sizeof(WCHAR));
587     components->dwHostNameLength = MAX_PATH;
588     components->lpszUrlPath = CryptMemAlloc(MAX_PATH * 2 * sizeof(WCHAR));
589     components->dwUrlPathLength = 2 * MAX_PATH;
590     ret = InternetCrackUrlW(pszURL, 0, ICU_DECODE, components);
591     if (ret)
592     {
593         if ((components->dwUrlPathLength == 2 * MAX_PATH - 1) ||
594             (components->dwHostNameLength == MAX_PATH - 1))
595             FIXME("Buffers are too small\n");
596         switch (components->nScheme)
597         {
598         case INTERNET_SCHEME_FTP:
599             if (!components->nPort)
600                 components->nPort = INTERNET_DEFAULT_FTP_PORT;
601             break;
602         case INTERNET_SCHEME_HTTP:
603             if (!components->nPort)
604                 components->nPort = INTERNET_DEFAULT_HTTP_PORT;
605             break;
606         default:
607             ; /* do nothing */
608         }
609     }
610     TRACE("returning %d\n", ret);
611     return ret;
612 }
613
614 struct InetContext
615 {
616     HANDLE event;
617     DWORD  timeout;
618     DWORD  error;
619 };
620
621 static struct InetContext *CRYPT_MakeInetContext(DWORD dwTimeout)
622 {
623     struct InetContext *context = CryptMemAlloc(sizeof(struct InetContext));
624
625     if (context)
626     {
627         context->event = CreateEventW(NULL, FALSE, FALSE, NULL);
628         if (!context->event)
629         {
630             CryptMemFree(context);
631             context = NULL;
632         }
633         else
634         {
635             context->timeout = dwTimeout;
636             context->error = ERROR_SUCCESS;
637         }
638     }
639     return context;
640 }
641
642 static BOOL CRYPT_DownloadObject(DWORD dwRetrievalFlags, HINTERNET hHttp,
643  struct InetContext *context, PCRYPT_BLOB_ARRAY pObject,
644  PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
645 {
646     CRYPT_DATA_BLOB object = { 0, NULL };
647     DWORD bytesAvailable;
648     BOOL ret;
649
650     do {
651         if ((ret = InternetQueryDataAvailable(hHttp, &bytesAvailable, 0, 0)))
652         {
653             if (bytesAvailable)
654             {
655                 if (object.pbData)
656                     object.pbData = CryptMemRealloc(object.pbData,
657                      object.cbData + bytesAvailable);
658                 else
659                     object.pbData = CryptMemAlloc(bytesAvailable);
660                 if (object.pbData)
661                 {
662                     INTERNET_BUFFERSA buffer = { sizeof(buffer), 0 };
663
664                     buffer.dwBufferLength = bytesAvailable;
665                     buffer.lpvBuffer = object.pbData + object.cbData;
666                     if (!(ret = InternetReadFileExA(hHttp, &buffer, IRF_NO_WAIT,
667                      (DWORD_PTR)context)))
668                     {
669                         if (GetLastError() == ERROR_IO_PENDING)
670                         {
671                             if (WaitForSingleObject(context->event,
672                              context->timeout) == WAIT_TIMEOUT)
673                                 SetLastError(ERROR_TIMEOUT);
674                             else if (context->error)
675                                 SetLastError(context->error);
676                             else
677                                 ret = TRUE;
678                         }
679                     }
680                     if (ret)
681                         object.cbData += buffer.dwBufferLength;
682                 }
683                 else
684                 {
685                     SetLastError(ERROR_OUTOFMEMORY);
686                     ret = FALSE;
687                 }
688             }
689         }
690         else if (GetLastError() == ERROR_IO_PENDING)
691         {
692             if (WaitForSingleObject(context->event, context->timeout) ==
693              WAIT_TIMEOUT)
694                 SetLastError(ERROR_TIMEOUT);
695             else
696                 ret = TRUE;
697         }
698     } while (ret && bytesAvailable);
699     if (ret)
700     {
701         pObject->rgBlob = CryptMemAlloc(sizeof(CRYPT_DATA_BLOB));
702         if (!pObject->rgBlob)
703         {
704             CryptMemFree(object.pbData);
705             SetLastError(ERROR_OUTOFMEMORY);
706             ret = FALSE;
707         }
708         else
709         {
710             pObject->rgBlob[0].cbData = object.cbData;
711             pObject->rgBlob[0].pbData = object.pbData;
712             pObject->cBlob = 1;
713         }
714     }
715     TRACE("returning %d\n", ret);
716     return ret;
717 }
718
719 /* Finds the object specified by pszURL in the cache.  If it's not found,
720  * creates a new cache entry for the object and writes the object to it.
721  * Sets the expiration time of the cache entry to expires.
722  */
723 static void CRYPT_CacheURL(LPCWSTR pszURL, const CRYPT_BLOB_ARRAY *pObject,
724  DWORD dwRetrievalFlags, FILETIME expires)
725 {
726     WCHAR cacheFileName[MAX_PATH];
727     DWORD size = 0;
728     BOOL ret, create = FALSE;
729
730     GetUrlCacheEntryInfoW(pszURL, NULL, &size);
731     if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
732     {
733         INTERNET_CACHE_ENTRY_INFOW *info = CryptMemAlloc(size);
734
735         if (info)
736         {
737             FILETIME ft;
738
739             ret = GetUrlCacheEntryInfoW(pszURL, info, &size);
740             if (ret)
741                 lstrcpyW(cacheFileName, info->lpszLocalFileName);
742             /* Check if the existing cache entry is up to date.  If it isn't,
743              * remove the existing cache entry, and create a new one with the
744              * new value.
745              */
746             GetSystemTimeAsFileTime(&ft);
747             if (CompareFileTime(&info->ExpireTime, &ft) < 0)
748             {
749                 create = TRUE;
750                 DeleteUrlCacheEntryW(pszURL);
751                 DeleteFileW(cacheFileName);
752             }
753             CryptMemFree(info);
754         }
755         else
756             ret = FALSE;
757     }
758     else
759     {
760         ret = CreateUrlCacheEntryW(pszURL, pObject->rgBlob[0].cbData, NULL,
761          cacheFileName, 0);
762         create = TRUE;
763     }
764     if (ret)
765     {
766         DWORD entryType;
767         FILETIME ft = { 0 };
768
769         if (create)
770         {
771             HANDLE hCacheFile = CreateFileW(cacheFileName, GENERIC_WRITE, 0,
772              NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
773
774             if (hCacheFile != INVALID_HANDLE_VALUE)
775             {
776                 DWORD bytesWritten;
777
778                 WriteFile(hCacheFile, pObject->rgBlob[0].pbData,
779                  pObject->rgBlob[0].cbData, &bytesWritten, NULL);
780                 CloseHandle(hCacheFile);
781             }
782             else
783                 ret = FALSE;
784         }
785         if (ret)
786         {
787             if (!(dwRetrievalFlags & CRYPT_STICKY_CACHE_RETRIEVAL))
788                 entryType = NORMAL_CACHE_ENTRY;
789             else
790                 entryType = STICKY_CACHE_ENTRY;
791             CommitUrlCacheEntryW(pszURL, cacheFileName, expires, ft, entryType,
792              NULL, 0, NULL, NULL);
793         }
794     }
795 }
796
797 static void CALLBACK CRYPT_InetStatusCallback(HINTERNET hInt,
798  DWORD_PTR dwContext, DWORD status, void *statusInfo, DWORD statusInfoLen)
799 {
800     struct InetContext *context = (struct InetContext *)dwContext;
801     LPINTERNET_ASYNC_RESULT result;
802
803     switch (status)
804     {
805     case INTERNET_STATUS_REQUEST_COMPLETE:
806         result = statusInfo;
807         context->error = result->dwError;
808         SetEvent(context->event);
809     }
810 }
811
812 static BOOL CRYPT_Connect(const URL_COMPONENTSW *components,
813  struct InetContext *context, PCRYPT_CREDENTIALS pCredentials,
814  HINTERNET *phInt, HINTERNET *phHost)
815 {
816     BOOL ret;
817
818     TRACE("(%s:%d, %p, %p, %p, %p)\n", debugstr_w(components->lpszHostName),
819      components->nPort, context, pCredentials, phInt, phInt);
820
821     *phHost = NULL;
822     *phInt = InternetOpenW(NULL, INTERNET_OPEN_TYPE_DIRECT, NULL, NULL,
823      context ? INTERNET_FLAG_ASYNC : 0);
824     if (*phInt)
825     {
826         DWORD service;
827
828         if (context)
829             InternetSetStatusCallbackW(*phInt, CRYPT_InetStatusCallback);
830         switch (components->nScheme)
831         {
832         case INTERNET_SCHEME_FTP:
833             service = INTERNET_SERVICE_FTP;
834             break;
835         case INTERNET_SCHEME_HTTP:
836             service = INTERNET_SERVICE_HTTP;
837             break;
838         default:
839             service = 0;
840         }
841         /* FIXME: use pCredentials for username/password */
842         *phHost = InternetConnectW(*phInt, components->lpszHostName,
843          components->nPort, NULL, NULL, service, 0, (DWORD_PTR)context);
844         if (!*phHost)
845         {
846             InternetCloseHandle(*phInt);
847             *phInt = NULL;
848             ret = FALSE;
849         }
850         else
851             ret = TRUE;
852     }
853     else
854         ret = FALSE;
855     TRACE("returning %d\n", ret);
856     return ret;
857 }
858
859 static BOOL WINAPI FTP_RetrieveEncodedObjectW(LPCWSTR pszURL,
860  LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
861  PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
862  void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
863  PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
864 {
865     FIXME("(%s, %s, %08x, %d, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
866      debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject,
867      ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo);
868
869     pObject->cBlob = 0;
870     pObject->rgBlob = NULL;
871     *ppfnFreeObject = CRYPT_FreeBlob;
872     *ppvFreeContext = NULL;
873     return FALSE;
874 }
875
876 static const WCHAR x509cacert[] = { 'a','p','p','l','i','c','a','t','i','o','n',
877  '/','x','-','x','5','0','9','-','c','a','-','c','e','r','t',0 };
878 static const WCHAR x509emailcert[] = { 'a','p','p','l','i','c','a','t','i','o',
879  'n','/','x','-','x','5','0','9','-','e','m','a','i','l','-','c','e','r','t',
880  0 };
881 static const WCHAR x509servercert[] = { 'a','p','p','l','i','c','a','t','i','o',
882  'n','/','x','-','x','5','0','9','-','s','e','r','v','e','r','-','c','e','r',
883  't',0 };
884 static const WCHAR x509usercert[] = { 'a','p','p','l','i','c','a','t','i','o',
885  'n','/','x','-','x','5','0','9','-','u','s','e','r','-','c','e','r','t',0 };
886 static const WCHAR pkcs7cert[] = { 'a','p','p','l','i','c','a','t','i','o','n',
887  '/','x','-','p','k','c','s','7','-','c','e','r','t','i','f','c','a','t','e',
888  's',0 };
889 static const WCHAR pkixCRL[] = { 'a','p','p','l','i','c','a','t','i','o','n',
890  '/','p','k','i','x','-','c','r','l',0 };
891 static const WCHAR pkcs7CRL[] = { 'a','p','p','l','i','c','a','t','i','o','n',
892  '/','x','-','p','k','c','s','7','-','c','r','l',0 };
893 static const WCHAR pkcs7sig[] = { 'a','p','p','l','i','c','a','t','i','o','n',
894  '/','x','-','p','k','c','s','7','-','s','i','g','n','a','t','u','r','e',0 };
895 static const WCHAR pkcs7mime[] = { 'a','p','p','l','i','c','a','t','i','o','n',
896  '/','x','-','p','k','c','s','7','-','m','i','m','e',0 };
897
898 static BOOL WINAPI HTTP_RetrieveEncodedObjectW(LPCWSTR pszURL,
899  LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
900  PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
901  void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
902  PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
903 {
904     BOOL ret = FALSE;
905
906     TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
907      debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject,
908      ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo);
909
910     pObject->cBlob = 0;
911     pObject->rgBlob = NULL;
912     *ppfnFreeObject = CRYPT_FreeBlob;
913     *ppvFreeContext = NULL;
914
915     if (!(dwRetrievalFlags & CRYPT_WIRE_ONLY_RETRIEVAL))
916         ret = CRYPT_GetObjectFromCache(pszURL, pObject, pAuxInfo);
917     if (!ret && (!(dwRetrievalFlags & CRYPT_CACHE_ONLY_RETRIEVAL) ||
918      (dwRetrievalFlags & CRYPT_WIRE_ONLY_RETRIEVAL)))
919     {
920         URL_COMPONENTSW components;
921
922         if ((ret = CRYPT_CrackUrl(pszURL, &components)))
923         {
924             HINTERNET hInt, hHost;
925             struct InetContext *context = NULL;
926
927             if (dwTimeout)
928                 context = CRYPT_MakeInetContext(dwTimeout);
929             ret = CRYPT_Connect(&components, context, pCredentials, &hInt,
930              &hHost);
931             if (ret)
932             {
933                 static LPCWSTR types[] = { x509cacert, x509emailcert,
934                  x509servercert, x509usercert, pkcs7cert, pkixCRL, pkcs7CRL,
935                  pkcs7sig, pkcs7mime, NULL };
936                 HINTERNET hHttp = HttpOpenRequestW(hHost, NULL,
937                  components.lpszUrlPath, NULL, NULL, types,
938                  INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_UI,
939                  (DWORD_PTR)context);
940
941                 if (hHttp)
942                 {
943                     if (dwTimeout)
944                     {
945                         InternetSetOptionW(hHttp,
946                          INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeout,
947                          sizeof(dwTimeout));
948                         InternetSetOptionW(hHttp, INTERNET_OPTION_SEND_TIMEOUT,
949                          &dwTimeout, sizeof(dwTimeout));
950                     }
951                     ret = HttpSendRequestExW(hHttp, NULL, NULL, 0,
952                      (DWORD_PTR)context);
953                     if (!ret && GetLastError() == ERROR_IO_PENDING)
954                     {
955                         if (WaitForSingleObject(context->event,
956                          context->timeout) == WAIT_TIMEOUT)
957                             SetLastError(ERROR_TIMEOUT);
958                         else
959                             ret = TRUE;
960                     }
961                     /* We don't set ret to TRUE in this block to avoid masking
962                      * an error from HttpSendRequestExW.
963                      */
964                     if (!HttpEndRequestW(hHttp, NULL, 0, (DWORD_PTR)context) &&
965                      GetLastError() == ERROR_IO_PENDING)
966                     {
967                         if (WaitForSingleObject(context->event,
968                          context->timeout) == WAIT_TIMEOUT)
969                         {
970                             SetLastError(ERROR_TIMEOUT);
971                             ret = FALSE;
972                         }
973                     }
974                     if (ret)
975                         ret = CRYPT_DownloadObject(dwRetrievalFlags, hHttp,
976                          context, pObject, pAuxInfo);
977                     if (ret && !(dwRetrievalFlags & CRYPT_DONT_CACHE_RESULT))
978                     {
979                         SYSTEMTIME st;
980                         DWORD len = sizeof(st);
981
982                         if (HttpQueryInfoW(hHttp,
983                          HTTP_QUERY_EXPIRES | HTTP_QUERY_FLAG_SYSTEMTIME, &st,
984                          &len, NULL))
985                         {
986                             FILETIME ft;
987
988                             SystemTimeToFileTime(&st, &ft);
989                             CRYPT_CacheURL(pszURL, pObject, dwRetrievalFlags,
990                              ft);
991                         }
992                     }
993                     InternetCloseHandle(hHttp);
994                 }
995                 InternetCloseHandle(hHost);
996                 InternetCloseHandle(hInt);
997             }
998             if (context)
999             {
1000                 CloseHandle(context->event);
1001                 CryptMemFree(context);
1002             }
1003             CryptMemFree(components.lpszUrlPath);
1004             CryptMemFree(components.lpszHostName);
1005         }
1006     }
1007     TRACE("returning %d\n", ret);
1008     return ret;
1009 }
1010
1011 static BOOL WINAPI File_RetrieveEncodedObjectW(LPCWSTR pszURL,
1012  LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
1013  PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
1014  void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
1015  PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
1016 {
1017     URL_COMPONENTSW components = { sizeof(components), 0 };
1018     BOOL ret;
1019
1020     TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
1021      debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject,
1022      ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo);
1023
1024     pObject->cBlob = 0;
1025     pObject->rgBlob = NULL;
1026     *ppfnFreeObject = CRYPT_FreeBlob;
1027     *ppvFreeContext = NULL;
1028
1029     components.lpszUrlPath = CryptMemAlloc(MAX_PATH * 2 * sizeof(WCHAR));
1030     components.dwUrlPathLength = 2 * MAX_PATH;
1031     ret = InternetCrackUrlW(pszURL, 0, ICU_DECODE, &components);
1032     if (ret)
1033     {
1034         LPWSTR path;
1035
1036         if (components.dwUrlPathLength == 2 * MAX_PATH - 1)
1037             FIXME("Buffers are too small\n");
1038
1039         /* 3 == lstrlenW(L"c:") + 1 */
1040         path = CryptMemAlloc((components.dwUrlPathLength + 3) * sizeof(WCHAR));
1041         if (path)
1042         {
1043             HANDLE hFile;
1044
1045             /* Try to create the file directly - Wine handles / in pathnames */
1046             lstrcpynW(path, components.lpszUrlPath,
1047              components.dwUrlPathLength + 1);
1048             hFile = CreateFileW(path, GENERIC_READ, 0, NULL, OPEN_EXISTING,
1049              FILE_ATTRIBUTE_NORMAL, NULL);
1050             if (hFile == INVALID_HANDLE_VALUE)
1051             {
1052                 /* Try again on the current drive */
1053                 GetCurrentDirectoryW(components.dwUrlPathLength, path);
1054                 if (path[1] == ':')
1055                 {
1056                     lstrcpynW(path + 2, components.lpszUrlPath,
1057                      components.dwUrlPathLength + 1);
1058                     hFile = CreateFileW(path, GENERIC_READ, 0, NULL,
1059                      OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1060                 }
1061                 if (hFile == INVALID_HANDLE_VALUE)
1062                 {
1063                     /* Try again on the Windows drive */
1064                     GetWindowsDirectoryW(path, components.dwUrlPathLength);
1065                     if (path[1] == ':')
1066                     {
1067                         lstrcpynW(path + 2, components.lpszUrlPath,
1068                          components.dwUrlPathLength + 1);
1069                         hFile = CreateFileW(path, GENERIC_READ, 0, NULL,
1070                          OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1071                     }
1072                 }
1073             }
1074             if (hFile != INVALID_HANDLE_VALUE)
1075             {
1076                 if ((ret = CRYPT_GetObjectFromFile(hFile, pObject)))
1077                 {
1078                     if (pAuxInfo && pAuxInfo->cbSize >=
1079                      offsetof(CRYPT_RETRIEVE_AUX_INFO,
1080                      pLastSyncTime) + sizeof(PFILETIME) &&
1081                      pAuxInfo->pLastSyncTime)
1082                         GetFileTime(hFile, NULL, NULL,
1083                          pAuxInfo->pLastSyncTime);
1084                 }
1085                 CloseHandle(hFile);
1086             }
1087             else
1088                 ret = FALSE;
1089             CryptMemFree(path);
1090         }
1091     }
1092     CryptMemFree(components.lpszUrlPath);
1093     return ret;
1094 }
1095
1096 typedef BOOL (WINAPI *SchemeDllRetrieveEncodedObjectW)(LPCWSTR pwszUrl,
1097  LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
1098  PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
1099  void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
1100  PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo);
1101
1102 static BOOL CRYPT_GetRetrieveFunction(LPCWSTR pszURL,
1103  SchemeDllRetrieveEncodedObjectW *pFunc, HCRYPTOIDFUNCADDR *phFunc)
1104 {
1105     URL_COMPONENTSW components = { sizeof(components), 0 };
1106     BOOL ret;
1107
1108     TRACE("(%s, %p, %p)\n", debugstr_w(pszURL), pFunc, phFunc);
1109
1110     *pFunc = NULL;
1111     *phFunc = 0;
1112     components.dwSchemeLength = 1;
1113     ret = InternetCrackUrlW(pszURL, 0, 0, &components);
1114     if (ret)
1115     {
1116         /* Microsoft always uses CryptInitOIDFunctionSet/
1117          * CryptGetOIDFunctionAddress, but there doesn't seem to be a pressing
1118          * reason to do so for builtin schemes.
1119          */
1120         switch (components.nScheme)
1121         {
1122         case INTERNET_SCHEME_FTP:
1123             *pFunc = FTP_RetrieveEncodedObjectW;
1124             break;
1125         case INTERNET_SCHEME_HTTP:
1126             *pFunc = HTTP_RetrieveEncodedObjectW;
1127             break;
1128         case INTERNET_SCHEME_FILE:
1129             *pFunc = File_RetrieveEncodedObjectW;
1130             break;
1131         default:
1132         {
1133             int len = WideCharToMultiByte(CP_ACP, 0, components.lpszScheme,
1134              components.dwSchemeLength, NULL, 0, NULL, NULL);
1135
1136             if (len)
1137             {
1138                 LPSTR scheme = CryptMemAlloc(len);
1139
1140                 if (scheme)
1141                 {
1142                     static HCRYPTOIDFUNCSET set = NULL;
1143
1144                     if (!set)
1145                         set = CryptInitOIDFunctionSet(
1146                          SCHEME_OID_RETRIEVE_ENCODED_OBJECTW_FUNC, 0);
1147                     WideCharToMultiByte(CP_ACP, 0, components.lpszScheme,
1148                      components.dwSchemeLength, scheme, len, NULL, NULL);
1149                     ret = CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING,
1150                      scheme, 0, (void **)pFunc, phFunc);
1151                     CryptMemFree(scheme);
1152                 }
1153                 else
1154                 {
1155                     SetLastError(ERROR_OUTOFMEMORY);
1156                     ret = FALSE;
1157                 }
1158             }
1159             else
1160                 ret = FALSE;
1161         }
1162         }
1163     }
1164     TRACE("returning %d\n", ret);
1165     return ret;
1166 }
1167
1168 static BOOL WINAPI CRYPT_CreateBlob(LPCSTR pszObjectOid,
1169  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1170 {
1171     DWORD size, i;
1172     CRYPT_BLOB_ARRAY *context;
1173     BOOL ret = FALSE;
1174
1175     size = sizeof(CRYPT_BLOB_ARRAY) + pObject->cBlob * sizeof(CRYPT_DATA_BLOB);
1176     for (i = 0; i < pObject->cBlob; i++)
1177         size += pObject->rgBlob[i].cbData;
1178     context = CryptMemAlloc(size);
1179     if (context)
1180     {
1181         LPBYTE nextData;
1182
1183         context->cBlob = 0;
1184         context->rgBlob =
1185          (CRYPT_DATA_BLOB *)((LPBYTE)context + sizeof(CRYPT_BLOB_ARRAY));
1186         nextData =
1187          (LPBYTE)context->rgBlob + pObject->cBlob * sizeof(CRYPT_DATA_BLOB);
1188         for (i = 0; i < pObject->cBlob; i++)
1189         {
1190             memcpy(nextData, pObject->rgBlob[i].pbData,
1191              pObject->rgBlob[i].cbData);
1192             context->rgBlob[i].pbData = nextData;
1193             context->rgBlob[i].cbData = pObject->rgBlob[i].cbData;
1194             nextData += pObject->rgBlob[i].cbData;
1195             context->cBlob++;
1196         }
1197         *ppvContext = context;
1198         ret = TRUE;
1199     }
1200     return ret;
1201 }
1202
1203 typedef BOOL (WINAPI *AddContextToStore)(HCERTSTORE hCertStore,
1204  const void *pContext, DWORD dwAddDisposition, const void **ppStoreContext);
1205
1206 static BOOL CRYPT_CreateContext(const CRYPT_BLOB_ARRAY *pObject,
1207  DWORD dwExpectedContentTypeFlags, AddContextToStore addFunc, void **ppvContext)
1208 {
1209     BOOL ret = TRUE;
1210
1211     if (!pObject->cBlob)
1212     {
1213         SetLastError(ERROR_INVALID_DATA);
1214         *ppvContext = NULL;
1215         ret = FALSE;
1216     }
1217     else if (pObject->cBlob == 1)
1218     {
1219         if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &pObject->rgBlob[0],
1220          dwExpectedContentTypeFlags, CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL,
1221          NULL, NULL, NULL, NULL, (const void **)ppvContext))
1222         {
1223             SetLastError(CRYPT_E_NO_MATCH);
1224             ret = FALSE;
1225         }
1226     }
1227     else
1228     {
1229         HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
1230          CERT_STORE_CREATE_NEW_FLAG, NULL);
1231
1232         if (store)
1233         {
1234             DWORD i;
1235             const void *context;
1236
1237             for (i = 0; i < pObject->cBlob; i++)
1238             {
1239                 if (CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
1240                  &pObject->rgBlob[i], dwExpectedContentTypeFlags,
1241                  CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL, NULL,
1242                  NULL, &context))
1243                 {
1244                     if (!addFunc(store, context, CERT_STORE_ADD_ALWAYS, NULL))
1245                         ret = FALSE;
1246                 }
1247                 else
1248                 {
1249                     SetLastError(CRYPT_E_NO_MATCH);
1250                     ret = FALSE;
1251                 }
1252             }
1253         }
1254         else
1255             ret = FALSE;
1256         *ppvContext = store;
1257     }
1258     return ret;
1259 }
1260
1261 static BOOL WINAPI CRYPT_CreateCert(LPCSTR pszObjectOid,
1262  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1263 {
1264     return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CERT,
1265      (AddContextToStore)CertAddCertificateContextToStore, ppvContext);
1266 }
1267
1268 static BOOL WINAPI CRYPT_CreateCRL(LPCSTR pszObjectOid,
1269  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1270 {
1271     return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CRL,
1272      (AddContextToStore)CertAddCRLContextToStore, ppvContext);
1273 }
1274
1275 static BOOL WINAPI CRYPT_CreateCTL(LPCSTR pszObjectOid,
1276  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1277 {
1278     return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CTL,
1279      (AddContextToStore)CertAddCTLContextToStore, ppvContext);
1280 }
1281
1282 static BOOL WINAPI CRYPT_CreatePKCS7(LPCSTR pszObjectOid,
1283  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1284 {
1285     BOOL ret;
1286
1287     if (!pObject->cBlob)
1288     {
1289         SetLastError(ERROR_INVALID_DATA);
1290         *ppvContext = NULL;
1291         ret = FALSE;
1292     }
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
1299     {
1300         FIXME("multiple messages unimplemented\n");
1301         ret = FALSE;
1302     }
1303     return ret;
1304 }
1305
1306 static BOOL WINAPI CRYPT_CreateAny(LPCSTR pszObjectOid,
1307  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1308 {
1309     BOOL ret;
1310
1311     if (!pObject->cBlob)
1312     {
1313         SetLastError(ERROR_INVALID_DATA);
1314         *ppvContext = NULL;
1315         ret = FALSE;
1316     }
1317     else
1318     {
1319         HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
1320          CERT_STORE_CREATE_NEW_FLAG, NULL);
1321
1322         if (store)
1323         {
1324             HCERTSTORE memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
1325              CERT_STORE_CREATE_NEW_FLAG, NULL);
1326
1327             if (memStore)
1328             {
1329                 CertAddStoreToCollection(store, memStore,
1330                  CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 0);
1331                 CertCloseStore(memStore, 0);
1332             }
1333             else
1334             {
1335                 CertCloseStore(store, 0);
1336                 store = NULL;
1337             }
1338         }
1339         if (store)
1340         {
1341             DWORD i;
1342
1343             ret = TRUE;
1344             for (i = 0; i < pObject->cBlob; i++)
1345             {
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;
1354
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))
1359                 {
1360                     switch (contentType)
1361                     {
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);
1382                     }
1383                     CertCloseStore(contextStore, 0);
1384                 }
1385                 else
1386                     ret = FALSE;
1387             }
1388         }
1389         else
1390             ret = FALSE;
1391         *ppvContext = store;
1392     }
1393     return ret;
1394 }
1395
1396 typedef BOOL (WINAPI *ContextDllCreateObjectContext)(LPCSTR pszObjectOid,
1397  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext);
1398
1399 static BOOL CRYPT_GetCreateFunction(LPCSTR pszObjectOid,
1400  ContextDllCreateObjectContext *pFunc, HCRYPTOIDFUNCADDR *phFunc)
1401 {
1402     BOOL ret = TRUE;
1403
1404     TRACE("(%s, %p, %p)\n", debugstr_a(pszObjectOid), pFunc, phFunc);
1405
1406     *pFunc = NULL;
1407     *phFunc = 0;
1408     if (IS_INTOID(pszObjectOid))
1409     {
1410         switch (LOWORD(pszObjectOid))
1411         {
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;
1430         }
1431     }
1432     if (!*pFunc)
1433     {
1434         static HCRYPTOIDFUNCSET set = NULL;
1435
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);
1441     }
1442     TRACE("returning %d\n", ret);
1443     return ret;
1444 }
1445
1446 typedef BOOL (*get_object_expiration_func)(const void *pvContext,
1447  FILETIME *expiration);
1448
1449 static BOOL CRYPT_GetExpirationFromCert(const void *pvObject, FILETIME *expiration)
1450 {
1451     PCCERT_CONTEXT cert = pvObject;
1452
1453     *expiration = cert->pCertInfo->NotAfter;
1454     return TRUE;
1455 }
1456
1457 static BOOL CRYPT_GetExpirationFromCRL(const void *pvObject, FILETIME *expiration)
1458 {
1459     PCCRL_CONTEXT cert = pvObject;
1460
1461     *expiration = cert->pCrlInfo->NextUpdate;
1462     return TRUE;
1463 }
1464
1465 static BOOL CRYPT_GetExpirationFromCTL(const void *pvObject, FILETIME *expiration)
1466 {
1467     PCCTL_CONTEXT cert = pvObject;
1468
1469     *expiration = cert->pCtlInfo->NextUpdate;
1470     return TRUE;
1471 }
1472
1473 static BOOL CRYPT_GetExpirationFunction(LPCSTR pszObjectOid,
1474  get_object_expiration_func *getExpiration)
1475 {
1476     BOOL ret;
1477
1478     if (IS_INTOID(pszObjectOid))
1479     {
1480         switch (LOWORD(pszObjectOid))
1481         {
1482         case LOWORD(CONTEXT_OID_CERTIFICATE):
1483             *getExpiration = CRYPT_GetExpirationFromCert;
1484             ret = TRUE;
1485             break;
1486         case LOWORD(CONTEXT_OID_CRL):
1487             *getExpiration = CRYPT_GetExpirationFromCRL;
1488             ret = TRUE;
1489             break;
1490         case LOWORD(CONTEXT_OID_CTL):
1491             *getExpiration = CRYPT_GetExpirationFromCTL;
1492             ret = TRUE;
1493             break;
1494         default:
1495             ret = FALSE;
1496         }
1497     }
1498     else
1499         ret = FALSE;
1500     return ret;
1501 }
1502
1503 /***********************************************************************
1504  *    CryptRetrieveObjectByUrlW (CRYPTNET.@)
1505  */
1506 BOOL WINAPI CryptRetrieveObjectByUrlW(LPCWSTR pszURL, LPCSTR pszObjectOid,
1507  DWORD dwRetrievalFlags, DWORD dwTimeout, LPVOID *ppvObject,
1508  HCRYPTASYNC hAsyncRetrieve, PCRYPT_CREDENTIALS pCredentials, LPVOID pvVerify,
1509  PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
1510 {
1511     BOOL ret;
1512     SchemeDllRetrieveEncodedObjectW retrieve;
1513     ContextDllCreateObjectContext create;
1514     HCRYPTOIDFUNCADDR hRetrieve = 0, hCreate = 0;
1515
1516     TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
1517      debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, ppvObject,
1518      hAsyncRetrieve, pCredentials, pvVerify, pAuxInfo);
1519
1520     if (!pszURL)
1521     {
1522         SetLastError(ERROR_INVALID_PARAMETER);
1523         return FALSE;
1524     }
1525     ret = CRYPT_GetRetrieveFunction(pszURL, &retrieve, &hRetrieve);
1526     if (ret)
1527         ret = CRYPT_GetCreateFunction(pszObjectOid, &create, &hCreate);
1528     if (ret)
1529     {
1530         CRYPT_BLOB_ARRAY object = { 0, NULL };
1531         PFN_FREE_ENCODED_OBJECT_FUNC freeObject;
1532         void *freeContext;
1533
1534         ret = retrieve(pszURL, pszObjectOid, dwRetrievalFlags, dwTimeout,
1535          &object, &freeObject, &freeContext, hAsyncRetrieve, pCredentials,
1536          pAuxInfo);
1537         if (ret)
1538         {
1539             get_object_expiration_func getExpiration;
1540
1541             ret = create(pszObjectOid, dwRetrievalFlags, &object, ppvObject);
1542             if (ret && !(dwRetrievalFlags & CRYPT_DONT_CACHE_RESULT) &&
1543              CRYPT_GetExpirationFunction(pszObjectOid, &getExpiration))
1544             {
1545                 FILETIME expires;
1546
1547                 if (getExpiration(*ppvObject, &expires))
1548                     CRYPT_CacheURL(pszURL, &object, dwRetrievalFlags, expires);
1549             }
1550             freeObject(pszObjectOid, &object, freeContext);
1551         }
1552     }
1553     if (hCreate)
1554         CryptFreeOIDFunctionAddress(hCreate, 0);
1555     if (hRetrieve)
1556         CryptFreeOIDFunctionAddress(hRetrieve, 0);
1557     TRACE("returning %d\n", ret);
1558     return ret;
1559 }
1560
1561 static DWORD verify_cert_revocation_with_crl(PCCERT_CONTEXT cert,
1562  PCCRL_CONTEXT crl, DWORD index, FILETIME *pTime,
1563  PCERT_REVOCATION_STATUS pRevStatus)
1564 {
1565     DWORD error;
1566
1567     if (CertVerifyCRLTimeValidity(pTime, crl->pCrlInfo))
1568     {
1569         /* The CRL isn't time valid */
1570         error = CRYPT_E_NO_REVOCATION_CHECK;
1571     }
1572     else
1573     {
1574         PCRL_ENTRY entry = NULL;
1575
1576         CertFindCertificateInCRL(cert, crl, 0, NULL, &entry);
1577         if (entry)
1578         {
1579             error = CRYPT_E_REVOKED;
1580             pRevStatus->dwIndex = index;
1581         }
1582         else
1583             error = ERROR_SUCCESS;
1584     }
1585     return error;
1586 }
1587
1588 static DWORD verify_cert_revocation_from_dist_points_ext(
1589  const CRYPT_DATA_BLOB *value, PCCERT_CONTEXT cert, DWORD index,
1590  FILETIME *pTime, DWORD dwFlags, const CERT_REVOCATION_PARA *pRevPara,
1591  PCERT_REVOCATION_STATUS pRevStatus)
1592 {
1593     DWORD error = ERROR_SUCCESS, cbUrlArray;
1594
1595     if (CRYPT_GetUrlFromCRLDistPointsExt(value, NULL, &cbUrlArray, NULL, NULL))
1596     {
1597         CRYPT_URL_ARRAY *urlArray = CryptMemAlloc(cbUrlArray);
1598
1599         if (urlArray)
1600         {
1601             DWORD j, retrievalFlags = 0, startTime, endTime, timeout;
1602             BOOL ret;
1603
1604             ret = CRYPT_GetUrlFromCRLDistPointsExt(value, urlArray,
1605              &cbUrlArray, NULL, NULL);
1606             if (dwFlags & CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION)
1607                 retrievalFlags |= CRYPT_CACHE_ONLY_RETRIEVAL;
1608             if (dwFlags & CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG &&
1609              pRevPara && pRevPara->cbSize >= offsetof(CERT_REVOCATION_PARA,
1610              dwUrlRetrievalTimeout) + sizeof(DWORD))
1611             {
1612                 startTime = GetTickCount();
1613                 endTime = startTime + pRevPara->dwUrlRetrievalTimeout;
1614                 timeout = pRevPara->dwUrlRetrievalTimeout;
1615             }
1616             else
1617                 endTime = timeout = 0;
1618             if (!ret)
1619                 error = GetLastError();
1620             for (j = 0; !error && j < urlArray->cUrl; j++)
1621             {
1622                 PCCRL_CONTEXT crl;
1623
1624                 ret = CryptRetrieveObjectByUrlW(urlArray->rgwszUrl[j],
1625                  CONTEXT_OID_CRL, retrievalFlags, timeout, (void **)&crl,
1626                  NULL, NULL, NULL, NULL);
1627                 if (ret)
1628                 {
1629                     error = verify_cert_revocation_with_crl(cert, crl, index,
1630                      pTime, pRevStatus);
1631                     if (!error && timeout)
1632                     {
1633                         DWORD time = GetTickCount();
1634
1635                         if ((int)(endTime - time) <= 0)
1636                         {
1637                             error = ERROR_TIMEOUT;
1638                             pRevStatus->dwIndex = index;
1639                         }
1640                         else
1641                             timeout = endTime - time;
1642                     }
1643                     CertFreeCRLContext(crl);
1644                 }
1645                 else
1646                     error = CRYPT_E_REVOCATION_OFFLINE;
1647             }
1648             CryptMemFree(urlArray);
1649         }
1650         else
1651         {
1652             error = ERROR_OUTOFMEMORY;
1653             pRevStatus->dwIndex = index;
1654         }
1655     }
1656     else
1657     {
1658         error = GetLastError();
1659         pRevStatus->dwIndex = index;
1660     }
1661     return error;
1662 }
1663
1664 static DWORD verify_cert_revocation_from_aia_ext(
1665  const CRYPT_DATA_BLOB *value, PCCERT_CONTEXT cert, DWORD index,
1666  FILETIME *pTime, DWORD dwFlags, PCERT_REVOCATION_PARA pRevPara,
1667  PCERT_REVOCATION_STATUS pRevStatus)
1668 {
1669     BOOL ret;
1670     DWORD error, size;
1671     CERT_AUTHORITY_INFO_ACCESS *aia;
1672
1673     ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_AUTHORITY_INFO_ACCESS,
1674      value->pbData, value->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &aia, &size);
1675     if (ret)
1676     {
1677         DWORD i;
1678
1679         for (i = 0; i < aia->cAccDescr; i++)
1680             if (!strcmp(aia->rgAccDescr[i].pszAccessMethod,
1681              szOID_PKIX_OCSP))
1682             {
1683                 if (aia->rgAccDescr[i].AccessLocation.dwAltNameChoice ==
1684                  CERT_ALT_NAME_URL)
1685                     FIXME("OCSP URL = %s\n",
1686                      debugstr_w(aia->rgAccDescr[i].AccessLocation.u.pwszURL));
1687                 else
1688                     FIXME("unsupported AccessLocation type %d\n",
1689                      aia->rgAccDescr[i].AccessLocation.dwAltNameChoice);
1690             }
1691         LocalFree(aia);
1692         /* FIXME: lie and pretend OCSP validated the cert */
1693         error = ERROR_SUCCESS;
1694     }
1695     else
1696         error = GetLastError();
1697     return error;
1698 }
1699
1700 static DWORD verify_cert_revocation(PCCERT_CONTEXT cert, DWORD index,
1701  FILETIME *pTime, DWORD dwFlags, PCERT_REVOCATION_PARA pRevPara,
1702  PCERT_REVOCATION_STATUS pRevStatus)
1703 {
1704     DWORD error = ERROR_SUCCESS;
1705     PCERT_EXTENSION ext;
1706
1707     if ((ext = CertFindExtension(szOID_CRL_DIST_POINTS,
1708      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
1709         error = verify_cert_revocation_from_dist_points_ext(&ext->Value, cert,
1710          index, pTime, dwFlags, pRevPara, pRevStatus);
1711     else if ((ext = CertFindExtension(szOID_AUTHORITY_INFO_ACCESS,
1712      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
1713         error = verify_cert_revocation_from_aia_ext(&ext->Value, cert,
1714          index, pTime, dwFlags, pRevPara, pRevStatus);
1715     else
1716     {
1717         if (pRevPara && pRevPara->hCrlStore && pRevPara->pIssuerCert)
1718         {
1719             PCCRL_CONTEXT crl = NULL;
1720             BOOL canSignCRLs;
1721
1722             /* If the caller told us about the issuer, make sure the issuer
1723              * can sign CRLs before looking for one.
1724              */
1725             if ((ext = CertFindExtension(szOID_KEY_USAGE,
1726              pRevPara->pIssuerCert->pCertInfo->cExtension,
1727              pRevPara->pIssuerCert->pCertInfo->rgExtension)))
1728             {
1729                 CRYPT_BIT_BLOB usage;
1730                 DWORD size = sizeof(usage);
1731
1732                 if (!CryptDecodeObjectEx(cert->dwCertEncodingType, X509_BITS,
1733                  ext->Value.pbData, ext->Value.cbData,
1734                  CRYPT_DECODE_NOCOPY_FLAG, NULL, &usage, &size))
1735                     canSignCRLs = FALSE;
1736                 else if (usage.cbData > 2)
1737                 {
1738                     /* The key usage extension only defines 9 bits => no more
1739                      * than 2 bytes are needed to encode all known usages.
1740                      */
1741                     canSignCRLs = FALSE;
1742                 }
1743                 else
1744                 {
1745                     BYTE usageBits = usage.pbData[usage.cbData - 1];
1746
1747                     canSignCRLs = usageBits & CERT_CRL_SIGN_KEY_USAGE;
1748                 }
1749             }
1750             else
1751                 canSignCRLs = TRUE;
1752             if (canSignCRLs)
1753             {
1754                 /* If the caller was helpful enough to tell us where to find a
1755                  * CRL for the cert, look for one and check it.
1756                  */
1757                 crl = CertFindCRLInStore(pRevPara->hCrlStore,
1758                  cert->dwCertEncodingType,
1759                  CRL_FIND_ISSUED_BY_SIGNATURE_FLAG |
1760                  CRL_FIND_ISSUED_BY_AKI_FLAG,
1761                  CRL_FIND_ISSUED_BY, pRevPara->pIssuerCert, NULL);
1762             }
1763             if (crl)
1764             {
1765                 error = verify_cert_revocation_with_crl(cert, crl, index,
1766                  pTime, pRevStatus);
1767                 CertFreeCRLContext(crl);
1768             }
1769             else
1770             {
1771                 error = CRYPT_E_NO_REVOCATION_CHECK;
1772                 pRevStatus->dwIndex = index;
1773             }
1774         }
1775         else
1776         {
1777             error = CRYPT_E_NO_REVOCATION_CHECK;
1778             pRevStatus->dwIndex = index;
1779         }
1780     }
1781     return error;
1782 }
1783
1784 typedef struct _CERT_REVOCATION_PARA_NO_EXTRA_FIELDS {
1785     DWORD                     cbSize;
1786     PCCERT_CONTEXT            pIssuerCert;
1787     DWORD                     cCertStore;
1788     HCERTSTORE               *rgCertStore;
1789     HCERTSTORE                hCrlStore;
1790     LPFILETIME                pftTimeToUse;
1791 } CERT_REVOCATION_PARA_NO_EXTRA_FIELDS, *PCERT_REVOCATION_PARA_NO_EXTRA_FIELDS;
1792
1793 typedef struct _OLD_CERT_REVOCATION_STATUS {
1794     DWORD cbSize;
1795     DWORD dwIndex;
1796     DWORD dwError;
1797     DWORD dwReason;
1798 } OLD_CERT_REVOCATION_STATUS, *POLD_CERT_REVOCATION_STATUS;
1799
1800 /***********************************************************************
1801  *    CertDllVerifyRevocation (CRYPTNET.@)
1802  */
1803 BOOL WINAPI CertDllVerifyRevocation(DWORD dwEncodingType, DWORD dwRevType,
1804  DWORD cContext, PVOID rgpvContext[], DWORD dwFlags,
1805  PCERT_REVOCATION_PARA pRevPara, PCERT_REVOCATION_STATUS pRevStatus)
1806 {
1807     DWORD error = 0, i;
1808     FILETIME now;
1809     LPFILETIME pTime = NULL;
1810
1811     TRACE("(%08x, %d, %d, %p, %08x, %p, %p)\n", dwEncodingType, dwRevType,
1812      cContext, rgpvContext, dwFlags, pRevPara, pRevStatus);
1813
1814     if (pRevStatus->cbSize != sizeof(OLD_CERT_REVOCATION_STATUS) &&
1815      pRevStatus->cbSize != sizeof(CERT_REVOCATION_STATUS))
1816     {
1817         SetLastError(E_INVALIDARG);
1818         return FALSE;
1819     }
1820     if (!cContext)
1821     {
1822         SetLastError(E_INVALIDARG);
1823         return FALSE;
1824     }
1825     if (pRevPara && pRevPara->cbSize >=
1826      sizeof(CERT_REVOCATION_PARA_NO_EXTRA_FIELDS))
1827         pTime = pRevPara->pftTimeToUse;
1828     if (!pTime)
1829     {
1830         GetSystemTimeAsFileTime(&now);
1831         pTime = &now;
1832     }
1833     memset(&pRevStatus->dwIndex, 0, pRevStatus->cbSize - sizeof(DWORD));
1834     if (dwRevType != CERT_CONTEXT_REVOCATION_TYPE)
1835         error = CRYPT_E_NO_REVOCATION_CHECK;
1836     else
1837     {
1838         for (i = 0; !error && i < cContext; i++)
1839             error = verify_cert_revocation(rgpvContext[i], i, pTime, dwFlags,
1840              pRevPara, pRevStatus);
1841     }
1842     if (error)
1843     {
1844         SetLastError(error);
1845         pRevStatus->dwError = error;
1846     }
1847     TRACE("returning %d (%08x)\n", !error, error);
1848     return !error;
1849 }