cryptnet: Implement CryptRetrieveObjectByUrlW for the file: protocol.
[wine] / dlls / cryptnet / cryptnet_main.c
1 /*
2  * Copyright (C) 2006 Maarten Lankhorst
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  *
18  */
19
20 #include "config.h"
21 #include "wine/port.h"
22 #include <stdio.h>
23 #include "windef.h"
24 #include "wine/debug.h"
25 #include "winbase.h"
26 #include "winnt.h"
27 #include "winnls.h"
28 #include "wininet.h"
29 #define NONAMELESSUNION
30 #include "wincrypt.h"
31
32 WINE_DEFAULT_DEBUG_CHANNEL(cryptnet);
33
34 BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
35 {
36    TRACE("(0x%p, %d, %p)\n", hinstDLL, fdwReason, lpvReserved);
37
38    switch (fdwReason) {
39       case DLL_WINE_PREATTACH:
40          return FALSE;  /* prefer native version */
41       case DLL_PROCESS_ATTACH:
42          DisableThreadLibraryCalls(hinstDLL);
43          break;
44       case DLL_PROCESS_DETACH:
45          /* Do uninitialisation here */
46          break;
47       default: break;
48    }
49    return TRUE;
50 }
51
52 static const WCHAR cryptNet[] = { 'c','r','y','p','t','n','e','t','.',
53    'd','l','l',0 };
54 static const WCHAR ldapProvOpenStore[] = { 'L','d','a','p','P','r','o','v',
55    'O','p','e','S','t','o','r','e',0 };
56
57 /***********************************************************************
58  *    DllRegisterServer (CRYPTNET.@)
59  */
60 HRESULT WINAPI DllRegisterServer(void)
61 {
62    TRACE("\n");
63    CryptRegisterDefaultOIDFunction(X509_ASN_ENCODING,
64     CRYPT_OID_VERIFY_REVOCATION_FUNC, 0, cryptNet);
65    CryptRegisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC, "Ldap",
66     cryptNet, "LdapProvOpenStore");
67    CryptRegisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC,
68     CERT_STORE_PROV_LDAP_W, cryptNet, "LdapProvOpenStore");
69    return S_OK;
70 }
71
72 /***********************************************************************
73  *    DllUnregisterServer (CRYPTNET.@)
74  */
75 HRESULT WINAPI DllUnregisterServer(void)
76 {
77    TRACE("\n");
78    CryptUnregisterDefaultOIDFunction(X509_ASN_ENCODING,
79     CRYPT_OID_VERIFY_REVOCATION_FUNC, cryptNet);
80    CryptUnregisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC, "Ldap");
81    CryptUnregisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC,
82     CERT_STORE_PROV_LDAP_W);
83    return S_OK;
84 }
85
86 static const char *url_oid_to_str(LPCSTR oid)
87 {
88     if (HIWORD(oid))
89         return oid;
90     else
91     {
92         static char buf[10];
93
94         switch (LOWORD(oid))
95         {
96 #define _x(oid) case LOWORD(oid): return #oid
97         _x(URL_OID_CERTIFICATE_ISSUER);
98         _x(URL_OID_CERTIFICATE_CRL_DIST_POINT);
99         _x(URL_OID_CTL_ISSUER);
100         _x(URL_OID_CTL_NEXT_UPDATE);
101         _x(URL_OID_CRL_ISSUER);
102         _x(URL_OID_CERTIFICATE_FRESHEST_CRL);
103         _x(URL_OID_CRL_FRESHEST_CRL);
104         _x(URL_OID_CROSS_CERT_DIST_POINT);
105 #undef _x
106         default:
107             snprintf(buf, sizeof(buf), "%d", LOWORD(oid));
108             return buf;
109         }
110     }
111 }
112
113 typedef BOOL (WINAPI *UrlDllGetObjectUrlFunc)(LPCSTR, LPVOID, DWORD,
114  PCRYPT_URL_ARRAY, DWORD *, PCRYPT_URL_INFO, DWORD *, LPVOID);
115
116 static BOOL WINAPI CRYPT_GetUrlFromCertificateIssuer(LPCSTR pszUrlOid,
117  LPVOID pvPara, DWORD dwFlags, PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray,
118  PCRYPT_URL_INFO pUrlInfo, DWORD *pcbUrlInfo, LPVOID pvReserved)
119 {
120     /* FIXME: This depends on the AIA (authority info access) extension being
121      * supported in crypt32.
122      */
123     FIXME("\n");
124     SetLastError(CRYPT_E_NOT_FOUND);
125     return FALSE;
126 }
127
128 static BOOL WINAPI CRYPT_GetUrlFromCertificateCRLDistPoint(LPCSTR pszUrlOid,
129  LPVOID pvPara, DWORD dwFlags, PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray,
130  PCRYPT_URL_INFO pUrlInfo, DWORD *pcbUrlInfo, LPVOID pvReserved)
131 {
132     PCCERT_CONTEXT cert = (PCCERT_CONTEXT)pvPara;
133     PCERT_EXTENSION ext;
134     BOOL ret = FALSE;
135
136     /* The only applicable flag is CRYPT_GET_URL_FROM_EXTENSION */
137     if (dwFlags && !(dwFlags & CRYPT_GET_URL_FROM_EXTENSION))
138     {
139         SetLastError(CRYPT_E_NOT_FOUND);
140         return FALSE;
141     }
142     if ((ext = CertFindExtension(szOID_CRL_DIST_POINTS,
143      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
144     {
145         CRL_DIST_POINTS_INFO *info;
146         DWORD size;
147
148         ret = CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CRL_DIST_POINTS,
149          ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
150          &info, &size);
151         if (ret)
152         {
153             DWORD i, cUrl, bytesNeeded = sizeof(CRYPT_URL_ARRAY);
154
155             for (i = 0, cUrl = 0; i < info->cDistPoint; i++)
156                 if (info->rgDistPoint[i].DistPointName.dwDistPointNameChoice
157                  == CRL_DIST_POINT_FULL_NAME)
158                 {
159                     DWORD j;
160                     CERT_ALT_NAME_INFO *name =
161                      &info->rgDistPoint[i].DistPointName.FullName;
162
163                     for (j = 0; j < name->cAltEntry; j++)
164                         if (name->rgAltEntry[j].dwAltNameChoice ==
165                          CERT_ALT_NAME_URL)
166                         {
167                             if (name->rgAltEntry[j].pwszURL)
168                             {
169                                 cUrl++;
170                                 bytesNeeded += sizeof(LPWSTR) +
171                                  (lstrlenW(name->rgAltEntry[j].pwszURL) + 1)
172                                  * sizeof(WCHAR);
173                             }
174                         }
175                 }
176             if (!pcbUrlArray)
177                 SetLastError(E_INVALIDARG);
178             else if (!pUrlArray)
179                 *pcbUrlArray = bytesNeeded;
180             else if (*pcbUrlArray < bytesNeeded)
181             {
182                 SetLastError(ERROR_MORE_DATA);
183                 *pcbUrlArray = bytesNeeded;
184                 ret = FALSE;
185             }
186             else
187             {
188                 LPWSTR nextUrl;
189
190                 *pcbUrlArray = bytesNeeded;
191                 pUrlArray->cUrl = 0;
192                 pUrlArray->rgwszUrl =
193                  (LPWSTR *)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY));
194                 nextUrl = (LPWSTR)((BYTE *)pUrlArray + sizeof(CRYPT_URL_ARRAY)
195                  + cUrl * sizeof(LPWSTR));
196                 for (i = 0; i < info->cDistPoint; i++)
197                     if (info->rgDistPoint[i].DistPointName.dwDistPointNameChoice
198                      == CRL_DIST_POINT_FULL_NAME)
199                     {
200                         DWORD j;
201                         CERT_ALT_NAME_INFO *name =
202                          &info->rgDistPoint[i].DistPointName.FullName;
203
204                         for (j = 0; j < name->cAltEntry; j++)
205                             if (name->rgAltEntry[j].dwAltNameChoice ==
206                              CERT_ALT_NAME_URL)
207                             {
208                                 if (name->rgAltEntry[j].pwszURL)
209                                 {
210                                     lstrcpyW(nextUrl,
211                                      name->rgAltEntry[j].pwszURL);
212                                     pUrlArray->rgwszUrl[pUrlArray->cUrl++] =
213                                      nextUrl;
214                                     nextUrl +=
215                                      (lstrlenW(name->rgAltEntry[j].pwszURL) + 1)
216                                      * sizeof(WCHAR);
217                                 }
218                             }
219                     }
220             }
221             if (ret)
222             {
223                 if (pcbUrlInfo)
224                 {
225                     FIXME("url info: stub\n");
226                     if (!pUrlInfo)
227                         *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
228                     else if (*pcbUrlInfo < sizeof(CRYPT_URL_INFO))
229                     {
230                         *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
231                         SetLastError(ERROR_MORE_DATA);
232                         ret = FALSE;
233                     }
234                     else
235                     {
236                         *pcbUrlInfo = sizeof(CRYPT_URL_INFO);
237                         memset(pUrlInfo, 0, sizeof(CRYPT_URL_INFO));
238                     }
239                 }
240             }
241             LocalFree(info);
242         }
243     }
244     else
245         SetLastError(CRYPT_E_NOT_FOUND);
246     return ret;
247 }
248
249 /***********************************************************************
250  *    CryptGetObjectUrl (CRYPTNET.@)
251  */
252 BOOL WINAPI CryptGetObjectUrl(LPCSTR pszUrlOid, LPVOID pvPara, DWORD dwFlags,
253  PCRYPT_URL_ARRAY pUrlArray, DWORD *pcbUrlArray, PCRYPT_URL_INFO pUrlInfo,
254  DWORD *pcbUrlInfo, LPVOID pvReserved)
255 {
256     UrlDllGetObjectUrlFunc func = NULL;
257     HCRYPTOIDFUNCADDR hFunc = NULL;
258     BOOL ret = FALSE;
259
260     TRACE("(%s, %p, %08x, %p, %p, %p, %p, %p)\n", debugstr_a(pszUrlOid),
261      pvPara, dwFlags, pUrlArray, pcbUrlArray, pUrlInfo, pcbUrlInfo, pvReserved);
262
263     if (!HIWORD(pszUrlOid))
264     {
265         switch (LOWORD(pszUrlOid))
266         {
267         case LOWORD(URL_OID_CERTIFICATE_ISSUER):
268             func = CRYPT_GetUrlFromCertificateIssuer;
269             break;
270         case LOWORD(URL_OID_CERTIFICATE_CRL_DIST_POINT):
271             func = CRYPT_GetUrlFromCertificateCRLDistPoint;
272             break;
273         default:
274             FIXME("unimplemented for %s\n", url_oid_to_str(pszUrlOid));
275             SetLastError(ERROR_FILE_NOT_FOUND);
276         }
277     }
278     else
279     {
280         static HCRYPTOIDFUNCSET set = NULL;
281
282         if (!set)
283             set = CryptInitOIDFunctionSet(URL_OID_GET_OBJECT_URL_FUNC, 0);
284         CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, pszUrlOid, 0,
285          (void **)&func, &hFunc);
286     }
287     if (func)
288         ret = func(pszUrlOid, pvPara, dwFlags, pUrlArray, pcbUrlArray,
289          pUrlInfo, pcbUrlInfo, pvReserved);
290     if (hFunc)
291         CryptFreeOIDFunctionAddress(hFunc, 0);
292     return ret;
293 }
294
295 /***********************************************************************
296  *    CryptRetrieveObjectByUrlA (CRYPTNET.@)
297  */
298 BOOL WINAPI CryptRetrieveObjectByUrlA(LPCSTR pszURL, LPCSTR pszObjectOid,
299  DWORD dwRetrievalFlags, DWORD dwTimeout, LPVOID *ppvObject,
300  HCRYPTASYNC hAsyncRetrieve, PCRYPT_CREDENTIALS pCredentials, LPVOID pvVerify,
301  PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
302 {
303     BOOL ret = FALSE;
304     int len;
305
306     TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p)\n", debugstr_a(pszURL),
307      debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, ppvObject,
308      hAsyncRetrieve, pCredentials, pvVerify, pAuxInfo);
309
310     if (!pszURL)
311     {
312         SetLastError(ERROR_INVALID_PARAMETER);
313         return FALSE;
314     }
315     len = MultiByteToWideChar(CP_ACP, 0, pszURL, -1, NULL, 0);
316     if (len)
317     {
318         LPWSTR url = CryptMemAlloc(len * sizeof(WCHAR));
319
320         if (url)
321         {
322             MultiByteToWideChar(CP_ACP, 0, pszURL, -1, url, len);
323             ret = CryptRetrieveObjectByUrlW(url, pszObjectOid,
324              dwRetrievalFlags, dwTimeout, ppvObject, hAsyncRetrieve,
325              pCredentials, pvVerify, pAuxInfo);
326             CryptMemFree(url);
327         }
328         else
329             SetLastError(ERROR_OUTOFMEMORY);
330     }
331     return ret;
332 }
333
334 static void WINAPI CRYPT_FreeBlob(LPCSTR pszObjectOid,
335  PCRYPT_BLOB_ARRAY pObject, void *pvFreeContext)
336 {
337     DWORD i;
338
339     for (i = 0; i < pObject->cBlob; i++)
340         CryptMemFree(pObject->rgBlob[i].pbData);
341     CryptMemFree(pObject->rgBlob);
342 }
343
344 static BOOL WINAPI FTP_RetrieveEncodedObjectW(LPCWSTR pszURL,
345  LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
346  PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
347  void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
348  PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
349 {
350     FIXME("(%s, %s, %08x, %d, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
351      debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject,
352      ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo);
353
354     pObject->cBlob = 0;
355     pObject->rgBlob = NULL;
356     *ppfnFreeObject = CRYPT_FreeBlob;
357     *ppvFreeContext = NULL;
358     return FALSE;
359 }
360
361 static BOOL WINAPI HTTP_RetrieveEncodedObjectW(LPCWSTR pszURL,
362  LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
363  PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
364  void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
365  PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
366 {
367     FIXME("(%s, %s, %08x, %d, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
368      debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject,
369      ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo);
370
371     pObject->cBlob = 0;
372     pObject->rgBlob = NULL;
373     *ppfnFreeObject = CRYPT_FreeBlob;
374     *ppvFreeContext = NULL;
375     return FALSE;
376 }
377
378 static BOOL WINAPI File_RetrieveEncodedObjectW(LPCWSTR pszURL,
379  LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
380  PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
381  void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
382  PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
383 {
384     URL_COMPONENTSW components = { sizeof(components), 0 };
385     BOOL ret;
386
387     TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
388      debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, pObject,
389      ppfnFreeObject, ppvFreeContext, hAsyncRetrieve, pCredentials, pAuxInfo);
390
391     pObject->cBlob = 0;
392     pObject->rgBlob = NULL;
393     *ppfnFreeObject = CRYPT_FreeBlob;
394     *ppvFreeContext = NULL;
395
396     components.dwUrlPathLength = 1;
397     ret = InternetCrackUrlW(pszURL, 0, ICU_DECODE, &components);
398     if (ret)
399     {
400         LPWSTR path;
401
402         /* 3 == lstrlenW(L"c:") + 1 */
403         path = CryptMemAlloc((components.dwUrlPathLength + 3) * sizeof(WCHAR));
404         if (path)
405         {
406             HANDLE hFile;
407
408             /* Try to create the file directly - Wine handles / in pathnames */
409             lstrcpynW(path, components.lpszUrlPath,
410              components.dwUrlPathLength + 1);
411             hFile = CreateFileW(path, GENERIC_READ, 0, NULL, OPEN_EXISTING,
412              FILE_ATTRIBUTE_NORMAL, NULL);
413             if (hFile == INVALID_HANDLE_VALUE)
414             {
415                 /* Try again on the current drive */
416                 GetCurrentDirectoryW(components.dwUrlPathLength, path);
417                 if (path[1] == ':')
418                 {
419                     lstrcpynW(path + 2, components.lpszUrlPath,
420                      components.dwUrlPathLength + 1);
421                     hFile = CreateFileW(path, GENERIC_READ, 0, NULL,
422                      OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
423                 }
424                 if (hFile == INVALID_HANDLE_VALUE)
425                 {
426                     /* Try again on the Windows drive */
427                     GetWindowsDirectoryW(path, components.dwUrlPathLength);
428                     if (path[1] == ':')
429                     {
430                         lstrcpynW(path + 2, components.lpszUrlPath,
431                          components.dwUrlPathLength + 1);
432                         hFile = CreateFileW(path, GENERIC_READ, 0, NULL,
433                          OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
434                     }
435                 }
436             }
437             if (hFile != INVALID_HANDLE_VALUE)
438             {
439                 LARGE_INTEGER size;
440
441                 if ((ret = GetFileSizeEx(hFile, &size)))
442                 {
443                     if (size.HighPart)
444                     {
445                         WARN("file too big\n");
446                         SetLastError(ERROR_INVALID_DATA);
447                         ret = FALSE;
448                     }
449                     else
450                     {
451                         CRYPT_DATA_BLOB blob;
452
453                         blob.pbData = CryptMemAlloc(size.LowPart);
454                         if (blob.pbData)
455                         {
456                             blob.cbData = size.LowPart;
457                             ret = ReadFile(hFile, blob.pbData, size.LowPart,
458                              &blob.cbData, NULL);
459                             if (ret)
460                             {
461                                 pObject->rgBlob =
462                                  CryptMemAlloc(sizeof(CRYPT_DATA_BLOB));
463                                 if (pObject->rgBlob)
464                                 {
465                                     pObject->cBlob = 1;
466                                     memcpy(pObject->rgBlob, &blob,
467                                      sizeof(CRYPT_DATA_BLOB));
468                                 }
469                                 else
470                                 {
471                                     SetLastError(ERROR_OUTOFMEMORY);
472                                     ret = FALSE;
473                                 }
474                             }
475                             if (!ret)
476                                 CryptMemFree(blob.pbData);
477                             else
478                             {
479                                 if (pAuxInfo && pAuxInfo->cbSize >=
480                                  offsetof(CRYPT_RETRIEVE_AUX_INFO,
481                                  pLastSyncTime) + sizeof(PFILETIME) &&
482                                  pAuxInfo->pLastSyncTime)
483                                     GetFileTime(hFile, NULL, NULL,
484                                      pAuxInfo->pLastSyncTime);
485                             }
486                         }
487                         else
488                         {
489                             SetLastError(ERROR_OUTOFMEMORY);
490                             ret = FALSE;
491                         }
492                     }
493                 }
494                 CloseHandle(hFile);
495             }
496             else
497                 ret = FALSE;
498             CryptMemFree(path);
499         }
500     }
501     return ret;
502 }
503
504 typedef BOOL (WINAPI *SchemeDllRetrieveEncodedObjectW)(LPCWSTR pwszUrl,
505  LPCSTR pszObjectOid, DWORD dwRetrievalFlags, DWORD dwTimeout,
506  PCRYPT_BLOB_ARRAY pObject, PFN_FREE_ENCODED_OBJECT_FUNC *ppfnFreeObject,
507  void **ppvFreeContext, HCRYPTASYNC hAsyncRetrieve,
508  PCRYPT_CREDENTIALS pCredentials, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo);
509
510 static BOOL CRYPT_GetRetrieveFunction(LPCWSTR pszURL,
511  SchemeDllRetrieveEncodedObjectW *pFunc, HCRYPTOIDFUNCADDR *phFunc)
512 {
513     URL_COMPONENTSW components = { sizeof(components), 0 };
514     BOOL ret;
515
516     *pFunc = NULL;
517     *phFunc = 0;
518     components.dwSchemeLength = 1;
519     ret = InternetCrackUrlW(pszURL, 0, ICU_DECODE, &components);
520     if (ret)
521     {
522         /* Microsoft always uses CryptInitOIDFunctionSet/
523          * CryptGetOIDFunctionAddress, but there doesn't seem to be a pressing
524          * reason to do so for builtin schemes.
525          */
526         switch (components.nScheme)
527         {
528         case INTERNET_SCHEME_FTP:
529             *pFunc = FTP_RetrieveEncodedObjectW;
530             break;
531         case INTERNET_SCHEME_HTTP:
532             *pFunc = HTTP_RetrieveEncodedObjectW;
533             break;
534         case INTERNET_SCHEME_FILE:
535             *pFunc = File_RetrieveEncodedObjectW;
536             break;
537         default:
538         {
539             int len = WideCharToMultiByte(CP_ACP, 0, components.lpszScheme,
540              components.dwSchemeLength, NULL, 0, NULL, NULL);
541
542             if (len)
543             {
544                 LPSTR scheme = CryptMemAlloc(len);
545
546                 if (scheme)
547                 {
548                     static HCRYPTOIDFUNCSET set = NULL;
549
550                     if (!set)
551                         set = CryptInitOIDFunctionSet(
552                          SCHEME_OID_RETRIEVE_ENCODED_OBJECTW_FUNC, 0);
553                     WideCharToMultiByte(CP_ACP, 0, components.lpszScheme,
554                      components.dwSchemeLength, scheme, len, NULL, NULL);
555                     ret = CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING,
556                      scheme, 0, (void **)pFunc, phFunc);
557                     CryptMemFree(scheme);
558                 }
559                 else
560                 {
561                     SetLastError(ERROR_OUTOFMEMORY);
562                     ret = FALSE;
563                 }
564             }
565             else
566                 ret = FALSE;
567         }
568         }
569     }
570     return ret;
571 }
572
573 static BOOL WINAPI CRYPT_CreateBlob(LPCSTR pszObjectOid,
574  DWORD dwRetrievalFlags, PCRYPT_BLOB_ARRAY pObject, void **ppvContext)
575 {
576     DWORD size, i;
577     CRYPT_BLOB_ARRAY *context;
578     BOOL ret = FALSE;
579
580     size = sizeof(CRYPT_BLOB_ARRAY) + pObject->cBlob * sizeof(CRYPT_DATA_BLOB);
581     for (i = 0; i < pObject->cBlob; i++)
582         size += pObject->rgBlob[i].cbData;
583     context = CryptMemAlloc(size);
584     if (context)
585     {
586         LPBYTE nextData;
587
588         context->cBlob = 0;
589         context->rgBlob =
590          (CRYPT_DATA_BLOB *)((LPBYTE)context + sizeof(CRYPT_BLOB_ARRAY));
591         nextData =
592          (LPBYTE)context->rgBlob + pObject->cBlob * sizeof(CRYPT_DATA_BLOB);
593         for (i = 0; i < pObject->cBlob; i++)
594         {
595             memcpy(nextData, pObject->rgBlob[i].pbData,
596              pObject->rgBlob[i].cbData);
597             context->rgBlob[i].pbData = nextData;
598             context->rgBlob[i].cbData = pObject->rgBlob[i].cbData;
599             nextData += pObject->rgBlob[i].cbData;
600             context->cBlob++;
601         }
602         *ppvContext = context;
603         ret = TRUE;
604     }
605     return ret;
606 }
607
608 typedef BOOL (WINAPI *AddContextToStore)(HCERTSTORE hCertStore,
609  const void *pContext, DWORD dwAddDisposition, const void **ppStoreContext);
610
611 static BOOL CRYPT_CreateContext(PCRYPT_BLOB_ARRAY pObject,
612  DWORD dwExpectedContentTypeFlags, AddContextToStore addFunc, void **ppvContext)
613 {
614     BOOL ret = TRUE;
615
616     if (!pObject->cBlob)
617     {
618         SetLastError(ERROR_INVALID_DATA);
619         *ppvContext = NULL;
620         ret = FALSE;
621     }
622     else if (pObject->cBlob == 1)
623     {
624         if (!CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &pObject->rgBlob[0],
625          dwExpectedContentTypeFlags, CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL,
626          NULL, NULL, NULL, NULL, (const void **)ppvContext))
627         {
628             SetLastError(CRYPT_E_NO_MATCH);
629             ret = FALSE;
630         }
631     }
632     else
633     {
634         HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
635          CERT_STORE_CREATE_NEW_FLAG, NULL);
636
637         if (store)
638         {
639             DWORD i;
640             const void *context;
641
642             for (i = 0; i < pObject->cBlob; i++)
643             {
644                 if (CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
645                  &pObject->rgBlob[i], dwExpectedContentTypeFlags,
646                  CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, NULL, NULL, NULL,
647                  NULL, &context))
648                 {
649                     if (!addFunc(store, context, CERT_STORE_ADD_ALWAYS, NULL))
650                         ret = FALSE;
651                 }
652                 else
653                 {
654                     SetLastError(CRYPT_E_NO_MATCH);
655                     ret = FALSE;
656                 }
657             }
658         }
659         else
660             ret = FALSE;
661         *ppvContext = store;
662     }
663     return ret;
664 }
665
666 static BOOL WINAPI CRYPT_CreateCert(LPCSTR pszObjectOid,
667  DWORD dwRetrievalFlags, PCRYPT_BLOB_ARRAY pObject, void **ppvContext)
668 {
669     return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CERT,
670      (AddContextToStore)CertAddCertificateContextToStore, ppvContext);
671 }
672
673 static BOOL WINAPI CRYPT_CreateCRL(LPCSTR pszObjectOid,
674  DWORD dwRetrievalFlags, PCRYPT_BLOB_ARRAY pObject, void **ppvContext)
675 {
676     return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CRL,
677      (AddContextToStore)CertAddCRLContextToStore, ppvContext);
678 }
679
680 static BOOL WINAPI CRYPT_CreateCTL(LPCSTR pszObjectOid,
681  DWORD dwRetrievalFlags, PCRYPT_BLOB_ARRAY pObject, void **ppvContext)
682 {
683     return CRYPT_CreateContext(pObject, CERT_QUERY_CONTENT_FLAG_CTL,
684      (AddContextToStore)CertAddCTLContextToStore, ppvContext);
685 }
686
687 static BOOL WINAPI CRYPT_CreatePKCS7(LPCSTR pszObjectOid,
688  DWORD dwRetrievalFlags, PCRYPT_BLOB_ARRAY pObject, void **ppvContext)
689 {
690     BOOL ret;
691
692     if (!pObject->cBlob)
693     {
694         SetLastError(ERROR_INVALID_DATA);
695         *ppvContext = NULL;
696         ret = FALSE;
697     }
698     else if (pObject->cBlob == 1)
699         ret = CryptQueryObject(CERT_QUERY_OBJECT_BLOB, &pObject->rgBlob[0],
700          CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
701          CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED, CERT_QUERY_FORMAT_FLAG_BINARY,
702          0, NULL, NULL, NULL, (HCERTSTORE *)ppvContext, NULL, NULL);
703     else
704     {
705         FIXME("multiple messages unimplemented\n");
706         ret = FALSE;
707     }
708     return ret;
709 }
710
711 static BOOL WINAPI CRYPT_CreateAny(LPCSTR pszObjectOid,
712  DWORD dwRetrievalFlags, PCRYPT_BLOB_ARRAY pObject, void **ppvContext)
713 {
714     BOOL ret;
715
716     if (!pObject->cBlob)
717     {
718         SetLastError(ERROR_INVALID_DATA);
719         *ppvContext = NULL;
720         ret = FALSE;
721     }
722     else
723     {
724         HCERTSTORE store = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
725          CERT_STORE_CREATE_NEW_FLAG, NULL);
726
727         if (store)
728         {
729             HCERTSTORE memStore = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0,
730              CERT_STORE_CREATE_NEW_FLAG, NULL);
731
732             if (memStore)
733             {
734                 CertAddStoreToCollection(store, memStore,
735                  CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG, 0);
736                 CertCloseStore(memStore, 0);
737             }
738             else
739             {
740                 CertCloseStore(store, 0);
741                 store = NULL;
742             }
743         }
744         if (store)
745         {
746             DWORD i;
747
748             ret = TRUE;
749             for (i = 0; i < pObject->cBlob; i++)
750             {
751                 DWORD contentType, expectedContentTypes =
752                  CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
753                  CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED |
754                  CERT_QUERY_CONTENT_FLAG_CERT |
755                  CERT_QUERY_CONTENT_FLAG_CRL |
756                  CERT_QUERY_CONTENT_FLAG_CTL;
757                 HCERTSTORE contextStore;
758                 const void *context;
759
760                 if (CryptQueryObject(CERT_QUERY_OBJECT_BLOB,
761                  &pObject->rgBlob[i], expectedContentTypes,
762                  CERT_QUERY_FORMAT_FLAG_BINARY, 0, NULL, &contentType, NULL,
763                  &contextStore, NULL, &context))
764                 {
765                     switch (contentType)
766                     {
767                     case CERT_QUERY_CONTENT_CERT:
768                         if (!CertAddCertificateContextToStore(store,
769                          (PCCERT_CONTEXT)context, CERT_STORE_ADD_ALWAYS, NULL))
770                             ret = FALSE;
771                         break;
772                     case CERT_QUERY_CONTENT_CRL:
773                         if (!CertAddCRLContextToStore(store,
774                          (PCCRL_CONTEXT)context, CERT_STORE_ADD_ALWAYS, NULL))
775                              ret = FALSE;
776                         break;
777                     case CERT_QUERY_CONTENT_CTL:
778                         if (!CertAddCTLContextToStore(store,
779                          (PCCTL_CONTEXT)context, CERT_STORE_ADD_ALWAYS, NULL))
780                              ret = FALSE;
781                         break;
782                     default:
783                         CertAddStoreToCollection(store, contextStore, 0, 0);
784                     }
785                 }
786                 else
787                     ret = FALSE;
788             }
789         }
790         else
791             ret = FALSE;
792         *ppvContext = store;
793     }
794     return ret;
795 }
796
797 typedef BOOL (WINAPI *ContextDllCreateObjectContext)(LPCSTR pszObjectOid,
798  DWORD dwRetrievalFlags, PCRYPT_BLOB_ARRAY pObject, void **ppvContext);
799
800 static BOOL CRYPT_GetCreateFunction(LPCSTR pszObjectOid,
801  ContextDllCreateObjectContext *pFunc, HCRYPTOIDFUNCADDR *phFunc)
802 {
803     BOOL ret = TRUE;
804
805     *pFunc = NULL;
806     *phFunc = 0;
807     if (!HIWORD(pszObjectOid))
808     {
809         switch (LOWORD(pszObjectOid))
810         {
811         case 0:
812             *pFunc = CRYPT_CreateBlob;
813             break;
814         case LOWORD(CONTEXT_OID_CERTIFICATE):
815             *pFunc = CRYPT_CreateCert;
816             break;
817         case LOWORD(CONTEXT_OID_CRL):
818             *pFunc = CRYPT_CreateCRL;
819             break;
820         case LOWORD(CONTEXT_OID_CTL):
821             *pFunc = CRYPT_CreateCTL;
822             break;
823         case LOWORD(CONTEXT_OID_PKCS7):
824             *pFunc = CRYPT_CreatePKCS7;
825             break;
826         case LOWORD(CONTEXT_OID_CAPI2_ANY):
827             *pFunc = CRYPT_CreateAny;
828             break;
829         }
830     }
831     if (!*pFunc)
832     {
833         static HCRYPTOIDFUNCSET set = NULL;
834
835         if (!set)
836             set = CryptInitOIDFunctionSet(
837              CONTEXT_OID_CREATE_OBJECT_CONTEXT_FUNC, 0);
838         ret = CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, pszObjectOid,
839          0, (void **)pFunc, phFunc);
840     }
841     return ret;
842 }
843
844 /***********************************************************************
845  *    CryptRetrieveObjectByUrlW (CRYPTNET.@)
846  */
847 BOOL WINAPI CryptRetrieveObjectByUrlW(LPCWSTR pszURL, LPCSTR pszObjectOid,
848  DWORD dwRetrievalFlags, DWORD dwTimeout, LPVOID *ppvObject,
849  HCRYPTASYNC hAsyncRetrieve, PCRYPT_CREDENTIALS pCredentials, LPVOID pvVerify,
850  PCRYPT_RETRIEVE_AUX_INFO pAuxInfo)
851 {
852     BOOL ret;
853     SchemeDllRetrieveEncodedObjectW retrieve;
854     ContextDllCreateObjectContext create;
855     HCRYPTOIDFUNCADDR hRetrieve = 0, hCreate = 0;
856
857     TRACE("(%s, %s, %08x, %d, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL),
858      debugstr_a(pszObjectOid), dwRetrievalFlags, dwTimeout, ppvObject,
859      hAsyncRetrieve, pCredentials, pvVerify, pAuxInfo);
860
861     if (!pszURL)
862     {
863         SetLastError(ERROR_INVALID_PARAMETER);
864         return FALSE;
865     }
866     ret = CRYPT_GetRetrieveFunction(pszURL, &retrieve, &hRetrieve);
867     if (ret)
868         ret = CRYPT_GetCreateFunction(pszObjectOid, &create, &hCreate);
869     if (ret)
870     {
871         CRYPT_BLOB_ARRAY object = { 0, NULL };
872         PFN_FREE_ENCODED_OBJECT_FUNC freeObject;
873         void *freeContext;
874
875         ret = retrieve(pszURL, pszObjectOid, dwRetrievalFlags, dwTimeout,
876          &object, &freeObject, &freeContext, hAsyncRetrieve, pCredentials,
877          pAuxInfo);
878         if (ret)
879         {
880             ret = create(pszObjectOid, dwRetrievalFlags, &object, ppvObject);
881             freeObject(pszObjectOid, &object, freeContext);
882         }
883     }
884     if (hCreate)
885         CryptFreeOIDFunctionAddress(hCreate, 0);
886     if (hRetrieve)
887         CryptFreeOIDFunctionAddress(hRetrieve, 0);
888     return ret;
889 }
890
891 /***********************************************************************
892  *    CertDllVerifyRevocation (CRYPTNET.@)
893  */
894 BOOL WINAPI CertDllVerifyRevocation(DWORD dwEncodingType, DWORD dwRevType,
895  DWORD cContext, void *rgpvContext[], DWORD dwFlags,
896  PCERT_REVOCATION_PARA pRevPara, PCERT_REVOCATION_STATUS pRevStatus)
897 {
898    FIXME("(%08x, %d, %d, %p, %08x, %p, %p): stub\n", dwEncodingType, dwRevType,
899     cContext, rgpvContext, dwFlags, pRevPara, pRevStatus);
900    return FALSE;
901 }