You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1938 lines
63 KiB
1938 lines
63 KiB
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1995 - 1999
|
|
//
|
|
// File: policy.cpp
|
|
//
|
|
// Contents: Certificate Chain Policy APIs
|
|
//
|
|
// Functions: CertChainPolicyDllMain
|
|
// CertVerifyCertificateChainPolicy
|
|
// CertDllVerifyBaseCertificateChainPolicy
|
|
// CertDllVerifyBasicConstraintsCertificateChainPolicy
|
|
// CertDllVerifyAuthenticodeCertificateChainPolicy
|
|
// CertDllVerifyAuthenticodeTimeStampCertificateChainPolicy
|
|
// CertDllVerifySSLCertificateChainPolicy
|
|
// CertDllVerifyNTAuthCertificateChainPolicy
|
|
// CertDllVerifyMicrosoftRootCertificateChainPolicy
|
|
//
|
|
// History: 16-Feb-98 philh created
|
|
//--------------------------------------------------------------------------
|
|
|
|
|
|
#include "global.hxx"
|
|
#include <dbgdef.h>
|
|
#include "wintrust.h"
|
|
#include "softpub.h"
|
|
|
|
#include "wininet.h"
|
|
#ifndef SECURITY_FLAG_IGNORE_REVOCATION
|
|
# define SECURITY_FLAG_IGNORE_REVOCATION 0x00000080
|
|
# define SECURITY_FLAG_IGNORE_UNKNOWN_CA 0x00000100
|
|
#endif
|
|
|
|
#ifndef SECURITY_FLAG_IGNORE_WRONG_USAGE
|
|
# define SECURITY_FLAG_IGNORE_WRONG_USAGE 0x00000200
|
|
#endif
|
|
|
|
|
|
#define INVALID_NAME_ERROR_STATUS ( \
|
|
CERT_TRUST_INVALID_NAME_CONSTRAINTS | \
|
|
CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT | \
|
|
CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT | \
|
|
CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT | \
|
|
CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT \
|
|
)
|
|
|
|
#define INVALID_POLICY_ERROR_STATUS ( \
|
|
CERT_TRUST_INVALID_POLICY_CONSTRAINTS | \
|
|
CERT_TRUST_NO_ISSUANCE_CHAIN_POLICY \
|
|
)
|
|
|
|
BOOL fWildcardsEnabledInSslServerCerts = TRUE;
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Global cert policy critical section.
|
|
//--------------------------------------------------------------------------
|
|
static CRITICAL_SECTION CertPolicyCriticalSection;
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Cached certificate store used for NTAuth certificate chain policy.
|
|
//--------------------------------------------------------------------------
|
|
static HCERTSTORE hNTAuthCertStore = NULL;
|
|
|
|
//
|
|
// support for MS test roots!!!!
|
|
//
|
|
static BYTE rgbTestRoot[] =
|
|
{
|
|
#include "mstest1.h"
|
|
};
|
|
|
|
static BYTE rgbTestRootCorrected[] =
|
|
{
|
|
#include "mstest2.h"
|
|
};
|
|
|
|
static BYTE rgbTestRootBeta1[] =
|
|
{
|
|
#include "mstestb1.h"
|
|
};
|
|
|
|
static CERT_PUBLIC_KEY_INFO rgTestRootPublicKeyInfo[] =
|
|
{
|
|
{szOID_RSA_RSA, 0, NULL, sizeof(rgbTestRoot), rgbTestRoot, 0},
|
|
{szOID_RSA_RSA, 0, NULL,
|
|
sizeof(rgbTestRootCorrected), rgbTestRootCorrected, 0},
|
|
{szOID_RSA_RSA, 0, NULL, sizeof(rgbTestRootBeta1), rgbTestRootBeta1, 0}
|
|
};
|
|
#define NTESTROOTS (sizeof(rgTestRootPublicKeyInfo)/ \
|
|
sizeof(rgTestRootPublicKeyInfo[0]))
|
|
|
|
HCRYPTOIDFUNCSET hChainPolicyFuncSet;
|
|
|
|
typedef BOOL (WINAPI *PFN_CHAIN_POLICY_FUNC) (
|
|
IN LPCSTR pszPolicyOID,
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
IN OUT PCERT_CHAIN_POLICY_STATUS pPolicyStatus
|
|
);
|
|
|
|
BOOL
|
|
WINAPI
|
|
CertDllVerifyBaseCertificateChainPolicy(
|
|
IN LPCSTR pszPolicyOID,
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
IN OUT PCERT_CHAIN_POLICY_STATUS pPolicyStatus
|
|
);
|
|
|
|
BOOL
|
|
WINAPI
|
|
CertDllVerifyAuthenticodeCertificateChainPolicy(
|
|
IN LPCSTR pszPolicyOID,
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
IN OUT PCERT_CHAIN_POLICY_STATUS pPolicyStatus
|
|
);
|
|
|
|
BOOL
|
|
WINAPI
|
|
CertDllVerifyAuthenticodeTimeStampCertificateChainPolicy(
|
|
IN LPCSTR pszPolicyOID,
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
IN OUT PCERT_CHAIN_POLICY_STATUS pPolicyStatus
|
|
);
|
|
|
|
BOOL
|
|
WINAPI
|
|
CertDllVerifySSLCertificateChainPolicy(
|
|
IN LPCSTR pszPolicyOID,
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
IN OUT PCERT_CHAIN_POLICY_STATUS pPolicyStatus
|
|
);
|
|
|
|
BOOL
|
|
WINAPI
|
|
CertDllVerifyBasicConstraintsCertificateChainPolicy(
|
|
IN LPCSTR pszPolicyOID,
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
IN OUT PCERT_CHAIN_POLICY_STATUS pPolicyStatus
|
|
);
|
|
|
|
BOOL
|
|
WINAPI
|
|
CertDllVerifyNTAuthCertificateChainPolicy(
|
|
IN LPCSTR pszPolicyOID,
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
IN OUT PCERT_CHAIN_POLICY_STATUS pPolicyStatus
|
|
);
|
|
|
|
BOOL
|
|
WINAPI
|
|
CertDllVerifyMicrosoftRootCertificateChainPolicy(
|
|
IN LPCSTR pszPolicyOID,
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
IN OUT PCERT_CHAIN_POLICY_STATUS pPolicyStatus
|
|
);
|
|
|
|
static const CRYPT_OID_FUNC_ENTRY ChainPolicyFuncTable[] = {
|
|
CERT_CHAIN_POLICY_BASE, CertDllVerifyBaseCertificateChainPolicy,
|
|
CERT_CHAIN_POLICY_AUTHENTICODE,
|
|
CertDllVerifyAuthenticodeCertificateChainPolicy,
|
|
CERT_CHAIN_POLICY_AUTHENTICODE_TS,
|
|
CertDllVerifyAuthenticodeTimeStampCertificateChainPolicy,
|
|
CERT_CHAIN_POLICY_SSL,
|
|
CertDllVerifySSLCertificateChainPolicy,
|
|
CERT_CHAIN_POLICY_BASIC_CONSTRAINTS,
|
|
CertDllVerifyBasicConstraintsCertificateChainPolicy,
|
|
CERT_CHAIN_POLICY_NT_AUTH,
|
|
CertDllVerifyNTAuthCertificateChainPolicy,
|
|
CERT_CHAIN_POLICY_MICROSOFT_ROOT,
|
|
CertDllVerifyMicrosoftRootCertificateChainPolicy,
|
|
};
|
|
|
|
#define CHAIN_POLICY_FUNC_COUNT (sizeof(ChainPolicyFuncTable) / \
|
|
sizeof(ChainPolicyFuncTable[0]))
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
CertChainPolicyDllMain(
|
|
HMODULE hInst,
|
|
ULONG ulReason,
|
|
LPVOID lpReserved)
|
|
{
|
|
BOOL fRet;
|
|
|
|
switch (ulReason) {
|
|
case DLL_PROCESS_ATTACH:
|
|
if (NULL == (hChainPolicyFuncSet = CryptInitOIDFunctionSet(
|
|
CRYPT_OID_VERIFY_CERTIFICATE_CHAIN_POLICY_FUNC,
|
|
0)))
|
|
goto CryptInitOIDFunctionSetError;
|
|
|
|
if (!CryptInstallOIDFunctionAddress(
|
|
NULL, // hModule
|
|
0, // dwEncodingType
|
|
CRYPT_OID_VERIFY_CERTIFICATE_CHAIN_POLICY_FUNC,
|
|
CHAIN_POLICY_FUNC_COUNT,
|
|
ChainPolicyFuncTable,
|
|
0)) // dwFlags
|
|
goto CryptInstallOIDFunctionAddressError;
|
|
|
|
if (!Pki_InitializeCriticalSection(&CertPolicyCriticalSection))
|
|
goto InitCritSectionError;
|
|
break;
|
|
|
|
case DLL_PROCESS_DETACH:
|
|
DeleteCriticalSection(&CertPolicyCriticalSection);
|
|
if (hNTAuthCertStore)
|
|
CertCloseStore(hNTAuthCertStore, 0);
|
|
break;
|
|
|
|
case DLL_THREAD_DETACH:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
fRet = TRUE;
|
|
CommonReturn:
|
|
return fRet;
|
|
|
|
ErrorReturn:
|
|
fRet = FALSE;
|
|
goto CommonReturn;
|
|
TRACE_ERROR(InitCritSectionError)
|
|
TRACE_ERROR(CryptInitOIDFunctionSetError)
|
|
TRACE_ERROR(CryptInstallOIDFunctionAddressError)
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Lock and unlock global cert policy functions
|
|
//--------------------------------------------------------------------------
|
|
static inline void CertPolicyLock()
|
|
{
|
|
EnterCriticalSection(&CertPolicyCriticalSection);
|
|
}
|
|
static inline void CertPolicyUnlock()
|
|
{
|
|
LeaveCriticalSection(&CertPolicyCriticalSection);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Verify that the certificate chain satisfies the specified policy
|
|
// requirements. If we were able to verify the chain policy, TRUE is returned
|
|
// and the dwError field of the pPolicyStatus is updated. A dwError of 0
|
|
// (ERROR_SUCCESS, S_OK) indicates the chain satisfies the specified policy.
|
|
//
|
|
// If dwError applies to the entire chain context, both lChainIndex and
|
|
// lElementIndex are set to -1. If dwError applies to a simple chain,
|
|
// lElementIndex is set to -1 and lChainIndex is set to the index of the
|
|
// first offending chain having the error. If dwError applies to a
|
|
// certificate element, lChainIndex and lElementIndex are updated to
|
|
// index the first offending certificate having the error, where, the
|
|
// the certificate element is at:
|
|
// pChainContext->rgpChain[lChainIndex]->rgpElement[lElementIndex].
|
|
//
|
|
// The dwFlags in pPolicyPara can be set to change the default policy checking
|
|
// behaviour. In addition, policy specific parameters can be passed in
|
|
// the pvExtraPolicyPara field of pPolicyPara.
|
|
//
|
|
// In addition to returning dwError, in pPolicyStatus, policy OID specific
|
|
// extra status may be returned via pvExtraPolicyStatus.
|
|
//--------------------------------------------------------------------------
|
|
BOOL
|
|
WINAPI
|
|
CertVerifyCertificateChainPolicy(
|
|
IN LPCSTR pszPolicyOID,
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
IN OUT PCERT_CHAIN_POLICY_STATUS pPolicyStatus
|
|
)
|
|
{
|
|
BOOL fResult;
|
|
void *pvFuncAddr;
|
|
HCRYPTOIDFUNCADDR hFuncAddr = NULL;
|
|
|
|
assert(pPolicyStatus && offsetof(CERT_CHAIN_POLICY_STATUS, lElementIndex) <
|
|
pPolicyStatus->cbSize);
|
|
pPolicyStatus->dwError = 0;
|
|
pPolicyStatus->lChainIndex = -1;
|
|
pPolicyStatus->lElementIndex = -1;
|
|
|
|
if (!CryptGetOIDFunctionAddress(
|
|
hChainPolicyFuncSet,
|
|
0, // dwEncodingType,
|
|
pszPolicyOID,
|
|
0, // dwFlags
|
|
&pvFuncAddr,
|
|
&hFuncAddr))
|
|
goto GetOIDFuncAddrError;
|
|
|
|
fResult = ((PFN_CHAIN_POLICY_FUNC) pvFuncAddr)(
|
|
pszPolicyOID,
|
|
pChainContext,
|
|
pPolicyPara,
|
|
pPolicyStatus
|
|
);
|
|
CryptFreeOIDFunctionAddress(hFuncAddr, 0);
|
|
CommonReturn:
|
|
return fResult;
|
|
ErrorReturn:
|
|
fResult = FALSE;
|
|
goto CommonReturn;
|
|
TRACE_ERROR(GetOIDFuncAddrError)
|
|
}
|
|
|
|
|
|
static inline PCERT_CHAIN_ELEMENT GetRootChainElement(
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext
|
|
)
|
|
{
|
|
DWORD dwRootChainIndex = pChainContext->cChain - 1;
|
|
DWORD dwRootElementIndex =
|
|
pChainContext->rgpChain[dwRootChainIndex]->cElement - 1;
|
|
|
|
return pChainContext->rgpChain[dwRootChainIndex]->
|
|
rgpElement[dwRootElementIndex];
|
|
}
|
|
|
|
void GetElementIndexOfFirstError(
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN DWORD dwErrorStatusMask,
|
|
OUT LONG *plChainIndex,
|
|
OUT LONG *plElementIndex
|
|
)
|
|
{
|
|
DWORD i;
|
|
for (i = 0; i < pChainContext->cChain; i++) {
|
|
DWORD j;
|
|
PCERT_SIMPLE_CHAIN pChain = pChainContext->rgpChain[i];
|
|
|
|
for (j = 0; j < pChain->cElement; j++) {
|
|
PCERT_CHAIN_ELEMENT pEle = pChain->rgpElement[j];
|
|
|
|
if (pEle->TrustStatus.dwErrorStatus & dwErrorStatusMask) {
|
|
*plChainIndex = (LONG) i;
|
|
*plElementIndex = (LONG) j;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
*plChainIndex = -1;
|
|
*plElementIndex = -1;
|
|
}
|
|
|
|
void GetChainIndexOfFirstError(
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN DWORD dwErrorStatusMask,
|
|
OUT LONG *plChainIndex
|
|
)
|
|
{
|
|
DWORD i;
|
|
for (i = 0; i < pChainContext->cChain; i++) {
|
|
PCERT_SIMPLE_CHAIN pChain = pChainContext->rgpChain[i];
|
|
|
|
if (pChain->TrustStatus.dwErrorStatus & dwErrorStatusMask) {
|
|
*plChainIndex = (LONG) i;
|
|
return;
|
|
}
|
|
}
|
|
|
|
*plChainIndex = -1;
|
|
}
|
|
|
|
|
|
//+=========================================================================
|
|
// CertDllVerifyBaseCertificateChainPolicy Functions
|
|
//==========================================================================
|
|
|
|
BOOL
|
|
WINAPI
|
|
CertDllVerifyBaseCertificateChainPolicy(
|
|
IN LPCSTR pszPolicyOID,
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
IN OUT PCERT_CHAIN_POLICY_STATUS pPolicyStatus
|
|
)
|
|
{
|
|
DWORD dwError;
|
|
DWORD dwFlags;
|
|
DWORD dwContextError;
|
|
LONG lChainIndex = -1;
|
|
LONG lElementIndex = -1;
|
|
DWORD dwErrorStatusMask;
|
|
|
|
dwContextError = pChainContext->TrustStatus.dwErrorStatus;
|
|
|
|
|
|
if (0 == dwContextError) {
|
|
// Valid chain
|
|
dwError = 0;
|
|
goto CommonReturn;
|
|
}
|
|
|
|
if (pPolicyPara && offsetof(CERT_CHAIN_POLICY_PARA, dwFlags) <
|
|
pPolicyPara->cbSize)
|
|
dwFlags = pPolicyPara->dwFlags;
|
|
else
|
|
dwFlags = 0;
|
|
|
|
if (dwContextError &
|
|
(CERT_TRUST_IS_NOT_SIGNATURE_VALID |
|
|
CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID)) {
|
|
dwError = (DWORD) TRUST_E_CERT_SIGNATURE;
|
|
dwErrorStatusMask =
|
|
CERT_TRUST_IS_NOT_SIGNATURE_VALID |
|
|
CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID;
|
|
if (dwErrorStatusMask & CERT_TRUST_IS_NOT_SIGNATURE_VALID)
|
|
goto GetElementIndexReturn;
|
|
else
|
|
goto GetChainIndexReturn;
|
|
}
|
|
|
|
if (dwContextError & CERT_TRUST_IS_UNTRUSTED_ROOT) {
|
|
dwErrorStatusMask = CERT_TRUST_IS_UNTRUSTED_ROOT;
|
|
if (dwFlags & CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG) {
|
|
;
|
|
} else if (0 == (dwFlags & CERT_CHAIN_POLICY_ALLOW_TESTROOT_FLAG)) {
|
|
dwError = (DWORD) CERT_E_UNTRUSTEDROOT;
|
|
goto GetElementIndexReturn;
|
|
} else {
|
|
// Check if one of the "test" roots
|
|
DWORD i;
|
|
BOOL fTestRoot;
|
|
PCERT_CHAIN_ELEMENT pRootElement;
|
|
PCCERT_CONTEXT pRootCert;
|
|
|
|
pRootElement = GetRootChainElement(pChainContext);
|
|
assert(pRootElement->TrustStatus.dwInfoStatus &
|
|
CERT_TRUST_IS_SELF_SIGNED);
|
|
pRootCert = pRootElement->pCertContext;
|
|
|
|
fTestRoot = FALSE;
|
|
for (i = 0; i < NTESTROOTS; i++) {
|
|
if (CertComparePublicKeyInfo(
|
|
pRootCert->dwCertEncodingType,
|
|
&pRootCert->pCertInfo->SubjectPublicKeyInfo,
|
|
&rgTestRootPublicKeyInfo[i])) {
|
|
fTestRoot = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (fTestRoot) {
|
|
if (0 == (dwFlags & CERT_CHAIN_POLICY_TRUST_TESTROOT_FLAG)) {
|
|
dwError = (DWORD) CERT_E_UNTRUSTEDTESTROOT;
|
|
goto GetElementIndexReturn;
|
|
}
|
|
} else {
|
|
dwError = (DWORD) CERT_E_UNTRUSTEDROOT;
|
|
goto GetElementIndexReturn;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dwContextError & CERT_TRUST_IS_PARTIAL_CHAIN) {
|
|
if (0 == (dwFlags & CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG)) {
|
|
dwError = (DWORD) CERT_E_CHAINING;
|
|
dwErrorStatusMask = CERT_TRUST_IS_PARTIAL_CHAIN;
|
|
goto GetChainIndexReturn;
|
|
}
|
|
}
|
|
|
|
if (dwContextError & CERT_TRUST_IS_REVOKED) {
|
|
dwError = (DWORD) CRYPT_E_REVOKED;
|
|
dwErrorStatusMask = CERT_TRUST_IS_REVOKED;
|
|
goto GetElementIndexReturn;
|
|
}
|
|
|
|
if (dwContextError & (CERT_TRUST_IS_NOT_VALID_FOR_USAGE |
|
|
CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE)) {
|
|
if (0 == (dwFlags & CERT_CHAIN_POLICY_IGNORE_WRONG_USAGE_FLAG)) {
|
|
dwError = (DWORD) CERT_E_WRONG_USAGE;
|
|
dwErrorStatusMask = CERT_TRUST_IS_NOT_VALID_FOR_USAGE |
|
|
CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE;
|
|
if (dwContextError & CERT_TRUST_IS_NOT_VALID_FOR_USAGE)
|
|
goto GetElementIndexReturn;
|
|
else
|
|
goto GetChainIndexReturn;
|
|
}
|
|
}
|
|
|
|
if (dwContextError & CERT_TRUST_IS_NOT_TIME_VALID) {
|
|
if (0 == (dwFlags & CERT_CHAIN_POLICY_IGNORE_NOT_TIME_VALID_FLAG)) {
|
|
dwError = (DWORD) CERT_E_EXPIRED;
|
|
dwErrorStatusMask = CERT_TRUST_IS_NOT_TIME_VALID;
|
|
goto GetElementIndexReturn;
|
|
}
|
|
}
|
|
|
|
if (dwContextError & CERT_TRUST_CTL_IS_NOT_TIME_VALID) {
|
|
if (0 == (dwFlags & CERT_CHAIN_POLICY_IGNORE_CTL_NOT_TIME_VALID_FLAG)) {
|
|
dwErrorStatusMask = CERT_TRUST_CTL_IS_NOT_TIME_VALID;
|
|
dwError = (DWORD) CERT_E_EXPIRED;
|
|
goto GetChainIndexReturn;
|
|
}
|
|
}
|
|
|
|
if (dwContextError & INVALID_NAME_ERROR_STATUS) {
|
|
if (0 == (dwFlags & CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG)) {
|
|
dwError = (DWORD) CERT_E_INVALID_NAME;
|
|
dwErrorStatusMask = INVALID_NAME_ERROR_STATUS;
|
|
goto GetElementIndexReturn;
|
|
}
|
|
}
|
|
|
|
if (dwContextError & INVALID_POLICY_ERROR_STATUS) {
|
|
if (0 == (dwFlags & CERT_CHAIN_POLICY_IGNORE_INVALID_POLICY_FLAG)) {
|
|
dwError = (DWORD) CERT_E_INVALID_POLICY;
|
|
dwErrorStatusMask = INVALID_POLICY_ERROR_STATUS;
|
|
goto GetElementIndexReturn;
|
|
}
|
|
}
|
|
|
|
if (dwContextError & CERT_TRUST_INVALID_BASIC_CONSTRAINTS) {
|
|
if (0 == (dwFlags &
|
|
CERT_CHAIN_POLICY_IGNORE_INVALID_BASIC_CONSTRAINTS_FLAG)) {
|
|
dwError = (DWORD) TRUST_E_BASIC_CONSTRAINTS;
|
|
dwErrorStatusMask = CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
|
|
goto GetElementIndexReturn;
|
|
}
|
|
}
|
|
|
|
if (dwContextError & CERT_TRUST_IS_NOT_TIME_NESTED) {
|
|
if (0 == (dwFlags & CERT_CHAIN_POLICY_IGNORE_NOT_TIME_NESTED_FLAG)) {
|
|
dwErrorStatusMask = CERT_TRUST_IS_NOT_TIME_NESTED;
|
|
dwError = (DWORD) CERT_E_VALIDITYPERIODNESTING;
|
|
goto GetElementIndexReturn;
|
|
}
|
|
}
|
|
|
|
dwError = 0;
|
|
|
|
// Note, OFFLINE takes precedence over NO_CHECK
|
|
|
|
if (dwContextError & CERT_TRUST_REVOCATION_STATUS_UNKNOWN) {
|
|
if ((dwFlags & CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS) !=
|
|
CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS) {
|
|
DWORD i;
|
|
for (i = 0; i < pChainContext->cChain; i++) {
|
|
DWORD j;
|
|
PCERT_SIMPLE_CHAIN pChain = pChainContext->rgpChain[i];
|
|
|
|
for (j = 0; j < pChain->cElement; j++) {
|
|
PCERT_CHAIN_ELEMENT pEle = pChain->rgpElement[j];
|
|
DWORD dwEleError = pEle->TrustStatus.dwErrorStatus;
|
|
DWORD dwEleInfo = pEle->TrustStatus.dwInfoStatus;
|
|
DWORD dwRevokeError;
|
|
BOOL fEnableRevokeError;
|
|
|
|
if (0 == (dwEleError &
|
|
CERT_TRUST_REVOCATION_STATUS_UNKNOWN))
|
|
continue;
|
|
|
|
assert(pEle->pRevocationInfo);
|
|
dwRevokeError = pEle->pRevocationInfo->dwRevocationResult;
|
|
if (CRYPT_E_NO_REVOCATION_CHECK != dwRevokeError)
|
|
dwRevokeError = (DWORD) CRYPT_E_REVOCATION_OFFLINE;
|
|
fEnableRevokeError = FALSE;
|
|
|
|
if (dwEleInfo & CERT_TRUST_IS_SELF_SIGNED) {
|
|
// Chain Root
|
|
if (0 == (dwFlags &
|
|
CERT_CHAIN_POLICY_IGNORE_ROOT_REV_UNKNOWN_FLAG)) {
|
|
fEnableRevokeError = TRUE;
|
|
}
|
|
} else if (0 == i && 0 == j) {
|
|
// End certificate
|
|
if (0 == (dwFlags &
|
|
CERT_CHAIN_POLICY_IGNORE_END_REV_UNKNOWN_FLAG)) {
|
|
fEnableRevokeError = TRUE;
|
|
}
|
|
} else if (0 == j) {
|
|
// CTL signer certificate
|
|
if (0 ==
|
|
(dwFlags & CERT_CHAIN_POLICY_IGNORE_CTL_SIGNER_REV_UNKNOWN_FLAG)) {
|
|
fEnableRevokeError = TRUE;
|
|
}
|
|
} else {
|
|
// CA certificate
|
|
if (0 ==
|
|
(dwFlags & CERT_CHAIN_POLICY_IGNORE_CA_REV_UNKNOWN_FLAG)) {
|
|
fEnableRevokeError = TRUE;
|
|
}
|
|
}
|
|
|
|
if (fEnableRevokeError) {
|
|
if (0 == dwError ||
|
|
CRYPT_E_REVOCATION_OFFLINE == dwRevokeError) {
|
|
dwError = dwRevokeError;
|
|
lChainIndex = (LONG) i;
|
|
lElementIndex = (LONG) j;
|
|
|
|
if (CRYPT_E_REVOCATION_OFFLINE == dwError)
|
|
goto CommonReturn;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
CommonReturn:
|
|
assert(pPolicyStatus && offsetof(CERT_CHAIN_POLICY_STATUS, lElementIndex) <
|
|
pPolicyStatus->cbSize);
|
|
|
|
pPolicyStatus->dwError = dwError;
|
|
pPolicyStatus->lChainIndex = lChainIndex;
|
|
pPolicyStatus->lElementIndex = lElementIndex;
|
|
return TRUE;
|
|
|
|
GetElementIndexReturn:
|
|
GetElementIndexOfFirstError(pChainContext, dwErrorStatusMask,
|
|
&lChainIndex, &lElementIndex);
|
|
goto CommonReturn;
|
|
|
|
GetChainIndexReturn:
|
|
GetChainIndexOfFirstError(pChainContext, dwErrorStatusMask,
|
|
&lChainIndex);
|
|
goto CommonReturn;
|
|
}
|
|
|
|
//+=========================================================================
|
|
// CertDllVerifyBasicConstraintsCertificateChainPolicy Functions
|
|
//==========================================================================
|
|
|
|
// If dwFlags is 0, allow either CA or END_ENTITY for dwEleIndex == 0
|
|
BOOL CheckChainElementBasicConstraints(
|
|
IN PCERT_CHAIN_ELEMENT pEle,
|
|
IN DWORD dwEleIndex,
|
|
IN DWORD dwFlags
|
|
)
|
|
{
|
|
BOOL fResult;
|
|
|
|
PCERT_INFO pCertInfo = pEle->pCertContext->pCertInfo;
|
|
PCERT_EXTENSION pExt;
|
|
PCERT_BASIC_CONSTRAINTS_INFO pInfo = NULL;
|
|
PCERT_BASIC_CONSTRAINTS2_INFO pInfo2 = NULL;
|
|
DWORD cbInfo;
|
|
|
|
BOOL fCA;
|
|
BOOL fEndEntity;
|
|
|
|
if (pEle->TrustStatus.dwErrorStatus &
|
|
CERT_TRUST_INVALID_BASIC_CONSTRAINTS)
|
|
goto ChainBasicContraintsError;
|
|
|
|
if (0 == dwFlags || 0 != dwEleIndex || 0 == pCertInfo->cExtension)
|
|
goto SuccessReturn;
|
|
// else
|
|
// Only need to do additional checking to see if the end cert is
|
|
// a CA or END_ENTITY.
|
|
|
|
if (pExt = CertFindExtension(
|
|
szOID_BASIC_CONSTRAINTS2,
|
|
pCertInfo->cExtension,
|
|
pCertInfo->rgExtension
|
|
)) {
|
|
if (!CryptDecodeObjectEx(
|
|
pEle->pCertContext->dwCertEncodingType,
|
|
X509_BASIC_CONSTRAINTS2,
|
|
pExt->Value.pbData,
|
|
pExt->Value.cbData,
|
|
CRYPT_DECODE_NOCOPY_FLAG | CRYPT_DECODE_ALLOC_FLAG |
|
|
CRYPT_DECODE_SHARE_OID_STRING_FLAG,
|
|
&PkiDecodePara,
|
|
(void *) &pInfo2,
|
|
&cbInfo
|
|
)) {
|
|
if (pExt->fCritical)
|
|
goto DecodeError;
|
|
else
|
|
goto SuccessReturn;
|
|
}
|
|
fCA = pInfo2->fCA;
|
|
fEndEntity = !fCA;
|
|
} else if (pExt = CertFindExtension(
|
|
szOID_BASIC_CONSTRAINTS,
|
|
pCertInfo->cExtension,
|
|
pCertInfo->rgExtension
|
|
)) {
|
|
if (!CryptDecodeObjectEx(
|
|
pEle->pCertContext->dwCertEncodingType,
|
|
X509_BASIC_CONSTRAINTS,
|
|
pExt->Value.pbData,
|
|
pExt->Value.cbData,
|
|
CRYPT_DECODE_NOCOPY_FLAG | CRYPT_DECODE_ALLOC_FLAG |
|
|
CRYPT_DECODE_SHARE_OID_STRING_FLAG,
|
|
&PkiDecodePara,
|
|
(void *) &pInfo,
|
|
&cbInfo
|
|
)) {
|
|
if (pExt->fCritical)
|
|
goto DecodeError;
|
|
else
|
|
goto SuccessReturn;
|
|
}
|
|
if (pExt->fCritical && pInfo->cSubtreesConstraint)
|
|
goto SubtreesError;
|
|
|
|
if (pInfo->SubjectType.cbData > 0) {
|
|
BYTE bRole = pInfo->SubjectType.pbData[0];
|
|
fCA = (0 != (bRole & CERT_CA_SUBJECT_FLAG));
|
|
fEndEntity = (0 != (bRole & CERT_END_ENTITY_SUBJECT_FLAG));
|
|
} else {
|
|
fCA = FALSE;
|
|
fEndEntity = FALSE;
|
|
}
|
|
} else
|
|
goto SuccessReturn;
|
|
|
|
|
|
if (dwFlags & BASIC_CONSTRAINTS_CERT_CHAIN_POLICY_END_ENTITY_FLAG) {
|
|
if (!fEndEntity)
|
|
goto NotAnEndEntity;
|
|
}
|
|
if (dwFlags & BASIC_CONSTRAINTS_CERT_CHAIN_POLICY_CA_FLAG) {
|
|
if (!fCA)
|
|
goto NotACA;
|
|
}
|
|
|
|
SuccessReturn:
|
|
fResult = TRUE;
|
|
CommonReturn:
|
|
PkiFree(pInfo);
|
|
PkiFree(pInfo2);
|
|
return fResult;
|
|
ErrorReturn:
|
|
fResult = FALSE;
|
|
goto CommonReturn;
|
|
|
|
TRACE_ERROR(ChainBasicContraintsError)
|
|
TRACE_ERROR(NotACA)
|
|
TRACE_ERROR(NotAnEndEntity)
|
|
TRACE_ERROR(SubtreesError)
|
|
TRACE_ERROR(DecodeError)
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
CertDllVerifyBasicConstraintsCertificateChainPolicy(
|
|
IN LPCSTR pszPolicyOID,
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
IN OUT PCERT_CHAIN_POLICY_STATUS pPolicyStatus
|
|
)
|
|
{
|
|
DWORD dwFlags;
|
|
|
|
if (pPolicyPara && offsetof(CERT_CHAIN_POLICY_PARA, dwFlags) <
|
|
pPolicyPara->cbSize) {
|
|
dwFlags = pPolicyPara->dwFlags;
|
|
dwFlags &= (BASIC_CONSTRAINTS_CERT_CHAIN_POLICY_CA_FLAG |
|
|
BASIC_CONSTRAINTS_CERT_CHAIN_POLICY_END_ENTITY_FLAG);
|
|
if (dwFlags == (BASIC_CONSTRAINTS_CERT_CHAIN_POLICY_CA_FLAG |
|
|
BASIC_CONSTRAINTS_CERT_CHAIN_POLICY_END_ENTITY_FLAG))
|
|
dwFlags = 0; // 0 => allow CA or END_ENTITY
|
|
} else
|
|
dwFlags = 0;
|
|
|
|
DWORD i;
|
|
for (i = 0; i < pChainContext->cChain; i++) {
|
|
DWORD j;
|
|
PCERT_SIMPLE_CHAIN pChain = pChainContext->rgpChain[i];
|
|
|
|
for (j = 0; j < pChain->cElement; j++) {
|
|
if (!CheckChainElementBasicConstraints(pChain->rgpElement[j], j,
|
|
dwFlags)) {
|
|
assert(pPolicyStatus && offsetof(CERT_CHAIN_POLICY_STATUS,
|
|
lElementIndex) < pPolicyStatus->cbSize);
|
|
pPolicyStatus->dwError = (DWORD) TRUST_E_BASIC_CONSTRAINTS;
|
|
pPolicyStatus->lChainIndex = (LONG) i;
|
|
pPolicyStatus->lElementIndex = (LONG) j;
|
|
return TRUE;
|
|
}
|
|
}
|
|
// Allow CTL to be signed by either a CA or END_ENTITY
|
|
dwFlags = 0;
|
|
}
|
|
|
|
assert(pPolicyStatus && offsetof(CERT_CHAIN_POLICY_STATUS, lElementIndex) <
|
|
pPolicyStatus->cbSize);
|
|
pPolicyStatus->dwError = 0;
|
|
pPolicyStatus->lChainIndex = -1;
|
|
pPolicyStatus->lElementIndex = -1;
|
|
return TRUE;
|
|
}
|
|
|
|
//+=========================================================================
|
|
// CertDllVerifyAuthenticodeCertificateChainPolicy Functions
|
|
//
|
|
// On July 1, 2000 philh removed all of the individual/commercial
|
|
// stuff. It hasn't been used for years!.
|
|
//==========================================================================
|
|
|
|
// Returns TRUE if the signer cert has the Code Signing EKU or if the signer
|
|
// cert has the legacy Key Usage extension with either the individual or
|
|
// commercial usage.
|
|
//
|
|
// For backwards compatibility, allow a signer cert without any EKU's
|
|
BOOL CheckAuthenticodeChainPurpose(
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext
|
|
)
|
|
{
|
|
BOOL fResult;
|
|
PCERT_CHAIN_ELEMENT pEle;
|
|
PCCERT_CONTEXT pCert;
|
|
PCERT_INFO pCertInfo;
|
|
PCERT_EXTENSION pExt;
|
|
PCERT_KEY_USAGE_RESTRICTION_INFO pInfo = NULL;
|
|
DWORD cbInfo;
|
|
|
|
pEle = pChainContext->rgpChain[0]->rgpElement[0];
|
|
if (NULL == pEle->pApplicationUsage) {
|
|
// No usages. Good for any usage.
|
|
// Do we want to allow this?? Yes, for backward compatibility
|
|
goto SuccessReturn;
|
|
} else {
|
|
DWORD cUsage;
|
|
LPSTR *ppszUsage;
|
|
|
|
cUsage = pEle->pApplicationUsage->cUsageIdentifier;
|
|
ppszUsage = pEle->pApplicationUsage->rgpszUsageIdentifier;
|
|
for ( ; cUsage > 0; cUsage--, ppszUsage++) {
|
|
if (0 == strcmp(*ppszUsage, szOID_PKIX_KP_CODE_SIGNING))
|
|
goto SuccessReturn;
|
|
}
|
|
}
|
|
|
|
pCert = pEle->pCertContext;
|
|
pCertInfo = pCert->pCertInfo;
|
|
|
|
if (0 == pCertInfo->cExtension)
|
|
goto NoSignerCertExtensions;
|
|
|
|
if (NULL == (pExt = CertFindExtension(
|
|
szOID_KEY_USAGE_RESTRICTION,
|
|
pCertInfo->cExtension,
|
|
pCertInfo->rgExtension
|
|
)))
|
|
goto NoSignerKeyUsageExtension;
|
|
|
|
if (!CryptDecodeObjectEx(
|
|
pCert->dwCertEncodingType,
|
|
X509_KEY_USAGE_RESTRICTION,
|
|
pExt->Value.pbData,
|
|
pExt->Value.cbData,
|
|
CRYPT_DECODE_NOCOPY_FLAG | CRYPT_DECODE_ALLOC_FLAG |
|
|
CRYPT_DECODE_SHARE_OID_STRING_FLAG,
|
|
&PkiDecodePara,
|
|
(void *) &pInfo,
|
|
&cbInfo
|
|
))
|
|
goto DecodeError;
|
|
|
|
if (pInfo->cCertPolicyId) {
|
|
DWORD cPolicyId;
|
|
PCERT_POLICY_ID pPolicyId;
|
|
|
|
cPolicyId = pInfo->cCertPolicyId;
|
|
pPolicyId = pInfo->rgCertPolicyId;
|
|
for ( ; cPolicyId > 0; cPolicyId--, pPolicyId++) {
|
|
DWORD cElementId = pPolicyId->cCertPolicyElementId;
|
|
LPSTR *ppszElementId = pPolicyId->rgpszCertPolicyElementId;
|
|
|
|
for ( ; cElementId > 0; cElementId--, ppszElementId++)
|
|
{
|
|
if (0 == strcmp(*ppszElementId,
|
|
SPC_COMMERCIAL_SP_KEY_PURPOSE_OBJID))
|
|
goto SuccessReturn;
|
|
else if (0 == strcmp(*ppszElementId,
|
|
SPC_INDIVIDUAL_SP_KEY_PURPOSE_OBJID))
|
|
goto SuccessReturn;
|
|
}
|
|
}
|
|
}
|
|
|
|
goto NoSignerLegacyPurpose;
|
|
|
|
SuccessReturn:
|
|
fResult = TRUE;
|
|
CommonReturn:
|
|
PkiFree(pInfo);
|
|
return fResult;
|
|
|
|
ErrorReturn:
|
|
fResult = FALSE;
|
|
goto CommonReturn;
|
|
|
|
TRACE_ERROR(NoSignerCertExtensions)
|
|
TRACE_ERROR(NoSignerKeyUsageExtension)
|
|
TRACE_ERROR(DecodeError);
|
|
TRACE_ERROR(NoSignerLegacyPurpose)
|
|
}
|
|
|
|
void MapAuthenticodeRegPolicySettingsToBaseChainPolicyFlags(
|
|
IN DWORD dwRegPolicySettings,
|
|
IN OUT DWORD *pdwFlags
|
|
)
|
|
{
|
|
DWORD dwFlags;
|
|
|
|
if (0 == dwRegPolicySettings)
|
|
return;
|
|
|
|
dwFlags = *pdwFlags;
|
|
if (dwRegPolicySettings & WTPF_TRUSTTEST)
|
|
dwFlags |= CERT_CHAIN_POLICY_TRUST_TESTROOT_FLAG;
|
|
if (dwRegPolicySettings & WTPF_TESTCANBEVALID)
|
|
dwFlags |= CERT_CHAIN_POLICY_ALLOW_TESTROOT_FLAG;
|
|
if (dwRegPolicySettings & WTPF_IGNOREEXPIRATION)
|
|
dwFlags |= CERT_CHAIN_POLICY_IGNORE_ALL_NOT_TIME_VALID_FLAGS;
|
|
|
|
if (dwRegPolicySettings & WTPF_IGNOREREVOKATION)
|
|
dwFlags |= CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS;
|
|
else if (dwRegPolicySettings & (WTPF_OFFLINEOK_COM | WTPF_OFFLINEOK_IND))
|
|
dwFlags |= CERT_CHAIN_POLICY_IGNORE_END_REV_UNKNOWN_FLAG;
|
|
|
|
*pdwFlags = dwFlags;
|
|
}
|
|
|
|
|
|
void GetAuthenticodePara(
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
OUT DWORD *pdwRegPolicySettings,
|
|
OUT PCMSG_SIGNER_INFO *ppSignerInfo
|
|
)
|
|
{
|
|
*ppSignerInfo = NULL;
|
|
*pdwRegPolicySettings = 0;
|
|
if (pPolicyPara && offsetof(CERT_CHAIN_POLICY_PARA, pvExtraPolicyPara) <
|
|
pPolicyPara->cbSize && pPolicyPara->pvExtraPolicyPara) {
|
|
PAUTHENTICODE_EXTRA_CERT_CHAIN_POLICY_PARA pAuthPara =
|
|
(PAUTHENTICODE_EXTRA_CERT_CHAIN_POLICY_PARA)
|
|
pPolicyPara->pvExtraPolicyPara;
|
|
|
|
if (offsetof(AUTHENTICODE_EXTRA_CERT_CHAIN_POLICY_PARA,
|
|
dwRegPolicySettings) < pAuthPara->cbSize)
|
|
*pdwRegPolicySettings = pAuthPara->dwRegPolicySettings;
|
|
if (offsetof(AUTHENTICODE_EXTRA_CERT_CHAIN_POLICY_PARA,
|
|
pSignerInfo) < pAuthPara->cbSize)
|
|
*ppSignerInfo = pAuthPara->pSignerInfo;
|
|
}
|
|
}
|
|
|
|
// Map the CRYPT_E_ revocation errors to the corresponding CERT_E_
|
|
// revocation errors
|
|
static DWORD MapToAuthenticodeError(
|
|
IN DWORD dwError
|
|
)
|
|
{
|
|
switch (dwError) {
|
|
case CRYPT_E_REVOKED:
|
|
return (DWORD) CERT_E_REVOKED;
|
|
break;
|
|
case CRYPT_E_NO_REVOCATION_DLL:
|
|
case CRYPT_E_NO_REVOCATION_CHECK:
|
|
case CRYPT_E_REVOCATION_OFFLINE:
|
|
case CRYPT_E_NOT_IN_REVOCATION_DATABASE:
|
|
return (DWORD) CERT_E_REVOCATION_FAILURE;
|
|
break;
|
|
}
|
|
return dwError;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
CertDllVerifyAuthenticodeCertificateChainPolicy(
|
|
IN LPCSTR pszPolicyOID,
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
IN OUT PCERT_CHAIN_POLICY_STATUS pPolicyStatus
|
|
)
|
|
{
|
|
DWORD dwError;
|
|
DWORD dwFlags;
|
|
DWORD dwRegPolicySettings;
|
|
PCMSG_SIGNER_INFO pSignerInfo;
|
|
LONG lChainIndex = -1;
|
|
LONG lElementIndex = -1;
|
|
|
|
|
|
CERT_CHAIN_POLICY_PARA BasePolicyPara;
|
|
memset(&BasePolicyPara, 0, sizeof(BasePolicyPara));
|
|
BasePolicyPara.cbSize = sizeof(BasePolicyPara);
|
|
|
|
CERT_CHAIN_POLICY_STATUS BasePolicyStatus;
|
|
memset(&BasePolicyStatus, 0, sizeof(BasePolicyStatus));
|
|
BasePolicyStatus.cbSize = sizeof(BasePolicyStatus);
|
|
|
|
if (pPolicyPara && offsetof(CERT_CHAIN_POLICY_PARA, dwFlags) <
|
|
pPolicyPara->cbSize)
|
|
dwFlags = pPolicyPara->dwFlags;
|
|
else
|
|
dwFlags = 0;
|
|
GetAuthenticodePara(pPolicyPara, &dwRegPolicySettings, &pSignerInfo);
|
|
|
|
MapAuthenticodeRegPolicySettingsToBaseChainPolicyFlags(
|
|
dwRegPolicySettings, &dwFlags);
|
|
|
|
// Do the basic chain policy verification. Authenticode overrides
|
|
// the defaults for the following:
|
|
dwFlags |=
|
|
CERT_CHAIN_POLICY_ALLOW_TESTROOT_FLAG |
|
|
CERT_CHAIN_POLICY_IGNORE_CTL_SIGNER_REV_UNKNOWN_FLAG |
|
|
CERT_CHAIN_POLICY_IGNORE_CA_REV_UNKNOWN_FLAG |
|
|
CERT_CHAIN_POLICY_IGNORE_ROOT_REV_UNKNOWN_FLAG |
|
|
CERT_CHAIN_POLICY_IGNORE_NOT_TIME_NESTED_FLAG;
|
|
|
|
BasePolicyPara.dwFlags = dwFlags;
|
|
if (!CertVerifyCertificateChainPolicy(
|
|
CERT_CHAIN_POLICY_BASE,
|
|
pChainContext,
|
|
&BasePolicyPara,
|
|
&BasePolicyStatus
|
|
))
|
|
return FALSE;
|
|
if (dwError = BasePolicyStatus.dwError) {
|
|
dwError = MapToAuthenticodeError(dwError);
|
|
lChainIndex = BasePolicyStatus.lChainIndex;
|
|
lElementIndex = BasePolicyStatus.lElementIndex;
|
|
|
|
if (CERT_E_REVOCATION_FAILURE != dwError)
|
|
goto CommonReturn;
|
|
// else
|
|
// for REVOCATION_FAILURE let
|
|
// PURPOSE and BASIC_CONSTRAINTS errors take precedence
|
|
}
|
|
|
|
if (pSignerInfo) {
|
|
// Check that either the chain has the code signing EKU or
|
|
// the signer cert has the legacy Key Usage extension containing
|
|
// the commerical or individual policy.
|
|
if (!CheckAuthenticodeChainPurpose(pChainContext)) {
|
|
dwError = (DWORD) CERT_E_PURPOSE;
|
|
lChainIndex = 0;
|
|
lElementIndex = 0;
|
|
goto CommonReturn;
|
|
}
|
|
}
|
|
|
|
if (pSignerInfo)
|
|
BasePolicyPara.dwFlags =
|
|
BASIC_CONSTRAINTS_CERT_CHAIN_POLICY_END_ENTITY_FLAG;
|
|
else
|
|
BasePolicyPara.dwFlags = 0;
|
|
if (!CertVerifyCertificateChainPolicy(
|
|
CERT_CHAIN_POLICY_BASIC_CONSTRAINTS,
|
|
pChainContext,
|
|
&BasePolicyPara,
|
|
&BasePolicyStatus
|
|
))
|
|
return FALSE;
|
|
if (BasePolicyStatus.dwError) {
|
|
dwError = BasePolicyStatus.dwError;
|
|
lChainIndex = BasePolicyStatus.lChainIndex;
|
|
lElementIndex = BasePolicyStatus.lElementIndex;
|
|
goto CommonReturn;
|
|
}
|
|
|
|
CommonReturn:
|
|
assert(pPolicyStatus && offsetof(CERT_CHAIN_POLICY_STATUS, lElementIndex) <
|
|
pPolicyStatus->cbSize);
|
|
pPolicyStatus->dwError = dwError;
|
|
pPolicyStatus->lChainIndex = lChainIndex;
|
|
pPolicyStatus->lElementIndex = lElementIndex;
|
|
|
|
if (offsetof(CERT_CHAIN_POLICY_STATUS, pvExtraPolicyStatus) <
|
|
pPolicyStatus->cbSize &&
|
|
pPolicyStatus->pvExtraPolicyStatus) {
|
|
// Since the signer statement's Commercial/Individual flag is no
|
|
// longer used, default to individual
|
|
PAUTHENTICODE_EXTRA_CERT_CHAIN_POLICY_STATUS pAuthStatus =
|
|
(PAUTHENTICODE_EXTRA_CERT_CHAIN_POLICY_STATUS)
|
|
pPolicyStatus->pvExtraPolicyStatus;
|
|
if (offsetof(AUTHENTICODE_EXTRA_CERT_CHAIN_POLICY_STATUS,
|
|
fCommercial) < pAuthStatus->cbSize)
|
|
pAuthStatus->fCommercial = FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
//+=========================================================================
|
|
// CertDllVerifyAuthenticodeTimeStampCertificateChainPolicy Functions
|
|
//==========================================================================
|
|
|
|
void MapAuthenticodeTimeStampRegPolicySettingsToBaseChainPolicyFlags(
|
|
IN DWORD dwRegPolicySettings,
|
|
IN OUT DWORD *pdwFlags
|
|
)
|
|
{
|
|
DWORD dwFlags;
|
|
|
|
if (0 == dwRegPolicySettings)
|
|
return;
|
|
|
|
dwFlags = *pdwFlags;
|
|
if (dwRegPolicySettings & WTPF_TRUSTTEST)
|
|
dwFlags |= CERT_CHAIN_POLICY_TRUST_TESTROOT_FLAG;
|
|
if (dwRegPolicySettings & WTPF_TESTCANBEVALID)
|
|
dwFlags |= CERT_CHAIN_POLICY_ALLOW_TESTROOT_FLAG;
|
|
if (dwRegPolicySettings & WTPF_IGNOREEXPIRATION)
|
|
dwFlags |= CERT_CHAIN_POLICY_IGNORE_ALL_NOT_TIME_VALID_FLAGS;
|
|
|
|
if (dwRegPolicySettings & WTPF_IGNOREREVOCATIONONTS)
|
|
dwFlags |= CERT_CHAIN_POLICY_IGNORE_ALL_REV_UNKNOWN_FLAGS;
|
|
else if (dwRegPolicySettings & (WTPF_OFFLINEOK_COM | WTPF_OFFLINEOK_IND))
|
|
dwFlags |= CERT_CHAIN_POLICY_IGNORE_END_REV_UNKNOWN_FLAG;
|
|
|
|
*pdwFlags = dwFlags;
|
|
}
|
|
|
|
|
|
void GetAuthenticodeTimeStampPara(
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
OUT DWORD *pdwRegPolicySettings
|
|
)
|
|
{
|
|
*pdwRegPolicySettings = 0;
|
|
if (pPolicyPara && offsetof(CERT_CHAIN_POLICY_PARA, pvExtraPolicyPara) <
|
|
pPolicyPara->cbSize && pPolicyPara->pvExtraPolicyPara) {
|
|
PAUTHENTICODE_TS_EXTRA_CERT_CHAIN_POLICY_PARA pAuthPara =
|
|
(PAUTHENTICODE_TS_EXTRA_CERT_CHAIN_POLICY_PARA)
|
|
pPolicyPara->pvExtraPolicyPara;
|
|
|
|
if (offsetof(AUTHENTICODE_TS_EXTRA_CERT_CHAIN_POLICY_PARA,
|
|
dwRegPolicySettings) < pAuthPara->cbSize)
|
|
*pdwRegPolicySettings = pAuthPara->dwRegPolicySettings;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
CertDllVerifyAuthenticodeTimeStampCertificateChainPolicy(
|
|
IN LPCSTR pszPolicyOID,
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
IN OUT PCERT_CHAIN_POLICY_STATUS pPolicyStatus
|
|
)
|
|
{
|
|
DWORD dwError;
|
|
DWORD dwFlags;
|
|
DWORD dwRegPolicySettings;
|
|
LONG lChainIndex = -1;
|
|
LONG lElementIndex = -1;
|
|
|
|
CERT_CHAIN_POLICY_PARA BasePolicyPara;
|
|
memset(&BasePolicyPara, 0, sizeof(BasePolicyPara));
|
|
BasePolicyPara.cbSize = sizeof(BasePolicyPara);
|
|
|
|
CERT_CHAIN_POLICY_STATUS BasePolicyStatus;
|
|
memset(&BasePolicyStatus, 0, sizeof(BasePolicyStatus));
|
|
BasePolicyStatus.cbSize = sizeof(BasePolicyStatus);
|
|
|
|
if (pPolicyPara && offsetof(CERT_CHAIN_POLICY_PARA, dwFlags) <
|
|
pPolicyPara->cbSize)
|
|
dwFlags = pPolicyPara->dwFlags;
|
|
else
|
|
dwFlags = 0;
|
|
GetAuthenticodeTimeStampPara(
|
|
pPolicyPara, &dwRegPolicySettings);
|
|
|
|
MapAuthenticodeTimeStampRegPolicySettingsToBaseChainPolicyFlags(
|
|
dwRegPolicySettings, &dwFlags);
|
|
|
|
// Do the basic chain policy verification. Authenticode overrides
|
|
// the defaults for the following:
|
|
dwFlags |=
|
|
CERT_CHAIN_POLICY_ALLOW_TESTROOT_FLAG |
|
|
CERT_CHAIN_POLICY_IGNORE_CTL_SIGNER_REV_UNKNOWN_FLAG |
|
|
CERT_CHAIN_POLICY_IGNORE_CA_REV_UNKNOWN_FLAG |
|
|
CERT_CHAIN_POLICY_IGNORE_ROOT_REV_UNKNOWN_FLAG |
|
|
CERT_CHAIN_POLICY_IGNORE_NOT_TIME_NESTED_FLAG;
|
|
|
|
BasePolicyPara.dwFlags = dwFlags;
|
|
if (!CertVerifyCertificateChainPolicy(
|
|
CERT_CHAIN_POLICY_BASE,
|
|
pChainContext,
|
|
&BasePolicyPara,
|
|
&BasePolicyStatus
|
|
))
|
|
return FALSE;
|
|
if (dwError = BasePolicyStatus.dwError) {
|
|
dwError = MapToAuthenticodeError(dwError);
|
|
lChainIndex = BasePolicyStatus.lChainIndex;
|
|
lElementIndex = BasePolicyStatus.lElementIndex;
|
|
goto CommonReturn;
|
|
}
|
|
|
|
CommonReturn:
|
|
assert(pPolicyStatus && offsetof(CERT_CHAIN_POLICY_STATUS, lElementIndex) <
|
|
pPolicyStatus->cbSize);
|
|
pPolicyStatus->dwError = dwError;
|
|
pPolicyStatus->lChainIndex = lChainIndex;
|
|
pPolicyStatus->lElementIndex = lElementIndex;
|
|
return TRUE;
|
|
}
|
|
|
|
//+=========================================================================
|
|
// CertDllVerifySSLCertificateChainPolicy Functions
|
|
//==========================================================================
|
|
|
|
// www.foobar.com == www.foobar.com
|
|
// Www.Foobar.com == www.fooBar.cOm
|
|
// www.foobar.com == *.foobar.com
|
|
// www.foobar.com == w*.foobar.com
|
|
// www.foobar.com == ww*.foobar.com
|
|
// www.foobar.com != *ww.foobar.com
|
|
// abcdef.foobar.com != ab*ef.foobar.com
|
|
// abcdef.foobar.com != abc*ef.foobar.com
|
|
// abcdef.foobar.com != abc*def.foobar.com
|
|
// www.foobar.com != www.f*bar.com
|
|
// www.foobar.com != www.*bar.com
|
|
// www.foobar.com != www.foo*.com
|
|
// www.foobar.com != www.*.com
|
|
// foobar.com != *.com
|
|
// www.foobar.abc.com != *.abc.com
|
|
// foobar.com != *.*
|
|
// foobar != *
|
|
// abc.def.foobar.com != a*.d*.foobar.com
|
|
// abc.foobar.com.au != *.*.com.au
|
|
// abc.foobar.com.au != www.*.com.au
|
|
|
|
BOOL CompareSSLDNStoCommonName(LPCWSTR pDNS, LPCWSTR pCN)
|
|
{
|
|
BOOL fUseWildCardRules = FALSE;
|
|
|
|
if (NULL == pDNS || L'\0' == *pDNS ||
|
|
NULL == pCN || L'\0' == *pCN)
|
|
return FALSE;
|
|
|
|
if(fWildcardsEnabledInSslServerCerts)
|
|
{
|
|
if(wcschr(pCN, L'*') != NULL)
|
|
{
|
|
fUseWildCardRules = TRUE;
|
|
}
|
|
}
|
|
|
|
if(fUseWildCardRules)
|
|
{
|
|
DWORD nCountPeriods = 1;
|
|
BOOL fExactMatch = TRUE;
|
|
BOOL fComp;
|
|
LPCWSTR pWild;
|
|
|
|
pWild = wcschr(pCN, L'*');
|
|
if(pWild)
|
|
{
|
|
// Fail if CN contains more than one '*'.
|
|
if(wcschr(pWild + 1, L'*'))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// Fail if the wildcard isn't in the first name component.
|
|
if(pWild > wcschr(pCN, L'.'))
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
while(TRUE)
|
|
{
|
|
fComp = (CSTR_EQUAL == CompareStringU(
|
|
LOCALE_USER_DEFAULT,
|
|
NORM_IGNORECASE,
|
|
pDNS,
|
|
1, // cchCount1
|
|
pCN,
|
|
1)); // cchCount2
|
|
|
|
if ((!fComp && *pCN != L'*') || !(*pDNS) || !(*pCN))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!fComp)
|
|
{
|
|
fExactMatch = FALSE;
|
|
}
|
|
|
|
if (*pCN == L'*')
|
|
{
|
|
nCountPeriods = 0;
|
|
|
|
if (*pDNS == L'.')
|
|
{
|
|
pCN++;
|
|
}
|
|
else
|
|
{
|
|
pDNS++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (*pDNS == L'.')
|
|
{
|
|
nCountPeriods++;
|
|
}
|
|
|
|
pDNS++;
|
|
pCN++;
|
|
}
|
|
}
|
|
|
|
return((*pDNS == 0) && (*pCN == 0) && ((nCountPeriods >= 2) || fExactMatch));
|
|
}
|
|
else
|
|
{
|
|
if (CSTR_EQUAL == CompareStringU(
|
|
LOCALE_USER_DEFAULT,
|
|
NORM_IGNORECASE,
|
|
pDNS,
|
|
-1, // cchCount1
|
|
pCN,
|
|
-1 // cchCount2
|
|
))
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL IsSSLServerNameInNameInfo(
|
|
IN DWORD dwCertEncodingType,
|
|
IN PCERT_NAME_BLOB pNameInfoBlob,
|
|
IN LPCWSTR pwszServerName
|
|
)
|
|
{
|
|
BOOL fResult;
|
|
PCERT_NAME_INFO pInfo = NULL;
|
|
DWORD cbInfo;
|
|
DWORD cRDN;
|
|
PCERT_RDN pRDN;
|
|
|
|
if (!CryptDecodeObjectEx(
|
|
dwCertEncodingType,
|
|
X509_UNICODE_NAME,
|
|
pNameInfoBlob->pbData,
|
|
pNameInfoBlob->cbData,
|
|
CRYPT_DECODE_NOCOPY_FLAG | CRYPT_DECODE_ALLOC_FLAG |
|
|
CRYPT_DECODE_SHARE_OID_STRING_FLAG,
|
|
&PkiDecodePara,
|
|
(void *) &pInfo,
|
|
&cbInfo
|
|
))
|
|
goto DecodeError;
|
|
|
|
cRDN = pInfo->cRDN;
|
|
pRDN = pInfo->rgRDN;
|
|
for ( ; cRDN > 0; cRDN--, pRDN++) {
|
|
DWORD cAttr = pRDN->cRDNAttr;
|
|
PCERT_RDN_ATTR pAttr = pRDN->rgRDNAttr;
|
|
for ( ; cAttr > 0; cAttr--, pAttr++) {
|
|
if (!IS_CERT_RDN_CHAR_STRING(pAttr->dwValueType))
|
|
continue;
|
|
if (0 == strcmp(pAttr->pszObjId, szOID_COMMON_NAME)) {
|
|
if (CompareSSLDNStoCommonName(pwszServerName,
|
|
(LPCWSTR) pAttr->Value.pbData))
|
|
goto SuccessReturn;
|
|
}
|
|
}
|
|
}
|
|
|
|
goto ErrorReturn;
|
|
SuccessReturn:
|
|
fResult = TRUE;
|
|
CommonReturn:
|
|
PkiFree(pInfo);
|
|
return fResult;
|
|
ErrorReturn:
|
|
fResult = FALSE;
|
|
goto CommonReturn;
|
|
|
|
TRACE_ERROR(DecodeError)
|
|
}
|
|
|
|
//
|
|
// Returns:
|
|
// 1 - found a matching DNS_NAME choice
|
|
// 0 - AltName has DNS_NAME choices, no match
|
|
// -1 - AltName doesn't have DNS_NAME choices
|
|
//
|
|
int IsSSLServerNameInAltName(
|
|
IN DWORD dwCertEncodingType,
|
|
IN PCRYPT_DER_BLOB pAltNameBlob,
|
|
IN LPCWSTR pwszServerName
|
|
)
|
|
{
|
|
int iResult = -1; // default to no DNS_NAME choices
|
|
PCERT_ALT_NAME_INFO pInfo = NULL;
|
|
DWORD cbInfo;
|
|
DWORD cEntry;
|
|
PCERT_ALT_NAME_ENTRY pEntry;
|
|
|
|
if (!CryptDecodeObjectEx(
|
|
dwCertEncodingType,
|
|
X509_ALTERNATE_NAME,
|
|
pAltNameBlob->pbData,
|
|
pAltNameBlob->cbData,
|
|
CRYPT_DECODE_NOCOPY_FLAG | CRYPT_DECODE_ALLOC_FLAG |
|
|
CRYPT_DECODE_SHARE_OID_STRING_FLAG,
|
|
&PkiDecodePara,
|
|
(void *) &pInfo,
|
|
&cbInfo
|
|
))
|
|
goto DecodeError;
|
|
|
|
cEntry = pInfo->cAltEntry;
|
|
pEntry = pInfo->rgAltEntry;
|
|
for ( ; cEntry > 0; cEntry--, pEntry++) {
|
|
if (CERT_ALT_NAME_DNS_NAME == pEntry->dwAltNameChoice) {
|
|
if (CompareSSLDNStoCommonName(pwszServerName,
|
|
pEntry->pwszDNSName))
|
|
goto SuccessReturn;
|
|
else
|
|
iResult = 0;
|
|
}
|
|
}
|
|
|
|
goto ErrorReturn;
|
|
|
|
SuccessReturn:
|
|
iResult = 1;
|
|
CommonReturn:
|
|
PkiFree(pInfo);
|
|
return iResult;
|
|
ErrorReturn:
|
|
goto CommonReturn;
|
|
|
|
TRACE_ERROR(DecodeError)
|
|
}
|
|
|
|
BOOL IsSSLServerName(
|
|
IN PCCERT_CONTEXT pCertContext,
|
|
IN LPCWSTR pwszServerName
|
|
)
|
|
{
|
|
PCERT_INFO pCertInfo = pCertContext->pCertInfo;
|
|
DWORD dwCertEncodingType = pCertContext->dwCertEncodingType;
|
|
PCERT_EXTENSION pExt;
|
|
|
|
pExt = CertFindExtension(
|
|
szOID_SUBJECT_ALT_NAME2,
|
|
pCertInfo->cExtension,
|
|
pCertInfo->rgExtension
|
|
);
|
|
|
|
if (NULL == pExt) {
|
|
pExt = CertFindExtension(
|
|
szOID_SUBJECT_ALT_NAME,
|
|
pCertInfo->cExtension,
|
|
pCertInfo->rgExtension
|
|
);
|
|
}
|
|
|
|
if (pExt) {
|
|
int iResult;
|
|
iResult = IsSSLServerNameInAltName(dwCertEncodingType,
|
|
&pExt->Value, pwszServerName);
|
|
|
|
if (0 < iResult)
|
|
return TRUE;
|
|
else if (0 == iResult)
|
|
return FALSE;
|
|
// else
|
|
// AltName didn't have any DNS_NAME choices
|
|
}
|
|
|
|
return IsSSLServerNameInNameInfo(dwCertEncodingType,
|
|
&pCertInfo->Subject, pwszServerName);
|
|
}
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
CertDllVerifySSLCertificateChainPolicy(
|
|
IN LPCSTR pszPolicyOID,
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
IN OUT PCERT_CHAIN_POLICY_STATUS pPolicyStatus
|
|
)
|
|
{
|
|
DWORD dwError;
|
|
DWORD dwFlags;
|
|
DWORD fdwChecks;
|
|
LONG lChainIndex = -1;
|
|
LONG lElementIndex = -1;
|
|
|
|
SSL_EXTRA_CERT_CHAIN_POLICY_PARA NullSSLExtraPara;
|
|
PSSL_EXTRA_CERT_CHAIN_POLICY_PARA pSSLExtraPara; // not allocated
|
|
|
|
CERT_CHAIN_POLICY_PARA BasePolicyPara;
|
|
memset(&BasePolicyPara, 0, sizeof(BasePolicyPara));
|
|
BasePolicyPara.cbSize = sizeof(BasePolicyPara);
|
|
|
|
CERT_CHAIN_POLICY_STATUS BasePolicyStatus;
|
|
memset(&BasePolicyStatus, 0, sizeof(BasePolicyStatus));
|
|
BasePolicyStatus.cbSize = sizeof(BasePolicyStatus);
|
|
|
|
|
|
if (pPolicyPara && offsetof(CERT_CHAIN_POLICY_PARA, dwFlags) <
|
|
pPolicyPara->cbSize)
|
|
dwFlags = pPolicyPara->dwFlags;
|
|
else
|
|
dwFlags = 0;
|
|
|
|
if (pPolicyPara && offsetof(CERT_CHAIN_POLICY_PARA, pvExtraPolicyPara) <
|
|
pPolicyPara->cbSize && pPolicyPara->pvExtraPolicyPara) {
|
|
pSSLExtraPara =
|
|
(PSSL_EXTRA_CERT_CHAIN_POLICY_PARA) pPolicyPara->pvExtraPolicyPara;
|
|
if (offsetof(SSL_EXTRA_CERT_CHAIN_POLICY_PARA, pwszServerName) >=
|
|
pSSLExtraPara->cbSize) {
|
|
SetLastError((DWORD) ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
} else {
|
|
pSSLExtraPara = &NullSSLExtraPara;
|
|
memset(&NullSSLExtraPara, 0, sizeof(NullSSLExtraPara));
|
|
NullSSLExtraPara.cbSize = sizeof(NullSSLExtraPara);
|
|
NullSSLExtraPara.dwAuthType = AUTHTYPE_SERVER;
|
|
}
|
|
|
|
fdwChecks = pSSLExtraPara->fdwChecks;
|
|
if (fdwChecks) {
|
|
if (fdwChecks & SECURITY_FLAG_IGNORE_UNKNOWN_CA)
|
|
// 11-Nov-98 per Sanjay Shenoy removed
|
|
// CERT_CHAIN_POLICY_IGNORE_WRONG_USAGE_FLAG;
|
|
dwFlags |= CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG;
|
|
|
|
if (fdwChecks & SECURITY_FLAG_IGNORE_WRONG_USAGE)
|
|
dwFlags |= CERT_CHAIN_POLICY_IGNORE_WRONG_USAGE_FLAG;
|
|
if (fdwChecks & SECURITY_FLAG_IGNORE_CERT_DATE_INVALID)
|
|
dwFlags |= CERT_CHAIN_POLICY_IGNORE_ALL_NOT_TIME_VALID_FLAGS;
|
|
if (fdwChecks & SECURITY_FLAG_IGNORE_CERT_CN_INVALID)
|
|
dwFlags |= CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG;
|
|
}
|
|
|
|
// Do the basic chain policy verification. SSL overrides
|
|
// the defaults for the following:
|
|
dwFlags |=
|
|
CERT_CHAIN_POLICY_IGNORE_CTL_SIGNER_REV_UNKNOWN_FLAG |
|
|
CERT_CHAIN_POLICY_IGNORE_CA_REV_UNKNOWN_FLAG |
|
|
CERT_CHAIN_POLICY_IGNORE_ROOT_REV_UNKNOWN_FLAG |
|
|
CERT_CHAIN_POLICY_IGNORE_NOT_TIME_NESTED_FLAG;
|
|
|
|
BasePolicyPara.dwFlags = dwFlags;
|
|
if (!CertVerifyCertificateChainPolicy(
|
|
CERT_CHAIN_POLICY_BASE,
|
|
pChainContext,
|
|
&BasePolicyPara,
|
|
&BasePolicyStatus
|
|
))
|
|
return FALSE;
|
|
if (dwError = BasePolicyStatus.dwError) {
|
|
// Map to errors understood by wininet
|
|
switch (dwError) {
|
|
case CERT_E_CHAINING:
|
|
dwError = (DWORD) CERT_E_UNTRUSTEDROOT;
|
|
break;
|
|
case CERT_E_INVALID_NAME:
|
|
dwError = (DWORD) CERT_E_CN_NO_MATCH;
|
|
break;
|
|
case CERT_E_INVALID_POLICY:
|
|
dwError = (DWORD) CERT_E_PURPOSE;
|
|
break;
|
|
case TRUST_E_BASIC_CONSTRAINTS:
|
|
dwError = (DWORD) CERT_E_ROLE;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
lChainIndex = BasePolicyStatus.lChainIndex;
|
|
lElementIndex = BasePolicyStatus.lElementIndex;
|
|
if (CRYPT_E_NO_REVOCATION_CHECK != dwError &&
|
|
CRYPT_E_REVOCATION_OFFLINE != dwError)
|
|
goto CommonReturn;
|
|
// else
|
|
// for NO_REVOCATION or REVOCATION_OFFLINE let
|
|
// ServerName errors take precedence
|
|
}
|
|
|
|
|
|
// Note, this policy can also be used for LDAP ServerName strings. These
|
|
// strings can have the following syntax:
|
|
// svc-class/host/service-name[@domain]
|
|
//
|
|
// Will parse the ServerName as follows:
|
|
// take everything after the last forward slash and before the "@"
|
|
// (if any).
|
|
|
|
if (pSSLExtraPara->pwszServerName) {
|
|
DWORD cbServerName;
|
|
LPWSTR pwszAllocServerName = NULL;
|
|
LPWSTR pwszServerName = NULL;
|
|
LPWSTR pwsz;
|
|
WCHAR wc;
|
|
|
|
cbServerName = (wcslen(pSSLExtraPara->pwszServerName) + 1) *
|
|
sizeof(WCHAR);
|
|
|
|
pwszAllocServerName = (LPWSTR) PkiNonzeroAlloc(cbServerName);
|
|
if (NULL == pwszAllocServerName) {
|
|
dwError = (DWORD) E_OUTOFMEMORY;
|
|
goto EndCertError;
|
|
}
|
|
pwszServerName = pwszAllocServerName;
|
|
|
|
memcpy(pwszServerName, pSSLExtraPara->pwszServerName, cbServerName);
|
|
|
|
for (pwsz = pwszServerName; wc = *pwsz; pwsz++) {
|
|
if (L'/' == wc)
|
|
pwszServerName = pwsz + 1;
|
|
else if (L'@' == wc) {
|
|
*pwsz = L'\0';
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (0 == (fdwChecks & SECURITY_FLAG_IGNORE_CERT_CN_INVALID)) {
|
|
if (!IsSSLServerName(
|
|
pChainContext->rgpChain[0]->rgpElement[0]->pCertContext,
|
|
pwszServerName
|
|
)) {
|
|
PkiFree(pwszAllocServerName);
|
|
dwError = (DWORD) CERT_E_CN_NO_MATCH;
|
|
goto EndCertError;
|
|
}
|
|
}
|
|
|
|
PkiFree(pwszAllocServerName);
|
|
}
|
|
|
|
CommonReturn:
|
|
assert(pPolicyStatus && offsetof(CERT_CHAIN_POLICY_STATUS, lElementIndex) <
|
|
pPolicyStatus->cbSize);
|
|
pPolicyStatus->dwError = dwError;
|
|
pPolicyStatus->lChainIndex = lChainIndex;
|
|
pPolicyStatus->lElementIndex = lElementIndex;
|
|
|
|
return TRUE;
|
|
|
|
EndCertError:
|
|
lChainIndex = 0;
|
|
lElementIndex = 0;
|
|
goto CommonReturn;
|
|
}
|
|
|
|
|
|
//+=========================================================================
|
|
// CertDllVerifyNTAuthCertificateChainPolicy Functions
|
|
//==========================================================================
|
|
|
|
// Open and cache the store containing CAs trusted for NT Authentication.
|
|
// Also, enable auto resync for the cached store.
|
|
HCERTSTORE OpenNTAuthStore()
|
|
{
|
|
HCERTSTORE hStore;
|
|
|
|
hStore = hNTAuthCertStore;
|
|
if (NULL == hStore) {
|
|
// Serialize opening of the cached store
|
|
CertPolicyLock();
|
|
|
|
hStore = hNTAuthCertStore;
|
|
if (NULL == hStore) {
|
|
hStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_REGISTRY_W,
|
|
0, // dwEncodingType
|
|
0, // hCryptProv
|
|
CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE |
|
|
CERT_STORE_MAXIMUM_ALLOWED_FLAG,
|
|
L"NTAuth"
|
|
);
|
|
if (hStore) {
|
|
CertControlStore(
|
|
hStore,
|
|
0, // dwFlags
|
|
CERT_STORE_CTRL_AUTO_RESYNC,
|
|
NULL // pvCtrlPara
|
|
);
|
|
hNTAuthCertStore = hStore;
|
|
}
|
|
}
|
|
|
|
CertPolicyUnlock();
|
|
}
|
|
|
|
return hStore;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
CertDllVerifyNTAuthCertificateChainPolicy(
|
|
IN LPCSTR pszPolicyOID,
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
IN OUT PCERT_CHAIN_POLICY_STATUS pPolicyStatus
|
|
)
|
|
{
|
|
BOOL fResult;
|
|
DWORD dwError;
|
|
LONG lChainIndex = 0;
|
|
LONG lElementIndex = 0;
|
|
PCERT_SIMPLE_CHAIN pChain;
|
|
|
|
assert(pPolicyStatus && offsetof(CERT_CHAIN_POLICY_STATUS, lElementIndex) <
|
|
pPolicyStatus->cbSize);
|
|
|
|
if (!CertDllVerifyBaseCertificateChainPolicy(
|
|
pszPolicyOID,
|
|
pChainContext,
|
|
pPolicyPara,
|
|
pPolicyStatus
|
|
))
|
|
return FALSE;
|
|
if (dwError = pPolicyStatus->dwError) {
|
|
if (CRYPT_E_NO_REVOCATION_CHECK != dwError &&
|
|
CRYPT_E_REVOCATION_OFFLINE != dwError)
|
|
return TRUE;
|
|
// else
|
|
// for NO_REVOCATION or REVOCATION_OFFLINE let
|
|
// following errors take precedence
|
|
|
|
// Remember revocation indices
|
|
lChainIndex = pPolicyStatus->lChainIndex;
|
|
lElementIndex = pPolicyStatus->lElementIndex;
|
|
}
|
|
|
|
fResult = CertDllVerifyBasicConstraintsCertificateChainPolicy(
|
|
pszPolicyOID,
|
|
pChainContext,
|
|
pPolicyPara,
|
|
pPolicyStatus
|
|
);
|
|
if (!fResult || 0 != pPolicyStatus->dwError)
|
|
return fResult;
|
|
|
|
// Check if we have a CA certificate that issued the end entity
|
|
// certificate. Its Element[1] in the first simple chain.
|
|
pChain = pChainContext->rgpChain[0];
|
|
if (2 > pChain->cElement)
|
|
goto MissingCACert;
|
|
|
|
if (IPR_IsNTAuthRequiredDisabled() &&
|
|
(pChain->TrustStatus.dwInfoStatus &
|
|
CERT_TRUST_HAS_VALID_NAME_CONSTRAINTS)) {
|
|
// If its not required that the issuing CA be in the NTAuth store
|
|
// and there are valid name constraints for all name spaces including
|
|
// UPN, then, we can skip the test for being in the NTAuth store.
|
|
;
|
|
} else {
|
|
PCCERT_CONTEXT pFindCert; // freed if found
|
|
HCERTSTORE hStore; // cached, don't close
|
|
BYTE rgbCertHash[SHA1_HASH_LEN];
|
|
CRYPT_HASH_BLOB CertHash;
|
|
|
|
// Open the store where the CA certificate must exist to be trusted.
|
|
// Note, this store is cached with auto resync enabled.
|
|
if (NULL == (hStore = OpenNTAuthStore()))
|
|
goto OpenNTAuthStoreError;
|
|
|
|
// Try to find the CA certificate in the store
|
|
CertHash.cbData = sizeof(rgbCertHash);
|
|
CertHash.pbData = rgbCertHash;
|
|
if (!CertGetCertificateContextProperty(
|
|
pChain->rgpElement[1]->pCertContext,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
CertHash.pbData,
|
|
&CertHash.cbData
|
|
))
|
|
goto GetHashPropertyError;
|
|
if (NULL == (pFindCert = CertFindCertificateInStore(
|
|
hStore,
|
|
0, // dwCertEncodingType
|
|
0, // dwFindFlags
|
|
CERT_FIND_SHA1_HASH,
|
|
&CertHash,
|
|
NULL // pPrevCertContext
|
|
)))
|
|
goto UntrustedNTAuthCert;
|
|
CertFreeCertificateContext(pFindCert);
|
|
}
|
|
|
|
if (dwError) {
|
|
// For NO_REVOCATION or REVOCATION_OFFLINE update indices
|
|
pPolicyStatus->lChainIndex = lChainIndex;
|
|
pPolicyStatus->lElementIndex = lElementIndex;
|
|
}
|
|
CommonReturn:
|
|
pPolicyStatus->dwError = dwError;
|
|
return TRUE;
|
|
|
|
ErrorReturn:
|
|
pPolicyStatus->lChainIndex = 0;
|
|
pPolicyStatus->lElementIndex = 1;
|
|
MissingCACert:
|
|
dwError = (DWORD) CERT_E_UNTRUSTEDCA;
|
|
goto CommonReturn;
|
|
TRACE_ERROR(OpenNTAuthStoreError)
|
|
TRACE_ERROR(GetHashPropertyError)
|
|
TRACE_ERROR(UntrustedNTAuthCert)
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// SHA1 Key Identifier of the Microsoft roots
|
|
//--------------------------------------------------------------------------
|
|
const BYTE MicrosoftRootList[][SHA1_HASH_LEN] = {
|
|
// The following is the sha1 key identifier for the Microsoft root
|
|
{
|
|
0x4A, 0x5C, 0x75, 0x22, 0xAA, 0x46, 0xBF, 0xA4, 0x08, 0x9D,
|
|
0x39, 0x97, 0x4E, 0xBD, 0xB4, 0xA3, 0x60, 0xF7, 0xA0, 0x1D
|
|
},
|
|
|
|
// The following is the sha1 key identifier for the Microsoft root
|
|
// generated in 2001 with a key length of 4096 bits
|
|
{
|
|
0x0E, 0xAC, 0x82, 0x60, 0x40, 0x56, 0x27, 0x97, 0xE5, 0x25,
|
|
0x13, 0xFC, 0x2A, 0xE1, 0x0A, 0x53, 0x95, 0x59, 0xE4, 0xA4
|
|
},
|
|
};
|
|
|
|
#define MICROSOFT_ROOT_LIST_CNT (sizeof(MicrosoftRootList) / \
|
|
sizeof(MicrosoftRootList[0]))
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// SHA1 Key Identifier of the Microsoft test roots
|
|
//--------------------------------------------------------------------------
|
|
const BYTE MicrosoftTestRootList[][SHA1_HASH_LEN] = {
|
|
// The following is the sha1 key for the Microsoft Test Root:
|
|
// CN=Microsoft Test Root Authority
|
|
// OU=Microsoft Corporation
|
|
// OU=Copyright (c) 1999 Microsoft Corp.
|
|
//
|
|
// NotBefore:: Sat Jan 09 23:00:00 1999
|
|
// NotAfter:: Wed Dec 30 23:00:00 2020
|
|
{
|
|
0x22, 0xCD, 0x37, 0xF1, 0xB1, 0x47, 0x50, 0xAE, 0x53, 0x7C,
|
|
0x8C, 0x6A, 0x03, 0x67, 0x47, 0xE2, 0xB7, 0x1E, 0x17, 0xB7
|
|
},
|
|
};
|
|
|
|
#define MICROSOFT_TEST_ROOT_LIST_CNT (sizeof(MicrosoftTestRootList) / \
|
|
sizeof(MicrosoftTestRootList[0]))
|
|
|
|
|
|
BOOL
|
|
WINAPI
|
|
CertDllVerifyMicrosoftRootCertificateChainPolicy(
|
|
IN LPCSTR pszPolicyOID,
|
|
IN PCCERT_CHAIN_CONTEXT pChainContext,
|
|
IN PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
|
IN OUT PCERT_CHAIN_POLICY_STATUS pPolicyStatus
|
|
)
|
|
{
|
|
DWORD dwError;
|
|
LONG lElementIndex;
|
|
PCERT_SIMPLE_CHAIN pChain;
|
|
DWORD cChainElement;
|
|
PCCERT_CONTEXT pCert; // not refCount'ed
|
|
BYTE rgbKeyId[SHA1_HASH_LEN];
|
|
DWORD cbKeyId;
|
|
DWORD i;
|
|
DWORD dwFlags;
|
|
|
|
assert(pPolicyStatus && offsetof(CERT_CHAIN_POLICY_STATUS, lElementIndex) <
|
|
pPolicyStatus->cbSize);
|
|
|
|
pChain = pChainContext->rgpChain[0];
|
|
cChainElement = pChain->cElement;
|
|
|
|
// Check that the top level certificate contains the public
|
|
// key for the Microsoft root.
|
|
pCert = pChain->rgpElement[cChainElement - 1]->pCertContext;
|
|
|
|
cbKeyId = SHA1_HASH_LEN;
|
|
if (!CryptHashPublicKeyInfo(
|
|
NULL, // hCryptProv
|
|
CALG_SHA1,
|
|
0, // dwFlags
|
|
X509_ASN_ENCODING,
|
|
&pCert->pCertInfo->SubjectPublicKeyInfo,
|
|
rgbKeyId,
|
|
&cbKeyId) || SHA1_HASH_LEN != cbKeyId)
|
|
goto HashPublicKeyInfoError;
|
|
|
|
for (i = 0; i < MICROSOFT_ROOT_LIST_CNT; i++) {
|
|
if (0 == memcmp(MicrosoftRootList[i], rgbKeyId, SHA1_HASH_LEN))
|
|
goto SuccessReturn;
|
|
}
|
|
|
|
if (pPolicyPara && offsetof(CERT_CHAIN_POLICY_PARA, dwFlags) <
|
|
pPolicyPara->cbSize)
|
|
dwFlags = pPolicyPara->dwFlags;
|
|
else
|
|
dwFlags = 0;
|
|
|
|
if (dwFlags & MICROSOFT_ROOT_CERT_CHAIN_POLICY_ENABLE_TEST_ROOT_FLAG) {
|
|
for (i = 0; i < MICROSOFT_TEST_ROOT_LIST_CNT; i++) {
|
|
if (0 == memcmp(MicrosoftTestRootList[i], rgbKeyId, SHA1_HASH_LEN))
|
|
goto SuccessReturn;
|
|
}
|
|
}
|
|
|
|
goto InvalidMicrosoftRoot;
|
|
|
|
SuccessReturn:
|
|
dwError = 0;
|
|
lElementIndex = 0;
|
|
CommonReturn:
|
|
pPolicyStatus->dwError = dwError;
|
|
pPolicyStatus->lChainIndex = 0;
|
|
pPolicyStatus->lElementIndex = lElementIndex;
|
|
return TRUE;
|
|
|
|
ErrorReturn:
|
|
dwError = (DWORD) CERT_E_UNTRUSTEDROOT;
|
|
lElementIndex = cChainElement - 1;
|
|
goto CommonReturn;
|
|
TRACE_ERROR(HashPublicKeyInfoError)
|
|
TRACE_ERROR(InvalidMicrosoftRoot)
|
|
}
|