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