pkiutilities/ocsp/src/client.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 26 Jan 2010 15:20:08 +0200
changeset 0 164170e6151a
permissions -rw-r--r--
Revision: 201004

// 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 "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 "transaction.h"
#include "validator.h"
#include "panic.h"
#include <ocsp.h>
#include <x509certext.h>
#include <x509certchain.h>
#include <x509cert.h>
#include <asn1dec.h>

EXPORT_C COCSPClient* COCSPClient::NewL(const COCSPParameters* aParams)
	{
	COCSPClient* self = new (ELeave) COCSPClient();
	CleanupStack::PushL(self);
	self->ConstructL(aParams);
	CleanupStack::Pop(self);
	return self;
	}

COCSPClient::COCSPClient() :
	CActive(EPriorityNormal)
	{
	CActiveScheduler::Add(this);
	}

void COCSPClient::ConstructL(const COCSPParameters* aParams)
	{
	__ASSERT_ALWAYS(aParams, Panic(KErrArgument));

	// Caller must supply transport
	if (!aParams->Transport())
		{
		User::Leave(KErrArgument);
		}

	// Range check request timeout and retry values to avoid later panics
	if (aParams->Timeout() < KTransportDefaultRequestTimeout)
		{
		User::Leave(KErrArgument);
		}
	if (aParams->RetryCount() < KTransportDefaultRequestRetryCount)
		{
		User::Leave(KErrArgument);
		}

	iParams = aParams; // Take ownership
	
	// Make a request for every certificate
	for (TUint i = 0 ; i < aParams->CertCount() ; ++i)
		{
		// Following are the condition in which the request would not be 
		// added for ocsp check:
		// If CheckCertsWithAiaOnly is enabled and certificate does 
		// not contain AIA extension.
		//					Or
		// CheckCertsWithAiaOnly is disabled and 
		// GenerateResponseFromMissingURI is disabled and 
		// AIA is absent and
		// Global OCSP URL is absent
		
		if (! (	(	aParams->CheckCertsWithAiaOnly() && 
					!OCSPUtils::IsAIAForOCSPPresentL( aParams->SubjectCert(i) )
				) || 
				(	!aParams->CheckCertsWithAiaOnly() 
					&& !aParams->GenerateResponseForMissingUri() 
					&& !OCSPUtils::IsAIAForOCSPPresentL(aParams->SubjectCert(i)) 
					&& aParams->DefaultURI() == KNullDesC8()
				)
			   )
			)
			{
			COCSPRequest* request = COCSPRequest::NewLC(aParams->UseNonce());
			request->AddCertificateL(aParams->SubjectCert(i), aParams->IssuerCert(i));
			User::LeaveIfError(iRequests.Append(request));
			CleanupStack::Pop(request);
			}
			
		}
	iValidator = COCSPValidator::NewL(*aParams);
	}


COCSPClient::~COCSPClient()
	{
	Cancel();
	delete iParams;
	delete iURI;
	delete iTransport;
	delete iTransaction;
	delete iValidator;
	iRequests.ResetAndDestroy();
	iResponses.ResetAndDestroy();
	iOutcomes.Close();
	}

EXPORT_C OCSP::TResult COCSPClient::SummaryResult(void) const
	{
	__ASSERT_ALWAYS(iState == EHaveResult, Panic(KErrNotReady));
	return iSummaryResult;
	}

EXPORT_C TInt COCSPClient::TransactionCount(void) const
	{
	__ASSERT_ALWAYS(iState == EHaveResult, Panic(KErrNotReady));
	return iResponses.Count();
	}

EXPORT_C const COCSPRequest& COCSPClient::Request(TInt aIndex) const
	{
	__ASSERT_ALWAYS(iState == EHaveResult, Panic(KErrNotReady));
	__ASSERT_ALWAYS(aIndex >= 0 && aIndex < iRequests.Count(), Panic(KErrNotFound));
	return *(iRequests[aIndex]);
	}

EXPORT_C const TOCSPOutcome& COCSPClient::Outcome(TInt aIndex) const
	{
	__ASSERT_ALWAYS(iState == EHaveResult, Panic(KErrNotReady));
	__ASSERT_ALWAYS(aIndex >= 0 && aIndex < iOutcomes.Count(), Panic(KErrNotFound));
	return iOutcomes[aIndex];
	}

EXPORT_C const COCSPResponse* COCSPClient::Response(TInt aIndex) const
	{
	__ASSERT_ALWAYS(iState == EHaveResult, Panic(KErrNotReady));
	__ASSERT_ALWAYS(aIndex >= 0 && aIndex < iResponses.Count(), Panic(KErrNotFound));
	return iResponses[aIndex];
	}

EXPORT_C void COCSPClient::Check(TRequestStatus& aStatus)
	{
	__ASSERT_ALWAYS(iState == EInitial, Panic(KErrInUse));

	iClientStatus = &aStatus;
	TInt err = KErrNone;

	if (iRequests.Count() == 0)
		{
		err = OCSP::KErrNoCertificates;
		}

	if (err == KErrNone)
		{
		aStatus = KRequestPending;
		DoCheck();
		}

	if (err != KErrNone)
		{
		User::RequestComplete(iClientStatus, err);
		iState = EError;
		}
	}

EXPORT_C void COCSPClient::CancelCheck()
	{
	Cancel();
	}

