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