2 * Copyright (C) 2006 Maarten Lankhorst
3 * Copyright 2007 Juan Lang
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.
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.
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
21 #define CERT_REVOCATION_PARA_HAS_EXTRA_FIELDS
35 #include "knownfolders.h"
38 #include "wine/debug.h"
40 WINE_DEFAULT_DEBUG_CHANNEL(cryptnet
);
42 #define IS_INTOID(x) (((ULONG_PTR)(x) >> 16) == 0)
45 /***********************************************************************
46 * DllRegisterServer (CRYPTNET.@)
48 HRESULT WINAPI
DllRegisterServer(void)
51 CryptRegisterDefaultOIDFunction(X509_ASN_ENCODING
,
52 CRYPT_OID_VERIFY_REVOCATION_FUNC
, 0, L
"cryptnet.dll");
53 CryptRegisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC
, "Ldap",
54 L
"cryptnet.dll", "LdapProvOpenStore");
55 CryptRegisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC
,
56 CERT_STORE_PROV_LDAP_W
, L
"cryptnet.dll", "LdapProvOpenStore");
60 /***********************************************************************
61 * DllUnregisterServer (CRYPTNET.@)
63 HRESULT WINAPI
DllUnregisterServer(void)
66 CryptUnregisterDefaultOIDFunction(X509_ASN_ENCODING
,
67 CRYPT_OID_VERIFY_REVOCATION_FUNC
, L
"cryptnet.dll");
68 CryptUnregisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC
, "Ldap");
69 CryptUnregisterOIDFunction(0, CRYPT_OID_OPEN_STORE_PROV_FUNC
,
70 CERT_STORE_PROV_LDAP_W
);
74 static const char *url_oid_to_str(LPCSTR oid
)
82 #define _x(oid) case LOWORD(oid): return #oid
83 _x(URL_OID_CERTIFICATE_ISSUER
);
84 _x(URL_OID_CERTIFICATE_CRL_DIST_POINT
);
85 _x(URL_OID_CTL_ISSUER
);
86 _x(URL_OID_CTL_NEXT_UPDATE
);
87 _x(URL_OID_CRL_ISSUER
);
88 _x(URL_OID_CERTIFICATE_FRESHEST_CRL
);
89 _x(URL_OID_CRL_FRESHEST_CRL
);
90 _x(URL_OID_CROSS_CERT_DIST_POINT
);
93 snprintf(buf
, sizeof(buf
), "%d", LOWORD(oid
));
101 typedef BOOL (WINAPI
*UrlDllGetObjectUrlFunc
)(LPCSTR
, LPVOID
, DWORD
,
102 PCRYPT_URL_ARRAY
, DWORD
*, PCRYPT_URL_INFO
, DWORD
*, LPVOID
);
104 static BOOL WINAPI
CRYPT_GetUrlFromCertificateIssuer(LPCSTR pszUrlOid
,
105 LPVOID pvPara
, DWORD dwFlags
, PCRYPT_URL_ARRAY pUrlArray
, DWORD
*pcbUrlArray
,
106 PCRYPT_URL_INFO pUrlInfo
, DWORD
*pcbUrlInfo
, LPVOID pvReserved
)
108 PCCERT_CONTEXT cert
= pvPara
;
112 /* The only applicable flag is CRYPT_GET_URL_FROM_EXTENSION */
113 if (dwFlags
&& !(dwFlags
& CRYPT_GET_URL_FROM_EXTENSION
))
115 SetLastError(CRYPT_E_NOT_FOUND
);
118 if ((ext
= CertFindExtension(szOID_AUTHORITY_INFO_ACCESS
,
119 cert
->pCertInfo
->cExtension
, cert
->pCertInfo
->rgExtension
)))
121 CERT_AUTHORITY_INFO_ACCESS
*aia
;
124 ret
= CryptDecodeObjectEx(X509_ASN_ENCODING
, X509_AUTHORITY_INFO_ACCESS
,
125 ext
->Value
.pbData
, ext
->Value
.cbData
, CRYPT_DECODE_ALLOC_FLAG
, NULL
,
129 DWORD i
, cUrl
, bytesNeeded
= sizeof(CRYPT_URL_ARRAY
);
131 for (i
= 0, cUrl
= 0; i
< aia
->cAccDescr
; i
++)
132 if (!strcmp(aia
->rgAccDescr
[i
].pszAccessMethod
,
133 szOID_PKIX_CA_ISSUERS
))
135 if (aia
->rgAccDescr
[i
].AccessLocation
.dwAltNameChoice
==
138 if (aia
->rgAccDescr
[i
].AccessLocation
.pwszURL
)
141 bytesNeeded
+= sizeof(LPWSTR
) +
142 (lstrlenW(aia
->rgAccDescr
[i
].AccessLocation
.
143 pwszURL
) + 1) * sizeof(WCHAR
);
147 FIXME("unsupported alt name type %ld\n",
148 aia
->rgAccDescr
[i
].AccessLocation
.dwAltNameChoice
);
152 SetLastError(E_INVALIDARG
);
156 *pcbUrlArray
= bytesNeeded
;
157 else if (*pcbUrlArray
< bytesNeeded
)
159 SetLastError(ERROR_MORE_DATA
);
160 *pcbUrlArray
= bytesNeeded
;
167 *pcbUrlArray
= bytesNeeded
;
169 pUrlArray
->rgwszUrl
=
170 (LPWSTR
*)((BYTE
*)pUrlArray
+ sizeof(CRYPT_URL_ARRAY
));
171 nextUrl
= (LPWSTR
)((BYTE
*)pUrlArray
+ sizeof(CRYPT_URL_ARRAY
)
172 + cUrl
* sizeof(LPWSTR
));
173 for (i
= 0; i
< aia
->cAccDescr
; i
++)
174 if (!strcmp(aia
->rgAccDescr
[i
].pszAccessMethod
,
175 szOID_PKIX_CA_ISSUERS
))
177 if (aia
->rgAccDescr
[i
].AccessLocation
.dwAltNameChoice
178 == CERT_ALT_NAME_URL
)
180 if (aia
->rgAccDescr
[i
].AccessLocation
.pwszURL
)
183 aia
->rgAccDescr
[i
].AccessLocation
.pwszURL
);
184 pUrlArray
->rgwszUrl
[pUrlArray
->cUrl
++] =
186 nextUrl
+= (lstrlenW(nextUrl
) + 1);
195 FIXME("url info: stub\n");
197 *pcbUrlInfo
= sizeof(CRYPT_URL_INFO
);
198 else if (*pcbUrlInfo
< sizeof(CRYPT_URL_INFO
))
200 *pcbUrlInfo
= sizeof(CRYPT_URL_INFO
);
201 SetLastError(ERROR_MORE_DATA
);
206 *pcbUrlInfo
= sizeof(CRYPT_URL_INFO
);
207 memset(pUrlInfo
, 0, sizeof(CRYPT_URL_INFO
));
215 SetLastError(CRYPT_E_NOT_FOUND
);
219 static BOOL
CRYPT_GetUrlFromCRLDistPointsExt(const CRYPT_DATA_BLOB
*value
,
220 PCRYPT_URL_ARRAY pUrlArray
, DWORD
*pcbUrlArray
, PCRYPT_URL_INFO pUrlInfo
,
224 CRL_DIST_POINTS_INFO
*info
;
227 ret
= CryptDecodeObjectEx(X509_ASN_ENCODING
, X509_CRL_DIST_POINTS
,
228 value
->pbData
, value
->cbData
, CRYPT_DECODE_ALLOC_FLAG
, NULL
, &info
, &size
);
231 DWORD i
, cUrl
, bytesNeeded
= sizeof(CRYPT_URL_ARRAY
);
233 for (i
= 0, cUrl
= 0; i
< info
->cDistPoint
; i
++)
234 if (info
->rgDistPoint
[i
].DistPointName
.dwDistPointNameChoice
235 == CRL_DIST_POINT_FULL_NAME
)
238 CERT_ALT_NAME_INFO
*name
=
239 &info
->rgDistPoint
[i
].DistPointName
.FullName
;
241 for (j
= 0; j
< name
->cAltEntry
; j
++)
242 if (name
->rgAltEntry
[j
].dwAltNameChoice
==
245 if (name
->rgAltEntry
[j
].pwszURL
)
248 bytesNeeded
+= sizeof(LPWSTR
) +
249 (lstrlenW(name
->rgAltEntry
[j
].pwszURL
) + 1)
256 SetLastError(E_INVALIDARG
);
260 *pcbUrlArray
= bytesNeeded
;
261 else if (*pcbUrlArray
< bytesNeeded
)
263 SetLastError(ERROR_MORE_DATA
);
264 *pcbUrlArray
= bytesNeeded
;
271 *pcbUrlArray
= bytesNeeded
;
273 pUrlArray
->rgwszUrl
=
274 (LPWSTR
*)((BYTE
*)pUrlArray
+ sizeof(CRYPT_URL_ARRAY
));
275 nextUrl
= (LPWSTR
)((BYTE
*)pUrlArray
+ sizeof(CRYPT_URL_ARRAY
)
276 + cUrl
* sizeof(LPWSTR
));
277 for (i
= 0; i
< info
->cDistPoint
; i
++)
278 if (info
->rgDistPoint
[i
].DistPointName
.dwDistPointNameChoice
279 == CRL_DIST_POINT_FULL_NAME
)
282 CERT_ALT_NAME_INFO
*name
=
283 &info
->rgDistPoint
[i
].DistPointName
.FullName
;
285 for (j
= 0; j
< name
->cAltEntry
; j
++)
286 if (name
->rgAltEntry
[j
].dwAltNameChoice
==
289 if (name
->rgAltEntry
[j
].pwszURL
)
292 name
->rgAltEntry
[j
].pwszURL
);
293 pUrlArray
->rgwszUrl
[pUrlArray
->cUrl
++] =
296 (lstrlenW(name
->rgAltEntry
[j
].pwszURL
) + 1);
305 FIXME("url info: stub\n");
307 *pcbUrlInfo
= sizeof(CRYPT_URL_INFO
);
308 else if (*pcbUrlInfo
< sizeof(CRYPT_URL_INFO
))
310 *pcbUrlInfo
= sizeof(CRYPT_URL_INFO
);
311 SetLastError(ERROR_MORE_DATA
);
316 *pcbUrlInfo
= sizeof(CRYPT_URL_INFO
);
317 memset(pUrlInfo
, 0, sizeof(CRYPT_URL_INFO
));
326 static BOOL WINAPI
CRYPT_GetUrlFromCertificateCRLDistPoint(LPCSTR pszUrlOid
,
327 LPVOID pvPara
, DWORD dwFlags
, PCRYPT_URL_ARRAY pUrlArray
, DWORD
*pcbUrlArray
,
328 PCRYPT_URL_INFO pUrlInfo
, DWORD
*pcbUrlInfo
, LPVOID pvReserved
)
330 PCCERT_CONTEXT cert
= pvPara
;
334 /* The only applicable flag is CRYPT_GET_URL_FROM_EXTENSION */
335 if (dwFlags
&& !(dwFlags
& CRYPT_GET_URL_FROM_EXTENSION
))
337 SetLastError(CRYPT_E_NOT_FOUND
);
340 if ((ext
= CertFindExtension(szOID_CRL_DIST_POINTS
,
341 cert
->pCertInfo
->cExtension
, cert
->pCertInfo
->rgExtension
)))
342 ret
= CRYPT_GetUrlFromCRLDistPointsExt(&ext
->Value
, pUrlArray
,
343 pcbUrlArray
, pUrlInfo
, pcbUrlInfo
);
345 SetLastError(CRYPT_E_NOT_FOUND
);
349 /***********************************************************************
350 * CryptGetObjectUrl (CRYPTNET.@)
352 BOOL WINAPI
CryptGetObjectUrl(LPCSTR pszUrlOid
, LPVOID pvPara
, DWORD dwFlags
,
353 PCRYPT_URL_ARRAY pUrlArray
, DWORD
*pcbUrlArray
, PCRYPT_URL_INFO pUrlInfo
,
354 DWORD
*pcbUrlInfo
, LPVOID pvReserved
)
356 UrlDllGetObjectUrlFunc func
= NULL
;
357 HCRYPTOIDFUNCADDR hFunc
= NULL
;
360 TRACE("(%s, %p, %08lx, %p, %p, %p, %p, %p)\n", debugstr_a(pszUrlOid
),
361 pvPara
, dwFlags
, pUrlArray
, pcbUrlArray
, pUrlInfo
, pcbUrlInfo
, pvReserved
);
363 if (IS_INTOID(pszUrlOid
))
365 switch (LOWORD(pszUrlOid
))
367 case LOWORD(URL_OID_CERTIFICATE_ISSUER
):
368 func
= CRYPT_GetUrlFromCertificateIssuer
;
370 case LOWORD(URL_OID_CERTIFICATE_CRL_DIST_POINT
):
371 func
= CRYPT_GetUrlFromCertificateCRLDistPoint
;
374 FIXME("unimplemented for %s\n", url_oid_to_str(pszUrlOid
));
375 SetLastError(ERROR_FILE_NOT_FOUND
);
380 static HCRYPTOIDFUNCSET set
= NULL
;
383 set
= CryptInitOIDFunctionSet(URL_OID_GET_OBJECT_URL_FUNC
, 0);
384 CryptGetOIDFunctionAddress(set
, X509_ASN_ENCODING
, pszUrlOid
, 0,
385 (void **)&func
, &hFunc
);
388 ret
= func(pszUrlOid
, pvPara
, dwFlags
, pUrlArray
, pcbUrlArray
,
389 pUrlInfo
, pcbUrlInfo
, pvReserved
);
391 CryptFreeOIDFunctionAddress(hFunc
, 0);
395 /***********************************************************************
396 * CryptRetrieveObjectByUrlA (CRYPTNET.@)
398 BOOL WINAPI
CryptRetrieveObjectByUrlA(LPCSTR pszURL
, LPCSTR pszObjectOid
,
399 DWORD dwRetrievalFlags
, DWORD dwTimeout
, LPVOID
*ppvObject
,
400 HCRYPTASYNC hAsyncRetrieve
, PCRYPT_CREDENTIALS pCredentials
, LPVOID pvVerify
,
401 PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
406 TRACE("(%s, %s, %08lx, %ld, %p, %p, %p, %p, %p)\n", debugstr_a(pszURL
),
407 debugstr_a(pszObjectOid
), dwRetrievalFlags
, dwTimeout
, ppvObject
,
408 hAsyncRetrieve
, pCredentials
, pvVerify
, pAuxInfo
);
412 SetLastError(ERROR_INVALID_PARAMETER
);
415 len
= MultiByteToWideChar(CP_ACP
, 0, pszURL
, -1, NULL
, 0);
418 LPWSTR url
= CryptMemAlloc(len
* sizeof(WCHAR
));
422 MultiByteToWideChar(CP_ACP
, 0, pszURL
, -1, url
, len
);
423 ret
= CryptRetrieveObjectByUrlW(url
, pszObjectOid
,
424 dwRetrievalFlags
, dwTimeout
, ppvObject
, hAsyncRetrieve
,
425 pCredentials
, pvVerify
, pAuxInfo
);
429 SetLastError(ERROR_OUTOFMEMORY
);
434 static void WINAPI
CRYPT_FreeBlob(LPCSTR pszObjectOid
,
435 PCRYPT_BLOB_ARRAY pObject
, void *pvFreeContext
)
439 for (i
= 0; i
< pObject
->cBlob
; i
++)
440 CryptMemFree(pObject
->rgBlob
[i
].pbData
);
441 CryptMemFree(pObject
->rgBlob
);
444 static BOOL
CRYPT_GetObjectFromFile(HANDLE hFile
, PCRYPT_BLOB_ARRAY pObject
)
449 if ((ret
= GetFileSizeEx(hFile
, &size
)))
453 WARN("file too big\n");
454 SetLastError(ERROR_INVALID_DATA
);
459 CRYPT_DATA_BLOB blob
;
461 blob
.pbData
= CryptMemAlloc(size
.LowPart
);
464 ret
= ReadFile(hFile
, blob
.pbData
, size
.LowPart
, &blob
.cbData
,
468 pObject
->rgBlob
= CryptMemAlloc(sizeof(CRYPT_DATA_BLOB
));
472 memcpy(pObject
->rgBlob
, &blob
, sizeof(CRYPT_DATA_BLOB
));
476 SetLastError(ERROR_OUTOFMEMORY
);
481 CryptMemFree(blob
.pbData
);
485 SetLastError(ERROR_OUTOFMEMORY
);
493 static BOOL
CRYPT_GetObjectFromCache(LPCWSTR pszURL
, PCRYPT_BLOB_ARRAY pObject
,
494 PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
497 INTERNET_CACHE_ENTRY_INFOW
*pCacheInfo
= NULL
;
500 TRACE("(%s, %p, %p)\n", debugstr_w(pszURL
), pObject
, pAuxInfo
);
502 RetrieveUrlCacheEntryFileW(pszURL
, NULL
, &size
, 0);
503 if (GetLastError() != ERROR_INSUFFICIENT_BUFFER
)
506 pCacheInfo
= CryptMemAlloc(size
);
509 SetLastError(ERROR_OUTOFMEMORY
);
513 if ((ret
= RetrieveUrlCacheEntryFileW(pszURL
, pCacheInfo
, &size
, 0)))
517 GetSystemTimeAsFileTime(&ft
);
518 if (CompareFileTime(&pCacheInfo
->ExpireTime
, &ft
) >= 0)
520 HANDLE hFile
= CreateFileW(pCacheInfo
->lpszLocalFileName
, GENERIC_READ
,
521 FILE_SHARE_READ
, NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
523 if (hFile
!= INVALID_HANDLE_VALUE
)
525 if ((ret
= CRYPT_GetObjectFromFile(hFile
, pObject
)))
527 if (pAuxInfo
&& pAuxInfo
->cbSize
>= RTL_SIZEOF_THROUGH_FIELD(CRYPT_RETRIEVE_AUX_INFO
, pLastSyncTime
)
528 && pAuxInfo
->pLastSyncTime
)
530 memcpy(pAuxInfo
->pLastSyncTime
,
531 &pCacheInfo
->LastSyncTime
,
539 DeleteUrlCacheEntryW(pszURL
);
545 DeleteUrlCacheEntryW(pszURL
);
548 UnlockUrlCacheEntryFileW(pszURL
, 0);
550 CryptMemFree(pCacheInfo
);
551 TRACE("returning %d\n", ret
);
555 /* Parses the URL, and sets components' lpszHostName and lpszUrlPath members
556 * to NULL-terminated copies of those portions of the URL (to be freed with
559 static BOOL
CRYPT_CrackUrl(LPCWSTR pszURL
, URL_COMPONENTSW
*components
)
563 TRACE("(%s, %p)\n", debugstr_w(pszURL
), components
);
565 memset(components
, 0, sizeof(*components
));
566 components
->dwStructSize
= sizeof(*components
);
567 components
->lpszHostName
= CryptMemAlloc(INTERNET_MAX_HOST_NAME_LENGTH
* sizeof(WCHAR
));
568 components
->dwHostNameLength
= INTERNET_MAX_HOST_NAME_LENGTH
;
569 if (!components
->lpszHostName
)
571 SetLastError(ERROR_OUTOFMEMORY
);
574 components
->lpszUrlPath
= CryptMemAlloc(INTERNET_MAX_PATH_LENGTH
* sizeof(WCHAR
));
575 components
->dwUrlPathLength
= INTERNET_MAX_PATH_LENGTH
;
576 if (!components
->lpszUrlPath
)
578 CryptMemFree(components
->lpszHostName
);
579 SetLastError(ERROR_OUTOFMEMORY
);
583 ret
= InternetCrackUrlW(pszURL
, 0, ICU_DECODE
, components
);
586 switch (components
->nScheme
)
588 case INTERNET_SCHEME_FTP
:
589 if (!components
->nPort
)
590 components
->nPort
= INTERNET_DEFAULT_FTP_PORT
;
592 case INTERNET_SCHEME_HTTP
:
593 if (!components
->nPort
)
594 components
->nPort
= INTERNET_DEFAULT_HTTP_PORT
;
600 TRACE("returning %d\n", ret
);
611 static struct InetContext
*CRYPT_MakeInetContext(DWORD dwTimeout
)
613 struct InetContext
*context
= CryptMemAlloc(sizeof(struct InetContext
));
617 context
->event
= CreateEventW(NULL
, FALSE
, FALSE
, NULL
);
620 CryptMemFree(context
);
625 context
->timeout
= dwTimeout
;
626 context
->error
= ERROR_SUCCESS
;
632 static BOOL
CRYPT_DownloadObject(DWORD dwRetrievalFlags
, HINTERNET hHttp
,
633 struct InetContext
*context
, PCRYPT_BLOB_ARRAY pObject
,
634 PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
636 CRYPT_DATA_BLOB object
= { 0, NULL
};
637 DWORD bytesAvailable
;
641 if ((ret
= InternetQueryDataAvailable(hHttp
, &bytesAvailable
, 0, 0)))
646 object
.pbData
= CryptMemRealloc(object
.pbData
,
647 object
.cbData
+ bytesAvailable
);
649 object
.pbData
= CryptMemAlloc(bytesAvailable
);
652 INTERNET_BUFFERSA buffer
= { sizeof(buffer
), 0 };
654 buffer
.dwBufferLength
= bytesAvailable
;
655 buffer
.lpvBuffer
= object
.pbData
+ object
.cbData
;
656 if (!(ret
= InternetReadFileExA(hHttp
, &buffer
, IRF_NO_WAIT
,
657 (DWORD_PTR
)context
)))
659 if (GetLastError() == ERROR_IO_PENDING
)
661 if (WaitForSingleObject(context
->event
,
662 context
->timeout
) == WAIT_TIMEOUT
)
663 SetLastError(ERROR_TIMEOUT
);
664 else if (context
->error
)
665 SetLastError(context
->error
);
671 object
.cbData
+= buffer
.dwBufferLength
;
675 SetLastError(ERROR_OUTOFMEMORY
);
680 else if (GetLastError() == ERROR_IO_PENDING
)
682 if (WaitForSingleObject(context
->event
, context
->timeout
) ==
684 SetLastError(ERROR_TIMEOUT
);
688 } while (ret
&& bytesAvailable
);
691 pObject
->rgBlob
= CryptMemAlloc(sizeof(CRYPT_DATA_BLOB
));
692 if (!pObject
->rgBlob
)
694 CryptMemFree(object
.pbData
);
695 SetLastError(ERROR_OUTOFMEMORY
);
700 pObject
->rgBlob
[0].cbData
= object
.cbData
;
701 pObject
->rgBlob
[0].pbData
= object
.pbData
;
705 TRACE("returning %d\n", ret
);
709 /* Finds the object specified by pszURL in the cache. If it's not found,
710 * creates a new cache entry for the object and writes the object to it.
711 * Sets the expiration time of the cache entry to expires.
713 static void CRYPT_CacheURL(LPCWSTR pszURL
, const CRYPT_BLOB_ARRAY
*pObject
,
714 DWORD dwRetrievalFlags
, FILETIME expires
)
716 WCHAR cacheFileName
[MAX_PATH
];
718 DWORD size
= 0, entryType
;
721 GetUrlCacheEntryInfoW(pszURL
, NULL
, &size
);
722 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER
)
724 INTERNET_CACHE_ENTRY_INFOW
*info
= CryptMemAlloc(size
);
728 ERR("out of memory\n");
732 if (GetUrlCacheEntryInfoW(pszURL
, info
, &size
))
734 lstrcpyW(cacheFileName
, info
->lpszLocalFileName
);
735 /* Check if the existing cache entry is up to date. If it isn't,
736 * remove the existing cache entry, and create a new one with the
739 GetSystemTimeAsFileTime(&ft
);
740 if (CompareFileTime(&info
->ExpireTime
, &ft
) < 0)
742 DeleteUrlCacheEntryW(pszURL
);
746 info
->ExpireTime
= expires
;
747 SetUrlCacheEntryInfoW(pszURL
, info
, CACHE_ENTRY_EXPTIME_FC
);
755 if (!CreateUrlCacheEntryW(pszURL
, pObject
->rgBlob
[0].cbData
, NULL
, cacheFileName
, 0))
758 hCacheFile
= CreateFileW(cacheFileName
, GENERIC_WRITE
, 0,
759 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
760 if(hCacheFile
== INVALID_HANDLE_VALUE
)
763 WriteFile(hCacheFile
, pObject
->rgBlob
[0].pbData
,
764 pObject
->rgBlob
[0].cbData
, &size
, NULL
);
765 CloseHandle(hCacheFile
);
767 if (!(dwRetrievalFlags
& CRYPT_STICKY_CACHE_RETRIEVAL
))
768 entryType
= NORMAL_CACHE_ENTRY
;
770 entryType
= STICKY_CACHE_ENTRY
;
771 memset(&ft
, 0, sizeof(ft
));
772 CommitUrlCacheEntryW(pszURL
, cacheFileName
, expires
, ft
, entryType
,
773 NULL
, 0, NULL
, NULL
);
776 static void CALLBACK
CRYPT_InetStatusCallback(HINTERNET hInt
,
777 DWORD_PTR dwContext
, DWORD status
, void *statusInfo
, DWORD statusInfoLen
)
779 struct InetContext
*context
= (struct InetContext
*)dwContext
;
780 LPINTERNET_ASYNC_RESULT result
;
784 case INTERNET_STATUS_REQUEST_COMPLETE
:
786 context
->error
= result
->dwError
;
787 SetEvent(context
->event
);
791 static BOOL
CRYPT_Connect(const URL_COMPONENTSW
*components
,
792 struct InetContext
*context
, PCRYPT_CREDENTIALS pCredentials
,
793 HINTERNET
*phInt
, HINTERNET
*phHost
)
797 TRACE("(%s:%d, %p, %p, %p, %p)\n", debugstr_w(components
->lpszHostName
),
798 components
->nPort
, context
, pCredentials
, phInt
, phInt
);
801 *phInt
= InternetOpenW(NULL
, INTERNET_OPEN_TYPE_PRECONFIG
, NULL
, NULL
,
802 context
? INTERNET_FLAG_ASYNC
: 0);
808 InternetSetStatusCallbackW(*phInt
, CRYPT_InetStatusCallback
);
809 switch (components
->nScheme
)
811 case INTERNET_SCHEME_FTP
:
812 service
= INTERNET_SERVICE_FTP
;
814 case INTERNET_SCHEME_HTTP
:
815 service
= INTERNET_SERVICE_HTTP
;
820 /* FIXME: use pCredentials for username/password */
821 *phHost
= InternetConnectW(*phInt
, components
->lpszHostName
,
822 components
->nPort
, NULL
, NULL
, service
, 0, (DWORD_PTR
)context
);
825 InternetCloseHandle(*phInt
);
834 TRACE("returning %d\n", ret
);
838 static BOOL WINAPI
FTP_RetrieveEncodedObjectW(LPCWSTR pszURL
,
839 LPCSTR pszObjectOid
, DWORD dwRetrievalFlags
, DWORD dwTimeout
,
840 PCRYPT_BLOB_ARRAY pObject
, PFN_FREE_ENCODED_OBJECT_FUNC
*ppfnFreeObject
,
841 void **ppvFreeContext
, HCRYPTASYNC hAsyncRetrieve
,
842 PCRYPT_CREDENTIALS pCredentials
, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
844 FIXME("(%s, %s, %08lx, %ld, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL
),
845 debugstr_a(pszObjectOid
), dwRetrievalFlags
, dwTimeout
, pObject
,
846 ppfnFreeObject
, ppvFreeContext
, hAsyncRetrieve
, pCredentials
, pAuxInfo
);
849 pObject
->rgBlob
= NULL
;
850 *ppfnFreeObject
= CRYPT_FreeBlob
;
851 *ppvFreeContext
= NULL
;
855 static BOOL WINAPI
HTTP_RetrieveEncodedObjectW(LPCWSTR pszURL
,
856 LPCSTR pszObjectOid
, DWORD dwRetrievalFlags
, DWORD dwTimeout
,
857 PCRYPT_BLOB_ARRAY pObject
, PFN_FREE_ENCODED_OBJECT_FUNC
*ppfnFreeObject
,
858 void **ppvFreeContext
, HCRYPTASYNC hAsyncRetrieve
,
859 PCRYPT_CREDENTIALS pCredentials
, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
863 TRACE("(%s, %s, %08lx, %ld, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL
),
864 debugstr_a(pszObjectOid
), dwRetrievalFlags
, dwTimeout
, pObject
,
865 ppfnFreeObject
, ppvFreeContext
, hAsyncRetrieve
, pCredentials
, pAuxInfo
);
868 pObject
->rgBlob
= NULL
;
869 *ppfnFreeObject
= CRYPT_FreeBlob
;
870 *ppvFreeContext
= NULL
;
872 if (!(dwRetrievalFlags
& CRYPT_WIRE_ONLY_RETRIEVAL
))
873 ret
= CRYPT_GetObjectFromCache(pszURL
, pObject
, pAuxInfo
);
874 if (!ret
&& (!(dwRetrievalFlags
& CRYPT_CACHE_ONLY_RETRIEVAL
) ||
875 (dwRetrievalFlags
& CRYPT_WIRE_ONLY_RETRIEVAL
)))
877 URL_COMPONENTSW components
;
879 if ((ret
= CRYPT_CrackUrl(pszURL
, &components
)))
881 HINTERNET hInt
, hHost
;
882 struct InetContext
*context
= NULL
;
885 context
= CRYPT_MakeInetContext(dwTimeout
);
886 ret
= CRYPT_Connect(&components
, context
, pCredentials
, &hInt
,
890 static LPCWSTR types
[] =
892 L
"application/x-x509-ca-cert", L
"application/x-x509-email-cert",
893 L
"application/x-x509-server-cert", L
"application/x-x509-user-cert",
894 L
"application/x-pkcs7-certificates", L
"application/pkix-crl",
895 L
"application/x-pkcs7-crl", L
"application/x-pkcs7-signature",
896 L
"application/x-pkcs7-mime", NULL
898 HINTERNET hHttp
= HttpOpenRequestW(hHost
, NULL
,
899 components
.lpszUrlPath
, NULL
, NULL
, types
,
900 INTERNET_FLAG_NO_COOKIES
| INTERNET_FLAG_NO_UI
,
907 InternetSetOptionW(hHttp
,
908 INTERNET_OPTION_RECEIVE_TIMEOUT
, &dwTimeout
,
910 InternetSetOptionW(hHttp
, INTERNET_OPTION_SEND_TIMEOUT
,
911 &dwTimeout
, sizeof(dwTimeout
));
913 ret
= HttpSendRequestExW(hHttp
, NULL
, NULL
, 0,
915 if (!ret
&& GetLastError() == ERROR_IO_PENDING
)
917 if (WaitForSingleObject(context
->event
,
918 context
->timeout
) == WAIT_TIMEOUT
)
919 SetLastError(ERROR_TIMEOUT
);
924 !(ret
= HttpEndRequestW(hHttp
, NULL
, 0, (DWORD_PTR
)context
)) &&
925 GetLastError() == ERROR_IO_PENDING
)
927 if (WaitForSingleObject(context
->event
,
928 context
->timeout
) == WAIT_TIMEOUT
)
929 SetLastError(ERROR_TIMEOUT
);
934 ret
= CRYPT_DownloadObject(dwRetrievalFlags
, hHttp
,
935 context
, pObject
, pAuxInfo
);
936 if (ret
&& !(dwRetrievalFlags
& CRYPT_DONT_CACHE_RESULT
))
940 DWORD len
= sizeof(st
);
942 if (HttpQueryInfoW(hHttp
, HTTP_QUERY_EXPIRES
| HTTP_QUERY_FLAG_SYSTEMTIME
,
943 &st
, &len
, NULL
) && SystemTimeToFileTime(&st
, &ft
))
944 CRYPT_CacheURL(pszURL
, pObject
, dwRetrievalFlags
, ft
);
946 InternetCloseHandle(hHttp
);
948 InternetCloseHandle(hHost
);
949 InternetCloseHandle(hInt
);
953 CloseHandle(context
->event
);
954 CryptMemFree(context
);
956 CryptMemFree(components
.lpszUrlPath
);
957 CryptMemFree(components
.lpszHostName
);
960 TRACE("returning %d\n", ret
);
964 static BOOL WINAPI
File_RetrieveEncodedObjectW(LPCWSTR pszURL
,
965 LPCSTR pszObjectOid
, DWORD dwRetrievalFlags
, DWORD dwTimeout
,
966 PCRYPT_BLOB_ARRAY pObject
, PFN_FREE_ENCODED_OBJECT_FUNC
*ppfnFreeObject
,
967 void **ppvFreeContext
, HCRYPTASYNC hAsyncRetrieve
,
968 PCRYPT_CREDENTIALS pCredentials
, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
970 URL_COMPONENTSW components
= { sizeof(components
), 0 };
973 TRACE("(%s, %s, %08lx, %ld, %p, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL
),
974 debugstr_a(pszObjectOid
), dwRetrievalFlags
, dwTimeout
, pObject
,
975 ppfnFreeObject
, ppvFreeContext
, hAsyncRetrieve
, pCredentials
, pAuxInfo
);
978 pObject
->rgBlob
= NULL
;
979 *ppfnFreeObject
= CRYPT_FreeBlob
;
980 *ppvFreeContext
= NULL
;
982 components
.lpszUrlPath
= CryptMemAlloc(INTERNET_MAX_PATH_LENGTH
* sizeof(WCHAR
));
983 components
.dwUrlPathLength
= INTERNET_MAX_PATH_LENGTH
;
984 if (!components
.lpszUrlPath
)
986 SetLastError(ERROR_OUTOFMEMORY
);
990 ret
= InternetCrackUrlW(pszURL
, 0, ICU_DECODE
, &components
);
995 /* 3 == lstrlenW(L"c:") + 1 */
996 path
= CryptMemAlloc((components
.dwUrlPathLength
+ 3) * sizeof(WCHAR
));
1001 /* Try to create the file directly - Wine handles / in pathnames */
1002 lstrcpynW(path
, components
.lpszUrlPath
,
1003 components
.dwUrlPathLength
+ 1);
1004 hFile
= CreateFileW(path
, GENERIC_READ
, FILE_SHARE_READ
,
1005 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
1006 if (hFile
== INVALID_HANDLE_VALUE
)
1008 /* Try again on the current drive */
1009 GetCurrentDirectoryW(components
.dwUrlPathLength
, path
);
1012 lstrcpynW(path
+ 2, components
.lpszUrlPath
,
1013 components
.dwUrlPathLength
+ 1);
1014 hFile
= CreateFileW(path
, GENERIC_READ
, FILE_SHARE_READ
,
1015 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
1017 if (hFile
== INVALID_HANDLE_VALUE
)
1019 /* Try again on the Windows drive */
1020 GetWindowsDirectoryW(path
, components
.dwUrlPathLength
);
1023 lstrcpynW(path
+ 2, components
.lpszUrlPath
,
1024 components
.dwUrlPathLength
+ 1);
1025 hFile
= CreateFileW(path
, GENERIC_READ
, FILE_SHARE_READ
,
1026 NULL
, OPEN_EXISTING
, FILE_ATTRIBUTE_NORMAL
, NULL
);
1030 if (hFile
!= INVALID_HANDLE_VALUE
)
1032 if ((ret
= CRYPT_GetObjectFromFile(hFile
, pObject
)))
1034 if (pAuxInfo
&& pAuxInfo
->cbSize
>= RTL_SIZEOF_THROUGH_FIELD(CRYPT_RETRIEVE_AUX_INFO
, pLastSyncTime
)
1035 && pAuxInfo
->pLastSyncTime
)
1037 GetFileTime(hFile
, NULL
, NULL
,
1038 pAuxInfo
->pLastSyncTime
);
1049 SetLastError(ERROR_OUTOFMEMORY
);
1053 CryptMemFree(components
.lpszUrlPath
);
1057 typedef BOOL (WINAPI
*SchemeDllRetrieveEncodedObjectW
)(LPCWSTR pwszUrl
,
1058 LPCSTR pszObjectOid
, DWORD dwRetrievalFlags
, DWORD dwTimeout
,
1059 PCRYPT_BLOB_ARRAY pObject
, PFN_FREE_ENCODED_OBJECT_FUNC
*ppfnFreeObject
,
1060 void **ppvFreeContext
, HCRYPTASYNC hAsyncRetrieve
,
1061 PCRYPT_CREDENTIALS pCredentials
, PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
);
1063 static BOOL
CRYPT_GetRetrieveFunction(LPCWSTR pszURL
,
1064 SchemeDllRetrieveEncodedObjectW
*pFunc
, HCRYPTOIDFUNCADDR
*phFunc
)
1066 URL_COMPONENTSW components
= { sizeof(components
), 0 };
1069 TRACE("(%s, %p, %p)\n", debugstr_w(pszURL
), pFunc
, phFunc
);
1073 components
.dwSchemeLength
= 1;
1074 ret
= InternetCrackUrlW(pszURL
, 0, 0, &components
);
1077 /* Microsoft always uses CryptInitOIDFunctionSet/
1078 * CryptGetOIDFunctionAddress, but there doesn't seem to be a pressing
1079 * reason to do so for builtin schemes.
1081 switch (components
.nScheme
)
1083 case INTERNET_SCHEME_FTP
:
1084 *pFunc
= FTP_RetrieveEncodedObjectW
;
1086 case INTERNET_SCHEME_HTTP
:
1087 *pFunc
= HTTP_RetrieveEncodedObjectW
;
1089 case INTERNET_SCHEME_FILE
:
1090 *pFunc
= File_RetrieveEncodedObjectW
;
1094 int len
= WideCharToMultiByte(CP_ACP
, 0, components
.lpszScheme
,
1095 components
.dwSchemeLength
, NULL
, 0, NULL
, NULL
);
1099 LPSTR scheme
= CryptMemAlloc(len
);
1103 static HCRYPTOIDFUNCSET set
= NULL
;
1106 set
= CryptInitOIDFunctionSet(
1107 SCHEME_OID_RETRIEVE_ENCODED_OBJECTW_FUNC
, 0);
1108 WideCharToMultiByte(CP_ACP
, 0, components
.lpszScheme
,
1109 components
.dwSchemeLength
, scheme
, len
, NULL
, NULL
);
1110 ret
= CryptGetOIDFunctionAddress(set
, X509_ASN_ENCODING
,
1111 scheme
, 0, (void **)pFunc
, phFunc
);
1112 CryptMemFree(scheme
);
1116 SetLastError(ERROR_OUTOFMEMORY
);
1125 TRACE("returning %d\n", ret
);
1129 static BOOL WINAPI
CRYPT_CreateBlob(LPCSTR pszObjectOid
,
1130 DWORD dwRetrievalFlags
, const CRYPT_BLOB_ARRAY
*pObject
, void **ppvContext
)
1133 CRYPT_BLOB_ARRAY
*context
;
1136 size
= sizeof(CRYPT_BLOB_ARRAY
) + pObject
->cBlob
* sizeof(CRYPT_DATA_BLOB
);
1137 for (i
= 0; i
< pObject
->cBlob
; i
++)
1138 size
+= pObject
->rgBlob
[i
].cbData
;
1139 context
= CryptMemAlloc(size
);
1146 (CRYPT_DATA_BLOB
*)((LPBYTE
)context
+ sizeof(CRYPT_BLOB_ARRAY
));
1148 (LPBYTE
)context
->rgBlob
+ pObject
->cBlob
* sizeof(CRYPT_DATA_BLOB
);
1149 for (i
= 0; i
< pObject
->cBlob
; i
++)
1151 memcpy(nextData
, pObject
->rgBlob
[i
].pbData
,
1152 pObject
->rgBlob
[i
].cbData
);
1153 context
->rgBlob
[i
].pbData
= nextData
;
1154 context
->rgBlob
[i
].cbData
= pObject
->rgBlob
[i
].cbData
;
1155 nextData
+= pObject
->rgBlob
[i
].cbData
;
1158 *ppvContext
= context
;
1164 typedef BOOL (WINAPI
*AddContextToStore
)(HCERTSTORE hCertStore
,
1165 const void *pContext
, DWORD dwAddDisposition
, const void **ppStoreContext
);
1167 static BOOL
decode_base64_blob( const CRYPT_DATA_BLOB
*in
, CRYPT_DATA_BLOB
*out
)
1170 DWORD len
= in
->cbData
;
1172 while (len
&& !in
->pbData
[len
- 1]) len
--;
1173 if (!CryptStringToBinaryA( (char *)in
->pbData
, len
, CRYPT_STRING_BASE64_ANY
,
1174 NULL
, &out
->cbData
, NULL
, NULL
)) return FALSE
;
1176 if (!(out
->pbData
= CryptMemAlloc( out
->cbData
))) return FALSE
;
1177 ret
= CryptStringToBinaryA( (char *)in
->pbData
, len
, CRYPT_STRING_BASE64_ANY
,
1178 out
->pbData
, &out
->cbData
, NULL
, NULL
);
1179 if (!ret
) CryptMemFree( out
->pbData
);
1183 static BOOL
CRYPT_CreateContext(const CRYPT_BLOB_ARRAY
*pObject
,
1184 DWORD dwExpectedContentTypeFlags
, AddContextToStore addFunc
, void **ppvContext
)
1187 CRYPT_DATA_BLOB blob
;
1189 if (!pObject
->cBlob
)
1191 SetLastError(ERROR_INVALID_DATA
);
1195 else if (pObject
->cBlob
== 1)
1197 if (decode_base64_blob(&pObject
->rgBlob
[0], &blob
))
1199 ret
= CryptQueryObject(CERT_QUERY_OBJECT_BLOB
, &blob
,
1200 dwExpectedContentTypeFlags
, CERT_QUERY_FORMAT_FLAG_BINARY
, 0,
1201 NULL
, NULL
, NULL
, NULL
, NULL
, (const void **)ppvContext
);
1202 CryptMemFree(blob
.pbData
);
1206 ret
= CryptQueryObject(CERT_QUERY_OBJECT_BLOB
, &pObject
->rgBlob
[0],
1207 dwExpectedContentTypeFlags
, CERT_QUERY_FORMAT_FLAG_BINARY
, 0,
1208 NULL
, NULL
, NULL
, NULL
, NULL
, (const void **)ppvContext
);
1212 SetLastError(CRYPT_E_NO_MATCH
);
1218 HCERTSTORE store
= CertOpenStore(CERT_STORE_PROV_MEMORY
, 0, 0,
1219 CERT_STORE_CREATE_NEW_FLAG
, NULL
);
1224 const void *context
;
1226 for (i
= 0; i
< pObject
->cBlob
; i
++)
1228 if (decode_base64_blob(&pObject
->rgBlob
[i
], &blob
))
1230 ret
= CryptQueryObject(CERT_QUERY_OBJECT_BLOB
, &blob
,
1231 dwExpectedContentTypeFlags
, CERT_QUERY_FORMAT_FLAG_BINARY
,
1232 0, NULL
, NULL
, NULL
, NULL
, NULL
, &context
);
1233 CryptMemFree(blob
.pbData
);
1237 ret
= CryptQueryObject(CERT_QUERY_OBJECT_BLOB
,
1238 &pObject
->rgBlob
[i
], dwExpectedContentTypeFlags
,
1239 CERT_QUERY_FORMAT_FLAG_BINARY
, 0, NULL
, NULL
, NULL
, NULL
,
1244 if (!addFunc(store
, context
, CERT_STORE_ADD_ALWAYS
, NULL
))
1249 SetLastError(CRYPT_E_NO_MATCH
);
1256 *ppvContext
= store
;
1261 static BOOL WINAPI
CRYPT_CreateCert(LPCSTR pszObjectOid
,
1262 DWORD dwRetrievalFlags
, const CRYPT_BLOB_ARRAY
*pObject
, void **ppvContext
)
1264 return CRYPT_CreateContext(pObject
, CERT_QUERY_CONTENT_FLAG_CERT
,
1265 (AddContextToStore
)CertAddCertificateContextToStore
, ppvContext
);
1268 static BOOL WINAPI
CRYPT_CreateCRL(LPCSTR pszObjectOid
,
1269 DWORD dwRetrievalFlags
, const CRYPT_BLOB_ARRAY
*pObject
, void **ppvContext
)
1271 return CRYPT_CreateContext(pObject
, CERT_QUERY_CONTENT_FLAG_CRL
,
1272 (AddContextToStore
)CertAddCRLContextToStore
, ppvContext
);
1275 static BOOL WINAPI
CRYPT_CreateCTL(LPCSTR pszObjectOid
,
1276 DWORD dwRetrievalFlags
, const CRYPT_BLOB_ARRAY
*pObject
, void **ppvContext
)
1278 return CRYPT_CreateContext(pObject
, CERT_QUERY_CONTENT_FLAG_CTL
,
1279 (AddContextToStore
)CertAddCTLContextToStore
, ppvContext
);
1282 static BOOL WINAPI
CRYPT_CreatePKCS7(LPCSTR pszObjectOid
,
1283 DWORD dwRetrievalFlags
, const CRYPT_BLOB_ARRAY
*pObject
, void **ppvContext
)
1287 if (!pObject
->cBlob
)
1289 SetLastError(ERROR_INVALID_DATA
);
1293 else if (pObject
->cBlob
== 1)
1294 ret
= CryptQueryObject(CERT_QUERY_OBJECT_BLOB
, &pObject
->rgBlob
[0],
1295 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED
|
1296 CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED
, CERT_QUERY_FORMAT_FLAG_BINARY
,
1297 0, NULL
, NULL
, NULL
, ppvContext
, NULL
, NULL
);
1300 FIXME("multiple messages unimplemented\n");
1306 static BOOL WINAPI
CRYPT_CreateAny(LPCSTR pszObjectOid
,
1307 DWORD dwRetrievalFlags
, const CRYPT_BLOB_ARRAY
*pObject
, void **ppvContext
)
1311 if (!pObject
->cBlob
)
1313 SetLastError(ERROR_INVALID_DATA
);
1319 HCERTSTORE store
= CertOpenStore(CERT_STORE_PROV_COLLECTION
, 0, 0,
1320 CERT_STORE_CREATE_NEW_FLAG
, NULL
);
1324 HCERTSTORE memStore
= CertOpenStore(CERT_STORE_PROV_MEMORY
, 0, 0,
1325 CERT_STORE_CREATE_NEW_FLAG
, NULL
);
1329 CertAddStoreToCollection(store
, memStore
,
1330 CERT_PHYSICAL_STORE_ADD_ENABLE_FLAG
, 0);
1331 CertCloseStore(memStore
, 0);
1335 CertCloseStore(store
, 0);
1344 for (i
= 0; i
< pObject
->cBlob
; i
++)
1346 DWORD contentType
, expectedContentTypes
=
1347 CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED
|
1348 CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED
|
1349 CERT_QUERY_CONTENT_FLAG_CERT
|
1350 CERT_QUERY_CONTENT_FLAG_CRL
|
1351 CERT_QUERY_CONTENT_FLAG_CTL
;
1352 HCERTSTORE contextStore
;
1353 const void *context
;
1355 if (CryptQueryObject(CERT_QUERY_OBJECT_BLOB
,
1356 &pObject
->rgBlob
[i
], expectedContentTypes
,
1357 CERT_QUERY_FORMAT_FLAG_BINARY
, 0, NULL
, &contentType
, NULL
,
1358 &contextStore
, NULL
, &context
))
1360 switch (contentType
)
1362 case CERT_QUERY_CONTENT_CERT
:
1363 if (!CertAddCertificateContextToStore(store
,
1364 context
, CERT_STORE_ADD_ALWAYS
, NULL
))
1366 CertFreeCertificateContext(context
);
1368 case CERT_QUERY_CONTENT_CRL
:
1369 if (!CertAddCRLContextToStore(store
,
1370 context
, CERT_STORE_ADD_ALWAYS
, NULL
))
1372 CertFreeCRLContext(context
);
1374 case CERT_QUERY_CONTENT_CTL
:
1375 if (!CertAddCTLContextToStore(store
,
1376 context
, CERT_STORE_ADD_ALWAYS
, NULL
))
1378 CertFreeCTLContext(context
);
1381 CertAddStoreToCollection(store
, contextStore
, 0, 0);
1383 CertCloseStore(contextStore
, 0);
1391 *ppvContext
= store
;
1396 typedef BOOL (WINAPI
*ContextDllCreateObjectContext
)(LPCSTR pszObjectOid
,
1397 DWORD dwRetrievalFlags
, const CRYPT_BLOB_ARRAY
*pObject
, void **ppvContext
);
1399 static BOOL
CRYPT_GetCreateFunction(LPCSTR pszObjectOid
,
1400 ContextDllCreateObjectContext
*pFunc
, HCRYPTOIDFUNCADDR
*phFunc
)
1404 TRACE("(%s, %p, %p)\n", debugstr_a(pszObjectOid
), pFunc
, phFunc
);
1408 if (IS_INTOID(pszObjectOid
))
1410 switch (LOWORD(pszObjectOid
))
1413 *pFunc
= CRYPT_CreateBlob
;
1415 case LOWORD(CONTEXT_OID_CERTIFICATE
):
1416 *pFunc
= CRYPT_CreateCert
;
1418 case LOWORD(CONTEXT_OID_CRL
):
1419 *pFunc
= CRYPT_CreateCRL
;
1421 case LOWORD(CONTEXT_OID_CTL
):
1422 *pFunc
= CRYPT_CreateCTL
;
1424 case LOWORD(CONTEXT_OID_PKCS7
):
1425 *pFunc
= CRYPT_CreatePKCS7
;
1427 case LOWORD(CONTEXT_OID_CAPI2_ANY
):
1428 *pFunc
= CRYPT_CreateAny
;
1434 static HCRYPTOIDFUNCSET set
= NULL
;
1437 set
= CryptInitOIDFunctionSet(
1438 CONTEXT_OID_CREATE_OBJECT_CONTEXT_FUNC
, 0);
1439 ret
= CryptGetOIDFunctionAddress(set
, X509_ASN_ENCODING
, pszObjectOid
,
1440 0, (void **)pFunc
, phFunc
);
1442 TRACE("returning %d\n", ret
);
1446 static BOOL
CRYPT_GetExpiration(const void *object
, const char *pszObjectOid
, FILETIME
*expiration
)
1448 if (!IS_INTOID(pszObjectOid
))
1451 switch (LOWORD(pszObjectOid
)) {
1452 case LOWORD(CONTEXT_OID_CERTIFICATE
):
1453 *expiration
= ((const CERT_CONTEXT
*)object
)->pCertInfo
->NotAfter
;
1455 case LOWORD(CONTEXT_OID_CRL
):
1456 *expiration
= ((const CRL_CONTEXT
*)object
)->pCrlInfo
->NextUpdate
;
1458 case LOWORD(CONTEXT_OID_CTL
):
1459 *expiration
= ((const CTL_CONTEXT
*)object
)->pCtlInfo
->NextUpdate
;
1466 /***********************************************************************
1467 * CryptRetrieveObjectByUrlW (CRYPTNET.@)
1469 BOOL WINAPI
CryptRetrieveObjectByUrlW(LPCWSTR pszURL
, LPCSTR pszObjectOid
,
1470 DWORD dwRetrievalFlags
, DWORD dwTimeout
, LPVOID
*ppvObject
,
1471 HCRYPTASYNC hAsyncRetrieve
, PCRYPT_CREDENTIALS pCredentials
, LPVOID pvVerify
,
1472 PCRYPT_RETRIEVE_AUX_INFO pAuxInfo
)
1475 SchemeDllRetrieveEncodedObjectW retrieve
;
1476 ContextDllCreateObjectContext create
;
1477 HCRYPTOIDFUNCADDR hRetrieve
= 0, hCreate
= 0;
1479 TRACE("(%s, %s, %08lx, %ld, %p, %p, %p, %p, %p)\n", debugstr_w(pszURL
),
1480 debugstr_a(pszObjectOid
), dwRetrievalFlags
, dwTimeout
, ppvObject
,
1481 hAsyncRetrieve
, pCredentials
, pvVerify
, pAuxInfo
);
1485 SetLastError(ERROR_INVALID_PARAMETER
);
1488 ret
= CRYPT_GetRetrieveFunction(pszURL
, &retrieve
, &hRetrieve
);
1490 ret
= CRYPT_GetCreateFunction(pszObjectOid
, &create
, &hCreate
);
1493 CRYPT_BLOB_ARRAY object
= { 0, NULL
};
1494 PFN_FREE_ENCODED_OBJECT_FUNC freeObject
;
1498 ret
= retrieve(pszURL
, pszObjectOid
, dwRetrievalFlags
, dwTimeout
,
1499 &object
, &freeObject
, &freeContext
, hAsyncRetrieve
, pCredentials
,
1503 ret
= create(pszObjectOid
, dwRetrievalFlags
, &object
, ppvObject
);
1504 if (ret
&& !(dwRetrievalFlags
& CRYPT_DONT_CACHE_RESULT
) &&
1505 CRYPT_GetExpiration(*ppvObject
, pszObjectOid
, &expires
))
1507 CRYPT_CacheURL(pszURL
, &object
, dwRetrievalFlags
, expires
);
1509 freeObject(pszObjectOid
, &object
, freeContext
);
1513 CryptFreeOIDFunctionAddress(hCreate
, 0);
1515 CryptFreeOIDFunctionAddress(hRetrieve
, 0);
1516 TRACE("returning %d\n", ret
);
1520 /* Store successful revocation checks (whether the certificate was revoked or
1521 * not) in an on-disk cache. This is not because of network latency—we already
1522 * have a cache for that—but rather because parsing very large CRLs can take a
1523 * long time (at the time of writing, 20 MB CRLs have been seen in the wild and
1524 * can take several hundred milliseconds) and applications expect chain building
1525 * to be much faster.
1527 * The cache is treated as invalid once we pass the nextUpdate field of the CRL.
1528 * This isn't quite what the field is meant for (it's rather meant to specify a
1529 * later bound for the next time the CRL will be reissued, and doesn't prescribe
1530 * a date by which the CRL is invalid; see RFC 5280 § 5.1.2.5) but it's the way
1531 * it's used in practice.
1533 * The location of the cache roughly matches Windows, but the file name and
1537 static const char revocation_cache_signature
[] = "Wine cached revocation";
1539 #define CACHED_CERT_HASH_SIZE 20
1541 static FILE *open_cached_revocation_file(const CERT_CONTEXT
*cert
, const CERT_REVOCATION_PARA
*params
,
1542 const WCHAR
*mode
, int sharing
)
1544 BYTE hash_data
[CACHED_CERT_HASH_SIZE
];
1545 WCHAR path
[MAX_PATH
];
1546 WCHAR
*appdata_path
;
1552 if (FAILED(hr
= SHGetKnownFolderPath(&FOLDERID_LocalAppDataLow
, 0, NULL
, &appdata_path
)))
1554 ERR("Failed to get LocalAppDataLow path, hr %#lx.\n", hr
);
1555 return INVALID_HANDLE_VALUE
;
1558 len
= swprintf(path
, ARRAY_SIZE(path
), L
"%s\\Microsoft\\CryptnetUrlCache\\Content\\", appdata_path
);
1559 CoTaskMemFree(appdata_path
);
1561 if (len
+ CACHED_CERT_HASH_SIZE
* 2 * sizeof(WCHAR
) > ARRAY_SIZE(path
) - 1)
1563 WARN("Hash length exceeds static buffer; not caching.\n");
1564 return INVALID_HANDLE_VALUE
;
1567 CryptAcquireContextW(&prov
, NULL
, NULL
, PROV_RSA_FULL
, CRYPT_VERIFYCONTEXT
);
1568 CryptCreateHash(prov
, CALG_SHA1
, 0, 0, &hash
);
1569 CryptHashData(hash
, cert
->pbCertEncoded
, cert
->cbCertEncoded
, 0);
1570 if (params
&& params
->pIssuerCert
)
1572 CryptHashData(hash
, (BYTE
*)¶ms
->pIssuerCert
->cbCertEncoded
, sizeof(params
->pIssuerCert
->cbCertEncoded
), 0);
1573 CryptHashData(hash
, params
->pIssuerCert
->pbCertEncoded
, params
->pIssuerCert
->cbCertEncoded
, 0);
1578 CryptHashData(hash
, (BYTE
*)&size
, sizeof(size
), 0);
1580 size
= sizeof(hash_data
);
1581 CryptGetHashParam(hash
, HP_HASHVAL
, hash_data
, &size
, 0);
1582 CryptDestroyHash(hash
);
1583 CryptReleaseContext(prov
, 0);
1585 SHCreateDirectoryExW(NULL
, path
, NULL
);
1587 for (i
= 0; i
< CACHED_CERT_HASH_SIZE
; ++i
)
1589 swprintf(path
+ len
, 3, L
"%02x", hash_data
[i
]);
1593 return _wfsopen(path
, mode
, sharing
);
1596 static BOOL
find_cached_revocation_status(const CERT_CONTEXT
*cert
, const CERT_REVOCATION_PARA
*params
,
1597 const FILETIME
*time
, CERT_REVOCATION_STATUS
*status
)
1599 char buffer
[sizeof(revocation_cache_signature
)];
1600 FILETIME update_time
;
1604 if (!(file
= open_cached_revocation_file(cert
, params
, L
"rb", _SH_DENYWR
)))
1607 if ((len
= fread(buffer
, 1, sizeof(buffer
), file
)) != sizeof(buffer
)
1608 || memcmp(buffer
, revocation_cache_signature
, len
))
1610 ERR("Invalid cache signature.\n");
1615 if (fread(&update_time
, sizeof(update_time
), 1, file
) != 1)
1617 ERR("Failed to read update time.\n");
1622 if (CompareFileTime(time
, &update_time
) > 0)
1624 TRACE("Cached revocation status is potentially out of date.\n");
1629 if (fread(&status
->dwError
, sizeof(status
->dwError
), 1, file
) != 1)
1631 ERR("Failed to read error code.\n");
1636 if (status
->dwError
== CERT_E_REVOKED
&& fread(&status
->dwReason
, sizeof(status
->dwReason
), 1, file
) != 1)
1638 ERR("Failed to read revocation reason.\n");
1643 TRACE("Using cached status %#lx, reason %#lx.\n", status
->dwError
, status
->dwReason
);
1647 static void cache_revocation_status(const CERT_CONTEXT
*cert
, const CERT_REVOCATION_PARA
*params
,
1648 const FILETIME
*time
, const CERT_REVOCATION_STATUS
*status
)
1652 if (!(file
= open_cached_revocation_file(cert
, params
, L
"wb", _SH_DENYRW
)))
1654 fwrite(revocation_cache_signature
, 1, sizeof(revocation_cache_signature
), file
);
1655 fwrite(time
, sizeof(*time
), 1, file
);
1656 fwrite(&status
->dwError
, sizeof(status
->dwError
), 1, file
);
1657 if (status
->dwError
== CERT_E_REVOKED
)
1658 fwrite(&status
->dwReason
, sizeof(status
->dwReason
), 1, file
);
1662 static DWORD
verify_cert_revocation_with_crl_online(const CERT_CONTEXT
*cert
,
1663 const CRL_CONTEXT
*crl
, FILETIME
*pTime
, CERT_REVOCATION_STATUS
*pRevStatus
)
1665 PCRL_ENTRY entry
= NULL
;
1667 CertFindCertificateInCRL(cert
, crl
, 0, NULL
, &entry
);
1669 return CRYPT_E_REVOKED
;
1671 /* Since the CRL was retrieved for the cert being checked, then it's
1672 * guaranteed to be fresh, and the cert is not revoked. */
1673 return ERROR_SUCCESS
;
1676 /* Try to retrieve a CRL from any one of the specified distribution points. */
1677 static const CRL_CONTEXT
*retrieve_crl_from_dist_points(const CRYPT_URL_ARRAY
*array
,
1678 DWORD verify_flags
, DWORD timeout
)
1680 DWORD retrieve_flags
= 0;
1681 const CRL_CONTEXT
*crl
;
1684 if (verify_flags
& CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION
)
1685 retrieve_flags
|= CRYPT_CACHE_ONLY_RETRIEVAL
;
1687 /* Yes, this is a weird algorithm, but the documentation for
1688 * CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT specifies this, and
1689 * tests seem to bear it out for CertVerifyRevocation() as well. */
1690 if (verify_flags
& CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG
)
1693 for (i
= 0; i
< array
->cUrl
; ++i
)
1695 if (CryptRetrieveObjectByUrlW(array
->rgwszUrl
[i
], CONTEXT_OID_CRL
, retrieve_flags
,
1696 timeout
, (void **)&crl
, NULL
, NULL
, NULL
, NULL
))
1699 /* We don't check the current time here. This may result in less
1700 * accurate timeouts, but this too seems to be true of Windows. */
1701 if ((verify_flags
& CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG
) && GetLastError() == ERROR_TIMEOUT
)
1708 static DWORD
verify_cert_revocation_from_dist_points_ext(const CRYPT_DATA_BLOB
*value
, const CERT_CONTEXT
*cert
,
1709 FILETIME
*time
, DWORD flags
, const CERT_REVOCATION_PARA
*params
, CERT_REVOCATION_STATUS
*status
,
1710 FILETIME
*next_update
)
1712 DWORD url_array_size
, error
;
1713 CRYPT_URL_ARRAY
*url_array
;
1714 const CRL_CONTEXT
*crl
;
1717 if (!params
|| !params
->pIssuerCert
)
1719 TRACE("no issuer certificate\n");
1720 return CRYPT_E_REVOCATION_OFFLINE
;
1723 if (!CRYPT_GetUrlFromCRLDistPointsExt(value
, NULL
, &url_array_size
, NULL
, NULL
))
1724 return GetLastError();
1726 if (!(url_array
= CryptMemAlloc(url_array_size
)))
1727 return ERROR_OUTOFMEMORY
;
1729 if (!CRYPT_GetUrlFromCRLDistPointsExt(value
, url_array
, &url_array_size
, NULL
, NULL
))
1731 CryptMemFree(url_array
);
1732 return GetLastError();
1735 if (params
&& params
->cbSize
>= RTL_SIZEOF_THROUGH_FIELD(CERT_REVOCATION_PARA
, dwUrlRetrievalTimeout
))
1736 timeout
= params
->dwUrlRetrievalTimeout
;
1738 if (!(crl
= retrieve_crl_from_dist_points(url_array
, flags
, timeout
)))
1740 CryptMemFree(url_array
);
1741 return CRYPT_E_REVOCATION_OFFLINE
;
1744 error
= verify_cert_revocation_with_crl_online(cert
, crl
, time
, status
);
1746 *next_update
= crl
->pCrlInfo
->NextUpdate
;
1748 CertFreeCRLContext(crl
);
1749 CryptMemFree(url_array
);
1753 static void sha1_hash(const BYTE
*data
, DWORD datalen
, BYTE
*buf
, DWORD
*buflen
)
1758 CryptAcquireContextW(&prov
, NULL
, NULL
, PROV_RSA_FULL
, CRYPT_VERIFYCONTEXT
);
1759 CryptCreateHash(prov
, CALG_SHA1
, 0, 0, &hash
);
1760 CryptHashData(hash
, data
, datalen
, 0);
1761 CryptGetHashParam(hash
, HP_HASHVAL
, buf
, buflen
, 0);
1763 CryptDestroyHash(hash
);
1764 CryptReleaseContext(prov
, 0);
1767 static BYTE
*build_ocsp_request(const CERT_CONTEXT
*cert
, const CERT_CONTEXT
*issuer_cert
, DWORD
*ret_size
)
1769 OCSP_REQUEST_ENTRY entry
;
1770 OCSP_REQUEST_INFO request
;
1771 OCSP_SIGNED_REQUEST_INFO request_signed
;
1772 CERT_INFO
*issuer
= issuer_cert
->pCertInfo
;
1773 BYTE issuer_name_hash
[20], issuer_key_hash
[20], *buf
, *ret
;
1774 DWORD size
= 0, hash_len
= sizeof(issuer_name_hash
);
1776 memset(&entry
, 0, sizeof(entry
));
1777 entry
.CertId
.HashAlgorithm
.pszObjId
= (char *)szOID_OIWSEC_sha1
;
1779 sha1_hash(issuer
->Subject
.pbData
, issuer
->Subject
.cbData
, issuer_name_hash
, &hash_len
);
1780 entry
.CertId
.IssuerNameHash
.cbData
= sizeof(issuer_name_hash
);
1781 entry
.CertId
.IssuerNameHash
.pbData
= issuer_name_hash
;
1783 sha1_hash(issuer
->SubjectPublicKeyInfo
.PublicKey
.pbData
, issuer
->SubjectPublicKeyInfo
.PublicKey
.cbData
,
1784 issuer_key_hash
, &hash_len
);
1785 entry
.CertId
.IssuerKeyHash
.cbData
= sizeof(issuer_key_hash
);
1786 entry
.CertId
.IssuerKeyHash
.pbData
= issuer_key_hash
;
1788 entry
.CertId
.SerialNumber
.cbData
= cert
->pCertInfo
->SerialNumber
.cbData
;
1789 entry
.CertId
.SerialNumber
.pbData
= cert
->pCertInfo
->SerialNumber
.pbData
;
1791 request
.dwVersion
= OCSP_REQUEST_V1
;
1792 request
.pRequestorName
= NULL
;
1793 request
.cRequestEntry
= 1;
1794 request
.rgRequestEntry
= &entry
;
1795 request
.cExtension
= 0;
1796 request
.rgExtension
= NULL
;
1797 if (!CryptEncodeObjectEx(X509_ASN_ENCODING
, OCSP_REQUEST
, &request
, CRYPT_ENCODE_ALLOC_FLAG
, NULL
, &buf
, &size
))
1799 ERR("failed to encode request %#lx\n", GetLastError());
1803 request_signed
.ToBeSigned
.pbData
= buf
;
1804 request_signed
.ToBeSigned
.cbData
= size
;
1805 request_signed
.pOptionalSignatureInfo
= NULL
;
1806 if (!CryptEncodeObjectEx(X509_ASN_ENCODING
, OCSP_SIGNED_REQUEST
, &request_signed
, CRYPT_ENCODE_ALLOC_FLAG
, NULL
,
1809 ERR("failed to encode signed request %#lx\n", GetLastError());
1819 static void escape_path(const WCHAR
*src
, DWORD src_len
, WCHAR
*dst
, DWORD
*dst_len
)
1821 static const WCHAR hex
[] = L
"0123456789ABCDEF";
1826 for (i
= 0; i
< src_len
; i
++)
1828 if (src
[i
] == '+' || src
[i
] == '/' || src
[i
] == '=')
1833 ptr
[1] = hex
[(src
[i
] >> 4) & 0xf];
1834 ptr
[2] = hex
[src
[i
] & 0xf];
1839 else if (dst
) *ptr
++ = src
[i
];
1843 static WCHAR
*build_request_path(const BYTE
*data
, DWORD data_size
)
1846 DWORD path_len
, ret_len
;
1848 if (!CryptBinaryToStringW(data
, data_size
, CRYPT_STRING_BASE64
| CRYPT_STRING_NOCRLF
, NULL
, &path_len
)) return NULL
;
1849 if (!(path
= malloc(path_len
* sizeof(WCHAR
)))) return NULL
;
1850 CryptBinaryToStringW(data
, data_size
, CRYPT_STRING_BASE64
| CRYPT_STRING_NOCRLF
, path
, &path_len
);
1852 escape_path(path
, path_len
, NULL
, &ret_len
);
1853 if (!(ret
= malloc((ret_len
+ 2) * sizeof(WCHAR
))))
1858 escape_path(path
, path_len
, ret
+ 1, &ret_len
);
1859 ret
[ret_len
+ 1] = 0;
1866 static WCHAR
*build_request_url(const WCHAR
*base_url
, const BYTE
*data
, DWORD data_size
)
1871 if (!(path
= build_request_path(data
, data_size
))) return NULL
;
1873 InternetCombineUrlW(base_url
, path
, NULL
, &len
, 0);
1874 if (!(ret
= malloc(len
* sizeof(WCHAR
))))
1879 InternetCombineUrlW(base_url
, path
, ret
, &len
, 0);
1884 static DWORD
map_ocsp_status(DWORD status
)
1888 case OCSP_BASIC_GOOD_CERT_STATUS
: return ERROR_SUCCESS
;
1889 case OCSP_BASIC_REVOKED_CERT_STATUS
: return CRYPT_E_REVOKED
;
1890 case OCSP_BASIC_UNKNOWN_CERT_STATUS
: return CRYPT_E_REVOCATION_OFFLINE
;
1892 FIXME("unhandled status %lu\n", status
);
1893 return CRYPT_E_REVOCATION_OFFLINE
;
1897 static BOOL
match_cert_id(const OCSP_CERT_ID
*id
, const CERT_INFO
*cert
, const CERT_INFO
*issuer
)
1900 DWORD hash_len
= sizeof(hash
);
1902 if (!id
->HashAlgorithm
.pszObjId
|| strcmp(id
->HashAlgorithm
.pszObjId
, szOID_OIWSEC_sha1
))
1904 FIXME("hash algorithm %s not supported\n", debugstr_a(id
->HashAlgorithm
.pszObjId
));
1908 sha1_hash(issuer
->Subject
.pbData
, issuer
->Subject
.cbData
, hash
, &hash_len
);
1909 if (id
->IssuerNameHash
.cbData
!= hash_len
) return FALSE
;
1910 if (memcmp(id
->IssuerNameHash
.pbData
, hash
, hash_len
)) return FALSE
;
1912 sha1_hash(issuer
->SubjectPublicKeyInfo
.PublicKey
.pbData
,
1913 issuer
->SubjectPublicKeyInfo
.PublicKey
.cbData
, hash
, &hash_len
);
1914 if (id
->IssuerKeyHash
.cbData
!= hash_len
) return FALSE
;
1915 if (memcmp(id
->IssuerKeyHash
.pbData
, hash
, hash_len
)) return FALSE
;
1917 if (cert
->SerialNumber
.cbData
!= id
->SerialNumber
.cbData
) return FALSE
;
1918 return !memcmp(cert
->SerialNumber
.pbData
, id
->SerialNumber
.pbData
, id
->SerialNumber
.cbData
);
1921 static DWORD
check_ocsp_response_info(const CERT_INFO
*cert
, const CERT_INFO
*issuer
,
1922 const CRYPT_OBJID_BLOB
*blob
, DWORD
*status
, FILETIME
*next_update
)
1924 OCSP_BASIC_RESPONSE_INFO
*info
;
1927 memset(next_update
, 0, sizeof(*next_update
));
1928 if (!CryptDecodeObjectEx(X509_ASN_ENCODING
, OCSP_BASIC_RESPONSE
, blob
->pbData
, blob
->cbData
,
1929 CRYPT_DECODE_ALLOC_FLAG
, NULL
, &info
, &size
)) return GetLastError();
1931 FIXME("check responder id\n");
1932 for (i
= 0; i
< info
->cResponseEntry
; i
++)
1934 OCSP_BASIC_RESPONSE_ENTRY
*entry
= &info
->rgResponseEntry
[i
];
1935 if (match_cert_id(&entry
->CertId
, cert
, issuer
))
1937 *status
= map_ocsp_status(entry
->dwCertStatus
);
1938 *next_update
= entry
->NextUpdate
;
1943 return ERROR_SUCCESS
;
1946 static DWORD
verify_signed_ocsp_response_info(const CERT_INFO
*cert
, const CERT_INFO
*issuer
,
1947 const CRYPT_OBJID_BLOB
*blob
, FILETIME
*next_update
)
1949 OCSP_BASIC_SIGNED_RESPONSE_INFO
*info
;
1950 DWORD size
, error
, status
= CRYPT_E_REVOCATION_OFFLINE
;
1951 CRYPT_ALGORITHM_IDENTIFIER
*alg
;
1952 CRYPT_BIT_BLOB
*sig
;
1953 HCRYPTPROV prov
= 0;
1954 HCRYPTHASH hash
= 0;
1958 if (!CryptDecodeObjectEx(X509_ASN_ENCODING
, OCSP_BASIC_SIGNED_RESPONSE
, blob
->pbData
, blob
->cbData
,
1959 CRYPT_DECODE_ALLOC_FLAG
, NULL
, &info
, &size
)) return GetLastError();
1961 if ((error
= check_ocsp_response_info(cert
, issuer
, &info
->ToBeSigned
, &status
, next_update
))) goto done
;
1963 alg
= &info
->SignatureInfo
.SignatureAlgorithm
;
1964 if (!alg
->pszObjId
|| !(algid
= CertOIDToAlgId(alg
->pszObjId
)))
1966 FIXME("unhandled signature algorithm %s\n", debugstr_a(alg
->pszObjId
));
1967 error
= CRYPT_E_NO_REVOCATION_CHECK
;
1971 if (!CryptAcquireContextW(&prov
, NULL
, NULL
, PROV_RSA_FULL
, CRYPT_VERIFYCONTEXT
)) goto done
;
1972 if (!CryptCreateHash(prov
, algid
, 0, 0, &hash
)) goto done
;
1973 if (!CryptHashData(hash
, info
->ToBeSigned
.pbData
, info
->ToBeSigned
.cbData
, 0)) goto done
;
1975 sig
= &info
->SignatureInfo
.Signature
;
1976 if (!CryptImportPublicKeyInfoEx(prov
, X509_ASN_ENCODING
, (CERT_PUBLIC_KEY_INFO
*)&issuer
->SubjectPublicKeyInfo
,
1979 error
= GetLastError();
1980 TRACE("failed to import public key %#lx\n", error
);
1982 else if (!CryptVerifySignatureW(hash
, sig
->pbData
, sig
->cbData
, key
, NULL
, 0))
1984 error
= GetLastError();
1985 TRACE("failed to verify signature %#lx\n", error
);
1987 else error
= ERROR_SUCCESS
;
1990 CryptDestroyKey(key
);
1991 CryptDestroyHash(hash
);
1992 CryptReleaseContext(prov
, 0);
1994 if (error
) return error
;
1998 static DWORD
handle_ocsp_response(const CERT_INFO
*cert
, const CERT_INFO
*issuer
, const BYTE
*encoded
,
1999 DWORD encoded_size
, FILETIME
*next_update
)
2001 OCSP_RESPONSE_INFO
*info
;
2002 DWORD size
, error
= CRYPT_E_NO_REVOCATION_CHECK
;
2004 if (!CryptDecodeObjectEx(X509_ASN_ENCODING
, OCSP_RESPONSE
, encoded
, encoded_size
, CRYPT_DECODE_ALLOC_FLAG
, NULL
,
2005 &info
, &size
)) return GetLastError();
2007 switch (info
->dwStatus
)
2009 case OCSP_SUCCESSFUL_RESPONSE
:
2010 if (!info
->pszObjId
|| strcmp(info
->pszObjId
, szOID_PKIX_OCSP_BASIC_SIGNED_RESPONSE
))
2012 FIXME("unhandled response type %s\n", debugstr_a(info
->pszObjId
));
2015 error
= verify_signed_ocsp_response_info(cert
, issuer
, &info
->Value
, next_update
);
2019 FIXME("unhandled status %lu\n", info
->dwStatus
);
2027 static DWORD
verify_cert_revocation_with_ocsp(const CERT_CONTEXT
*cert
, const WCHAR
*base_url
,
2028 const CERT_REVOCATION_PARA
*revpara
, FILETIME
*next_update
)
2030 HINTERNET ses
, con
, req
= NULL
;
2031 BYTE
*request_data
= NULL
, *response_data
= NULL
;
2032 DWORD size
, flags
, status
, request_len
, response_len
, count
, ret
= CRYPT_E_REVOCATION_OFFLINE
;
2033 URL_COMPONENTSW comp
;
2036 if (!revpara
|| !revpara
->pIssuerCert
)
2038 TRACE("no issuer certificate\n");
2039 return CRYPT_E_REVOCATION_OFFLINE
;
2041 if (!(request_data
= build_ocsp_request(cert
, revpara
->pIssuerCert
, &request_len
)))
2042 return CRYPT_E_REVOCATION_OFFLINE
;
2044 url
= build_request_url(base_url
, request_data
, request_len
);
2045 LocalFree(request_data
);
2046 if (!url
) return CRYPT_E_REVOCATION_OFFLINE
;
2048 memset(&comp
, 0, sizeof(comp
));
2049 comp
.dwStructSize
= sizeof(comp
);
2050 comp
.dwHostNameLength
= ~0u;
2051 comp
.dwUrlPathLength
= ~0u;
2052 if (!InternetCrackUrlW(url
, 0, 0, &comp
))
2055 return CRYPT_E_REVOCATION_OFFLINE
;
2058 switch (comp
.nScheme
)
2060 case INTERNET_SCHEME_HTTP
:
2063 case INTERNET_SCHEME_HTTPS
:
2064 flags
= INTERNET_FLAG_SECURE
;
2067 FIXME("scheme %u not supported\n", comp
.nScheme
);
2069 return ERROR_NOT_SUPPORTED
;
2072 if (!(ses
= InternetOpenW(L
"CryptoAPI", 0, NULL
, NULL
, 0))) return GetLastError();
2073 comp
.lpszHostName
[comp
.dwHostNameLength
] = 0;
2074 if (!(con
= InternetConnectW(ses
, comp
.lpszHostName
, comp
.nPort
, NULL
, NULL
, INTERNET_SERVICE_HTTP
, 0, 0)))
2077 InternetCloseHandle(ses
);
2078 return GetLastError();
2080 comp
.lpszHostName
[comp
.dwHostNameLength
] = '/';
2081 if (!(req
= HttpOpenRequestW(con
, NULL
, comp
.lpszUrlPath
, NULL
, NULL
, NULL
, flags
, 0)) ||
2082 !HttpSendRequestW(req
, NULL
, 0, NULL
, 0)) goto done
;
2084 size
= sizeof(status
);
2085 if (!HttpQueryInfoW(req
, HTTP_QUERY_STATUS_CODE
| HTTP_QUERY_FLAG_NUMBER
, &status
, &size
, NULL
)) goto done
;
2086 if (status
!= HTTP_STATUS_OK
)
2088 WARN("request status %lu\n", status
);
2092 size
= sizeof(response_len
);
2093 if (!HttpQueryInfoW(req
, HTTP_QUERY_FLAG_NUMBER
| HTTP_QUERY_CONTENT_LENGTH
, &response_len
, &size
, 0) ||
2094 !response_len
|| !(response_data
= malloc(response_len
)) ||
2095 !InternetReadFile(req
, response_data
, response_len
, &count
) || count
!= response_len
) goto done
;
2097 ret
= handle_ocsp_response(cert
->pCertInfo
, revpara
->pIssuerCert
->pCertInfo
, response_data
, response_len
,
2102 free(response_data
);
2103 InternetCloseHandle(req
);
2104 InternetCloseHandle(con
);
2105 InternetCloseHandle(ses
);
2109 static DWORD
verify_cert_revocation_from_aia_ext(const CRYPT_DATA_BLOB
*value
, const CERT_CONTEXT
*cert
,
2110 FILETIME
*pTime
, DWORD dwFlags
, CERT_REVOCATION_PARA
*pRevPara
, CERT_REVOCATION_STATUS
*pRevStatus
,
2111 FILETIME
*next_update
)
2114 DWORD size
, i
, error
= CRYPT_E_NO_REVOCATION_CHECK
;
2115 CERT_AUTHORITY_INFO_ACCESS
*aia
;
2117 ret
= CryptDecodeObjectEx(X509_ASN_ENCODING
, X509_AUTHORITY_INFO_ACCESS
, value
->pbData
, value
->cbData
,
2118 CRYPT_DECODE_ALLOC_FLAG
, NULL
, &aia
, &size
);
2119 if (!ret
) return GetLastError();
2121 for (i
= 0; i
< aia
->cAccDescr
; i
++)
2123 if (!strcmp(aia
->rgAccDescr
[i
].pszAccessMethod
, szOID_PKIX_OCSP
))
2125 if (aia
->rgAccDescr
[i
].AccessLocation
.dwAltNameChoice
== CERT_ALT_NAME_URL
)
2127 const WCHAR
*url
= aia
->rgAccDescr
[i
].AccessLocation
.pwszURL
;
2128 TRACE("OCSP URL = %s\n", debugstr_w(url
));
2129 error
= verify_cert_revocation_with_ocsp(cert
, url
, pRevPara
, next_update
);
2133 FIXME("unsupported AccessLocation type %lu\n", aia
->rgAccDescr
[i
].AccessLocation
.dwAltNameChoice
);
2134 error
= ERROR_NOT_SUPPORTED
;
2144 static DWORD
verify_cert_revocation_with_crl_offline(PCCERT_CONTEXT cert
,
2145 const CRL_CONTEXT
*crl
, FILETIME
*pTime
, CERT_REVOCATION_STATUS
*pRevStatus
)
2147 PCRL_ENTRY entry
= NULL
;
2150 valid
= CompareFileTime(pTime
, &crl
->pCrlInfo
->ThisUpdate
);
2153 /* If this CRL is not older than the time being verified, there's no
2154 * way to know whether the certificate was revoked.
2156 TRACE("CRL not old enough\n");
2157 return CRYPT_E_REVOCATION_OFFLINE
;
2160 CertFindCertificateInCRL(cert
, crl
, 0, NULL
, &entry
);
2162 return CRYPT_E_REVOKED
;
2164 /* Since the CRL was not retrieved for the cert being checked, there's no
2165 * guarantee it's fresh, so the cert *might* be okay, but it's safer not to
2167 TRACE("certificate not found\n");
2168 return CRYPT_E_REVOCATION_OFFLINE
;
2171 static DWORD
verify_cert_revocation(const CERT_CONTEXT
*cert
, FILETIME
*pTime
,
2172 DWORD dwFlags
, CERT_REVOCATION_PARA
*pRevPara
, CERT_REVOCATION_STATUS
*pRevStatus
)
2174 DWORD error
= ERROR_SUCCESS
;
2175 FILETIME next_update
= {0};
2176 PCERT_EXTENSION ext
;
2178 if (find_cached_revocation_status(cert
, pRevPara
, pTime
, pRevStatus
))
2180 if (pRevStatus
->dwError
== ERROR_SUCCESS
|| pRevStatus
->dwError
== CRYPT_E_REVOKED
)
2182 TRACE("Returning cached status.\n");
2183 return pRevStatus
->dwError
;
2187 if ((ext
= CertFindExtension(szOID_AUTHORITY_INFO_ACCESS
, cert
->pCertInfo
->cExtension
, cert
->pCertInfo
->rgExtension
)))
2189 error
= verify_cert_revocation_from_aia_ext(&ext
->Value
, cert
, pTime
, dwFlags
, pRevPara
, pRevStatus
,
2191 TRACE("verify_cert_revocation_from_aia_ext() returned %08lx\n", error
);
2192 if (error
== ERROR_SUCCESS
|| error
== CRYPT_E_REVOKED
) goto done
;
2194 if ((ext
= CertFindExtension(szOID_CRL_DIST_POINTS
, cert
->pCertInfo
->cExtension
, cert
->pCertInfo
->rgExtension
)))
2196 error
= verify_cert_revocation_from_dist_points_ext(&ext
->Value
, cert
, pTime
, dwFlags
, pRevPara
, pRevStatus
,
2198 TRACE("verify_cert_revocation_from_dist_points_ext() returned %08lx\n", error
);
2199 if (error
== ERROR_SUCCESS
|| error
== CRYPT_E_REVOKED
) goto done
;
2203 if (pRevPara
&& pRevPara
->hCrlStore
&& pRevPara
->pIssuerCert
)
2205 PCCRL_CONTEXT crl
= NULL
;
2208 /* If the caller told us about the issuer, make sure the issuer
2209 * can sign CRLs before looking for one.
2211 if ((ext
= CertFindExtension(szOID_KEY_USAGE
,
2212 pRevPara
->pIssuerCert
->pCertInfo
->cExtension
,
2213 pRevPara
->pIssuerCert
->pCertInfo
->rgExtension
)))
2215 CRYPT_BIT_BLOB usage
;
2216 DWORD size
= sizeof(usage
);
2218 if (!CryptDecodeObjectEx(cert
->dwCertEncodingType
, X509_BITS
,
2219 ext
->Value
.pbData
, ext
->Value
.cbData
,
2220 CRYPT_DECODE_NOCOPY_FLAG
, NULL
, &usage
, &size
))
2221 canSignCRLs
= FALSE
;
2222 else if (usage
.cbData
> 2)
2224 /* The key usage extension only defines 9 bits => no more
2225 * than 2 bytes are needed to encode all known usages.
2227 canSignCRLs
= FALSE
;
2231 BYTE usageBits
= usage
.pbData
[usage
.cbData
- 1];
2233 canSignCRLs
= usageBits
& CERT_CRL_SIGN_KEY_USAGE
;
2240 /* If the caller was helpful enough to tell us where to find a
2241 * CRL for the cert, look for one and check it.
2243 crl
= CertFindCRLInStore(pRevPara
->hCrlStore
,
2244 cert
->dwCertEncodingType
,
2245 CRL_FIND_ISSUED_BY_SIGNATURE_FLAG
|
2246 CRL_FIND_ISSUED_BY_AKI_FLAG
,
2247 CRL_FIND_ISSUED_BY
, pRevPara
->pIssuerCert
, NULL
);
2251 error
= verify_cert_revocation_with_crl_offline(cert
, crl
, pTime
, pRevStatus
);
2252 CertFreeCRLContext(crl
);
2256 TRACE("no CRL found\n");
2257 error
= CRYPT_E_NO_REVOCATION_CHECK
;
2263 WARN("no CERT_REVOCATION_PARA\n");
2264 else if (!pRevPara
->hCrlStore
)
2265 WARN("no dist points/aia extension and no CRL store\n");
2266 else if (!pRevPara
->pIssuerCert
)
2267 WARN("no dist points/aia extension and no issuer\n");
2268 error
= CRYPT_E_NO_REVOCATION_CHECK
;
2272 if ((next_update
.dwLowDateTime
|| next_update
.dwHighDateTime
)
2273 && (error
== ERROR_SUCCESS
|| error
== CRYPT_E_REVOKED
))
2275 CERT_REVOCATION_STATUS rev_status
;
2277 memset(&rev_status
, 0, sizeof(rev_status
));
2278 rev_status
.cbSize
= sizeof(rev_status
);
2279 rev_status
.dwError
= error
;
2280 cache_revocation_status(cert
, pRevPara
, &next_update
, &rev_status
);
2285 typedef struct _CERT_REVOCATION_PARA_NO_EXTRA_FIELDS
{
2287 PCCERT_CONTEXT pIssuerCert
;
2289 HCERTSTORE
*rgCertStore
;
2290 HCERTSTORE hCrlStore
;
2291 LPFILETIME pftTimeToUse
;
2292 } CERT_REVOCATION_PARA_NO_EXTRA_FIELDS
;
2294 typedef struct _OLD_CERT_REVOCATION_STATUS
{
2299 } OLD_CERT_REVOCATION_STATUS
;
2301 /***********************************************************************
2302 * CertDllVerifyRevocation (CRYPTNET.@)
2304 BOOL WINAPI
CertDllVerifyRevocation(DWORD dwEncodingType
, DWORD dwRevType
,
2305 DWORD cContext
, PVOID rgpvContext
[], DWORD dwFlags
,
2306 PCERT_REVOCATION_PARA pRevPara
, PCERT_REVOCATION_STATUS pRevStatus
)
2310 LPFILETIME pTime
= NULL
;
2312 TRACE("(%08lx, %ld, %ld, %p, %08lx, %p, %p)\n", dwEncodingType
, dwRevType
,
2313 cContext
, rgpvContext
, dwFlags
, pRevPara
, pRevStatus
);
2315 if (pRevStatus
->cbSize
!= sizeof(OLD_CERT_REVOCATION_STATUS
) &&
2316 pRevStatus
->cbSize
!= sizeof(CERT_REVOCATION_STATUS
))
2318 SetLastError(E_INVALIDARG
);
2323 SetLastError(E_INVALIDARG
);
2326 if (pRevPara
&& pRevPara
->cbSize
>=
2327 sizeof(CERT_REVOCATION_PARA_NO_EXTRA_FIELDS
))
2328 pTime
= pRevPara
->pftTimeToUse
;
2331 GetSystemTimeAsFileTime(&now
);
2334 memset(&pRevStatus
->dwIndex
, 0, pRevStatus
->cbSize
- sizeof(DWORD
));
2335 if (dwRevType
!= CERT_CONTEXT_REVOCATION_TYPE
)
2336 error
= CRYPT_E_NO_REVOCATION_CHECK
;
2339 for (i
= 0; i
< cContext
; i
++)
2341 if ((error
= verify_cert_revocation(rgpvContext
[i
], pTime
, dwFlags
, pRevPara
, pRevStatus
)))
2343 pRevStatus
->dwIndex
= i
;
2350 SetLastError(error
);
2351 pRevStatus
->dwError
= error
;
2353 TRACE("returning %d (%08lx)\n", !error
, error
);