cryptnet: Don't ignore HttpEndRequestW error in HTTP_RetrieveEncodedObjectW.
[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                         DWORD len = sizeof(st);
961
962                         if (HttpQueryInfoW(hHttp,
963                          HTTP_QUERY_EXPIRES | HTTP_QUERY_FLAG_SYSTEMTIME, &st,
964                          &len, NULL))
965                         {
966                             FILETIME ft;
967
968                             SystemTimeToFileTime(&st, &ft);
969                             CRYPT_CacheURL(pszURL, pObject, dwRetrievalFlags,
970                              ft);
971                         }
972                     }
973                     InternetCloseHandle(hHttp);
974                 }
975                 InternetCloseHandle(hHost);
976                 InternetCloseHandle(hInt);
977             }
978             if (context)
979             {
980                 CloseHandle(context->event);
981                 CryptMemFree(context);
982             }
983             CryptMemFree(components.lpszUrlPath);
984             CryptMemFree(components.lpszHostName);
985         }
986     }
987     TRACE("returning %d\n", ret);
988     return ret;
989 }
990
991 static BOOL WINAPI File_RetrieveEncodedObjectW(LPCWSTR pszURL,
992  LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
993  PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
994  void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
995  PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
996 {
997     URL_COMPONENTSW components = { sizeof(components), 0 };
998     BOOL ret;
999
1000     TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
1001      debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject,
1002      ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo);
1003
1004     pObject->cBlob = 0;
1005     pObject->rgBlob = NULL;
1006     *ppfnFreeObject = CRYPT_FreeBlob;
1007     *ppvFreeContext = NULL;
1008
1009     components.lpszUrlPath = CryptMemAlloc(INTERNET_MAX_PATH_LENGTH * sizeof(WCHAR));
1010     components.dwUrlPathLength = INTERNET_MAX_PATH_LENGTH;
1011     if (!components.lpszUrlPath)
1012     {
1013         SetLastError(ERROR_OUTOFMEMORY);
1014         return FALSE;
1015     }
1016
1017     ret = InternetCrackUrlW(pszURL, 0, ICU_DECODE, &components);
1018     if (ret)
1019     {
1020         LPWSTR path;
1021
1022         /* 3 == lstrlenW(L"c:") + 1 */
1023         path = CryptMemAlloc((components.dwUrlPathLength + 3) * sizeof(WCHAR));
1024         if (path)
1025         {
1026             HANDLE hFile;
1027
1028             /* Try to create the file directly - Wine handles / in pathnames */
1029             lstrcpynW(path, components.lpszUrlPath,
1030              components.dwUrlPathLength + 1);
1031             hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
1032              NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1033             if (hFile == INVALID_HANDLE_VALUE)
1034             {
1035                 /* Try again on the current drive */
1036                 GetCurrentDirectoryW(components.dwUrlPathLength, path);
1037                 if (path[1] == ':')
1038                 {
1039                     lstrcpynW(path + 2, components.lpszUrlPath,
1040                      components.dwUrlPathLength + 1);
1041                     hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
1042                      NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1043                 }
1044                 if (hFile == INVALID_HANDLE_VALUE)
1045                 {
1046                     /* Try again on the Windows drive */
1047                     GetWindowsDirectoryW(path, components.dwUrlPathLength);
1048                     if (path[1] == ':')
1049                     {
1050                         lstrcpynW(path + 2, components.lpszUrlPath,
1051                          components.dwUrlPathLength + 1);
1052                         hFile = CreateFileW(path, GENERIC_READ, FILE_SHARE_READ,
1053                          NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
1054                     }
1055                 }
1056             }
1057             if (hFile != INVALID_HANDLE_VALUE)
1058             {
1059                 if ((ret = CRYPT_GetObjectFromFile(hFile, pObject)))
1060                 {
1061                     if (pAuxInfo && pAuxInfo->cbSize >=
1062                      offsetof(CRYPT_RETRIEVE_AUX_INFO,
1063                      pLastSyncTime) + sizeof(PFILETIME) &&
1064                      pAuxInfo->pLastSyncTime)
1065                         GetFileTime(hFile, NULL, NULL,
1066                          pAuxInfo->pLastSyncTime);
1067                 }
1068                 CloseHandle(hFile);
1069             }
1070             else
1071                 ret = FALSE;
1072             CryptMemFree(path);
1073         }
1074         else
1075         {
1076             SetLastError(ERROR_OUTOFMEMORY);
1077             ret = FALSE;
1078         }
1079     }
1080     CryptMemFree(components.lpszUrlPath);
1081     return ret;
1082 }
1083
1084 typedef BOOL (WINAPI *SchemeDllRetrieveEncodedObjectW)(LPCWSTR pwszUrl,
1085  LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
1086  PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
1087  void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
1088  PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo);
1089
1090 static BOOL CRYPT_GetRetrieveFunction(LPCWSTR pszURL,
1091  SchemeDllRetrieveEncodedObjectW *pFunc, HCRYPTOIDFUNCADDR *phFunc)
1092 {
1093     URL_COMPONENTSW components = { sizeof(components), 0 };
1094     BOOL ret;
1095
1096     TRACE("(%s, %p, %p)\n", debugstr_w(pszURL), pFunc, phFunc);
1097
1098     *pFunc = NULL;
1099     *phFunc = 0;
1100     components.dwSchemeLength = 1;
1101     ret = InternetCrackUrlW(pszURL, 0, 0, &components);
1102     if (ret)
1103     {
1104         /* Microsoft always uses CryptInitOIDFunctionSet/
1105          * CryptGetOIDFunctionAddress, but there doesn't seem to be a pressing
1106          * reason to do so for builtin schemes.
1107          */
1108         switch (components.nScheme)
1109         {
1110         case INTERNET_SCHEME_FTP:
1111             *pFunc = FTP_RetrieveEncodedObjectW;
1112             break;
1113         case INTERNET_SCHEME_HTTP:
1114             *pFunc = HTTP_RetrieveEncodedObjectW;
1115             break;
1116         case INTERNET_SCHEME_FILE:
1117             *pFunc = File_RetrieveEncodedObjectW;
1118             break;
1119         default:
1120         {
1121             int len = WideCharToMultiByte(CP_ACP, 0, components.lpszScheme,
1122              components.dwSchemeLength, NULL, 0, NULL, NULL);
1123
1124             if (len)
1125             {
1126                 LPSTR scheme = CryptMemAlloc(len);
1127
1128                 if (scheme)
1129                 {
1130                     static HCRYPTOIDFUNCSET set = NULL;
1131
1132                     if (!set)
1133                         set = CryptInitOIDFunctionSet(
1134                          SCHEME_OID_RETRIEVE_ENCODED_OBJECTW_FUNC, 0);
1135                     WideCharToMultiByte(CP_ACP, 0, components.lpszScheme,
1136                      components.dwSchemeLength, scheme, len, NULL, NULL);
1137                     ret = CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING,
1138                      scheme, 0, (void **)pFunc, phFunc);
1139                     CryptMemFree(scheme);
1140                 }
1141                 else
1142                 {
1143                     SetLastError(ERROR_OUTOFMEMORY);
1144                     ret = FALSE;
1145                 }
1146             }
1147             else
1148                 ret = FALSE;
1149         }
1150         }
1151     }
1152     TRACE("returning %d\n", ret);
1153     return ret;
1154 }
1155
1156 static BOOL WINAPI CRYPT_CreateBlob(LPCSTR pszObjectOid,
1157  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1158 {
1159     DWORD size, i;
1160     CRYPT_BLOB_ARRAY *context;
1161     BOOL ret = FALSE;
1162
1163     size = sizeof(CRYPT_BLOB_ARRAY) + pObject->cBlob * sizeof(CRYPT_DATA_BLOB);
1164     for (i = 0; i < pObject->cBlob; i++)
1165         size += pObject->rgBlob[i].cbData;
1166     context = CryptMemAlloc(size);
1167     if (context)
1168     {
1169         LPBYTE nextData;
1170
1171         context->cBlob = 0;
1172         context->rgBlob =
1173          (CRYPT_DATA_BLOB *)((LPBYTE)context + sizeof(CRYPT_BLOB_ARRAY));
1174         nextData =
1175          (LPBYTE)context->rgBlob + pObject->cBlob * sizeof(CRYPT_DATA_BLOB);
1176         for (i = 0; i < pObject->cBlob; i++)
1177         {
1178             memcpy(nextData, pObject->rgBlob[i].pbData,
1179              pObject->rgBlob[i].cbData);
1180             context->rgBlob[i].pbData = nextData;
1181             context->rgBlob[i].cbData = pObject->rgBlob[i].cbData;
1182             nextData += pObject->rgBlob[i].cbData;
1183             context->cBlob++;
1184         }
1185         *ppvContext = context;
1186         ret = TRUE;
1187     }
1188     return ret;
1189 }
1190
1191 typedef BOOL (WINAPI *AddContextToStore)(HCERTSTORE hCertStore,
1192  const void *pContext, DWORD dwAddDisposition, const void **ppStoreContext);
1193
1194 static BOOL CRYPT_CreateContext(const CRYPT_BLOB_ARRAY *pObject,
1195  DWORD dwExpectedContentTypeFlags, AddContextToStore addFunc, void **ppvContext)
1196 {
1197     BOOL ret = TRUE;
1198
1199     if (!pObject->cBlob)
1200     {
1201         SetLastError(ERROR_INVALID_DATA);
1202         *ppvContext = NULL;
1203         ret = FALSE;
1204     }
1205     else if (pObject->cBlob == 1)
1206     {
1207         if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &pObject->rgBlob[0],
1208          dwExpectedContentTypeFlags, CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL,
1209          NULL, NULL, NULL, NULL, (const void **)ppvContext))
1210         {
1211             SetLastError(CRYPT_E_NO_MATCH);
1212             ret = FALSE;
1213         }
1214     }
1215     else
1216     {
1217         HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
1218          CERT_STORE_CREATE_NEW_FLAG, NULL);
1219
1220         if (store)
1221         {
1222             DWORD i;
1223             const void *context;
1224
1225             for (i = 0; i < pObject->cBlob; i++)
1226             {
1227                 if (CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
1228                  &pObject->rgBlob[i], dwExpectedContentTypeFlags,
1229                  CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL, NULL,
1230                  NULL, &context))
1231                 {
1232                     if (!addFunc(store, context, CERT_STORE_ADD_ALWAYS, NULL))
1233                         ret = FALSE;
1234                 }
1235                 else
1236                 {
1237                     SetLastError(CRYPT_E_NO_MATCH);
1238                     ret = FALSE;
1239                 }
1240             }
1241         }
1242         else
1243             ret = FALSE;
1244         *ppvContext = store;
1245     }
1246     return ret;
1247 }
1248
1249 static BOOL WINAPI CRYPT_CreateCert(LPCSTR pszObjectOid,
1250  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1251 {
1252     return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CERT,
1253      (AddContextToStore)CertAddCertificateContextToStore, ppvContext);
1254 }
1255
1256 static BOOL WINAPI CRYPT_CreateCRL(LPCSTR pszObjectOid,
1257  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1258 {
1259     return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CRL,
1260      (AddContextToStore)CertAddCRLContextToStore, ppvContext);
1261 }
1262
1263 static BOOL WINAPI CRYPT_CreateCTL(LPCSTR pszObjectOid,
1264  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1265 {
1266     return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CTL,
1267      (AddContextToStore)CertAddCTLContextToStore, ppvContext);
1268 }
1269
1270 static BOOL WINAPI CRYPT_CreatePKCS7(LPCSTR pszObjectOid,
1271  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1272 {
1273     BOOL ret;
1274
1275     if (!pObject->cBlob)
1276     {
1277         SetLastError(ERROR_INVALID_DATA);
1278         *ppvContext = NULL;
1279         ret = FALSE;
1280     }
1281     else if (pObject->cBlob == 1)
1282         ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &pObject->rgBlob[0],
1283          CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
1284          CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED, CERT_QUERY_FORMAT_FLAG_BINARY,
1285          0, NULL, NULL, NULL, ppvContext, NULL, NULL);
1286     else
1287     {
1288         FIXME("multiple messages unimplemented\n");
1289         ret = FALSE;
1290     }
1291     return ret;
1292 }
1293
1294 static BOOL WINAPI CRYPT_CreateAny(LPCSTR pszObjectOid,
1295  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext)
1296 {
1297     BOOL ret;
1298
1299     if (!pObject->cBlob)
1300     {
1301         SetLastError(ERROR_INVALID_DATA);
1302         *ppvContext = NULL;
1303         ret = FALSE;
1304     }
1305     else
1306     {
1307         HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
1308          CERT_STORE_CREATE_NEW_FLAG, NULL);
1309
1310         if (store)
1311         {
1312             HCERTSTORE memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
1313              CERT_STORE_CREATE_NEW_FLAG, NULL);
1314
1315             if (memStore)
1316             {
1317                 CertAddStoreToCollection(store, memStore,
1318                  CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 0);
1319                 CertCloseStore(memStore, 0);
1320             }
1321             else
1322             {
1323                 CertCloseStore(store, 0);
1324                 store = NULL;
1325             }
1326         }
1327         if (store)
1328         {
1329             DWORD i;
1330
1331             ret = TRUE;
1332             for (i = 0; i < pObject->cBlob; i++)
1333             {
1334                 DWORD contentType, expectedContentTypes =
1335                  CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
1336                  CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED |
1337                  CERT_QUERY_CONTENT_FLAG_CERT |
1338                  CERT_QUERY_CONTENT_FLAG_CRL |
1339                  CERT_QUERY_CONTENT_FLAG_CTL;
1340                 HCERTSTORE contextStore;
1341                 const void *context;
1342
1343                 if (CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
1344                  &pObject->rgBlob[i], expectedContentTypes,
1345                  CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, &contentType, NULL,
1346                  &contextStore, NULL, &context))
1347                 {
1348                     switch (contentType)
1349                     {
1350                     case CERT_QUERY_CONTENT_CERT:
1351                         if (!CertAddCertificateContextToStore(store,
1352                          context, CERT_STORE_ADD_ALWAYS, NULL))
1353                             ret = FALSE;
1354                         CertFreeCertificateContext(context);
1355                         break;
1356                     case CERT_QUERY_CONTENT_CRL:
1357                         if (!CertAddCRLContextToStore(store,
1358                          context, CERT_STORE_ADD_ALWAYS, NULL))
1359                              ret = FALSE;
1360                         CertFreeCRLContext(context);
1361                         break;
1362                     case CERT_QUERY_CONTENT_CTL:
1363                         if (!CertAddCTLContextToStore(store,
1364                          context, CERT_STORE_ADD_ALWAYS, NULL))
1365                              ret = FALSE;
1366                         CertFreeCTLContext(context);
1367                         break;
1368                     default:
1369                         CertAddStoreToCollection(store, contextStore, 0, 0);
1370                     }
1371                     CertCloseStore(contextStore, 0);
1372                 }
1373                 else
1374                     ret = FALSE;
1375             }
1376         }
1377         else
1378             ret = FALSE;
1379         *ppvContext = store;
1380     }
1381     return ret;
1382 }
1383
1384 typedef BOOL (WINAPI *ContextDllCreateObjectContext)(LPCSTR pszObjectOid,
1385  DWORD dwRetrievalFlags, const CRYPT_BLOB_ARRAY *pObject, void **ppvContext);
1386
1387 static BOOL CRYPT_GetCreateFunction(LPCSTR pszObjectOid,
1388  ContextDllCreateObjectContext *pFunc, HCRYPTOIDFUNCADDR *phFunc)
1389 {
1390     BOOL ret = TRUE;
1391
1392     TRACE("(%s, %p, %p)\n", debugstr_a(pszObjectOid), pFunc, phFunc);
1393
1394     *pFunc = NULL;
1395     *phFunc = 0;
1396     if (IS_INTOID(pszObjectOid))
1397     {
1398         switch (LOWORD(pszObjectOid))
1399         {
1400         case 0:
1401             *pFunc = CRYPT_CreateBlob;
1402             break;
1403         case LOWORD(CONTEXT_OID_CERTIFICATE):
1404             *pFunc = CRYPT_CreateCert;
1405             break;
1406         case LOWORD(CONTEXT_OID_CRL):
1407             *pFunc = CRYPT_CreateCRL;
1408             break;
1409         case LOWORD(CONTEXT_OID_CTL):
1410             *pFunc = CRYPT_CreateCTL;
1411             break;
1412         case LOWORD(CONTEXT_OID_PKCS7):
1413             *pFunc = CRYPT_CreatePKCS7;
1414             break;
1415         case LOWORD(CONTEXT_OID_CAPI2_ANY):
1416             *pFunc = CRYPT_CreateAny;
1417             break;
1418         }
1419     }
1420     if (!*pFunc)
1421     {
1422         static HCRYPTOIDFUNCSET set = NULL;
1423
1424         if (!set)
1425             set = CryptInitOIDFunctionSet(
1426              CONTEXT_OID_CREATE_OBJECT_CONTEXT_FUNC, 0);
1427         ret = CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, pszObjectOid,
1428          0, (void **)pFunc, phFunc);
1429     }
1430     TRACE("returning %d\n", ret);
1431     return ret;
1432 }
1433
1434 typedef BOOL (*get_object_expiration_func)(const void *pvContext,
1435  FILETIME *expiration);
1436
1437 static BOOL CRYPT_GetExpirationFromCert(const void *pvObject, FILETIME *expiration)
1438 {
1439     PCCERT_CONTEXT cert = pvObject;
1440
1441     *expiration = cert->pCertInfo->NotAfter;
1442     return TRUE;
1443 }
1444
1445 static BOOL CRYPT_GetExpirationFromCRL(const void *pvObject, FILETIME *expiration)
1446 {
1447     PCCRL_CONTEXT cert = pvObject;
1448
1449     *expiration = cert->pCrlInfo->NextUpdate;
1450     return TRUE;
1451 }
1452
1453 static BOOL CRYPT_GetExpirationFromCTL(const void *pvObject, FILETIME *expiration)
1454 {
1455     PCCTL_CONTEXT cert = pvObject;
1456
1457     *expiration = cert->pCtlInfo->NextUpdate;
1458     return TRUE;
1459 }
1460
1461 static BOOL CRYPT_GetExpirationFunction(LPCSTR pszObjectOid,
1462  get_object_expiration_func *getExpiration)
1463 {
1464     BOOL ret;
1465
1466     if (IS_INTOID(pszObjectOid))
1467     {
1468         switch (LOWORD(pszObjectOid))
1469         {
1470         case LOWORD(CONTEXT_OID_CERTIFICATE):
1471             *getExpiration = CRYPT_GetExpirationFromCert;
1472             ret = TRUE;
1473             break;
1474         case LOWORD(CONTEXT_OID_CRL):
1475             *getExpiration = CRYPT_GetExpirationFromCRL;
1476             ret = TRUE;
1477             break;
1478         case LOWORD(CONTEXT_OID_CTL):
1479             *getExpiration = CRYPT_GetExpirationFromCTL;
1480             ret = TRUE;
1481             break;
1482         default:
1483             ret = FALSE;
1484         }
1485     }
1486     else
1487         ret = FALSE;
1488     return ret;
1489 }
1490
1491 /***********************************************************************
1492  *    CryptRetrieveObjectByUrlW (CRYPTNET.@)
1493  */
1494 BOOL WINAPI CryptRetrieveObjectByUrlW(LPCWSTR pszURL, LPCSTR pszObjectOid,
1495  DWORD dwRetrievalFlags, DWORD dwTimeout, LPVOID *ppvObject,
1496  HCRYPTASYNC hAsyncRetrieve, PCRYPT_CREDENTIALS pCredentials, LPVOID pvVerify,
1497  PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
1498 {
1499     BOOL ret;
1500     SchemeDllRetrieveEncodedObjectW retrieve;
1501     ContextDllCreateObjectContext create;
1502     HCRYPTOIDFUNCADDR hRetrieve = 0, hCreate = 0;
1503
1504     TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
1505      debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, ppvObject,
1506      hAsyncRetrieve, pCredentials, pvVerify, pAuxInfo);
1507
1508     if (!pszURL)
1509     {
1510         SetLastError(ERROR_INVALID_PARAMETER);
1511         return FALSE;
1512     }
1513     ret = CRYPT_GetRetrieveFunction(pszURL, &retrieve, &hRetrieve);
1514     if (ret)
1515         ret = CRYPT_GetCreateFunction(pszObjectOid, &create, &hCreate);
1516     if (ret)
1517     {
1518         CRYPT_BLOB_ARRAY object = { 0, NULL };
1519         PFN_FREE_ENCODED_OBJECT_FUNC freeObject;
1520         void *freeContext;
1521
1522         ret = retrieve(pszURL, pszObjectOid, dwRetrievalFlags, dwTimeout,
1523          &object, &freeObject, &freeContext, hAsyncRetrieve, pCredentials,
1524          pAuxInfo);
1525         if (ret)
1526         {
1527             get_object_expiration_func getExpiration;
1528
1529             ret = create(pszObjectOid, dwRetrievalFlags, &object, ppvObject);
1530             if (ret && !(dwRetrievalFlags & CRYPT_DONT_CACHE_RESULT) &&
1531              CRYPT_GetExpirationFunction(pszObjectOid, &getExpiration))
1532             {
1533                 FILETIME expires;
1534
1535                 if (getExpiration(*ppvObject, &expires))
1536                     CRYPT_CacheURL(pszURL, &object, dwRetrievalFlags, expires);
1537             }
1538             freeObject(pszObjectOid, &object, freeContext);
1539         }
1540     }
1541     if (hCreate)
1542         CryptFreeOIDFunctionAddress(hCreate, 0);
1543     if (hRetrieve)
1544         CryptFreeOIDFunctionAddress(hRetrieve, 0);
1545     TRACE("returning %d\n", ret);
1546     return ret;
1547 }
1548
1549 static DWORD verify_cert_revocation_with_crl_online(PCCERT_CONTEXT cert,
1550  PCCRL_CONTEXT crl, DWORD index, FILETIME *pTime,
1551  PCERT_REVOCATION_STATUS pRevStatus)
1552 {
1553     DWORD error;
1554     PCRL_ENTRY entry = NULL;
1555
1556     CertFindCertificateInCRL(cert, crl, 0, NULL, &entry);
1557     if (entry)
1558     {
1559         error = CRYPT_E_REVOKED;
1560         pRevStatus->dwIndex = index;
1561     }
1562     else
1563     {
1564         /* Since the CRL was retrieved for the cert being checked, then it's
1565          * guaranteed to be fresh, and the cert is not revoked.
1566          */
1567         error = ERROR_SUCCESS;
1568     }
1569     return error;
1570 }
1571
1572 static DWORD verify_cert_revocation_from_dist_points_ext(
1573  const CRYPT_DATA_BLOB *value, PCCERT_CONTEXT cert, DWORD index,
1574  FILETIME *pTime, DWORD dwFlags, const CERT_REVOCATION_PARA *pRevPara,
1575  PCERT_REVOCATION_STATUS pRevStatus)
1576 {
1577     DWORD error = ERROR_SUCCESS, cbUrlArray;
1578
1579     if (CRYPT_GetUrlFromCRLDistPointsExt(value, NULL, &cbUrlArray, NULL, NULL))
1580     {
1581         CRYPT_URL_ARRAY *urlArray = CryptMemAlloc(cbUrlArray);
1582
1583         if (urlArray)
1584         {
1585             DWORD j, retrievalFlags = 0, startTime, endTime, timeout;
1586             BOOL ret;
1587
1588             ret = CRYPT_GetUrlFromCRLDistPointsExt(value, urlArray,
1589              &cbUrlArray, NULL, NULL);
1590             if (dwFlags & CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION)
1591                 retrievalFlags |= CRYPT_CACHE_ONLY_RETRIEVAL;
1592             if (dwFlags & CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG &&
1593              pRevPara && pRevPara->cbSize >= offsetof(CERT_REVOCATION_PARA,
1594              dwUrlRetrievalTimeout) + sizeof(DWORD))
1595             {
1596                 startTime = GetTickCount();
1597                 endTime = startTime + pRevPara->dwUrlRetrievalTimeout;
1598                 timeout = pRevPara->dwUrlRetrievalTimeout;
1599             }
1600             else
1601                 endTime = timeout = 0;
1602             if (!ret)
1603                 error = GetLastError();
1604             for (j = 0; !error && j < urlArray->cUrl; j++)
1605             {
1606                 PCCRL_CONTEXT crl;
1607
1608                 ret = CryptRetrieveObjectByUrlW(urlArray->rgwszUrl[j],
1609                  CONTEXT_OID_CRL, retrievalFlags, timeout, (void **)&crl,
1610                  NULL, NULL, NULL, NULL);
1611                 if (ret)
1612                 {
1613                     error = verify_cert_revocation_with_crl_online(cert, crl,
1614                      index, pTime, pRevStatus);
1615                     if (!error && timeout)
1616                     {
1617                         DWORD time = GetTickCount();
1618
1619                         if ((int)(endTime - time) <= 0)
1620                         {
1621                             error = ERROR_TIMEOUT;
1622                             pRevStatus->dwIndex = index;
1623                         }
1624                         else
1625                             timeout = endTime - time;
1626                     }
1627                     CertFreeCRLContext(crl);
1628                 }
1629                 else
1630                     error = CRYPT_E_REVOCATION_OFFLINE;
1631             }
1632             CryptMemFree(urlArray);
1633         }
1634         else
1635         {
1636             error = ERROR_OUTOFMEMORY;
1637             pRevStatus->dwIndex = index;
1638         }
1639     }
1640     else
1641     {
1642         error = GetLastError();
1643         pRevStatus->dwIndex = index;
1644     }
1645     return error;
1646 }
1647
1648 static DWORD verify_cert_revocation_from_aia_ext(
1649  const CRYPT_DATA_BLOB *value, PCCERT_CONTEXT cert, DWORD index,
1650  FILETIME *pTime, DWORD dwFlags, PCERT_REVOCATION_PARA pRevPara,
1651  PCERT_REVOCATION_STATUS pRevStatus)
1652 {
1653     BOOL ret;
1654     DWORD error, size;
1655     CERT_AUTHORITY_INFO_ACCESS *aia;
1656
1657     ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_AUTHORITY_INFO_ACCESS,
1658      value->pbData, value->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &aia, &size);
1659     if (ret)
1660     {
1661         DWORD i;
1662
1663         for (i = 0; i < aia->cAccDescr; i++)
1664             if (!strcmp(aia->rgAccDescr[i].pszAccessMethod,
1665              szOID_PKIX_OCSP))
1666             {
1667                 if (aia->rgAccDescr[i].AccessLocation.dwAltNameChoice ==
1668                  CERT_ALT_NAME_URL)
1669                     FIXME("OCSP URL = %s\n",
1670                      debugstr_w(aia->rgAccDescr[i].AccessLocation.u.pwszURL));
1671                 else
1672                     FIXME("unsupported AccessLocation type %d\n",
1673                      aia->rgAccDescr[i].AccessLocation.dwAltNameChoice);
1674             }
1675         LocalFree(aia);
1676         /* FIXME: lie and pretend OCSP validated the cert */
1677         error = ERROR_SUCCESS;
1678     }
1679     else
1680         error = GetLastError();
1681     return error;
1682 }
1683
1684 static DWORD verify_cert_revocation_with_crl_offline(PCCERT_CONTEXT cert,
1685  PCCRL_CONTEXT crl, DWORD index, FILETIME *pTime,
1686  PCERT_REVOCATION_STATUS pRevStatus)
1687 {
1688     DWORD error;
1689     LONG valid;
1690
1691     valid = CompareFileTime(pTime, &crl->pCrlInfo->ThisUpdate);
1692     if (valid <= 0)
1693     {
1694         /* If this CRL is not older than the time being verified, there's no
1695          * way to know whether the certificate was revoked.
1696          */
1697         TRACE("CRL not old enough\n");
1698         error = CRYPT_E_REVOCATION_OFFLINE;
1699     }
1700     else
1701     {
1702         PCRL_ENTRY entry = NULL;
1703
1704         CertFindCertificateInCRL(cert, crl, 0, NULL, &entry);
1705         if (entry)
1706         {
1707             error = CRYPT_E_REVOKED;
1708             pRevStatus->dwIndex = index;
1709         }
1710         else
1711         {
1712             /* Since the CRL was not retrieved for the cert being checked,
1713              * there's no guarantee it's fresh, so the cert *might* be okay,
1714              * but it's safer not to guess.
1715              */
1716             TRACE("certificate not found\n");
1717             error = CRYPT_E_REVOCATION_OFFLINE;
1718         }
1719     }
1720     return error;
1721 }
1722
1723 static DWORD verify_cert_revocation(PCCERT_CONTEXT cert, DWORD index,
1724  FILETIME *pTime, DWORD dwFlags, PCERT_REVOCATION_PARA pRevPara,
1725  PCERT_REVOCATION_STATUS pRevStatus)
1726 {
1727     DWORD error = ERROR_SUCCESS;
1728     PCERT_EXTENSION ext;
1729
1730     if ((ext = CertFindExtension(szOID_CRL_DIST_POINTS,
1731      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
1732         error = verify_cert_revocation_from_dist_points_ext(&ext->Value, cert,
1733          index, pTime, dwFlags, pRevPara, pRevStatus);
1734     else if ((ext = CertFindExtension(szOID_AUTHORITY_INFO_ACCESS,
1735      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
1736         error = verify_cert_revocation_from_aia_ext(&ext->Value, cert,
1737          index, pTime, dwFlags, pRevPara, pRevStatus);
1738     else
1739     {
1740         if (pRevPara && pRevPara->hCrlStore && pRevPara->pIssuerCert)
1741         {
1742             PCCRL_CONTEXT crl = NULL;
1743             BOOL canSignCRLs;
1744
1745             /* If the caller told us about the issuer, make sure the issuer
1746              * can sign CRLs before looking for one.
1747              */
1748             if ((ext = CertFindExtension(szOID_KEY_USAGE,
1749              pRevPara->pIssuerCert->pCertInfo->cExtension,
1750              pRevPara->pIssuerCert->pCertInfo->rgExtension)))
1751             {
1752                 CRYPT_BIT_BLOB usage;
1753                 DWORD size = sizeof(usage);
1754
1755                 if (!CryptDecodeObjectEx(cert->dwCertEncodingType, X509_BITS,
1756                  ext->Value.pbData, ext->Value.cbData,
1757                  CRYPT_DECODE_NOCOPY_FLAG, NULL, &usage, &size))
1758                     canSignCRLs = FALSE;
1759                 else if (usage.cbData > 2)
1760                 {
1761                     /* The key usage extension only defines 9 bits => no more
1762                      * than 2 bytes are needed to encode all known usages.
1763                      */
1764                     canSignCRLs = FALSE;
1765                 }
1766                 else
1767                 {
1768                     BYTE usageBits = usage.pbData[usage.cbData - 1];
1769
1770                     canSignCRLs = usageBits & CERT_CRL_SIGN_KEY_USAGE;
1771                 }
1772             }
1773             else
1774                 canSignCRLs = TRUE;
1775             if (canSignCRLs)
1776             {
1777                 /* If the caller was helpful enough to tell us where to find a
1778                  * CRL for the cert, look for one and check it.
1779                  */
1780                 crl = CertFindCRLInStore(pRevPara->hCrlStore,
1781                  cert->dwCertEncodingType,
1782                  CRL_FIND_ISSUED_BY_SIGNATURE_FLAG |
1783                  CRL_FIND_ISSUED_BY_AKI_FLAG,
1784                  CRL_FIND_ISSUED_BY, pRevPara->pIssuerCert, NULL);
1785             }
1786             if (crl)
1787             {
1788                 error = verify_cert_revocation_with_crl_offline(cert, crl,
1789                  index, pTime, pRevStatus);
1790                 CertFreeCRLContext(crl);
1791             }
1792             else
1793             {
1794                 TRACE("no CRL found\n");
1795                 error = CRYPT_E_NO_REVOCATION_CHECK;
1796                 pRevStatus->dwIndex = index;
1797             }
1798         }
1799         else
1800         {
1801             if (!pRevPara)
1802                 WARN("no CERT_REVOCATION_PARA\n");
1803             else if (!pRevPara->hCrlStore)
1804                 WARN("no dist points/aia extension and no CRL store\n");
1805             else if (!pRevPara->pIssuerCert)
1806                 WARN("no dist points/aia extension and no issuer\n");
1807             error = CRYPT_E_NO_REVOCATION_CHECK;
1808             pRevStatus->dwIndex = index;
1809         }
1810     }
1811     return error;
1812 }
1813
1814 typedef struct _CERT_REVOCATION_PARA_NO_EXTRA_FIELDS {
1815     DWORD                     cbSize;
1816     PCCERT_CONTEXT            pIssuerCert;
1817     DWORD                     cCertStore;
1818     HCERTSTORE               *rgCertStore;
1819     HCERTSTORE                hCrlStore;
1820     LPFILETIME                pftTimeToUse;
1821 } CERT_REVOCATION_PARA_NO_EXTRA_FIELDS, *PCERT_REVOCATION_PARA_NO_EXTRA_FIELDS;
1822
1823 typedef struct _OLD_CERT_REVOCATION_STATUS {
1824     DWORD cbSize;
1825     DWORD dwIndex;
1826     DWORD dwError;
1827     DWORD dwReason;
1828 } OLD_CERT_REVOCATION_STATUS, *POLD_CERT_REVOCATION_STATUS;
1829
1830 /***********************************************************************
1831  *    CertDllVerifyRevocation (CRYPTNET.@)
1832  */
1833 BOOL WINAPI CertDllVerifyRevocation(DWORD dwEncodingType, DWORD dwRevType,
1834  DWORD cContext, PVOID rgpvContext[], DWORD dwFlags,
1835  PCERT_REVOCATION_PARA pRevPara, PCERT_REVOCATION_STATUS pRevStatus)
1836 {
1837     DWORD error = 0, i;
1838     FILETIME now;
1839     LPFILETIME pTime = NULL;
1840
1841     TRACE("(%08x, %d, %d, %p, %08x, %p, %p)\n", dwEncodingType, dwRevType,
1842      cContext, rgpvContext, dwFlags, pRevPara, pRevStatus);
1843
1844     if (pRevStatus->cbSize != sizeof(OLD_CERT_REVOCATION_STATUS) &&
1845      pRevStatus->cbSize != sizeof(CERT_REVOCATION_STATUS))
1846     {
1847         SetLastError(E_INVALIDARG);
1848         return FALSE;
1849     }
1850     if (!cContext)
1851     {
1852         SetLastError(E_INVALIDARG);
1853         return FALSE;
1854     }
1855     if (pRevPara && pRevPara->cbSize >=
1856      sizeof(CERT_REVOCATION_PARA_NO_EXTRA_FIELDS))
1857         pTime = pRevPara->pftTimeToUse;
1858     if (!pTime)
1859     {
1860         GetSystemTimeAsFileTime(&now);
1861         pTime = &now;
1862     }
1863     memset(&pRevStatus->dwIndex, 0, pRevStatus->cbSize - sizeof(DWORD));
1864     if (dwRevType != CERT_CONTEXT_REVOCATION_TYPE)
1865         error = CRYPT_E_NO_REVOCATION_CHECK;
1866     else
1867     {
1868         for (i = 0; !error && i < cContext; i++)
1869             error = verify_cert_revocation(rgpvContext[i], i, pTime, dwFlags,
1870              pRevPara, pRevStatus);
1871     }
1872     if (error)
1873     {
1874         SetLastError(error);
1875         pRevStatus->dwError = error;
1876     }
1877     TRACE("returning %d (%08x)\n", !error, error);
1878     return !error;
1879 }