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