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