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