diff -r 000000000000 -r 2c201484c85f cryptoservices/certificateandkeymgmt/pkixcertbase/pkixcertchainao.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cryptoservices/certificateandkeymgmt/pkixcertbase/pkixcertchainao.cpp Wed Jul 08 11:25:26 2009 +0100 @@ -0,0 +1,613 @@ +/* +* Copyright (c) 1998-2009 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of the License "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* +*/ + + +#include "pkixcertchainao.h" +#include "pkixCons.h" +#include +#include +#include + +CPKIXCertChainAO* CPKIXCertChainAO::NewL(MCertStore& aCertStore, + CPKIXCertChainBase &aPKIXCertChain, + const RPointerArray& aRootCerts) + { + CPKIXCertChainAO* self = new(ELeave) CPKIXCertChainAO(aCertStore, aPKIXCertChain); + CleanupStack::PushL(self); + self->ConstructL(aRootCerts); + CleanupStack::Pop(self); + return self; + } + +CPKIXCertChainAO* CPKIXCertChainAO::NewL(MCertStore& aCertStore, + CPKIXCertChainBase &aPKIXCertChain, + const TUid aClient) + { + return new(ELeave) CPKIXCertChainAO(aCertStore, aPKIXCertChain, aClient); + } + +CPKIXCertChainAO::~CPKIXCertChainAO() + { + Cancel(); + + delete iRoots; + delete iBuilder; + delete iCertsFromStoreRoots; + } + +CPKIXCertChainAO::CPKIXCertChainAO(MCertStore& aCertStore, + CPKIXCertChainBase &aPKIXCertChain) + : CActive(EPriorityNormal), iCertStore(&aCertStore), iPKIXCertChain(aPKIXCertChain) + { + CActiveScheduler::Add(this); + } + +CPKIXCertChainAO::CPKIXCertChainAO(MCertStore& aCertStore, + CPKIXCertChainBase &aPKIXCertChain, + const TUid aClient) + : CActive(EPriorityNormal), iCertStore(&aCertStore), + iPKIXCertChain(aPKIXCertChain), iClient(aClient) + { + CActiveScheduler::Add(this); + } + +void CPKIXCertChainAO::ConstructL(const RPointerArray& aRootCerts) + { + CPKIXCertsFromClient* roots = CPKIXCertsFromClient::NewLC(aRootCerts); + iRoots = CPKIXChainBuilder::NewL(); + iRoots->AddSourceL(roots); + CleanupStack::Pop(roots); + } + +void CPKIXCertChainAO::RunL() + { + User::LeaveIfError(iStatus.Int()); + + switch (iState) + { + case EAddRoots: + HandleEAddRootsL(); + break; + + case ERootsInitialized: + HandleERootsInitializedL(); + break; + + case EBuildChainStart: + HandleEBuildChainStartL(); + break; + + case EBuildChainAddCandidateEnd: + HandleEBuildChainAddCandidateEndL(); + break; + + case EBuildChainCertsFromStoreBegin: + HandleEBuildChainCertsFromStoreBeginL(); + break; + + case EBuildChainCertsFromStoreEnd: + HandleEBuildChainCertsFromStoreEndL(); + break; + + case EAddCandidateIntermediateCertsEnd: + HandleEAddCandidateIntermediateCertsEndL(); + break; + + case EValidateEnd: + HandleEValidateEndL(); + break; + + default: + User::Panic(_L("CPKIXCertChainAO"), 1); + break; + } + } + +TInt CPKIXCertChainAO::RunError(TInt aError) +{ + iPKIXCertChain.RemoveLastCerts(iNumberOfAddedCertificates); + iNumberOfAddedCertificates = 0; + + delete iRoots; + iRoots = 0; + + delete iBuilder; + iBuilder = 0; + delete iCertsFromStoreRoots; + iCertsFromStoreRoots = 0; + + iValidationResult->RemovePolicies(); + + User::RequestComplete(iOriginalRequestStatus, aError); + return KErrNone; +} + +/** + * Creates a list of all the certificates retrieved from the store based on the filter passed. + */ + +void CPKIXCertChainAO::HandleEAddRootsL() + { + __ASSERT_DEBUG(!iCertsFromStoreRoots, User::Panic(_L("CPKICCertChainAO"), 1)); + iCertsFromStoreRoots = CPKIXCertsFromStore::NewL(*iCertStore, iClient); + iCertsFromStoreRoots->Initialize(iStatus); + iState = ERootsInitialized; + SetActive(); + } + +/** + * Adds the list of certificates retrieved from the store, iRoots (CPKIXChainBuilder) + * maintains a templatized list of all the certificates in MPKIXCertSource format. + */ + +void CPKIXCertChainAO::HandleERootsInitializedL() + { + iRoots->AddSourceL(iCertsFromStoreRoots); + // Ownership has been passed to iRoots + iCertsFromStoreRoots = 0; + iState = EBuildChainStart; + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNone); + SetActive(); + } + +void CPKIXCertChainAO::HandleEBuildChainStartL() + { + if ( false == iPKIXCertChain.ChainHasRoot()) + { + if (iPKIXCertChain.Chain().Count() == 0) + { + iState = EValidateEnd; + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNone); + } + else + { + //1) look for an issuer that's a root + iRoots->AddIssuer(iNumberOfAddedCertificates, iAddIssuerResult, iPKIXCertChain.Chain(), iStatus); + iState = EBuildChainAddCandidateEnd; + } + } + else + { + // This is the correct state as at this point the chain of certificate has been build upto a + // root certificate. + iState = EValidateEnd; + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNone); + } + SetActive(); + } + +void CPKIXCertChainAO::HandleEBuildChainAddCandidateEndL() + { + if (iAddIssuerResult) + { + iPKIXCertChain.SetChainHasRoot(ETrue); + iState = EValidateEnd; + } + else + { + //2) look for a non-root issuer in intermediate certs + iBuilder = CPKIXChainBuilder::NewL(); + + CPKIXCertsFromClient* serverCerts = CPKIXCertsFromClient::NewLC(iPKIXCertChain.IntermediateCerts()); + iBuilder->AddSourceL(serverCerts); + CleanupStack::Pop(serverCerts); + + iState = EBuildChainCertsFromStoreBegin; + } + + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNone); + SetActive(); + } + +void CPKIXCertChainAO::HandleEBuildChainCertsFromStoreBeginL() + { + //3) look for a non-root issuer in the store + iCertsFromStoreRoots = CPKIXCertsFromStore::NewL(*iCertStore); + iCertsFromStoreRoots->Initialize(iStatus); + iState = EBuildChainCertsFromStoreEnd; + SetActive(); + } + +void CPKIXCertChainAO::HandleEBuildChainCertsFromStoreEndL() + { + iBuilder->AddSourceL(iCertsFromStoreRoots); + iCertsFromStoreRoots = 0; + + iBuilder->AddIssuer(iNumberOfAddedCertificates, iAddIssuerResult, iPKIXCertChain.Chain(), iStatus); + iState = EAddCandidateIntermediateCertsEnd; + SetActive(); + } + +void CPKIXCertChainAO::HandleEAddCandidateIntermediateCertsEndL() + { + if (iAddIssuerResult) + { + // cert is a pointer to something we don't own + CX509Certificate* cert = iPKIXCertChain.Chain().At(iPKIXCertChain.Chain().Count() - 1); + + /* If the issuer is not a self signed certificate then it cannot be trusted anchor for the chain + * validation process, this means that we restart the certification validation process. + */ + + if (!(cert->IsSelfSignedL())) + { + iState = EBuildChainStart; + } + else + { + iState = EValidateEnd; + } + } + else + { + iState = EValidateEnd; + } + + delete iBuilder; + iBuilder = 0; + + TRequestStatus* status = &iStatus; + User::RequestComplete(status, KErrNone); + SetActive(); + } + +void CPKIXCertChainAO::HandleEValidateEndL() + { + InitParamsL(); + + __ASSERT_DEBUG(iValidationResult, User::Panic(_L("CPKICCertChainAO"), 1)); + DoValidateL(*iValidationResult, iValidationTime, iInitialPolicies); + + User::RequestComplete(iOriginalRequestStatus, KErrNone); + } + +void CPKIXCertChainAO::DoCancel() + { + delete iRoots; + iRoots = 0; + + delete iBuilder; + iBuilder = 0; + + delete iCertsFromStoreRoots; + iCertsFromStoreRoots = 0; + + User::RequestComplete(iOriginalRequestStatus, KErrCancel); + } + +void CPKIXCertChainAO::ValidateL(CPKIXValidationResultBase& aValidationResult, + const TTime& aValidationTime, + const CArrayPtr* aInitialPolicies, + TRequestStatus& aStatus) + { + aValidationResult.Reset(); + iValidationResult = &aValidationResult; + iValidationTime = aValidationTime; + iInitialPolicies = aInitialPolicies; + iOriginalRequestStatus = &aStatus; + iNumberOfAddedCertificates = 0; + + __ASSERT_ALWAYS(!IsActive(), User::Panic(_L("CPKICCertChainAO"), 1)); + + if (!iRoots) + { + // If iRoots is 0, it means that the caller gave a uid and that + // we must retrieve the trusted certificates from the different + // stores + iRoots = CPKIXChainBuilder::NewL(); + iState = EAddRoots; + } + else + { + // The caller gave a set of certificates it trusts, + // so we don't have to retrieve anything from the stores + iState = EBuildChainStart; + } + + aStatus = KRequestPending; + TRequestStatus *status = &iStatus; + User::RequestComplete(status, KErrNone); + SetActive(); + } + +void CPKIXCertChainAO::CancelValidate() + { + Cancel(); + } + +void CPKIXCertChainAO::InitParamsL() +/* +this function initialises signing key parameters for the certificates +-only DSA needs these at present +-we get the signing key, from the spki of the issuer +-if it's dsa, we look for params here + -if we find them we initialise the cert with them + -otherwise, we look in the issuer's issuer + -if we don't find them there we give up. +*/ + { + + // If the root is DSA signed, set its parameters + + TInt count = iPKIXCertChain.Chain().Count(); + + CX509Certificate* current = iPKIXCertChain.Chain().At(count-1); + TAlgorithmId signingAlgorithm = current->SigningAlgorithm().AsymmetricAlgorithm().Algorithm(); + + if (signingAlgorithm == EDSA) + { + + const CSubjectPublicKeyInfo& key = current->PublicKey(); + SetParamsL(*current, key.EncodedParams()); + + } + + // Also the rest of the chain + + for (TInt i = count - 2; i >= 0; i--) + { + + current = iPKIXCertChain.Chain().At(i); + TAlgorithmId signingAlgorithm = current->SigningAlgorithm().AsymmetricAlgorithm().Algorithm(); + + if (signingAlgorithm == EDSA) + { + + // Look down the chain for parameters + + for (TInt j = i+1; j < count; j++) + { + + CX509Certificate* issuer = iPKIXCertChain.Chain().At(j); + const CSubjectPublicKeyInfo& key = issuer->PublicKey(); + if (key.EncodedParams() != KNullDesC8 && key.AlgorithmId() == EDSA) + { + SetParamsL(*current, key.EncodedParams()); + break; + } + + } + + } + + } + } + +void CPKIXCertChainAO::SetParamsL(CX509Certificate& aCert, const TPtrC8& aEncodedParams) + { + TX509KeyFactory factory; + CDSAParameters* theDSAParams = factory.DSAParametersL(aEncodedParams); + CleanupStack::PushL(theDSAParams); + + CSigningKeyParameters* params = CSigningKeyParameters::NewLC(); + params->SetDSAParamsL(*theDSAParams); + + aCert.SetParametersL(*params); + + CleanupStack::PopAndDestroy(2, theDSAParams); + } + +void CPKIXCertChainAO::DoValidateL(CPKIXValidationResultBase& aValidationResult, + const TTime& aValidationTime, + const CArrayPtr* aInitialPolicies) + { + if (!iPKIXCertChain.ChainHasRoot()) + { + aValidationResult.SetError(EChainHasNoRoot, 0); + } + else + { + CPKIXValidationState* state = CPKIXValidationState::NewLC(aValidationTime, iPKIXCertChain.Chain().Count(), aInitialPolicies); + TRAPD(err, ProcessCertsL(*state, aValidationResult)); + //a leave here means either: + // -a validation error, in which case we've set the error field in result, or + // -some other error (e.g. OOM) in which case error is still EChainHasNoRoot + if ((err != KErrNone) && ((aValidationResult.Error().iReason) == EChainHasNoRoot)) + //then we left with a non-validation-related error, so leave again... + { + User::Leave(err); + } + CleanupStack::PopAndDestroy(state); + } + } + +// ProcessCertsL: This function validates a complete certificate +// chain. If an error occurs in this function the function +// SetErrorAndLeaveL must be called. +// +// Note Do not use SetErrorAndLeaveL with EChainHasNoRoot (see TRAP code in +// CPKIXCertChainAO::DoValidateL ) +void CPKIXCertChainAO::ProcessCertsL(CPKIXValidationState& aState, + CPKIXValidationResultBase& aResult) const + { + TPKIXPolicyConstraint policy(aState, aResult); + TPKIXNameConstraint name(aState, aResult); + TPKIXBasicConstraint basic(aState, aResult); + TPKIXKeyUsageConstraint keyUsage(aState, aResult); + for (; aState.iPos >= 0; aState.iPos--) + { + aState.iMaxPathLength--; + if (aState.iMaxPathLength < aState.iPos) + { + aResult.SetErrorAndLeaveL(EPathTooLong, aState.iPos); + } + const CX509Certificate* current = iPKIXCertChain.Chain().At(aState.iPos); + CCertificateValidationWarnings* certWarnings = CCertificateValidationWarnings::NewLC(aState.iPos); + aResult.AppendCertificateValidationObjectL(*certWarnings); + CleanupStack::Pop(certWarnings); + CriticalExtsL(aState, *current); + CheckCriticalExtsL(aState, aResult); + CheckSignatureAndNameL(*current, aState, aResult); + //!!!!NO!!checks for revocation at this time!! + + if (!(current->ValidityPeriod().Valid(aState.iValidationTime))) + { + //validity period invalid, now check how to report this + if (iPKIXCertChain.ValidityPeriodCheckFatal()) + { + aResult.SetErrorAndLeaveL(EDateOutOfRange, aState.iPos); + } + else + { + aResult.AppendWarningL(TValidationStatus(EDateOutOfRange, aState.iPos)); + } + } + + policy.CheckCertPoliciesL(*current); + name.CheckNameConstraintsL(*current); + keyUsage.CheckKeyUsageL(*current); + if (aState.iPos < (iPKIXCertChain.Chain().Count() - 1)) + { + basic.CheckCertSubjectTypeL(*current); + } + basic.UpdatePathLengthConstraintsL(*current); + name.UpdateNameConstraintsL(*current); + policy.UpdatePolicyConstraintsL(*current); + aState.iCriticalExts->Reset(); + } + policy.FinishPolicyCheckL(); + //*copy* all policies from aState.iAuthorityConstrainedPolicies into aResult.iPolicies + TInt policyCount = aState.iAuthorityConstrainedPolicies->Count(); + for (TInt i = 0; i < policyCount; i ++) + { + CX509CertPolicyInfo* policyInfo = CX509CertPolicyInfo::NewLC(*(aState.iAuthorityConstrainedPolicies->At(i))); + aResult.AppendPolicyL(*policyInfo); + CleanupStack::Pop(policyInfo); + } + + aResult.SetError(EValidatedOK, 0); + } + +void CPKIXCertChainAO::CriticalExtsL(CPKIXValidationState& aState, + const CX509Certificate& aCert) const + { + const CArrayPtrFlat& exts = aCert.Extensions(); + TInt count = exts.Count(); + for (TInt i = 0; i < count; i++) + { + CX509CertExtension* ext = exts.At(i); + if (ext->Critical()) + { + aState.iCriticalExts->AppendL(ext); + } + } + } + +void CPKIXCertChainAO::CheckSignatureAndNameL(const CX509Certificate& aCert, CPKIXValidationState& aState, + CPKIXValidationResultBase& aResult) const + { + TInt issuerPos = aState.iPos + 1; + if (issuerPos == iPKIXCertChain.Chain().Count()) + //then it's the root + { + if (aCert.IssuerName().ExactMatchL(aCert.SubjectName())) + //then it claims to be self signed, sig must verify + { + if (!(aCert.VerifySignatureL(aCert.PublicKey().KeyData()))) + { + aResult.SetErrorAndLeaveL(ESignatureInvalid, aState.iPos); + } + } + else + //we generate a warning + { + aResult.AppendWarningL(TValidationStatus(ERootCertNotSelfSigned, aState.iPos)); + } + } + else + //then it isn't the root: so names must chain & sigs must verify + { + const CX509Certificate* issuer = iPKIXCertChain.Chain().At(issuerPos); + if (!(aCert.IssuerName().ExactMatchL(issuer->SubjectName()))) + { + aResult.SetErrorAndLeaveL(ENamesDontChain, aState.iPos); + } + if (!(aCert.VerifySignatureL(issuer->PublicKey().KeyData()))) + { + aResult.SetErrorAndLeaveL(ESignatureInvalid, aState.iPos); + } + } + } + +void CPKIXCertChainAO::CheckCriticalExtsL(CPKIXValidationState& aState, CPKIXValidationResultBase& aResult) const + { + TBool foundUnrecognisedCritExt; + + // retrieve the supported list of critical extensions. If a critical extension is found whose OID matches an + // element in this set then certificate validation shall treat this as a warning instead of an error. + const RPointerArray& supportedCritExt = iPKIXCertChain.SupportedCriticalExtensions(); + + TInt count = aState.iCriticalExts->Count(); + TInt supportedCount = supportedCritExt.Count(); + for (TInt i = 0; i < count; i++) + { + foundUnrecognisedCritExt = ETrue; + const CX509CertExtension* ext = aState.iCriticalExts->At(i); + const TPtrC& extName = ext->Id(); + + for (TInt j = 0; j < supportedCount; ++j) + { + if (extName == *supportedCritExt[j]) + { + foundUnrecognisedCritExt = EFalse; + HBufC* oid = extName.AllocLC(); + aResult.AppendCriticalExtensionWarningL(*oid); + CleanupStack::Pop(oid); + break; + } + } + + if (extName == KExtendedKeyUsage) + { + aResult.AppendWarningL(TValidationStatus(ECriticalExtendedKeyUsage, aState.iPos)); + } + else if (extName == KPolicyMapping) + { + aResult.AppendWarningL(TValidationStatus(ECriticalPolicyMapping, aState.iPos)); + } + else if (extName == KInhibitAnyPolicy) + { + //ignore this in the same way + } + else if (extName == KDeviceIdListConstraint) + { + aResult.AppendWarningL(TValidationStatus(ECriticalDeviceId, aState.iPos)); + } + else if(extName == KSidListConstraint) + { + aResult.AppendWarningL(TValidationStatus(ECriticalSid, aState.iPos)); + } + else if(extName == KVidListConstraint) + { + aResult.AppendWarningL(TValidationStatus(ECriticalVid, aState.iPos)); + } + else if(extName == KCapabilitiesConstraint) + { + aResult.AppendWarningL(TValidationStatus(ECriticalCapabilities, aState.iPos)); + } + + if (foundUnrecognisedCritExt) + { + aResult.SetErrorAndLeaveL(EUnrecognizedCriticalExtension, aState.iPos); + } + + } + }