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