Leaked source code of windows server 2003
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

//+-------------------------------------------------------------------------
//
// 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)
}