pkiutilities/ocsp/src/delegateauthorisation.cpp
changeset 0 164170e6151a
equal deleted inserted replaced
-1:000000000000 0:164170e6151a
       
     1 // Copyright (c) 2005-2009 Nokia Corporation and/or its subsidiary(-ies).
       
     2 // All rights reserved.
       
     3 // This component and the accompanying materials are made available
       
     4 // under the terms of "Eclipse Public License v1.0"
       
     5 // which accompanies this distribution, and is available
       
     6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     7 //
       
     8 // Initial Contributors:
       
     9 // Nokia Corporation - initial contribution.
       
    10 //
       
    11 // Contributors:
       
    12 //
       
    13 // Description:
       
    14 //
       
    15 
       
    16 #include <asn1dec.h>
       
    17 #include <x509certext.h>
       
    18 #include "oids.h"
       
    19 #include "pkixcertchain.h"
       
    20 #include "ocsp.h"
       
    21 #include <mcertstore.h>
       
    22 #include <ccertattributefilter.h>
       
    23 #include "ocsprequestandresponse.h"
       
    24 
       
    25 const TInt CERT_SIZE = 1000;
       
    26 
       
    27 EXPORT_C COCSPDelegateAuthorisationScheme* COCSPDelegateAuthorisationScheme::NewLC(
       
    28 	MCertStore& aCertStore)
       
    29 /**
       
    30 	Factory function allocates new instance of
       
    31 	COCSPDelegateAuthorisationScheme.
       
    32 	
       
    33 	@param	aCertStore		Cert store interface.  This is used to
       
    34 							construct a certificate chain builder with
       
    35 							CPKIXCertChainBase::NewL().  Certificates from
       
    36 							the store are not used for validation if the response 
       
    37 							contains the responder certificate. If it does not contain 
       
    38 							the responder cert then validation would be done from store.
       
    39 	@return					New instance of COCSPDelegateAuthorisationScheme.
       
    40 							Leaves if cannot successfully construct.  The new
       
    41 							object is placed on the cleanup stack.
       
    42  */
       
    43 	{
       
    44 	COCSPDelegateAuthorisationScheme* scheme =
       
    45 		new(ELeave) COCSPDelegateAuthorisationScheme(aCertStore);
       
    46 	CleanupStack::PushL(scheme);
       
    47 	scheme->ConstructL();
       
    48 	
       
    49 	return scheme;
       
    50 	}
       
    51 
       
    52 COCSPDelegateAuthorisationScheme::COCSPDelegateAuthorisationScheme(
       
    53 	MCertStore& aCertStore)
       
    54 /**
       
    55 	Initializes the CActive base class object and adds
       
    56 	this object to the active scheduler.
       
    57 	
       
    58 	@param	aCertStore		Cert store interface.  This is only used to
       
    59 							construct a certificate chain builder with
       
    60 							CPKIXCertChainBase::NewL().  Certificates from
       
    61 							the store are not used for validation if the response 
       
    62 							contains the responder certificate. If it does not contain 
       
    63 							the responder cert then validation would be done from store.
       
    64  */
       
    65 :	CActive(EPriorityStandard),
       
    66 	iCertStore(aCertStore)
       
    67 	{
       
    68 	CActiveScheduler::Add(this);
       
    69 	}
       
    70 
       
    71 void COCSPDelegateAuthorisationScheme::ConstructL()
       
    72 /**
       
    73 	Strictly, none of the resources owned by this object
       
    74 	are required for all of its lifetime need to be preserved
       
    75 	between validations.
       
    76 	
       
    77 	Some of the resources, such as the CPKIXCertChain
       
    78 	instance have to be reallocated for every validation.
       
    79 	
       
    80 	The resources which can be allocated for the lifetime
       
    81 	of this object are allocated here.  This improves performance
       
    82 	and simplifies the validation process, at a RAM cost.
       
    83  */
       
    84 	{
       
    85 	// iRespSignCertChainBase is the array of intermediate
       
    86 	// certificates which CPKIXCertChain will use to
       
    87 	// chain (T->I) to (I->E).  Because only immediate
       
    88 	// delegates are supported, there will only be one
       
    89 	// intermediate (or trusted root from CPKIXCertChain's
       
    90 	// point-of-view.)  Therefore the array can be
       
    91 	// sized here.
       
    92 	
       
    93 	// RPointerArray<> doesn't have a Reserve() function.
       
    94 	const CX509Certificate* nullCert = 0;
       
    95 	User::LeaveIfError(iRespSignIntCert.Append(nullCert));
       
    96 	
       
    97 	iCertFilter = CCertAttributeFilter::NewL();
       
    98 	iCertFilter->SetOwnerType(ECACertificate);
       
    99 	iCertFilter->SetFormat(EX509Certificate);
       
   100 
       
   101 	}
       
   102 
       
   103 COCSPDelegateAuthorisationScheme::~COCSPDelegateAuthorisationScheme()
       
   104 /**
       
   105 	Cancels any outstanding validation and frees all resources
       
   106 	owned by this object.
       
   107  */
       
   108 	{
       
   109 	delete iCertFilter;
       
   110 	iCertStoreEntries.Close();
       
   111 	iRespSignIntCert.Close();
       
   112 	
       
   113 	delete iEncodedCert;
       
   114 	delete iResponseCert;
       
   115 	
       
   116 	delete iPKIXResultBase;
       
   117 	delete iRespSignCertChainBase;
       
   118 	
       
   119 	}
       
   120 
       
   121 /**
       
   122 	Implement MOCSPAuthorisationScheme.
       
   123 	
       
   124 	Validate the response if it is signed by an	immediate delegate of the intermediate entity.
       
   125 	I.e. if the request has the form
       
   126 		(T->I) (I->E)
       
   127 	
       
   128 	where T is trusted (at least for the purposes of this validation) and I is an intermediate, 
       
   129 	the response can be signed by R if (I->R).
       
   130 	
       
   131 	I is the CA, and can be equal to T, i.e. the certificate which is being tested for 
       
   132 	revocation can be signed by a root certificate.
       
   133 	
       
   134 	R must be immediately signed by I, and must have id-kp-OCSPSigning in its extended key usage.
       
   135 	(RFC 2560 S4.2.2.2)
       
   136  */
       
   137 void COCSPDelegateAuthorisationScheme::ValidateL(
       
   138 	OCSP::TStatus& aOCSPStatus, COCSPResponse& aResponse,
       
   139 	const TTime aValidationTime, TRequestStatus& aStatus,
       
   140 	const COCSPRequest& aRequest)
       
   141 	{
       
   142 	// store the client status, so that it can be used for request
       
   143 	// completion later.
       
   144 	iClientStatus = &aStatus;
       
   145 	aStatus = KRequestPending;
       
   146 	
       
   147 	// By assuming there is only one request / response pair,
       
   148 	// there is no need to iterate through each response, which
       
   149 	// simplifies this scheme's implementation.
       
   150 	// the OCSP requests are constructed in
       
   151 	// COCSPClient::ConstructL() such that exactly one certificate
       
   152 	// and its signer are sent in each request.
       
   153 	// (RFC 2560 S4.1.1 allows multiple pairs)
       
   154 	
       
   155 	iResponse = &aResponse;
       
   156 	iRequest = &aRequest;
       
   157 	
       
   158 	iValidationTime = aValidationTime;
       
   159 	
       
   160 	// This default value is changed once the whole validation 
       
   161 	// process has completed successfully else the default value 
       
   162 	// is returned on failure.
       
   163 	aOCSPStatus = OCSP::EResponseSignatureValidationFailure;
       
   164 	iOCSPStatus = &aOCSPStatus;
       
   165 	
       
   166 	// set this to false before starting the scheme validation
       
   167 	// for multiple certificates are being validated through 
       
   168 	// this scheme.
       
   169 	iValidateFromResponse = EFalse;
       
   170 	
       
   171 	// This authentication scheme supports 2 ways of validation:
       
   172 	// 1. If the response contains responder cert then validation 
       
   173 	// would be performed against the responder cert included in response. 
       
   174 	// This validation would also check whether the responder cert has been 
       
   175 	// issued by the CA cert which issued the certificate in question.
       
   176 	// 2. If the response does not contain responder cert, search for it 
       
   177 	// in the store based on the ResponderId which is included in the 
       
   178 	// response. If the responder cert is found in the store which has signed
       
   179 	// the response then authorize the response, in both the cases we check whether 
       
   180 	// responder cert has been issued by the CA which issued the cert in question, 
       
   181 	// and the CA should be present in the store.
       
   182 	const TPtrC8* certChainData = aResponse.DataElementEncoding(COCSPResponse::ECertificateChain);
       
   183 	if (certChainData == 0)		// no signing certs
       
   184 		{
       
   185 		ValidateFromRootsL();
       
   186 		}
       
   187 	else
       
   188 		{
       
   189 		iValidateFromResponse = ETrue;
       
   190 		ValidateDelegateCertL(*certChainData, iValidationTime);
       
   191 		}
       
   192 	}
       
   193 
       
   194 /**
       
   195 	Initialize this object to validate the certificate which was sent with the 
       
   196 	response against the CA which was used	to sign the certificate in question.
       
   197 	
       
   198 	@param	aResponseCertChain	DER-encoded cert chain that	was either sent with the response.
       
   199 								or was retrieved from the store as a single certificate.
       
   200 	@param 	aValidationTime		Time to be used for chain validation of the delegate certificate.
       
   201 	@post If successful, asynchronous validation will be set up.
       
   202  */
       
   203 
       
   204 void COCSPDelegateAuthorisationScheme::ValidateDelegateCertL(
       
   205 	const TDesC8& aResponseCertChain, const TTime aValidationTime)
       
   206 	{
       
   207 	// the response received can contain a chain of certificate, we need to extract the 
       
   208 	// responder certificate from the chain for further processing. If the certificate has been
       
   209 	// retrieved from store then there would be no chain but the delegate certificate would be
       
   210 	// retrieved.
       
   211 	CX509Certificate* decodedResponseCert = OCSPUtils::GetResponderCertLC(aResponseCertChain);
       
   212 	CleanupStack::Pop(decodedResponseCert);
       
   213 	
       
   214 	delete iResponseCert;
       
   215 	iResponseCert = NULL;
       
   216 	iResponseCert = decodedResponseCert;
       
   217 	
       
   218 	// First check the responder certificate in accordance to RFC 2560 for the following:
       
   219 	// 1. Does it contain extension id-kp-OCSPSigning
       
   220 	// 2. The responder id in the response matches the response certificate.
       
   221 	// 3. The response is signed by the responder certificate
       
   222 	if( OCSPUtils::DoesCertHaveOCSPSigningExtL(*iResponseCert) 
       
   223 		&&	OCSPUtils::DoesResponderIdMatchCertL(*iResponse, *iResponseCert)
       
   224 		&&	OCSPUtils::IsResponseSignedByCertL(iResponse, *iResponseCert) )
       
   225 		{
       
   226 		// construct a certificate chain containing the X -> R with T -> I as the intermediate.
       
   227 		delete iPKIXResultBase;
       
   228 		iPKIXResultBase = NULL;
       
   229 		iPKIXResultBase = CPKIXValidationResultBase::NewL();
       
   230 		
       
   231 		// get the intermediate which signed the EE
       
   232 		const CX509Certificate& caCert = iRequest->CertInfo(0).Issuer();
       
   233 		
       
   234 		// use the intermediate cert as the trusted root for
       
   235 		// the purpose of building the chain.  ("Intermediate"
       
   236 		// in this context is the certificate which signed the EE.)
       
   237 		iRespSignIntCert[0] = CONST_CAST(CX509Certificate*,&caCert);
       
   238 		
       
   239 		delete iRespSignCertChainBase;
       
   240 		iRespSignCertChainBase = NULL;
       
   241 		iRespSignCertChainBase = CPKIXCertChainBase::NewL(iCertStore, iResponseCert->Encoding(), iRespSignIntCert);
       
   242 		
       
   243 		// attempt to validate the chain.  I.e. test that X = I.
       
   244 		iRespSignCertChainBase->ValidateL(*iPKIXResultBase, aValidationTime, iStatus);
       
   245 		
       
   246 		iState = EOnChainValidation;
       
   247 		SetActive();
       
   248 		}
       
   249 	else
       
   250 		{
       
   251 		if(iValidateFromResponse)
       
   252 			{
       
   253 			User::RequestComplete(iClientStatus, KErrNone);
       
   254 			return;
       
   255 			}
       
   256 		// this means that we are trying to retrieve the responder certificate from the store
       
   257 		// and perform validation against it. As this certificate is not the valid responder
       
   258 		// hence set the state to retrieve the next certificate from the store.
       
   259 		else
       
   260 			{
       
   261 			iState = ERetrieveNext;
       
   262 			TRequestStatus* status = &iStatus;
       
   263 			User::RequestComplete(status,KErrNone);
       
   264 			SetActive();
       
   265 			return;
       
   266 			}
       
   267 		}
       
   268 	}
       
   269 
       
   270 void COCSPDelegateAuthorisationScheme::CancelValidate()
       
   271 /**
       
   272 	Implement MOCSPAuthorisationScheme.  This is an active
       
   273 	object, and this function just calls Cancel().  See
       
   274 	DoCancel() for information about the cancellation process.
       
   275 	
       
   276 	@see DoCancel
       
   277  */
       
   278 	{
       
   279 	ASSERT(iRespSignCertChainBase != 0);
       
   280 	iRespSignCertChainBase->CancelValidate();
       
   281 	}
       
   282 
       
   283 void COCSPDelegateAuthorisationScheme::RunL()
       
   284 	{
       
   285 	User::LeaveIfError(iStatus.Int());
       
   286 	switch(iState)
       
   287 		{
       
   288 		//Response validation after chain building.
       
   289 		case EOnChainValidation:
       
   290 			OnChainValidationL();
       
   291 			break;
       
   292 		
       
   293 		// state used to allocate sufficient memory for retrieving the next certificate
       
   294 		case ERetrieveNext:
       
   295 			OnRetrieveNextL();
       
   296 			break;
       
   297 
       
   298 		// state used to retrieve the next certificate
       
   299 		case ERetrievingEntry:
       
   300 			OnRetrievingEntryL();
       
   301 			break;
       
   302 		}
       
   303 	}
       
   304 
       
   305 void COCSPDelegateAuthorisationScheme::OnChainValidationL()
       
   306 	{	
       
   307 	TValidationError error = iPKIXResultBase->Error().iReason;
       
   308 	if (error != EValidatedOK)
       
   309 		{
       
   310 		User::Leave(error);
       
   311 		}
       
   312 	*iOCSPStatus = OCSP::EValid;
       
   313 	User::RequestComplete(iClientStatus, KErrNone);
       
   314 	}
       
   315 
       
   316 void COCSPDelegateAuthorisationScheme::DoCancel()
       
   317 /**
       
   318 	If a validation request is outstanding, then it is cancelled.
       
   319 	This objects client, i.e. the owner of the TRequestStatus which
       
   320 	was passed to Validate(), is completed with KErrCancel.
       
   321  */
       
   322 	{
       
   323 	// object should be waiting for the chain builder to complete.  
       
   324 	// Therefore there must be a valid client request status pointer 
       
   325 	// and an instance of CPKIXCertChain.
       
   326 	
       
   327 	iRespSignCertChainBase->CancelValidate();
       
   328 	
       
   329 	if (iClientStatus)
       
   330 		{		
       
   331 		User::RequestComplete(iClientStatus, KErrCancel);
       
   332 		}
       
   333 	}
       
   334 
       
   335 TInt COCSPDelegateAuthorisationScheme::RunError(TInt aError)
       
   336 	{
       
   337 	User::RequestComplete(iClientStatus, aError);
       
   338 	return KErrNone;
       
   339 	}
       
   340 
       
   341 /**
       
   342  * Initiates request to retrieve the responder certificate from store.
       
   343  */
       
   344 void COCSPDelegateAuthorisationScheme::ValidateFromRootsL()
       
   345 	{
       
   346 	iCertCount = -1;
       
   347 	
       
   348 	iCertStoreEntries.Close();
       
   349 	iCertStore.List(iCertStoreEntries, *iCertFilter, iStatus);
       
   350 	
       
   351 	delete iEncodedCert;
       
   352 	iEncodedCert = NULL;
       
   353 	iEncodedCert = HBufC8::NewL(CERT_SIZE);
       
   354 	
       
   355 	iState = ERetrieveNext;
       
   356 	SetActive();
       
   357 	}
       
   358 
       
   359 /**
       
   360  * For list of certificate entries in the store retrieve each certificate.
       
   361  */
       
   362 void COCSPDelegateAuthorisationScheme::OnRetrieveNextL()
       
   363 	{
       
   364 	if(++iCertCount < iCertStoreEntries.Count() )
       
   365 		{
       
   366 		iState = ERetrievingEntry;
       
   367 		TInt size = iCertStoreEntries[iCertCount]->Size();
       
   368 		if( size > iEncodedCert->Des().MaxLength() )
       
   369 			{
       
   370 			delete iEncodedCert;
       
   371 			iEncodedCert = NULL;
       
   372 			iEncodedCert = HBufC8::NewL(size);
       
   373 			}
       
   374 		TPtr8 encodedCertDesc = iEncodedCert->Des();
       
   375 		iCertStore.Retrieve(*iCertStoreEntries[iCertCount],	encodedCertDesc,iStatus);
       
   376 		SetActive();
       
   377 		}
       
   378 	else
       
   379 		{
       
   380 		User::RequestComplete(iClientStatus,OCSP::EResponseSignatureValidationFailure);	
       
   381 		}
       
   382 	}
       
   383 
       
   384 /**
       
   385  * Once the certificate has been retrieved from the store, it should be sent for chain validation.
       
   386  * This intermediate state is required so that we can retrieve the certificate from the store.
       
   387  */
       
   388 void COCSPDelegateAuthorisationScheme::OnRetrievingEntryL()
       
   389 	{
       
   390 	ValidateDelegateCertL(*iEncodedCert, iValidationTime);
       
   391 	}
       
   392 
       
   393 /**
       
   394  * Returns the responder certificate.
       
   395  */
       
   396 const CX509Certificate* COCSPDelegateAuthorisationScheme::ResponderCert() const	
       
   397 	{
       
   398 	return iResponseCert;
       
   399 	}