EXPORT_C TBool COCSPClient::CertsAvailableForOCSPCheck()
	{
	return iRequests.Count();
	}

TInt COCSPClient::RunError(TInt aError)
	{
	User::RequestComplete(iClientStatus, aError);
	iState = EError;
	return KErrNone;
	}

void COCSPClient::DoCancel()
	{
	if (iTransaction)
		{
		iTransaction->CancelRequest();
		}
	
	iValidator->Cancel();
	if (iClientStatus)
		{
		User::RequestComplete(iClientStatus, KErrCancel);
		}
	}	

void COCSPClient::DoCheck()
	{
	iSummaryResult = OCSP::EGood;
	SendRequest();
	}

void COCSPClient::RunL()
	{
	switch (iState)
		{
		case ESendingRequest:
			HandleResponseReceivedL();
			break;
			
		case EValidatingResponse:
			User::LeaveIfError(iStatus.Int());
			HandleResponseValidatedL();
			break;
						
		default:
			Panic(KErrCorrupt);
			break;
		}
	}

/**
 * Receive the response, if the response was received correctly, 
 * perform validation based on the scheme in use.
 */
void COCSPClient::HandleResponseReceivedL()
	{
	TInt status = iStatus.Int();
	
	if (status == KErrNone)
		{
		COCSPResponse* response = iTransaction->TakeResponse();
		CleanupStack::PushL(response);
		User::LeaveIfError(iResponses.Append(response));
		CleanupStack::Pop(response);
		ValidateResponseL();
		}
	else if (status > 0)
		{
		HandleTransactionErrorL(static_cast<OCSP::TStatus>(status));
		}
	else if ((status == OCSP::KErrTransportFailure) || (status == OCSP::KErrServerNotFound))
		{
		HandleTransactionErrorL(OCSP::ETransportError);
		}
	else if (status == OCSP::KErrInvalidURI)
		{
		HandleTransactionErrorL(OCSP::EInvalidURI);
		}
	else if (status == OCSP::KErrTransportTimeout)
		{
		HandleTransactionErrorL(OCSP::ETimeOut);
		}
	else
		{
		User::Leave(status);		   
		}
	}

/**
 * Called when there's an error getting a response, and it's one of our non-fatal errors.
 * We record the error and continue checking.
 */

void COCSPClient::HandleTransactionErrorL(OCSP::TStatus aStatus)
	{
	User::LeaveIfError(iOutcomes.Append(TOCSPOutcome(aStatus, OCSP::EUnknown)));
	User::LeaveIfError(iResponses.Append(NULL));
	HandleResponseValidatedL();
	}

/**
 * Following is the sequence followed in this method:
 * 1. Check the result for validation of the current response and update the Summary result accordingly.
 * 2. If delegate certificate has to be checked further initiate the same.
 * 3. if all request have not been processed then start validation for the next request.
 * 4. If all request have been processed complete the original client request.
 */
void COCSPClient::HandleResponseValidatedL()
	{
	TInt index = iOutcomes.Count() - 1;
	const TOCSPOutcome& outcome = iOutcomes[index];

	if (outcome.iResult > iSummaryResult)
		{
		iSummaryResult = outcome.iResult;
		}
	
	if (iResponses.Count() < iRequests.Count())
		{
		SendRequest();
		}
	else
		{
		iState = EHaveResult;
		User::RequestComplete(iClientStatus, KErrNone);	
		}
	}

void COCSPClient::SendRequest()
	{
	TRAPD(error, DoSendRequestL());

	// Handle errors in RunL
	if (error != KErrNone)
		{
		TRequestStatus* status = &iStatus;
		User::RequestComplete(status, error);
		}

	iState = ESendingRequest;
	SetActive();
	}

void COCSPClient::DoSendRequestL()
	{
	// Determine the next request to send by the number of responses received
	TInt index = iResponses.Count();
	COCSPRequest& request = *(iRequests[index]);
	
	TDesC8* uri = NULL;
	TRAPD(error, uri = OCSPUtils::ServerUriL(request.CertInfo(0).Subject(),iParams));
	
	if(error == KErrArgument)
		{
		TRequestStatus* status = &iStatus;
		User::RequestComplete(status, OCSP::ENoServerSpecified);
		return;
		}
	
	User::LeaveIfError(error);
	CleanupStack::PushL(uri);
	
	// if state is valid it means that uri has been retrieved.
	__ASSERT_ALWAYS(uri != NULL, Panic(OCSP::EInvalidURI));
	MOCSPTransport& transport = *iParams->Transport();
	
	delete iTransaction;
	iTransaction = NULL;
	iTransaction = COCSPTransaction::NewL(*uri, transport, iParams->RetryCount(), iParams->Timeout());
	iTransaction->SendRequest(request, iStatus);
	CleanupStack::PopAndDestroy(uri);		
	}

/**
 * Each response received has to undergo validation based on RFC 2560 guidelines.
 */
void COCSPClient::ValidateResponseL()
	{
	TInt index = iResponses.Count() - 1;

	User::LeaveIfError(iOutcomes.Append(TOCSPOutcome()));
	__ASSERT_ALWAYS(iOutcomes.Count() == iResponses.Count(), Panic(KErrCorrupt));

	iState = EValidatingResponse;
	iValidator->Validate(*(iRequests[index]), *(iResponses[index]), iOutcomes[index], iStatus);
	SetActive();
	}