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