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