//+------------------------------------------------------------------------- // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1999 - 1999 // // File: rootlist.cpp // // Contents: Signed List of Trusted Roots Helper Functions // // // Functions: IRL_VerifyAuthRootAutoUpdateCtl // // History: 01-Aug-99 philh created //-------------------------------------------------------------------------- #include "global.hxx" #include #ifdef STATIC #undef STATIC #endif #define STATIC //+------------------------------------------------------------------------- // If the certificate has an EKU extension, returns an allocated and // decoded EKU. Otherwise, returns NULL. // // PkiFree() must be called to free the returned EKU. //-------------------------------------------------------------------------- STATIC PCERT_ENHKEY_USAGE WINAPI GetAndAllocCertEKUExt( IN PCCERT_CONTEXT pCert ) { PCERT_ENHKEY_USAGE pUsage = NULL; DWORD cbUsage; cbUsage = 0; if (!CertGetEnhancedKeyUsage( pCert, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, NULL, // pUsage &cbUsage) || 0 == cbUsage) goto GetEnhancedKeyUsageError; if (NULL == (pUsage = (PCERT_ENHKEY_USAGE) PkiNonzeroAlloc(cbUsage))) goto OutOfMemory; if (!CertGetEnhancedKeyUsage( pCert, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG, pUsage, &cbUsage)) goto GetEnhancedKeyUsageError; CommonReturn: return pUsage; ErrorReturn: if (pUsage) { PkiFree(pUsage); pUsage = NULL; } goto CommonReturn; SET_ERROR(GetEnhancedKeyUsageError, CERT_E_WRONG_USAGE) TRACE_ERROR(OutOfMemory) } //+------------------------------------------------------------------------- // The signature of the CTL is verified. The signer of the CTL is verified // up to a trusted root containing the predefined Microsoft public key. // // The signer and intermediate certificates must have the // szOID_ROOT_LIST_SIGNER enhanced key usage extension. //-------------------------------------------------------------------------- STATIC BOOL WINAPI VerifyAuthRootAutoUpdateCtlSigner( IN HCRYPTMSG hCryptMsg ) { BOOL fResult; DWORD dwLastError = 0; HCERTSTORE hMsgStore = NULL; PCCERT_CONTEXT pSignerCert = NULL; LPSTR pszUsageOID; CERT_CHAIN_PARA ChainPara; PCCERT_CHAIN_CONTEXT pChainContext = NULL; PCERT_SIMPLE_CHAIN pChain; DWORD cChainElement; CERT_CHAIN_POLICY_PARA BasePolicyPara; CERT_CHAIN_POLICY_STATUS BasePolicyStatus; CERT_CHAIN_POLICY_PARA MicrosoftRootPolicyPara; CERT_CHAIN_POLICY_STATUS MicrosoftRootPolicyStatus; PCERT_ENHKEY_USAGE pUsage = NULL; DWORD i; if (NULL == (hMsgStore = CertOpenStore( CERT_STORE_PROV_MSG, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, // hCryptProv 0, // dwFlags hCryptMsg // pvPara ))) goto OpenMsgStoreError; if (!CryptMsgGetAndVerifySigner( hCryptMsg, 0, // cSignerStore NULL, // rghSignerStore 0, // dwFlags &pSignerCert, NULL // pdwSignerIndex )) goto CryptMsgGetAndVerifySignerError; pszUsageOID = szOID_ROOT_LIST_SIGNER; memset(&ChainPara, 0, sizeof(ChainPara)); ChainPara.cbSize = sizeof(ChainPara); ChainPara.RequestedUsage.dwType = USAGE_MATCH_TYPE_AND; ChainPara.RequestedUsage.Usage.cUsageIdentifier = 1; ChainPara.RequestedUsage.Usage.rgpszUsageIdentifier = &pszUsageOID; if (!CertGetCertificateChain( NULL, // hChainEngine pSignerCert, NULL, // pTime hMsgStore, &ChainPara, CERT_CHAIN_DISABLE_AUTH_ROOT_AUTO_UPDATE, NULL, // pvReserved &pChainContext )) goto GetChainError; // Do the basic chain policy verification memset(&BasePolicyPara, 0, sizeof(BasePolicyPara)); BasePolicyPara.cbSize = sizeof(BasePolicyPara); // We explicitly check for the Microsoft Root below. It doesn't need // to be in the root store. BasePolicyPara.dwFlags = CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG | CERT_CHAIN_POLICY_IGNORE_NOT_TIME_NESTED_FLAG; memset(&BasePolicyStatus, 0, sizeof(BasePolicyStatus)); BasePolicyStatus.cbSize = sizeof(BasePolicyStatus); if (!CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_BASE, pChainContext, &BasePolicyPara, &BasePolicyStatus )) goto VerifyChainBasePolicyError; if (0 != BasePolicyStatus.dwError) goto ChainBasePolicyError; // Check that we have more than just the signer cert. pChain = pChainContext->rgpChain[0]; cChainElement = pChain->cElement; if (2 > cChainElement) goto MissingSignerChainCertsError; // Check that the top level certificate contains the public // key for the Microsoft root. memset(&MicrosoftRootPolicyPara, 0, sizeof(MicrosoftRootPolicyPara)); MicrosoftRootPolicyPara.cbSize = sizeof(MicrosoftRootPolicyPara); memset(&MicrosoftRootPolicyStatus, 0, sizeof(MicrosoftRootPolicyStatus)); MicrosoftRootPolicyStatus.cbSize = sizeof(MicrosoftRootPolicyStatus); if (!CertVerifyCertificateChainPolicy( CERT_CHAIN_POLICY_MICROSOFT_ROOT, pChainContext, &MicrosoftRootPolicyPara, &MicrosoftRootPolicyStatus )) goto VerifyChainMicrosoftRootPolicyError; if (0 != MicrosoftRootPolicyStatus.dwError) goto ChainMicrosoftRootPolicyError; // Check that the signer and intermediate certs have the RootListSigner // Usage extension for (i = 0; i < cChainElement - 1; i++) { PCCERT_CONTEXT pCert; // not refCount'ed DWORD j; pCert = pChain->rgpElement[i]->pCertContext; pUsage = GetAndAllocCertEKUExt(pCert); if (NULL == pUsage) goto GetAndAllocCertEKUExtError; for (j = 0; j < pUsage->cUsageIdentifier; j++) { if (0 == strcmp(szOID_ROOT_LIST_SIGNER, pUsage->rgpszUsageIdentifier[j])) break; } if (j == pUsage->cUsageIdentifier) goto MissingRootListSignerUsageError; PkiFree(pUsage); pUsage = NULL; } fResult = TRUE; CommonReturn: if (pChainContext) CertFreeCertificateChain(pChainContext); if (pUsage) PkiFree(pUsage); if (pSignerCert) CertFreeCertificateContext(pSignerCert); if (hMsgStore) CertCloseStore(hMsgStore, 0); SetLastError(dwLastError); return fResult; ErrorReturn: dwLastError = GetLastError(); fResult = FALSE; goto CommonReturn; TRACE_ERROR(OpenMsgStoreError) TRACE_ERROR(CryptMsgGetAndVerifySignerError) TRACE_ERROR(GetChainError) TRACE_ERROR(VerifyChainBasePolicyError) SET_ERROR_VAR(ChainBasePolicyError, BasePolicyStatus.dwError) TRACE_ERROR(VerifyChainMicrosoftRootPolicyError) SET_ERROR_VAR(ChainMicrosoftRootPolicyError, MicrosoftRootPolicyStatus.dwError) SET_ERROR(MissingSignerChainCertsError, CERT_E_CHAINING) TRACE_ERROR(GetAndAllocCertEKUExtError) SET_ERROR(MissingRootListSignerUsageError, CERT_E_WRONG_USAGE) } //+------------------------------------------------------------------------- // Returns TRUE if all the CTL fields are valid. Checks for the following: // - The SubjectUsage is szOID_ROOT_LIST_SIGNER // - If NextUpdate isn't NULL, that the CTL is still time valid // - Only allow roots identified by their sha1 hash //-------------------------------------------------------------------------- STATIC BOOL WINAPI VerifyAuthRootAutoUpdateCtlFields( IN PCTL_INFO pCtlInfo ) { BOOL fResult; // Must have the szOID_ROOT_LIST_SIGNER usage if (1 != pCtlInfo->SubjectUsage.cUsageIdentifier || 0 != strcmp(szOID_ROOT_LIST_SIGNER, pCtlInfo->SubjectUsage.rgpszUsageIdentifier[0])) goto InvalidSubjectUsageError; // If NextUpdate is present, verify that the CTL hasn't expired. if (pCtlInfo->NextUpdate.dwLowDateTime || pCtlInfo->NextUpdate.dwHighDateTime) { SYSTEMTIME SystemTime; FILETIME FileTime; GetSystemTime(&SystemTime); SystemTimeToFileTime(&SystemTime, &FileTime); if (CompareFileTime(&FileTime, &pCtlInfo->NextUpdate) > 0) goto ExpiredCtlError; } // Only allow roots identified by their sha1 hash if (0 != strcmp(szOID_OIWSEC_sha1, pCtlInfo->SubjectAlgorithm.pszObjId)) goto InvalidSubjectAlgorithm; fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(InvalidSubjectUsageError, ERROR_INVALID_DATA) SET_ERROR(ExpiredCtlError, CERT_E_EXPIRED) SET_ERROR(InvalidSubjectAlgorithm, ERROR_INVALID_DATA) } //+------------------------------------------------------------------------- // Returns TRUE if the CTL doesn't have any critical extensions. //-------------------------------------------------------------------------- STATIC BOOL WINAPI VerifyAuthRootAutoUpdateCtlExtensions( IN PCTL_INFO pCtlInfo ) { BOOL fResult; PCERT_EXTENSION pExt; DWORD cExt; // Verify the extensions for (cExt = pCtlInfo->cExtension, pExt = pCtlInfo->rgExtension; 0 < cExt; cExt--, pExt++) { if (pExt->fCritical) { goto CriticalExtensionError; } } fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; SET_ERROR(CriticalExtensionError, ERROR_INVALID_DATA) } //+------------------------------------------------------------------------- // Verifies that the CTL contains a valid list of AuthRoots used for // Auto Update. // // The signature of the CTL is verified. The signer of the CTL is verified // up to a trusted root containing the predefined Microsoft public key. // The signer and intermediate certificates must have the // szOID_ROOT_LIST_SIGNER enhanced key usage extension. // // The CTL fields are validated as follows: // - The SubjectUsage is szOID_ROOT_LIST_SIGNER // - If NextUpdate isn't NULL, that the CTL is still time valid // - Only allow roots identified by their sha1 hash // // If the CTL contains any critical extensions, then, the // CTL verification fails. //-------------------------------------------------------------------------- BOOL WINAPI IRL_VerifyAuthRootAutoUpdateCtl( IN PCCTL_CONTEXT pCtl ) { BOOL fResult; PCTL_INFO pCtlInfo; // not allocated if (!VerifyAuthRootAutoUpdateCtlSigner(pCtl->hCryptMsg)) goto VerifyCtlSignerError; pCtlInfo = pCtl->pCtlInfo; if (!VerifyAuthRootAutoUpdateCtlFields(pCtlInfo)) goto VerifyCtlFieldsError; if (!VerifyAuthRootAutoUpdateCtlExtensions(pCtlInfo)) goto VerifyCtlExtensionsError; fResult = TRUE; CommonReturn: return fResult; ErrorReturn: fResult = FALSE; goto CommonReturn; TRACE_ERROR(VerifyCtlSignerError) TRACE_ERROR(VerifyCtlFieldsError) TRACE_ERROR(VerifyCtlExtensionsError) }