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