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.
376 lines
12 KiB
376 lines
12 KiB
//+-------------------------------------------------------------------------
|
|
// 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 <dbgdef.h>
|
|
|
|
#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)
|
|
}
|