pkiutilities/ocsp/src/client.cpp
changeset 0 164170e6151a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pkiutilities/ocsp/src/client.cpp	Tue Jan 26 15:20:08 2010 +0200
@@ -0,0 +1,360 @@
+// 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();
+	}