pkiutilities/ocsp/src/client.cpp
changeset 0 164170e6151a
equal deleted inserted replaced
-1:000000000000 0:164170e6151a
       
     1 // Copyright (c) 2002-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 "transaction.h"
       
    17 #include "validator.h"
       
    18 #include "panic.h"
       
    19 #include <ocsp.h>
       
    20 #include <x509certext.h>
       
    21 #include <x509certchain.h>
       
    22 #include <x509cert.h>
       
    23 #include <asn1dec.h>
       
    24 
       
    25 EXPORT_C COCSPClient* COCSPClient::NewL(const COCSPParameters* aParams)
       
    26 	{
       
    27 	COCSPClient* self = new (ELeave) COCSPClient();
       
    28 	CleanupStack::PushL(self);
       
    29 	self->ConstructL(aParams);
       
    30 	CleanupStack::Pop(self);
       
    31 	return self;
       
    32 	}
       
    33 
       
    34 COCSPClient::COCSPClient() :
       
    35 	CActive(EPriorityNormal)
       
    36 	{
       
    37 	CActiveScheduler::Add(this);
       
    38 	}
       
    39 
       
    40 void COCSPClient::ConstructL(const COCSPParameters* aParams)
       
    41 	{
       
    42 	__ASSERT_ALWAYS(aParams, Panic(KErrArgument));
       
    43 
       
    44 	// Caller must supply transport
       
    45 	if (!aParams->Transport())
       
    46 		{
       
    47 		User::Leave(KErrArgument);
       
    48 		}
       
    49 
       
    50 	// Range check request timeout and retry values to avoid later panics
       
    51 	if (aParams->Timeout() < KTransportDefaultRequestTimeout)
       
    52 		{
       
    53 		User::Leave(KErrArgument);
       
    54 		}
       
    55 	if (aParams->RetryCount() < KTransportDefaultRequestRetryCount)
       
    56 		{
       
    57 		User::Leave(KErrArgument);
       
    58 		}
       
    59 
       
    60 	iParams = aParams; // Take ownership
       
    61 	
       
    62 	// Make a request for every certificate
       
    63 	for (TUint i = 0 ; i < aParams->CertCount() ; ++i)
       
    64 		{
       
    65 		// Following are the condition in which the request would not be 
       
    66 		// added for ocsp check:
       
    67 		// If CheckCertsWithAiaOnly is enabled and certificate does 
       
    68 		// not contain AIA extension.
       
    69 		//					Or
       
    70 		// CheckCertsWithAiaOnly is disabled and 
       
    71 		// GenerateResponseFromMissingURI is disabled and 
       
    72 		// AIA is absent and
       
    73 		// Global OCSP URL is absent
       
    74 		
       
    75 		if (! (	(	aParams->CheckCertsWithAiaOnly() && 
       
    76 					!OCSPUtils::IsAIAForOCSPPresentL( aParams->SubjectCert(i) )
       
    77 				) || 
       
    78 				(	!aParams->CheckCertsWithAiaOnly() 
       
    79 					&& !aParams->GenerateResponseForMissingUri() 
       
    80 					&& !OCSPUtils::IsAIAForOCSPPresentL(aParams->SubjectCert(i)) 
       
    81 					&& aParams->DefaultURI() == KNullDesC8()
       
    82 				)
       
    83 			   )
       
    84 			)
       
    85 			{
       
    86 			COCSPRequest* request = COCSPRequest::NewLC(aParams->UseNonce());
       
    87 			request->AddCertificateL(aParams->SubjectCert(i), aParams->IssuerCert(i));
       
    88 			User::LeaveIfError(iRequests.Append(request));
       
    89 			CleanupStack::Pop(request);
       
    90 			}
       
    91 			
       
    92 		}
       
    93 	iValidator = COCSPValidator::NewL(*aParams);
       
    94 	}
       
    95 
       
    96 
       
    97 COCSPClient::~COCSPClient()
       
    98 	{
       
    99 	Cancel();
       
   100 	delete iParams;
       
   101 	delete iURI;
       
   102 	delete iTransport;
       
   103 	delete iTransaction;
       
   104 	delete iValidator;
       
   105 	iRequests.ResetAndDestroy();
       
   106 	iResponses.ResetAndDestroy();
       
   107 	iOutcomes.Close();
       
   108 	}
       
   109 
       
   110 EXPORT_C OCSP::TResult COCSPClient::SummaryResult(void) const
       
   111 	{
       
   112 	__ASSERT_ALWAYS(iState == EHaveResult, Panic(KErrNotReady));
       
   113 	return iSummaryResult;
       
   114 	}
       
   115 
       
   116 EXPORT_C TInt COCSPClient::TransactionCount(void) const
       
   117 	{
       
   118 	__ASSERT_ALWAYS(iState == EHaveResult, Panic(KErrNotReady));
       
   119 	return iResponses.Count();
       
   120 	}
       
   121 
       
   122 EXPORT_C const COCSPRequest& COCSPClient::Request(TInt aIndex) const
       
   123 	{
       
   124 	__ASSERT_ALWAYS(iState == EHaveResult, Panic(KErrNotReady));
       
   125 	__ASSERT_ALWAYS(aIndex >= 0 && aIndex < iRequests.Count(), Panic(KErrNotFound));
       
   126 	return *(iRequests[aIndex]);
       
   127 	}
       
   128 
       
   129 EXPORT_C const TOCSPOutcome& COCSPClient::Outcome(TInt aIndex) const
       
   130 	{
       
   131 	__ASSERT_ALWAYS(iState == EHaveResult, Panic(KErrNotReady));
       
   132 	__ASSERT_ALWAYS(aIndex >= 0 && aIndex < iOutcomes.Count(), Panic(KErrNotFound));
       
   133 	return iOutcomes[aIndex];
       
   134 	}
       
   135 
       
   136 EXPORT_C const COCSPResponse* COCSPClient::Response(TInt aIndex) const
       
   137 	{
       
   138 	__ASSERT_ALWAYS(iState == EHaveResult, Panic(KErrNotReady));
       
   139 	__ASSERT_ALWAYS(aIndex >= 0 && aIndex < iResponses.Count(), Panic(KErrNotFound));
       
   140 	return iResponses[aIndex];
       
   141 	}
       
   142 
       
   143 EXPORT_C void COCSPClient::Check(TRequestStatus& aStatus)
       
   144 	{
       
   145 	__ASSERT_ALWAYS(iState == EInitial, Panic(KErrInUse));
       
   146 
       
   147 	iClientStatus = &aStatus;
       
   148 	TInt err = KErrNone;
       
   149 
       
   150 	if (iRequests.Count() == 0)
       
   151 		{
       
   152 		err = OCSP::KErrNoCertificates;
       
   153 		}
       
   154 
       
   155 	if (err == KErrNone)
       
   156 		{
       
   157 		aStatus = KRequestPending;
       
   158 		DoCheck();
       
   159 		}
       
   160 
       
   161 	if (err != KErrNone)
       
   162 		{
       
   163 		User::RequestComplete(iClientStatus, err);
       
   164 		iState = EError;
       
   165 		}
       
   166 	}
       
   167 
       
   168 EXPORT_C void COCSPClient::CancelCheck()
       
   169 	{
       
   170 	Cancel();
       
   171 	}
       
   172 
       
   173 EXPORT_C TBool COCSPClient::CertsAvailableForOCSPCheck()
       
   174 	{
       
   175 	return iRequests.Count();
       
   176 	}
       
   177 
       
   178 TInt COCSPClient::RunError(TInt aError)
       
   179 	{
       
   180 	User::RequestComplete(iClientStatus, aError);
       
   181 	iState = EError;
       
   182 	return KErrNone;
       
   183 	}
       
   184 
       
   185 void COCSPClient::DoCancel()
       
   186 	{
       
   187 	if (iTransaction)
       
   188 		{
       
   189 		iTransaction->CancelRequest();
       
   190 		}
       
   191 	
       
   192 	iValidator->Cancel();
       
   193 	if (iClientStatus)
       
   194 		{
       
   195 		User::RequestComplete(iClientStatus, KErrCancel);
       
   196 		}
       
   197 	}	
       
   198 
       
   199 void COCSPClient::DoCheck()
       
   200 	{
       
   201 	iSummaryResult = OCSP::EGood;
       
   202 	SendRequest();
       
   203 	}
       
   204 
       
   205 void COCSPClient::RunL()
       
   206 	{
       
   207 	switch (iState)
       
   208 		{
       
   209 		case ESendingRequest:
       
   210 			HandleResponseReceivedL();
       
   211 			break;
       
   212 			
       
   213 		case EValidatingResponse:
       
   214 			User::LeaveIfError(iStatus.Int());
       
   215 			HandleResponseValidatedL();
       
   216 			break;
       
   217 						
       
   218 		default:
       
   219 			Panic(KErrCorrupt);
       
   220 			break;
       
   221 		}
       
   222 	}
       
   223 
       
   224 /**
       
   225  * Receive the response, if the response was received correctly, 
       
   226  * perform validation based on the scheme in use.
       
   227  */
       
   228 void COCSPClient::HandleResponseReceivedL()
       
   229 	{
       
   230 	TInt status = iStatus.Int();
       
   231 	
       
   232 	if (status == KErrNone)
       
   233 		{
       
   234 		COCSPResponse* response = iTransaction->TakeResponse();
       
   235 		CleanupStack::PushL(response);
       
   236 		User::LeaveIfError(iResponses.Append(response));
       
   237 		CleanupStack::Pop(response);
       
   238 		ValidateResponseL();
       
   239 		}
       
   240 	else if (status > 0)
       
   241 		{
       
   242 		HandleTransactionErrorL(static_cast<OCSP::TStatus>(status));
       
   243 		}
       
   244 	else if ((status == OCSP::KErrTransportFailure) || (status == OCSP::KErrServerNotFound))
       
   245 		{
       
   246 		HandleTransactionErrorL(OCSP::ETransportError);
       
   247 		}
       
   248 	else if (status == OCSP::KErrInvalidURI)
       
   249 		{
       
   250 		HandleTransactionErrorL(OCSP::EInvalidURI);
       
   251 		}
       
   252 	else if (status == OCSP::KErrTransportTimeout)
       
   253 		{
       
   254 		HandleTransactionErrorL(OCSP::ETimeOut);
       
   255 		}
       
   256 	else
       
   257 		{
       
   258 		User::Leave(status);		   
       
   259 		}
       
   260 	}
       
   261 
       
   262 /**
       
   263  * Called when there's an error getting a response, and it's one of our non-fatal errors.
       
   264  * We record the error and continue checking.
       
   265  */
       
   266 
       
   267 void COCSPClient::HandleTransactionErrorL(OCSP::TStatus aStatus)
       
   268 	{
       
   269 	User::LeaveIfError(iOutcomes.Append(TOCSPOutcome(aStatus, OCSP::EUnknown)));
       
   270 	User::LeaveIfError(iResponses.Append(NULL));
       
   271 	HandleResponseValidatedL();
       
   272 	}
       
   273 
       
   274 /**
       
   275  * Following is the sequence followed in this method:
       
   276  * 1. Check the result for validation of the current response and update the Summary result accordingly.
       
   277  * 2. If delegate certificate has to be checked further initiate the same.
       
   278  * 3. if all request have not been processed then start validation for the next request.
       
   279  * 4. If all request have been processed complete the original client request.
       
   280  */
       
   281 void COCSPClient::HandleResponseValidatedL()
       
   282 	{
       
   283 	TInt index = iOutcomes.Count() - 1;
       
   284 	const TOCSPOutcome& outcome = iOutcomes[index];
       
   285 
       
   286 	if (outcome.iResult > iSummaryResult)
       
   287 		{
       
   288 		iSummaryResult = outcome.iResult;
       
   289 		}
       
   290 	
       
   291 	if (iResponses.Count() < iRequests.Count())
       
   292 		{
       
   293 		SendRequest();
       
   294 		}
       
   295 	else
       
   296 		{
       
   297 		iState = EHaveResult;
       
   298 		User::RequestComplete(iClientStatus, KErrNone);	
       
   299 		}
       
   300 	}
       
   301 
       
   302 void COCSPClient::SendRequest()
       
   303 	{
       
   304 	TRAPD(error, DoSendRequestL());
       
   305 
       
   306 	// Handle errors in RunL
       
   307 	if (error != KErrNone)
       
   308 		{
       
   309 		TRequestStatus* status = &iStatus;
       
   310 		User::RequestComplete(status, error);
       
   311 		}
       
   312 
       
   313 	iState = ESendingRequest;
       
   314 	SetActive();
       
   315 	}
       
   316 
       
   317 void COCSPClient::DoSendRequestL()
       
   318 	{
       
   319 	// Determine the next request to send by the number of responses received
       
   320 	TInt index = iResponses.Count();
       
   321 	COCSPRequest& request = *(iRequests[index]);
       
   322 	
       
   323 	TDesC8* uri = NULL;
       
   324 	TRAPD(error, uri = OCSPUtils::ServerUriL(request.CertInfo(0).Subject(),iParams));
       
   325 	
       
   326 	if(error == KErrArgument)
       
   327 		{
       
   328 		TRequestStatus* status = &iStatus;
       
   329 		User::RequestComplete(status, OCSP::ENoServerSpecified);
       
   330 		return;
       
   331 		}
       
   332 	
       
   333 	User::LeaveIfError(error);
       
   334 	CleanupStack::PushL(uri);
       
   335 	
       
   336 	// if state is valid it means that uri has been retrieved.
       
   337 	__ASSERT_ALWAYS(uri != NULL, Panic(OCSP::EInvalidURI));
       
   338 	MOCSPTransport& transport = *iParams->Transport();
       
   339 	
       
   340 	delete iTransaction;
       
   341 	iTransaction = NULL;
       
   342 	iTransaction = COCSPTransaction::NewL(*uri, transport, iParams->RetryCount(), iParams->Timeout());
       
   343 	iTransaction->SendRequest(request, iStatus);
       
   344 	CleanupStack::PopAndDestroy(uri);		
       
   345 	}
       
   346 
       
   347 /**
       
   348  * Each response received has to undergo validation based on RFC 2560 guidelines.
       
   349  */
       
   350 void COCSPClient::ValidateResponseL()
       
   351 	{
       
   352 	TInt index = iResponses.Count() - 1;
       
   353 
       
   354 	User::LeaveIfError(iOutcomes.Append(TOCSPOutcome()));
       
   355 	__ASSERT_ALWAYS(iOutcomes.Count() == iResponses.Count(), Panic(KErrCorrupt));
       
   356 
       
   357 	iState = EValidatingResponse;
       
   358 	iValidator->Validate(*(iRequests[index]), *(iResponses[index]), iOutcomes[index], iStatus);
       
   359 	SetActive();
       
   360 	}