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