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