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