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