msvcrt: Fix malloc_func_t/free_func_t calling convention.
[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     DWORD size = 0;
735     BOOL ret, create = FALSE;
736
737     GetUrlCacheEntryInfoW(pszURL, NULL, &size);
738     if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
739     {
740         INTERNET_CACHE_ENTRY_INFOW *info = CryptMemAlloc(size);
741
742         if (info)
743         {
744             FILETIME ft;
745
746             ret = GetUrlCacheEntryInfoW(pszURL, info, &size);
747             if (ret)
748                 lstrcpyW(cacheFileName, info->lpszLocalFileName);
749             /* Check if the existing cache entry is up to date.  If it isn't,
750              * remove the existing cache entry, and create a new one with the
751              * new value.
752              */
753             GetSystemTimeAsFileTime(&ft);
754             if (CompareFileTime(&info->ExpireTime, &ft) < 0)
755             {
756                 create = TRUE;
757                 DeleteUrlCacheEntryW(pszURL);
758             }
759             CryptMemFree(info);
760         }
761         else
762             ret = FALSE;
763     }
764     else
765     {
766         ret = CreateUrlCacheEntryW(pszURL, pObject->rgBlob[0].cbData, NULL,
767          cacheFileName, 0);
768         create = TRUE;
769     }
770     if (ret)
771     {
772         DWORD entryType;
773         FILETIME ft = { 0 };
774
775         if (create)
776         {
777             HANDLE hCacheFile = CreateFileW(cacheFileName, GENERIC_WRITE, 0,
778              NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
779
780             if (hCacheFile != INVALID_HANDLE_VALUE)
781             {
782                 DWORD bytesWritten;
783
784                 WriteFile(hCacheFile, pObject->rgBlob[0].pbData,
785                  pObject->rgBlob[0].cbData, &bytesWritten, NULL);
786                 CloseHandle(hCacheFile);
787             }
788             else
789                 ret = FALSE;
790         }
791         if (ret)
792         {
793             if (!(dwRetrievalFlags & CRYPT_STICKY_CACHE_RETRIEVAL))
794                 entryType = NORMAL_CACHE_ENTRY;
795             else
796                 entryType = STICKY_CACHE_ENTRY;
797             CommitUrlCacheEntryW(pszURL, cacheFileName, expires, ft, entryType,
798              NULL, 0, NULL, NULL);
799         }
800     }
801 }
802
803 static void CALLBACK CRYPT_InetStatusCallback(HINTERNET hInt,
804  DWORD_PTR dwContext, DWORD status, void *statusInfo, DWORD statusInfoLen)
805 {
806     struct InetContext *context = (struct InetContext *)dwContext;
807     LPINTERNET_ASYNC_RESULT result;
808
809     switch (status)
810     {
811     case INTERNET_STATUS_REQUEST_COMPLETE:
812         result = statusInfo;
813         context->error = result->dwError;
814         SetEvent(context->event);
815     }
816 }
817
818 static BOOL CRYPT_Connect(const URL_COMPONENTSW *components,
819  struct InetContext *context, PCRYPT_CREDENTIALS pCredentials,
820  HINTERNET *phInt, HINTERNET *phHost)
821 {
822     BOOL ret;
823
824     TRACE("(%s:%d, %p, %p, %p, %p)\n", debugstr_w(components->lpszHostName),
825      components->nPort, context, pCredentials, phInt, phInt);
826
827     *phHost = NULL;
828     *phInt = InternetOpenW(NULL, INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL,
829      context ? INTERNET_FLAG_ASYNC : 0);
830     if (*phInt)
831     {
832         DWORD service;
833
834         if (context)
835             InternetSetStatusCallbackW(*phInt, CRYPT_InetStatusCallback);
836         switch (components->nScheme)
837         {
838         case INTERNET_SCHEME_FTP:
839             service = INTERNET_SERVICE_FTP;
840             break;
841         case INTERNET_SCHEME_HTTP:
842             service = INTERNET_SERVICE_HTTP;
843             break;
844         default:
845             service = 0;
846         }
847         /* FIXME: use pCredentials for username/password */
848         *phHost = InternetConnectW(*phInt, components->lpszHostName,
849          components->nPort, NULL, NULL, service, 0, (DWORD_PTR)context);
850         if (!*phHost)
851         {
852             InternetCloseHandle(*phInt);
853             *phInt = NULL;
854             ret = FALSE;
855         }
856         else
857             ret = TRUE;
858     }
859     else
860         ret = FALSE;
861     TRACE("returning %d\n", ret);
862     return ret;
863 }
864
865 static BOOL WINAPI FTP_RetrieveEncodedObjectW(LPCWSTR pszURL,
866  LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
867  PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
868  void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
869  PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
870 {
871     FIXME("(%s, %s, %08x, %d, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
872      debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject,
873      ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo);
874
875     pObject->cBlob = 0;
876     pObject->rgBlob = NULL;
877     *ppfnFreeObject = CRYPT_FreeBlob;
878     *ppvFreeContext = NULL;
879     return FALSE;
880 }
881
882 static const WCHAR x509cacert[] = { 'a','p','p','l','i','c','a','t','i','o','n',
883  '/','x','-','x','5','0','9','-','c','a','-','c','e','r','t',0 };
884 static const WCHAR x509emailcert[] = { 'a','p','p','l','i','c','a','t','i','o',
885  'n','/','x','-','x','5','0','9','-','e','m','a','i','l','-','c','e','r','t',
886  0 };
887 static const WCHAR x509servercert[] = { 'a','p','p','l','i','c','a','t','i','o',
888  'n','/','x','-','x','5','0','9','-','s','e','r','v','e','r','-','c','e','r',
889  't',0 };
890 static const WCHAR x509usercert[] = { 'a','p','p','l','i','c','a','t','i','o',
891  'n','/','x','-','x','5','0','9','-','u','s','e','r','-','c','e','r','t',0 };
892 static const WCHAR pkcs7cert[] = { 'a','p','p','l','i','c','a','t','i','o','n',
893  '/','x','-','p','k','c','s','7','-','c','e','r','t','i','f','c','a','t','e',
894  's',0 };
895 static const WCHAR pkixCRL[] = { 'a','p','p','l','i','c','a','t','i','o','n',
896  '/','p','k','i','x','-','c','r','l',0 };
897 static const WCHAR pkcs7CRL[] = { 'a','p','p','l','i','c','a','t','i','o','n',
898  '/','x','-','p','k','c','s','7','-','c','r','l',0 };
899 static const WCHAR pkcs7sig[] = { 'a','p','p','l','i','c','a','t','i','o','n',
900  '/','x','-','p','k','c','s','7','-','s','i','g','n','a','t','u','r','e',0 };
901 static const WCHAR pkcs7mime[] = { 'a','p','p','l','i','c','a','t','i','o','n',
902  '/','x','-','p','k','c','s','7','-','m','i','m','e',0 };
903
904 static BOOL WINAPI HTTP_RetrieveEncodedObjectW(LPCWSTR pszURL,
905  LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
906  PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
907  void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
908  PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
909 {
910     BOOL ret = FALSE;
911
912     TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
913      debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject,
914      ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo);
915
916     pObject->cBlob = 0;
917     pObject->rgBlob = NULL;
918     *ppfnFreeObject = CRYPT_FreeBlob;
919     *ppvFreeContext = NULL;
920
921     if (!(dwRetrievalFlags & CRYPT_WIRE_ONLY_RETRIEVAL))
922         ret = CRYPT_GetObjectFromCache(pszURL, pObject, pAuxInfo);
923     if (!ret && (!(dwRetrievalFlags & CRYPT_CACHE_ONLY_RETRIEVAL) ||
924      (dwRetrievalFlags & CRYPT_WIRE_ONLY_RETRIEVAL)))
925     {
926         URL_COMPONENTSW components;
927
928         if ((ret = CRYPT_CrackUrl(pszURL, &components)))
929         {
930             HINTERNET hInt, hHost;
931             struct InetContext *context = NULL;
932
933             if (dwTimeout)
934                 context = CRYPT_MakeInetContext(dwTimeout);
935             ret = CRYPT_Connect(&components, context, pCredentials, &hInt,
936              &hHost);
937             if (ret)
938             {
939                 static LPCWSTR types[] = { x509cacert, x509emailcert,
940                  x509servercert, x509usercert, pkcs7cert, pkixCRL, pkcs7CRL,
941                  pkcs7sig, pkcs7mime, NULL };
942                 HINTERNET hHttp = HttpOpenRequestW(hHost, NULL,
943                  components.lpszUrlPath, NULL, NULL, types,
944                  INTERNET_FLAG_NO_COOKIES | INTERNET_FLAG_NO_UI,
945                  (DWORD_PTR)context);
946
947                 if (hHttp)
948                 {
949                     if (dwTimeout)
950                     {
951                         InternetSetOptionW(hHttp,
952                          INTERNET_OPTION_RECEIVE_TIMEOUT, &dwTimeout,
953                          sizeof(dwTimeout));
954                         InternetSetOptionW(hHttp, INTERNET_OPTION_SEND_TIMEOUT,
955                          &dwTimeout, sizeof(dwTimeout));
956                     }
957                     ret = HttpSendRequestExW(hHttp, NULL, NULL, 0,
958                      (DWORD_PTR)context);
959                     if (!ret && GetLastError() == ERROR_IO_PENDING)
960                     {
961                         if (WaitForSingleObject(context->event,
962                          context->timeout) == WAIT_TIMEOUT)
963                             SetLastError(ERROR_TIMEOUT);
964                         else
965                             ret = TRUE;
966                     }
967                     /* We don't set ret to TRUE in this block to avoid masking
968                      * an error from HttpSendRequestExW.
969                      */
970                     if (ret &&
971                      !HttpEndRequestW(hHttp, NULL, 0, (DWORD_PTR)context) &&
972                      GetLastError() == ERROR_IO_PENDING)
973                     {
974                         if (WaitForSingleObject(context->event,
975                          context->timeout) == WAIT_TIMEOUT)
976                         {
977                             SetLastError(ERROR_TIMEOUT);
978                             ret = FALSE;
979                         }
980                     }
981                     if (ret)
982                         ret = CRYPT_DownloadObject(dwRetrievalFlags, hHttp,
983                          context, pObject, pAuxInfo);
984                     if (ret && !(dwRetrievalFlags & CRYPT_DONT_CACHE_RESULT))
985                     {
986                         SYSTEMTIME st;
987                         DWORD len = sizeof(st);
988
989                         if (HttpQueryInfoW(hHttp,
990                          HTTP_QUERY_EXPIRES | HTTP_QUERY_FLAG_SYSTEMTIME, &st,
991                          &len, NULL))
992                         {
993                             FILETIME ft;
994
995                             SystemTimeToFileTime(&st, &ft);
996                             CRYPT_CacheURL(pszURL, pObject, dwRetrievalFlags,
997                              ft);
998                         }
999                     }
1000                     InternetCloseHandle(hHttp);
1001                 }
1002                 InternetCloseHandle(hHost);
1003                 InternetCloseHandle(hInt);
1004             }
1005             if (context)
1006             {
1007                 CloseHandle(context->event);
1008                 CryptMemFree(context);
1009             }
1010             CryptMemFree(components.lpszUrlPath);
1011             CryptMemFree(components.lpszHostName);
1012         }
1013     }
1014     TRACE("returning %d\n", ret);
1015     return ret;
1016 }
1017
1018 static BOOL WINAPI File_RetrieveEncodedObjectW(LPCWSTR pszURL,
1019  LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
1020  PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
1021  void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
1022  PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
1023 {
1024     URL_COMPONENTSW components = { sizeof(components), 0 };
1025     BOOL ret;
1026
1027     TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
1028      debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject,
1029      ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo);
1030
1031     pObject->cBlob = 0;
1032     pObject->rgBlob = NULL;
1033     *ppfnFreeObject = CRYPT_FreeBlob;
1034     *ppvFreeContext = NULL;
1035
1036     components.lpszUrlPath = CryptMemAlloc(INTERNET_MAX_PATH_LENGTH * sizeof(WCHAR));
1037     components.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
1038     if (!components.lpszUrlPath)
1039     {
1040         SetLastError(ERROR_OUTOFMEMORY);
1041         return FALSE;
1042     }
1043
1044     ret = InternetCrackUrlW(pszURL, 0, ICU_DECODE, &components);
1045     if (ret)
1046     {
1047         LPWSTR path;
1048
1049         /* 3 == lstrlenW(L"c:") + 1 */
1050         path = CryptMemAlloc((components.dwUrlPathLength + 3) * sizeof(WCHAR));
1051         if (path)
1052         {
1053             HANDLE hFile;
1054
1055             /* Try to create the file directly - Wine handles / in pathnames */
1056             lstrcpynW(path, components.lpszUrlPath,
1057              components.dwUrlPathLength + 1);
1058             hFile = CreateFileW(path, GENERIC_READ, 0, NULL, OPEN_EXISTING,
1059              FILE_ATTRIBUTE_NORMAL, NULL);
1060             if (hFile == INVALID_HANDLE_VALUE)
1061             {
1062                 /* Try again on the current drive */
1063                 GetCurrentDirectoryW(components.dwUrlPathLength, path);
1064                 if (path[1] == ':')
1065                 {
1066                     lstrcpynW(path + 2, components.lpszUrlPath,
1067                      components.dwUrlPathLength + 1);
1068                     hFile = CreateFileW(path, GENERIC_READ, 0, NULL,
1069                      OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1070                 }
1071                 if (hFile == INVALID_HANDLE_VALUE)
1072                 {
1073                     /* Try again on the Windows drive */
1074                     GetWindowsDirectoryW(path, components.dwUrlPathLength);
1075                     if (path[1] == ':')
1076                     {
1077                         lstrcpynW(path + 2, components.lpszUrlPath,
1078                          components.dwUrlPathLength + 1);
1079                         hFile = CreateFileW(path, GENERIC_READ, 0, NULL,
1080                          OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1081                     }
1082                 }
1083             }
1084             if (hFile != INVALID_HANDLE_VALUE)
1085             {
1086                 if ((ret = CRYPT_GetObjectFromFile(hFile, pObject)))
1087                 {
1088                     if (pAuxInfo && pAuxInfo->cbSize >=
1089                      offsetof(CRYPT_RETRIEVE_AUX_INFO,
1090                      pLastSyncTime) + sizeof(PFILETIME) &&
1091                      pAuxInfo->pLastSyncTime)
1092                         GetFileTime(hFile, NULL, NULL,
1093                          pAuxInfo->pLastSyncTime);
1094                 }
1095                 CloseHandle(hFile);
1096             }
1097             else
1098                 ret = FALSE;
1099             CryptMemFree(path);
1100         }
1101         else
1102         {
1103             SetLastError(ERROR_OUTOFMEMORY);
1104             ret = FALSE;
1105         }
1106     }
1107     CryptMemFree(components.lpszUrlPath);
1108     return ret;
1109 }
1110
1111 typedef BOOL (WINAPI *SchemeDllRetrieveEncodedObjectW)(LPCWSTR pwszUrl,
1112  LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
1113  PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
1114  void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
1115  PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo);
1116
1117 static BOOL CRYPT_GetRetrieveFunction(LPCWSTR pszURL,
1118  SchemeDllRetrieveEncodedObjectW *pFunc, HCRYPTOIDFUNCADDR *phFunc)
1119 {
1120     URL_COMPONENTSW components = { sizeof(components), 0 };
1121     BOOL ret;
1122
1123     TRACE("(%s, %p, %p)\n", debugstr_w(pszURL), pFunc, phFunc);
1124
1125     *pFunc = NULL;
1126     *phFunc = 0;
1127     components.dwSchemeLength = 1;
1128     ret = InternetCrackUrlW(pszURL, 0, 0, &components);
1129     if (ret)
1130     {
1131         /* Microsoft always uses CryptInitOIDFunctionSet/
1132          * CryptGetOIDFunctionAddress, but there doesn't seem to be a pressing
1133          * reason to do so for builtin schemes.
1134          */
1135         switch (components.nScheme)
1136         {
1137         case INTERNET_SCHEME_FTP:
1138             *pFunc = FTP_RetrieveEncodedObjectW;
1139             break;
1140         case INTERNET_SCHEME_HTTP:
1141             *pFunc = HTTP_RetrieveEncodedObjectW;
1142             break;
1143         case INTERNET_SCHEME_FILE:
1144             *pFunc = File_RetrieveEncodedObjectW;
1145             break;
1146         default:
1147         {
1148             int len = WideCharToMultiByte(CP_ACP, 0, components.lpszScheme,
1149              components.dwSchemeLength, NULL, 0, NULL, NULL);
1150
1151             if (len)
1152             {
1153                 LPSTR scheme = CryptMemAlloc(len);
1154
1155                 if (scheme)
1156                 {
1157                     static HCRYPTOIDFUNCSET set = NULL;
1158
1159                     if (!set)
1160                         set = CryptInitOIDFunctionSet(
1161                          SCHEME_OID_RETRIEVE_ENCODED_OBJECTW_FUNC, 0);
1162                     WideCharToMultiByte(CP_ACP, 0, components.lpszScheme,
1163                      components.dwSchemeLength, scheme, len, NULL, NULL);
1164                     ret = CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING,
1165                      scheme, 0, (void **)pFunc, phFunc);
1166                     CryptMemFree(scheme);
1167                 }
1168                 else
1169                 {
1170                     SetLastError(ERROR_OUTOFMEMORY);
1171                     ret = FALSE;
1172                 }
1173             }
1174             else
1175                 ret = FALSE;
1176         }
1177         }
1178     }
1179     TRACE("returning %d\n", ret);
1180     return ret;
1181 }
1182
1183 static BOOL WINAPI CRYPT_CreateBlob(LPCSTR pszObjectOid,
1184  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1185 {
1186     DWORD size, i;
1187     CRYPT_BLOB_ARRAY *context;
1188     BOOL ret = FALSE;
1189
1190     size = sizeof(CRYPT_BLOB_ARRAY) + pObject->cBlob * sizeof(CRYPT_DATA_BLOB);
1191     for (i = 0; i < pObject->cBlob; i++)
1192         size += pObject->rgBlob[i].cbData;
1193     context = CryptMemAlloc(size);
1194     if (context)
1195     {
1196         LPBYTE nextData;
1197
1198         context->cBlob = 0;
1199         context->rgBlob =
1200          (CRYPT_DATA_BLOB *)((LPBYTE)context + sizeof(CRYPT_BLOB_ARRAY));
1201         nextData =
1202          (LPBYTE)context->rgBlob + pObject->cBlob * sizeof(CRYPT_DATA_BLOB);
1203         for (i = 0; i < pObject->cBlob; i++)
1204         {
1205             memcpy(nextData, pObject->rgBlob[i].pbData,
1206              pObject->rgBlob[i].cbData);
1207             context->rgBlob[i].pbData = nextData;
1208             context->rgBlob[i].cbData = pObject->rgBlob[i].cbData;
1209             nextData += pObject->rgBlob[i].cbData;
1210             context->cBlob++;
1211         }
1212         *ppvContext = context;
1213         ret = TRUE;
1214     }
1215     return ret;
1216 }
1217
1218 typedef BOOL (WINAPI *AddContextToStore)(HCERTSTORE hCertStore,
1219  const void *pContext, DWORD dwAddDisposition, const void **ppStoreContext);
1220
1221 static BOOL CRYPT_CreateContext(const CRYPT_BLOB_ARRAY *pObject,
1222  DWORD dwExpectedContentTypeFlags, AddContextToStore addFunc, void **ppvContext)
1223 {
1224     BOOL ret = TRUE;
1225
1226     if (!pObject->cBlob)
1227     {
1228         SetLastError(ERROR_INVALID_DATA);
1229         *ppvContext = NULL;
1230         ret = FALSE;
1231     }
1232     else if (pObject->cBlob == 1)
1233     {
1234         if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &pObject->rgBlob[0],
1235          dwExpectedContentTypeFlags, CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL,
1236          NULL, NULL, NULL, NULL, (const void **)ppvContext))
1237         {
1238             SetLastError(CRYPT_E_NO_MATCH);
1239             ret = FALSE;
1240         }
1241     }
1242     else
1243     {
1244         HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
1245          CERT_STORE_CREATE_NEW_FLAG, NULL);
1246
1247         if (store)
1248         {
1249             DWORD i;
1250             const void *context;
1251
1252             for (i = 0; i < pObject->cBlob; i++)
1253             {
1254                 if (CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
1255                  &pObject->rgBlob[i], dwExpectedContentTypeFlags,
1256                  CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL, NULL,
1257                  NULL, &context))
1258                 {
1259                     if (!addFunc(store, context, CERT_STORE_ADD_ALWAYS, NULL))
1260                         ret = FALSE;
1261                 }
1262                 else
1263                 {
1264                     SetLastError(CRYPT_E_NO_MATCH);
1265                     ret = FALSE;
1266                 }
1267             }
1268         }
1269         else
1270             ret = FALSE;
1271         *ppvContext = store;
1272     }
1273     return ret;
1274 }
1275
1276 static BOOL WINAPI CRYPT_CreateCert(LPCSTR pszObjectOid,
1277  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1278 {
1279     return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CERT,
1280      (AddContextToStore)CertAddCertificateContextToStore, ppvContext);
1281 }
1282
1283 static BOOL WINAPI CRYPT_CreateCRL(LPCSTR pszObjectOid,
1284  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1285 {
1286     return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CRL,
1287      (AddContextToStore)CertAddCRLContextToStore, ppvContext);
1288 }
1289
1290 static BOOL WINAPI CRYPT_CreateCTL(LPCSTR pszObjectOid,
1291  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1292 {
1293     return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CTL,
1294      (AddContextToStore)CertAddCTLContextToStore, ppvContext);
1295 }
1296
1297 static BOOL WINAPI CRYPT_CreatePKCS7(LPCSTR pszObjectOid,
1298  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1299 {
1300     BOOL ret;
1301
1302     if (!pObject->cBlob)
1303     {
1304         SetLastError(ERROR_INVALID_DATA);
1305         *ppvContext = NULL;
1306         ret = FALSE;
1307     }
1308     else if (pObject->cBlob == 1)
1309         ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &pObject->rgBlob[0],
1310          CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
1311          CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED, CERT_QUERY_FORMAT_FLAG_BINARY,
1312          0, NULL, NULL, NULL, ppvContext, NULL, NULL);
1313     else
1314     {
1315         FIXME("multiple messages unimplemented\n");
1316         ret = FALSE;
1317     }
1318     return ret;
1319 }
1320
1321 static BOOL WINAPI CRYPT_CreateAny(LPCSTR pszObjectOid,
1322  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1323 {
1324     BOOL ret;
1325
1326     if (!pObject->cBlob)
1327     {
1328         SetLastError(ERROR_INVALID_DATA);
1329         *ppvContext = NULL;
1330         ret = FALSE;
1331     }
1332     else
1333     {
1334         HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
1335          CERT_STORE_CREATE_NEW_FLAG, NULL);
1336
1337         if (store)
1338         {
1339             HCERTSTORE memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
1340              CERT_STORE_CREATE_NEW_FLAG, NULL);
1341
1342             if (memStore)
1343             {
1344                 CertAddStoreToCollection(store, memStore,
1345                  CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 0);
1346                 CertCloseStore(memStore, 0);
1347             }
1348             else
1349             {
1350                 CertCloseStore(store, 0);
1351                 store = NULL;
1352             }
1353         }
1354         if (store)
1355         {
1356             DWORD i;
1357
1358             ret = TRUE;
1359             for (i = 0; i < pObject->cBlob; i++)
1360             {
1361                 DWORD contentType, expectedContentTypes =
1362                  CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
1363                  CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED |
1364                  CERT_QUERY_CONTENT_FLAG_CERT |
1365                  CERT_QUERY_CONTENT_FLAG_CRL |
1366                  CERT_QUERY_CONTENT_FLAG_CTL;
1367                 HCERTSTORE contextStore;
1368                 const void *context;
1369
1370                 if (CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
1371                  &pObject->rgBlob[i], expectedContentTypes,
1372                  CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, &contentType, NULL,
1373                  &contextStore, NULL, &context))
1374                 {
1375                     switch (contentType)
1376                     {
1377                     case CERT_QUERY_CONTENT_CERT:
1378                         if (!CertAddCertificateContextToStore(store,
1379                          context, CERT_STORE_ADD_ALWAYS, NULL))
1380                             ret = FALSE;
1381                         CertFreeCertificateContext(context);
1382                         break;
1383                     case CERT_QUERY_CONTENT_CRL:
1384                         if (!CertAddCRLContextToStore(store,
1385                          context, CERT_STORE_ADD_ALWAYS, NULL))
1386                              ret = FALSE;
1387                         CertFreeCRLContext(context);
1388                         break;
1389                     case CERT_QUERY_CONTENT_CTL:
1390                         if (!CertAddCTLContextToStore(store,
1391                          context, CERT_STORE_ADD_ALWAYS, NULL))
1392                              ret = FALSE;
1393                         CertFreeCTLContext(context);
1394                         break;
1395                     default:
1396                         CertAddStoreToCollection(store, contextStore, 0, 0);
1397                     }
1398                     CertCloseStore(contextStore, 0);
1399                 }
1400                 else
1401                     ret = FALSE;
1402             }
1403         }
1404         else
1405             ret = FALSE;
1406         *ppvContext = store;
1407     }
1408     return ret;
1409 }
1410
1411 typedef BOOL (WINAPI *ContextDllCreateObjectContext)(LPCSTR pszObjectOid,
1412  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext);
1413
1414 static BOOL CRYPT_GetCreateFunction(LPCSTR pszObjectOid,
1415  ContextDllCreateObjectContext *pFunc, HCRYPTOIDFUNCADDR *phFunc)
1416 {
1417     BOOL ret = TRUE;
1418
1419     TRACE("(%s, %p, %p)\n", debugstr_a(pszObjectOid), pFunc, phFunc);
1420
1421     *pFunc = NULL;
1422     *phFunc = 0;
1423     if (IS_INTOID(pszObjectOid))
1424     {
1425         switch (LOWORD(pszObjectOid))
1426         {
1427         case 0:
1428             *pFunc = CRYPT_CreateBlob;
1429             break;
1430         case LOWORD(CONTEXT_OID_CERTIFICATE):
1431             *pFunc = CRYPT_CreateCert;
1432             break;
1433         case LOWORD(CONTEXT_OID_CRL):
1434             *pFunc = CRYPT_CreateCRL;
1435             break;
1436         case LOWORD(CONTEXT_OID_CTL):
1437             *pFunc = CRYPT_CreateCTL;
1438             break;
1439         case LOWORD(CONTEXT_OID_PKCS7):
1440             *pFunc = CRYPT_CreatePKCS7;
1441             break;
1442         case LOWORD(CONTEXT_OID_CAPI2_ANY):
1443             *pFunc = CRYPT_CreateAny;
1444             break;
1445         }
1446     }
1447     if (!*pFunc)
1448     {
1449         static HCRYPTOIDFUNCSET set = NULL;
1450
1451         if (!set)
1452             set = CryptInitOIDFunctionSet(
1453              CONTEXT_OID_CREATE_OBJECT_CONTEXT_FUNC, 0);
1454         ret = CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, pszObjectOid,
1455          0, (void **)pFunc, phFunc);
1456     }
1457     TRACE("returning %d\n", ret);
1458     return ret;
1459 }
1460
1461 typedef BOOL (*get_object_expiration_func)(const void *pvContext,
1462  FILETIME *expiration);
1463
1464 static BOOL CRYPT_GetExpirationFromCert(const void *pvObject, FILETIME *expiration)
1465 {
1466     PCCERT_CONTEXT cert = pvObject;
1467
1468     *expiration = cert->pCertInfo->NotAfter;
1469     return TRUE;
1470 }
1471
1472 static BOOL CRYPT_GetExpirationFromCRL(const void *pvObject, FILETIME *expiration)
1473 {
1474     PCCRL_CONTEXT cert = pvObject;
1475
1476     *expiration = cert->pCrlInfo->NextUpdate;
1477     return TRUE;
1478 }
1479
1480 static BOOL CRYPT_GetExpirationFromCTL(const void *pvObject, FILETIME *expiration)
1481 {
1482     PCCTL_CONTEXT cert = pvObject;
1483
1484     *expiration = cert->pCtlInfo->NextUpdate;
1485     return TRUE;
1486 }
1487
1488 static BOOL CRYPT_GetExpirationFunction(LPCSTR pszObjectOid,
1489  get_object_expiration_func *getExpiration)
1490 {
1491     BOOL ret;
1492
1493     if (IS_INTOID(pszObjectOid))
1494     {
1495         switch (LOWORD(pszObjectOid))
1496         {
1497         case LOWORD(CONTEXT_OID_CERTIFICATE):
1498             *getExpiration = CRYPT_GetExpirationFromCert;
1499             ret = TRUE;
1500             break;
1501         case LOWORD(CONTEXT_OID_CRL):
1502             *getExpiration = CRYPT_GetExpirationFromCRL;
1503             ret = TRUE;
1504             break;
1505         case LOWORD(CONTEXT_OID_CTL):
1506             *getExpiration = CRYPT_GetExpirationFromCTL;
1507             ret = TRUE;
1508             break;
1509         default:
1510             ret = FALSE;
1511         }
1512     }
1513     else
1514         ret = FALSE;
1515     return ret;
1516 }
1517
1518 /***********************************************************************
1519  *    CryptRetrieveObjectByUrlW (CRYPTNET.@)
1520  */
1521 BOOL WINAPI CryptRetrieveObjectByUrlW(LPCWSTR pszURL, LPCSTR pszObjectOid,
1522  DWORD dwRetrievalFlags, DWORD dwTimeout, LPVOID *ppvObject,
1523  HCRYPTASYNC hAsyncRetrieve, PCRYPT_CREDENTIALS pCredentials, LPVOID pvVerify,
1524  PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
1525 {
1526     BOOL ret;
1527     SchemeDllRetrieveEncodedObjectW retrieve;
1528     ContextDllCreateObjectContext create;
1529     HCRYPTOIDFUNCADDR hRetrieve = 0, hCreate = 0;
1530
1531     TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
1532      debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, ppvObject,
1533      hAsyncRetrieve, pCredentials, pvVerify, pAuxInfo);
1534
1535     if (!pszURL)
1536     {
1537         SetLastError(ERROR_INVALID_PARAMETER);
1538         return FALSE;
1539     }
1540     ret = CRYPT_GetRetrieveFunction(pszURL, &retrieve, &hRetrieve);
1541     if (ret)
1542         ret = CRYPT_GetCreateFunction(pszObjectOid, &create, &hCreate);
1543     if (ret)
1544     {
1545         CRYPT_BLOB_ARRAY object = { 0, NULL };
1546         PFN_FREE_ENCODED_OBJECT_FUNC freeObject;
1547         void *freeContext;
1548
1549         ret = retrieve(pszURL, pszObjectOid, dwRetrievalFlags, dwTimeout,
1550          &object, &freeObject, &freeContext, hAsyncRetrieve, pCredentials,
1551          pAuxInfo);
1552         if (ret)
1553         {
1554             get_object_expiration_func getExpiration;
1555
1556             ret = create(pszObjectOid, dwRetrievalFlags, &object, ppvObject);
1557             if (ret && !(dwRetrievalFlags & CRYPT_DONT_CACHE_RESULT) &&
1558              CRYPT_GetExpirationFunction(pszObjectOid, &getExpiration))
1559             {
1560                 FILETIME expires;
1561
1562                 if (getExpiration(*ppvObject, &expires))
1563                     CRYPT_CacheURL(pszURL, &object, dwRetrievalFlags, expires);
1564             }
1565             freeObject(pszObjectOid, &object, freeContext);
1566         }
1567     }
1568     if (hCreate)
1569         CryptFreeOIDFunctionAddress(hCreate, 0);
1570     if (hRetrieve)
1571         CryptFreeOIDFunctionAddress(hRetrieve, 0);
1572     TRACE("returning %d\n", ret);
1573     return ret;
1574 }
1575
1576 static DWORD verify_cert_revocation_with_crl_online(PCCERT_CONTEXT cert,
1577  PCCRL_CONTEXT crl, DWORD index, FILETIME *pTime,
1578  PCERT_REVOCATION_STATUS pRevStatus)
1579 {
1580     DWORD error;
1581     PCRL_ENTRY entry = NULL;
1582
1583     CertFindCertificateInCRL(cert, crl, 0, NULL, &entry);
1584     if (entry)
1585     {
1586         error = CRYPT_E_REVOKED;
1587         pRevStatus->dwIndex = index;
1588     }
1589     else
1590     {
1591         /* Since the CRL was retrieved for the cert being checked, then it's
1592          * guaranteed to be fresh, and the cert is not revoked.
1593          */
1594         error = ERROR_SUCCESS;
1595     }
1596     return error;
1597 }
1598
1599 static DWORD verify_cert_revocation_from_dist_points_ext(
1600  const CRYPT_DATA_BLOB *value, PCCERT_CONTEXT cert, DWORD index,
1601  FILETIME *pTime, DWORD dwFlags, const CERT_REVOCATION_PARA *pRevPara,
1602  PCERT_REVOCATION_STATUS pRevStatus)
1603 {
1604     DWORD error = ERROR_SUCCESS, cbUrlArray;
1605
1606     if (CRYPT_GetUrlFromCRLDistPointsExt(value, NULL, &cbUrlArray, NULL, NULL))
1607     {
1608         CRYPT_URL_ARRAY *urlArray = CryptMemAlloc(cbUrlArray);
1609
1610         if (urlArray)
1611         {
1612             DWORD j, retrievalFlags = 0, startTime, endTime, timeout;
1613             BOOL ret;
1614
1615             ret = CRYPT_GetUrlFromCRLDistPointsExt(value, urlArray,
1616              &cbUrlArray, NULL, NULL);
1617             if (dwFlags & CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION)
1618                 retrievalFlags |= CRYPT_CACHE_ONLY_RETRIEVAL;
1619             if (dwFlags & CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG &&
1620              pRevPara && pRevPara->cbSize >= offsetof(CERT_REVOCATION_PARA,
1621              dwUrlRetrievalTimeout) + sizeof(DWORD))
1622             {
1623                 startTime = GetTickCount();
1624                 endTime = startTime + pRevPara->dwUrlRetrievalTimeout;
1625                 timeout = pRevPara->dwUrlRetrievalTimeout;
1626             }
1627             else
1628                 endTime = timeout = 0;
1629             if (!ret)
1630                 error = GetLastError();
1631             for (j = 0; !error && j < urlArray->cUrl; j++)
1632             {
1633                 PCCRL_CONTEXT crl;
1634
1635                 ret = CryptRetrieveObjectByUrlW(urlArray->rgwszUrl[j],
1636                  CONTEXT_OID_CRL, retrievalFlags, timeout, (void **)&crl,
1637                  NULL, NULL, NULL, NULL);
1638                 if (ret)
1639                 {
1640                     error = verify_cert_revocation_with_crl_online(cert, crl,
1641                      index, pTime, pRevStatus);
1642                     if (!error && timeout)
1643                     {
1644                         DWORD time = GetTickCount();
1645
1646                         if ((int)(endTime - time) <= 0)
1647                         {
1648                             error = ERROR_TIMEOUT;
1649                             pRevStatus->dwIndex = index;
1650                         }
1651                         else
1652                             timeout = endTime - time;
1653                     }
1654                     CertFreeCRLContext(crl);
1655                 }
1656                 else
1657                     error = CRYPT_E_REVOCATION_OFFLINE;
1658             }
1659             CryptMemFree(urlArray);
1660         }
1661         else
1662         {
1663             error = ERROR_OUTOFMEMORY;
1664             pRevStatus->dwIndex = index;
1665         }
1666     }
1667     else
1668     {
1669         error = GetLastError();
1670         pRevStatus->dwIndex = index;
1671     }
1672     return error;
1673 }
1674
1675 static DWORD verify_cert_revocation_from_aia_ext(
1676  const CRYPT_DATA_BLOB *value, PCCERT_CONTEXT cert, DWORD index,
1677  FILETIME *pTime, DWORD dwFlags, PCERT_REVOCATION_PARA pRevPara,
1678  PCERT_REVOCATION_STATUS pRevStatus)
1679 {
1680     BOOL ret;
1681     DWORD error, size;
1682     CERT_AUTHORITY_INFO_ACCESS *aia;
1683
1684     ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_AUTHORITY_INFO_ACCESS,
1685      value->pbData, value->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &aia, &size);
1686     if (ret)
1687     {
1688         DWORD i;
1689
1690         for (i = 0; i < aia->cAccDescr; i++)
1691             if (!strcmp(aia->rgAccDescr[i].pszAccessMethod,
1692              szOID_PKIX_OCSP))
1693             {
1694                 if (aia->rgAccDescr[i].AccessLocation.dwAltNameChoice ==
1695                  CERT_ALT_NAME_URL)
1696                     FIXME("OCSP URL = %s\n",
1697                      debugstr_w(aia->rgAccDescr[i].AccessLocation.u.pwszURL));
1698                 else
1699                     FIXME("unsupported AccessLocation type %d\n",
1700                      aia->rgAccDescr[i].AccessLocation.dwAltNameChoice);
1701             }
1702         LocalFree(aia);
1703         /* FIXME: lie and pretend OCSP validated the cert */
1704         error = ERROR_SUCCESS;
1705     }
1706     else
1707         error = GetLastError();
1708     return error;
1709 }
1710
1711 static DWORD verify_cert_revocation_with_crl_offline(PCCERT_CONTEXT cert,
1712  PCCRL_CONTEXT crl, DWORD index, FILETIME *pTime,
1713  PCERT_REVOCATION_STATUS pRevStatus)
1714 {
1715     DWORD error;
1716     LONG valid;
1717
1718     valid = CompareFileTime(pTime, &crl->pCrlInfo->ThisUpdate);
1719     if (valid <= 0)
1720     {
1721         /* If this CRL is not older than the time being verified, there's no
1722          * way to know whether the certificate was revoked.
1723          */
1724         TRACE("CRL not old enough\n");
1725         error = CRYPT_E_REVOCATION_OFFLINE;
1726     }
1727     else
1728     {
1729         PCRL_ENTRY entry = NULL;
1730
1731         CertFindCertificateInCRL(cert, crl, 0, NULL, &entry);
1732         if (entry)
1733         {
1734             error = CRYPT_E_REVOKED;
1735             pRevStatus->dwIndex = index;
1736         }
1737         else
1738         {
1739             /* Since the CRL was not retrieved for the cert being checked,
1740              * there's no guarantee it's fresh, so the cert *might* be okay,
1741              * but it's safer not to guess.
1742              */
1743             TRACE("certificate not found\n");
1744             error = CRYPT_E_REVOCATION_OFFLINE;
1745         }
1746     }
1747     return error;
1748 }
1749
1750 static DWORD verify_cert_revocation(PCCERT_CONTEXT cert, DWORD index,
1751  FILETIME *pTime, DWORD dwFlags, PCERT_REVOCATION_PARA pRevPara,
1752  PCERT_REVOCATION_STATUS pRevStatus)
1753 {
1754     DWORD error = ERROR_SUCCESS;
1755     PCERT_EXTENSION ext;
1756
1757     if ((ext = CertFindExtension(szOID_CRL_DIST_POINTS,
1758      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
1759         error = verify_cert_revocation_from_dist_points_ext(&ext->Value, cert,
1760          index, pTime, dwFlags, pRevPara, pRevStatus);
1761     else if ((ext = CertFindExtension(szOID_AUTHORITY_INFO_ACCESS,
1762      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
1763         error = verify_cert_revocation_from_aia_ext(&ext->Value, cert,
1764          index, pTime, dwFlags, pRevPara, pRevStatus);
1765     else
1766     {
1767         if (pRevPara && pRevPara->hCrlStore && pRevPara->pIssuerCert)
1768         {
1769             PCCRL_CONTEXT crl = NULL;
1770             BOOL canSignCRLs;
1771
1772             /* If the caller told us about the issuer, make sure the issuer
1773              * can sign CRLs before looking for one.
1774              */
1775             if ((ext = CertFindExtension(szOID_KEY_USAGE,
1776              pRevPara->pIssuerCert->pCertInfo->cExtension,
1777              pRevPara->pIssuerCert->pCertInfo->rgExtension)))
1778             {
1779                 CRYPT_BIT_BLOB usage;
1780                 DWORD size = sizeof(usage);
1781
1782                 if (!CryptDecodeObjectEx(cert->dwCertEncodingType, X509_BITS,
1783                  ext->Value.pbData, ext->Value.cbData,
1784                  CRYPT_DECODE_NOCOPY_FLAG, NULL, &usage, &size))
1785                     canSignCRLs = FALSE;
1786                 else if (usage.cbData > 2)
1787                 {
1788                     /* The key usage extension only defines 9 bits => no more
1789                      * than 2 bytes are needed to encode all known usages.
1790                      */
1791                     canSignCRLs = FALSE;
1792                 }
1793                 else
1794                 {
1795                     BYTE usageBits = usage.pbData[usage.cbData - 1];
1796
1797                     canSignCRLs = usageBits & CERT_CRL_SIGN_KEY_USAGE;
1798                 }
1799             }
1800             else
1801                 canSignCRLs = TRUE;
1802             if (canSignCRLs)
1803             {
1804                 /* If the caller was helpful enough to tell us where to find a
1805                  * CRL for the cert, look for one and check it.
1806                  */
1807                 crl = CertFindCRLInStore(pRevPara->hCrlStore,
1808                  cert->dwCertEncodingType,
1809                  CRL_FIND_ISSUED_BY_SIGNATURE_FLAG |
1810                  CRL_FIND_ISSUED_BY_AKI_FLAG,
1811                  CRL_FIND_ISSUED_BY, pRevPara->pIssuerCert, NULL);
1812             }
1813             if (crl)
1814             {
1815                 error = verify_cert_revocation_with_crl_offline(cert, crl,
1816                  index, pTime, pRevStatus);
1817                 CertFreeCRLContext(crl);
1818             }
1819             else
1820             {
1821                 TRACE("no CRL found\n");
1822                 error = CRYPT_E_NO_REVOCATION_CHECK;
1823                 pRevStatus->dwIndex = index;
1824             }
1825         }
1826         else
1827         {
1828             if (!pRevPara)
1829                 WARN("no CERT_REVOCATION_PARA\n");
1830             else if (!pRevPara->hCrlStore)
1831                 WARN("no dist points/aia extension and no CRL store\n");
1832             else if (!pRevPara->pIssuerCert)
1833                 WARN("no dist points/aia extension and no issuer\n");
1834             error = CRYPT_E_NO_REVOCATION_CHECK;
1835             pRevStatus->dwIndex = index;
1836         }
1837     }
1838     return error;
1839 }
1840
1841 typedef struct _CERT_REVOCATION_PARA_NO_EXTRA_FIELDS {
1842     DWORD                     cbSize;
1843     PCCERT_CONTEXT            pIssuerCert;
1844     DWORD                     cCertStore;
1845     HCERTSTORE               *rgCertStore;
1846     HCERTSTORE                hCrlStore;
1847     LPFILETIME                pftTimeToUse;
1848 } CERT_REVOCATION_PARA_NO_EXTRA_FIELDS, *PCERT_REVOCATION_PARA_NO_EXTRA_FIELDS;
1849
1850 typedef struct _OLD_CERT_REVOCATION_STATUS {
1851     DWORD cbSize;
1852     DWORD dwIndex;
1853     DWORD dwError;
1854     DWORD dwReason;
1855 } OLD_CERT_REVOCATION_STATUS, *POLD_CERT_REVOCATION_STATUS;
1856
1857 /***********************************************************************
1858  *    CertDllVerifyRevocation (CRYPTNET.@)
1859  */
1860 BOOL WINAPI CertDllVerifyRevocation(DWORD dwEncodingType, DWORD dwRevType,
1861  DWORD cContext, PVOID rgpvContext[], DWORD dwFlags,
1862  PCERT_REVOCATION_PARA pRevPara, PCERT_REVOCATION_STATUS pRevStatus)
1863 {
1864     DWORD error = 0, i;
1865     FILETIME now;
1866     LPFILETIME pTime = NULL;
1867
1868     TRACE("(%08x, %d, %d, %p, %08x, %p, %p)\n", dwEncodingType, dwRevType,
1869      cContext, rgpvContext, dwFlags, pRevPara, pRevStatus);
1870
1871     if (pRevStatus->cbSize != sizeof(OLD_CERT_REVOCATION_STATUS) &&
1872      pRevStatus->cbSize != sizeof(CERT_REVOCATION_STATUS))
1873     {
1874         SetLastError(E_INVALIDARG);
1875         return FALSE;
1876     }
1877     if (!cContext)
1878     {
1879         SetLastError(E_INVALIDARG);
1880         return FALSE;
1881     }
1882     if (pRevPara && pRevPara->cbSize >=
1883      sizeof(CERT_REVOCATION_PARA_NO_EXTRA_FIELDS))
1884         pTime = pRevPara->pftTimeToUse;
1885     if (!pTime)
1886     {
1887         GetSystemTimeAsFileTime(&now);
1888         pTime = &now;
1889     }
1890     memset(&pRevStatus->dwIndex, 0, pRevStatus->cbSize - sizeof(DWORD));
1891     if (dwRevType != CERT_CONTEXT_REVOCATION_TYPE)
1892         error = CRYPT_E_NO_REVOCATION_CHECK;
1893     else
1894     {
1895         for (i = 0; !error && i < cContext; i++)
1896             error = verify_cert_revocation(rgpvContext[i], i, pTime, dwFlags,
1897              pRevPara, pRevStatus);
1898     }
1899     if (error)
1900     {
1901         SetLastError(error);
1902         pRevStatus->dwError = error;
1903     }
1904     TRACE("returning %d (%08x)\n", !error, error);
1905     return !error;
1906 }