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