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