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