diff -r 000000000000 -r 2c201484c85f cryptoservices/certificateandkeymgmt/certstore/CCheckedCertStore.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/cryptoservices/certificateandkeymgmt/certstore/CCheckedCertStore.cpp Wed Jul 08 11:25:26 2009 +0100 @@ -0,0 +1,786 @@ +/* +* Copyright (c) 2002-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 "CCheckedCertStore.h" + +#include +#include +#include +#include +#include +#include + +#include "certificateapps.h" + +_LIT(KPanicCategory, "CCheckedCertStore"); +#define assert(x) __ASSERT_ALWAYS((x), User::Panic(KPanicCategory, 1)); + +///////////////////////////////////////////////////////////////////// +// CCheckedCertStore +///////////////////////////////////////////////////////////////////// + +CCheckedCertStore::~CCheckedCertStore() + { + Cancel(); + Cleanup(); + + // Release the cert store - no need to release the token since this would + // have been done as part of MCTTokenInterface::Release() + iCertStore.Release(); + + iFs.Close(); + } + +CCheckedCertStore::CCheckedCertStore(MCTCertStore& aTokenIF, RProperty& aProperty) +: CActive(EPriorityStandard), + iCertStore(aTokenIF), + iPSCertstoreChangePropertyRef(aProperty) + { + // need to add reference since we now have the token + iCertStore.Token().AddRef(); + } + +CCheckedCertStore::CCheckedCertStore(MCTWritableCertStore& aTokenIF, RProperty& aProperty) +: CActive(EPriorityStandard), + iCertStore(aTokenIF), + iWritableCertStore(&aTokenIF), + iPSCertstoreChangePropertyRef(aProperty) + { + // need to add reference since we now have the token + iCertStore.Token().AddRef(); + } + +/*static*/ CCheckedCertStore* CCheckedCertStore::NewCheckedCertStoreL(MCTTokenInterface* aTokenIF, RProperty& aProperty) + { + assert(aTokenIF); + MCTCertStore& tokenInterface = static_cast(*aTokenIF); + CCheckedCertStore* me = new (ELeave) CCheckedCertStore(tokenInterface, aProperty); + CleanupReleasePushL(*me); + me->ConstructL(); + CleanupStack::Pop(); + return (me); + } + +/*static*/ CCheckedCertStore* CCheckedCertStore::NewCheckedWritableCertStoreL(MCTTokenInterface* aTokenIF, RProperty& aProperty) + { + assert(aTokenIF); + MCTWritableCertStore& tokenInterface = static_cast(*aTokenIF); + CCheckedCertStore* me = new (ELeave) CCheckedCertStore(tokenInterface, aProperty); + CleanupReleasePushL(*me); + me->ConstructL(); + CleanupStack::Pop(); + return (me); + } + +void CCheckedCertStore::ConstructL() + { + User::LeaveIfError(iFs.Connect()); + CActiveScheduler::Add(this); + } + +MCTToken& CCheckedCertStore::Token() + { + return iCertStore.Token(); + } + +// May require checking against the keystore *after* calling the server to complete +// the List request +void CCheckedCertStore::List(RMPointerArray& aCerts, const CCertAttributeFilter& aFilter, + TRequestStatus& aStatus) + { + assert(iState == EIdleState); + + // Only allow filtering on key usage for user certs + if (aFilter.iKeyUsage != EX509UsageAll && + (!aFilter.iOwnerTypeIsSet || EUserCertificate != aFilter.iOwnerType)) + { + TRequestStatus* status = &aStatus; + User::RequestComplete(status, KErrNotSupported); + return; + } + + // Store caller parameters for later reference + iCallerCerts = &aCerts; + iCallerFilter = &aFilter; + aStatus = KRequestPending; + iCallerStatus = &aStatus; + + iState = EList; + iCertStore.List(aCerts, aFilter, iStatus); + SetActive(); + } + +void CCheckedCertStore::CancelList() + { + if (iState == EList || + iState == EInitKeyStoreForList || + iState == EGetKeyInfosForList) + { + Cancel(); + } + } + +void CCheckedCertStore::GetCert(CCTCertInfo*& aCertInfo, const TCTTokenObjectHandle& aHandle, + TRequestStatus& aStatus) + { + assert(iState == EIdleState); + iCertStore.GetCert(aCertInfo, aHandle, aStatus); + } + +void CCheckedCertStore::CancelGetCert() + { + iCertStore.CancelGetCert(); + } + +void CCheckedCertStore::Applications(const CCTCertInfo& aCertInfo, RArray& aApplications, + TRequestStatus& aStatus) + { + assert(iState == EIdleState); + iCertStore.Applications(aCertInfo, aApplications, aStatus); + } + +void CCheckedCertStore::CancelApplications() + { + iCertStore.CancelApplications(); + } + +void CCheckedCertStore::IsApplicable(const CCTCertInfo& aCertInfo, TUid aApplication, + TBool& aIsApplicable, TRequestStatus& aStatus) + { + assert(iState == EIdleState); + iCertStore.IsApplicable(aCertInfo, aApplication, aIsApplicable, aStatus); + } + +void CCheckedCertStore::CancelIsApplicable() + { + iCertStore.CancelIsApplicable(); + } + +void CCheckedCertStore::Trusted(const CCTCertInfo& aCertInfo, TBool& aTrusted, + TRequestStatus& aStatus) + { + assert(iState == EIdleState); + iCertStore.Trusted(aCertInfo, aTrusted, aStatus); + } + +void CCheckedCertStore::CancelTrusted() + { + iCertStore.CancelTrusted(); + } + +void CCheckedCertStore::Retrieve(const CCTCertInfo& aCertInfo, TDes8& aEncodedCert, + TRequestStatus& aStatus) + { + assert(iState == EIdleState); + iCertStore.Retrieve(aCertInfo, aEncodedCert, aStatus); + } + +void CCheckedCertStore::CancelRetrieve() + { + iCertStore.CancelRetrieve(); + } + + +void CCheckedCertStore::Add(const TDesC& aLabel, + TCertificateFormat aFormat, + TCertificateOwnerType aCertificateOwnerType, + const TKeyIdentifier* aSubjectKeyId, + const TKeyIdentifier* aIssuerKeyId, + const TDesC8& aCert, + TRequestStatus& aStatus) + { + // default value for aDeletable = ETrue + Add( aLabel, aFormat, aCertificateOwnerType, aSubjectKeyId, + aIssuerKeyId, aCert, ETrue, aStatus ); + } + +// new Add(.., TBool aDeletable, ..) method from MCTWritableCertStore +void CCheckedCertStore::Add( const TDesC& aLabel, + TCertificateFormat aFormat, + TCertificateOwnerType aCertificateOwnerType, + const TKeyIdentifier* aSubjectKeyId, + const TKeyIdentifier* aIssuerKeyId, + const TDesC8& aCert, + const TBool aDeletable, + TRequestStatus& aStatus + ) + { + assert(iWritableCertStore); + assert(iState == EIdleState); + + TRAPD(err, DoAddL( aLabel, + aFormat, + aCertificateOwnerType, + aSubjectKeyId, + aIssuerKeyId, + aCert, + aDeletable, + aStatus ) ); + + if (err != KErrNone) + { + Complete(err); + } + } + + +void CCheckedCertStore::DoAddL( const TDesC& aLabel, + TCertificateFormat aFormat, + TCertificateOwnerType aCertificateOwnerType, + const TKeyIdentifier* aSubjectKeyId, + const TKeyIdentifier* aIssuerKeyId, + const TDesC8& aCert, + const TBool aDeletable, + TRequestStatus& aStatus) + { + + // Store caller parameters for later use + aStatus = KRequestPending; + iCallerStatus = &aStatus; + iFormat = aFormat; + iCertificateOwnerType = aCertificateOwnerType; + iSubjectKeyId = aSubjectKeyId; + iIssuerKeyId = aIssuerKeyId; + iDeletable = aDeletable; + + // Store (copy) aCert (cert data) into iCertificate[:HBufC8] + assert(!iCertificate); + iCertificate = HBufC8::NewMaxL(aCert.Size()); + TPtr8 theCert(iCertificate->Des()); + theCert.FillZ(); + theCert.Copy(aCert); + + // Store (copy) aLabel (cert label) into iCertLabel[:HBufC] + assert(!iCertLabel); + iCertLabel = HBufC::NewMaxL(aLabel.Length()); + TPtr theLabel(iCertLabel->Des()); + theLabel.FillZ(); + theLabel.Copy(aLabel); + + // Checks subject key ID with certificate data, and sets up key filter + // which is used later to determine whether there is a key with the + // appropriate subject and thus, if it is OK to add the certificate + ComputeAndCheckSubjectKeyIdL(); + + // Is keystore checking required? Only if a user certificate + if (EUserCertificate==aCertificateOwnerType) + { + InitialiseKeyStoreL(EInitKeyStoreForAdd); + } + else + { + iState = EAdd; + + // try new method first + iWritableCertStore->Add( *iCertLabel, // call new method + iFormat, + iCertificateOwnerType, + iSubjectKeyId, + iIssuerKeyId, + *iCertificate, + iDeletable, // with deletable param + iStatus ); + SetActive(); + } + } + + + +void CCheckedCertStore::CancelAdd() + { + if (iState == EInitKeyStoreForAdd || + iState == EGetKeyInfosForAdd || + iState == EAdd || iState == EOldAdd ) + { + Cancel(); + } + } + +void CCheckedCertStore::ComputeAndCheckSubjectKeyIdL() + { + switch (iFormat) + { + case EX509Certificate: + { + TPtr8 thePtr(iCertificate->Des()); + CX509Certificate* cert = CX509Certificate::NewLC(thePtr); + + TKeyUsageX509 x509Usage = EX509UsageNone; + const CX509CertExtension* ext = cert->Extension(KKeyUsage); + + if (!ext) + { + x509Usage = EX509UsageAll; + } + else + { + CX509KeyUsageExt* keyUsageExt = CX509KeyUsageExt::NewLC(ext->Data()); + + if (keyUsageExt->IsSet(EX509DigitalSignature)) + { + x509Usage |= EX509UsageDigitalSignature; + } + if (keyUsageExt->IsSet(EX509NonRepudiation)) + { + x509Usage |= EX509UsageNonRepudiation; + } + if (keyUsageExt->IsSet(EX509KeyEncipherment)) + { + x509Usage |= EX509UsageKeyEncipherment; + } + if (keyUsageExt->IsSet(EX509DataEncipherment)) + { + x509Usage |= EX509UsageDataEncipherment; + } + if (keyUsageExt->IsSet(EX509KeyAgreement)) + { + x509Usage |= EX509UsageKeyAgreement; + } + if (keyUsageExt->IsSet(EX509KeyCertSign)) + { + x509Usage |= EX509UsageKeyCertSign; + } + if (keyUsageExt->IsSet(EX509CRLSign)) + { + x509Usage |= EX509UsageCRLSign; + } + if (keyUsageExt->IsSet(EX509EncipherOnly)) + { + x509Usage |= EX509UsageEncipherOnly; + } + if (keyUsageExt->IsSet(EX509DecipherOnly)) + { + x509Usage |= EX509UsageDecipherOnly; + } + + CleanupStack::PopAndDestroy(keyUsageExt); + } + + iKeyFilter.iUsage = KeyUsageX509ToPKCS15Private(x509Usage); + + iComputedSubjectKeyId.Zero(); + // For non-user ceriticates (i.e. CA certificates), we use the SubjectKeyIdentifier API, as it + // tries to get the extension from cert., and calculates a value only if it is not found. This behaviour corresponds to the RFC. + // For user ceritificates, the key identifier is used for matching key store with cert store, so we cannot use the value in the certificate itself. + if (iCertificateOwnerType != EUserCertificate) + { + iComputedSubjectKeyId = cert->SubjectKeyIdentifierL(); + } + else + { + // For non-CA certs, use the recommended method of computing it from RFC3280, section 4.2.1.2 + iComputedSubjectKeyId = cert->KeyIdentifierL(); + } + if (!iSubjectKeyId || *iSubjectKeyId == KNullDesC8) + { + iSubjectKeyId = &iComputedSubjectKeyId; + } + else if (iSubjectKeyId->Compare(iComputedSubjectKeyId)!=0) + {// Different subject ids + User::Leave(KErrArgument); + } + + CleanupStack::PopAndDestroy(cert); + } + break; + + case EWTLSCertificate: + { + CCertificate* cert = CWTLSCertificate::NewLC(*iCertificate); + iComputedSubjectKeyId = cert->KeyIdentifierL(); + if (!iSubjectKeyId || *iSubjectKeyId == KNullDesC8) + { + iSubjectKeyId = &iComputedSubjectKeyId; + } + else if (iSubjectKeyId->Compare(iComputedSubjectKeyId)!=0) + {// Different subject ids + User::Leave(KErrArgument); + } + + CleanupStack::PopAndDestroy(cert); + } + break; + + case EX509CertificateUrl: + { + iKeyFilter.iUsage = EPKCS15UsageAll; + + if (!iSubjectKeyId || *iSubjectKeyId == KNullDesC8) + { + User::Leave(KErrArgument); + } + } + break; + + default: + User::Leave(KErrNotSupported); + break; + } + + iKeyFilter.iKeyId = *iSubjectKeyId; + } + +void CCheckedCertStore::Remove(const CCTCertInfo& aCertInfo, TRequestStatus& aStatus) + { + assert(iWritableCertStore); + assert(iState == EIdleState); + aStatus = KRequestPending; + iCallerStatus = &aStatus; + iState = ERemove; + iWritableCertStore->Remove(aCertInfo, iStatus); + SetActive(); + } + +void CCheckedCertStore::CancelRemove() + { + if (iState == ERemove) + { + Cancel(); + } + } + +void CCheckedCertStore::SetApplicability(const CCTCertInfo& aCertInfo, const RArray& aApplications, TRequestStatus &aStatus) + { + assert(iWritableCertStore); + assert(iState == EIdleState); + aStatus = KRequestPending; + iCallerStatus = &aStatus; + iState = ESetApplicability; + iWritableCertStore->SetApplicability(aCertInfo, aApplications, iStatus); + SetActive(); + } + +void CCheckedCertStore::CancelSetApplicability() + { + if (iState == ESetApplicability) + { + Cancel(); + } + } + +void CCheckedCertStore::SetTrust(const CCTCertInfo& aCertInfo, TBool aTrusted, TRequestStatus& aStatus) + { + assert(iWritableCertStore); + assert(iState == EIdleState); + aStatus = KRequestPending; + iCallerStatus = &aStatus; + iState = ESetTrust; + iWritableCertStore->SetTrust(aCertInfo, aTrusted, iStatus); + SetActive(); + } + +void CCheckedCertStore::CancelSetTrust() + { + if (iState == ESetTrust) + { + Cancel(); + } + } + +TInt CCheckedCertStore::RunError(TInt aError) + { + Complete(aError); + return KErrNone; + } + +void CCheckedCertStore::DoCancel() + { + // (see notes on cancellation in CUnifiedCertStore::DoCancel) + + switch (iState) + { + case EGetKeyInfosForList: + case EAdd: + case ERemove: + case ESetApplicability: + case ESetTrust: + if (iStatus == KRequestPending) + { + // Attempt to cancel outstanding request and pass status back to + // client + CancelOutstandingRequest(); + Complete(iStatus.Int()); + } + else + { + // We've already been completed - call RunL() to process results + // and complete client + TRAPD(err, RunL()); + if (err != KErrNone) + { + RunError(err); + } + } + break; + + default: + CancelOutstandingRequest(); + Complete(KErrCancel); + break; + } + } + +void CCheckedCertStore::CancelOutstandingRequest() + { + switch (iState) + { + case EList: + iCertStore.CancelList(); + break; + + case EInitKeyStoreForAdd: + case EInitKeyStoreForList: + assert(iUnifiedKeyStore); + iUnifiedKeyStore->CancelInitialize(); + break; + + case EGetKeyInfosForAdd: + case EGetKeyInfosForList: + assert(iUnifiedKeyStore); + iUnifiedKeyStore->CancelList(); + break; + + case EAdd: + case EOldAdd: + assert(iWritableCertStore); + iWritableCertStore->CancelAdd(); + break; + + case ERemove: + assert(iWritableCertStore); + iWritableCertStore->CancelRemove(); + break; + + case ESetApplicability: + assert(iWritableCertStore); + iWritableCertStore->CancelSetApplicability(); + break; + + case ESetTrust: + assert(iWritableCertStore); + iWritableCertStore->CancelSetTrust(); + break; + + + default: + assert(EFalse); + break; + } + + Complete(KErrCancel); + } + +void CCheckedCertStore::RunL() + { + assert(iCallerStatus); + + // we allow only KErrNone OR, possibly, KErrNotSupported after new Add() + // otherwise - Leave! + if (iStatus!=KErrNone && + !(iStatus==KErrNotSupported && iState==EAdd) && + !(iStatus == KErrNotFound && (iState==EList || iState==EGetKeyInfosForList || iState==EInitKeyStoreForList))) + { + User::Leave(iStatus.Int()); + } + + switch (iState) + { + case EList: + if (iCallerFilter->iKeyUsage == EX509UsageAll) + { + // No key usage filter, so we're done + Complete(KErrNone); + } + else + { + // Set up key filter according list cert parameters + if (iCallerFilter->iSubjectKeyIdIsSet) + { + iKeyFilter.iKeyId = iCallerFilter->iSubjectKeyId; + } + else + { + iKeyFilter.iKeyId = KNullDesC8; + } + iKeyFilter.iUsage = KeyUsageX509ToPKCS15Private(iCallerFilter->iKeyUsage); + InitialiseKeyStoreL(EInitKeyStoreForList); + } + break; + + case EInitKeyStoreForAdd: + case EInitKeyStoreForList: + assert(iUnifiedKeyStore); + iState = (iState == EInitKeyStoreForAdd) ? EGetKeyInfosForAdd : EGetKeyInfosForList; + iUnifiedKeyStore->List(iKeyInfos, iKeyFilter, iStatus); + SetActive(); + break; + + case EGetKeyInfosForList: + BuildCheckedCertificateListL(); // Not async + Complete(KErrNone); + break; + + case EGetKeyInfosForAdd: + // We have a filter list of keys - there should be one with + // the appropriate subject if we are to add it + if (iKeyInfos.Count() == 0) + { + // The private key can't be found in any key store + Complete(KErrPrivateKeyNotFound); + } + else + { + // OK to go ahead and add the key + assert(iWritableCertStore); + iState = EAdd; + + // try to use new Add(.., TBool aDeletable, ..) + // if it's not supported it will return with + // iStatus set to KErrNotSupported + iWritableCertStore->Add( *iCertLabel, // call new Add() method + iFormat, + iCertificateOwnerType, + iSubjectKeyId, + iIssuerKeyId, + *iCertificate, + iDeletable, // with deletable param + iStatus); + SetActive(); + } + break; + + case EAdd: + if (iStatus!=KErrNotSupported) + { + // Set published property + iPSCertstoreChangePropertyRef.Set(KUnifiedCertStorePropertyCat, // category + EUnifiedCertStoreFlag, // key + 1); // value + + // when here means MCTWritableCertStore was able to find + // child's new Add(..,aDeletable,..) method + // thus, ok and complete with whatever result it returned + Complete(iStatus.Int()); + } + else + { + // here: call to the new Add() method above didn't work, + // try to call old Add() method + iState = EOldAdd; + iStatus = KRequestPending; + iWritableCertStore->Add( *iCertLabel, + iFormat, + iCertificateOwnerType, + iSubjectKeyId, + iIssuerKeyId, + *iCertificate, + iStatus); + SetActive(); + } + break; + + case EOldAdd: + case ERemove: + case ESetApplicability: + case ESetTrust: + // Set published property + iPSCertstoreChangePropertyRef.Set(KUnifiedCertStorePropertyCat, // category + EUnifiedCertStoreFlag, // key + 1); // value + Complete(iStatus.Int()); + break; + + default: + assert(EFalse); + break; + } + } + +void CCheckedCertStore::InitialiseKeyStoreL(TState aNextState) + { + assert(aNextState == EInitKeyStoreForAdd || aNextState == EInitKeyStoreForList); + assert(!iUnifiedKeyStore); + + iUnifiedKeyStore = CUnifiedKeyStore::NewL(iFs); + iUnifiedKeyStore->Initialize(iStatus); + iState = aNextState; + SetActive(); + } + +void CCheckedCertStore::BuildCheckedCertificateListL() + { + TInt certCount = iCallerCerts->Count(); + TInt keyCount = iKeyInfos.Count(); + + // Iterate backwards through cert array so remove doesn't affect indicies + for (TInt i = certCount - 1 ; i >= 0 ; --i) + { + CCTCertInfo* certInfo = (*iCallerCerts)[i]; + + // It's problem in the certstore implementation if the list contains NULL pointers + assert(certInfo); + + // Check for key with corresponding id + TBool accept = EFalse; + for (TInt j = 0 ; j < keyCount ; ++j) + { + if (iKeyInfos[j]->ID()==certInfo->SubjectKeyId()) + { + accept = ETrue; + break; + } + } + + // If we don't have it, remove and release the cert info + if (!accept) + { + iCallerCerts->Remove(i); + certInfo->Release(); + } + } + } + +void CCheckedCertStore::Complete(TInt aError) + { + if (iCallerStatus) + { + User::RequestComplete(iCallerStatus, aError); + } + Cleanup(); + } + +void CCheckedCertStore::Cleanup() + { + // Reset the state machine + iState = EIdleState; + iKeyInfos.Close(); + iSubjectKeyId = NULL; + iIssuerKeyId = NULL; + + delete iCertLabel; + iCertLabel = NULL; + + delete iCertificate; + iCertificate = NULL; + + delete iUnifiedKeyStore; + iUnifiedKeyStore = 0; + + iCallerCerts = NULL; + iCallerFilter = NULL; + }