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