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