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