pkiutilities/ocsp/transport/transporthttp.cpp
changeset 0 164170e6151a
equal deleted inserted replaced
-1:000000000000 0:164170e6151a
       
     1 // Copyright (c) 2001-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 // Implement the transport class for using HTTP GET and POST.
       
    15 // Links the OCSP module to the HTTP module.
       
    16 // 
       
    17 //
       
    18 
       
    19 #include <http/cecomfilter.h>
       
    20 #include <escapeutils.h>
       
    21 #include "securitypolicy.h"
       
    22 #include "ocsppolicy.h"
       
    23 #include "ocsptransport.h"
       
    24 #include "panic.h"
       
    25 #include <tconvbase64.h>
       
    26 #include "callbacktimer.h"
       
    27 
       
    28 _LIT8(KOCSPContentTypeRequest, "application/ocsp-request");
       
    29 _LIT8(KOCSPContentTypeResponse, "application/ocsp-response");
       
    30 _LIT8(KSlash, "/");
       
    31 
       
    32 // Min request size for the use of POST method (else use GET method)
       
    33 const TInt KMinReqSizeForHTTPPOST = 255;
       
    34 
       
    35 const TInt KMillSecsToMicroSecs = 1000;
       
    36 
       
    37 // Default value of timeout means it's disabled
       
    38 const TInt KTimeoutDisabledValue = KTransportDefaultRequestTimeout;
       
    39 
       
    40 EXPORT_C COCSPTransportHttp* COCSPTransportHttp::NewL(const TDesC8& aUri, TUint32& aIap)
       
    41 	{
       
    42 	COCSPTransportHttp* self = new (ELeave) COCSPTransportHttp(aIap);
       
    43 	CleanupStack::PushL(self);
       
    44 	self->ConstructL(aUri);
       
    45 	CleanupStack::Pop(self);
       
    46 	return self;
       
    47 	}
       
    48 
       
    49 COCSPTransportHttp::COCSPTransportHttp(TUint32& aIap)
       
    50 	: CActive(CActive::EPriorityStandard), iIap(aIap), 
       
    51 	  iTimeout(KTimeoutDisabledValue)
       
    52 	{
       
    53 	CActiveScheduler::Add(this);
       
    54 	}
       
    55 	
       
    56 void COCSPTransportHttp::ConstructL(const TDesC8& aUri)
       
    57 	{
       
    58 	// Create the timer object
       
    59 	iTimer = CCallbackTimer::NewL(*this);
       
    60 
       
    61 	TUriParser8	uri;
       
    62 	uri.Parse(aUri);
       
    63 	iHTTPSession.OpenL(uri, iIap, EFalse);
       
    64 	
       
    65 	// Install the HTTP filter (filter UID is specified in swipolicy.ini file)
       
    66 	InstallHttpFilterL();
       
    67 	}
       
    68 
       
    69 COCSPTransportHttp::~COCSPTransportHttp()
       
    70 	{
       
    71 	Deque();
       
    72 	iHTTPTransaction.Close();
       
    73 	iHTTPSession.Close();
       
    74 
       
    75 	delete iUri;
       
    76 	delete iRequest;
       
    77 	delete iResponseData;
       
    78 	delete iTimer;
       
    79 	}
       
    80 
       
    81 // From MOCSPTransport
       
    82 void COCSPTransportHttp::SendRequest(const TDesC8& aURI, 
       
    83 										 const TDesC8& aRequest,
       
    84 										 const TInt aTimeout,
       
    85 										 TRequestStatus& aStatus)
       
    86 	{
       
    87 	iCallBack = &aStatus;
       
    88 	aStatus = KRequestPending;
       
    89 	iServerFound = ETrue;
       
    90 
       
    91 	delete iUri;
       
    92 	delete iRequest;
       
    93 
       
    94 	iTimeout = aTimeout;
       
    95 	iUri = aURI.Alloc();
       
    96 	iRequest = aRequest.Alloc();
       
    97 
       
    98 	// Check for out of memory
       
    99 	if (iUri == NULL || iRequest == NULL)
       
   100 		{
       
   101 		Complete(KErrNoMemory);
       
   102 		}
       
   103 
       
   104 	// Take action depending on current state
       
   105 	if (iState == ETransportConnectingState)
       
   106 		{
       
   107 		// Establish a connection
       
   108 		// Note: Timeout and retries don't apply to this operation
       
   109 		iState = ETransportConnectingState;
       
   110 		iHTTPSession.StartConnection(iStatus);
       
   111 		SetActive();
       
   112 		}
       
   113 	else
       
   114 		{
       
   115 		// We've connected already, send the request.
       
   116 		iStatus = KRequestPending;
       
   117 		TRAPD(err, DoSendRequestL(*iUri, *iRequest));
       
   118 		if (err == KErrNone)
       
   119 			{
       
   120 			// Start the timer (if enabled)
       
   121 			if (iTimeout > KTimeoutDisabledValue)
       
   122 				{
       
   123 				iTimer->After(iTimeout * KMillSecsToMicroSecs);
       
   124 				}
       
   125 			SetActive();
       
   126 			}
       
   127 		else
       
   128 			{
       
   129 			Complete(err);
       
   130 			}
       
   131 		}
       
   132 	}
       
   133 
       
   134 void COCSPTransportHttp::InstallHttpFilterL()
       
   135 	{
       
   136 	// Install filters (if any)
       
   137 	Swi::RSecPolHandle secPol;
       
   138 	secPol.OpenLC();
       
   139 	TUint32 filterId = secPol().OcspHttpHeaderFilter();
       
   140 	if (filterId != 0)
       
   141 		{
       
   142 		TUid headerFilterUid = {filterId};
       
   143 		CEComFilter::InstallFilterL(iHTTPSession.HTTPSession(), headerFilterUid);
       
   144 		}
       
   145 	CleanupStack::PopAndDestroy(&secPol);
       
   146 	}
       
   147 
       
   148 void COCSPTransportHttp::DoSendRequestL(const TDesC8& aURI, 
       
   149 											const TDesC8& aRequest)
       
   150 	{
       
   151 	delete iResponseData;
       
   152 	iResponseData = NULL;
       
   153 	iOCSPRequest.Set(aRequest);
       
   154 
       
   155 	// Close previous transaction, if any
       
   156 	iHTTPTransaction.Close();
       
   157 
       
   158 	// If the size of the DER encoded request is less than 255 bytes use the GET method (if enabled)
       
   159 	// else use the POST method
       
   160 	RBuf8 url8;
       
   161 	CleanupClosePushL(url8);
       
   162 	TInt reqSize = aRequest.Length();
       
   163 
       
   164 	COcspPolicy* ocspPolicy = COcspPolicy::NewL();
       
   165 	TBool useHTTPGETMethod = ocspPolicy->IsHttpGETMethodEnabled();
       
   166 
       
   167 	delete ocspPolicy;
       
   168 
       
   169 	if (useHTTPGETMethod && reqSize < KMinReqSizeForHTTPPOST)
       
   170 		{
       
   171 		// Set the OCSP request as part of the url after Base64 and URL encoding
       
   172 		
       
   173 		TBase64 base64;
       
   174   		
       
   175 		TInt destLen = ((iOCSPRequest.Length() - 1 ) / 3 + 1) * 4;
       
   176  		HBufC8* encodedBuf = HBufC8::NewMaxLC(destLen); // to get the decoded string
       
   177    		TPtr8 encodedPtr = encodedBuf->Des();
       
   178  		
       
   179  		base64.Encode(iOCSPRequest, encodedPtr);
       
   180 		 				
       
   181  		HBufC8* escEncodedReq = EscapeUtils::EscapeEncodeL(encodedPtr, EscapeUtils::EEscapeUrlEncoded);
       
   182 
       
   183 		CleanupStack::PushL(escEncodedReq);
       
   184 		url8.CreateL(aURI, aURI.Length() + KSlash().Length() + escEncodedReq->Length());
       
   185 		// Append a slash only if it's already not present
       
   186 		if ((aURI.Length() > 0) && (aURI.Right(1) != KSlash))
       
   187 			{
       
   188 			url8.Append(KSlash);
       
   189 			}
       
   190 		url8.Append(*escEncodedReq);
       
   191 		CleanupStack::PopAndDestroy(2, encodedBuf);
       
   192 
       
   193 		if (iURI.Parse(url8))
       
   194 			{
       
   195 			User::Leave(OCSP::KErrInvalidURI);
       
   196 			}
       
   197 
       
   198 		// Create HTTP transaction, method = GET (default)
       
   199 		iHTTPTransaction = iHTTPSession().OpenTransactionL(iURI, *this);
       
   200 
       
   201 		// Add the appropriate Http headers
       
   202 		AddHttpHeadersL();
       
   203 		}
       
   204 	else
       
   205 		{
       
   206 		// Use the passed in URL as is
       
   207 		if (iURI.Parse(aURI))
       
   208 			{
       
   209 			User::Leave(OCSP::KErrInvalidURI);
       
   210 			}
       
   211 
       
   212 		// Create HTTP transaction, method = POST
       
   213 		RStringPool stringPool = iHTTPSession().StringPool();
       
   214 		iHTTPTransaction = iHTTPSession().OpenTransactionL(iURI, *this, stringPool.StringF(HTTP::EPOST,RHTTPSession::GetTable()));
       
   215 
       
   216 		// Add the appropriate Http headers
       
   217 		AddHttpHeadersL();
       
   218 
       
   219 		// Setup the data supplier
       
   220 		iHTTPTransaction.Request().SetBody(*this);
       
   221 		}
       
   222 
       
   223 	// Finally submit the transaction
       
   224 	iHTTPTransaction.SubmitL();
       
   225 	CleanupStack::PopAndDestroy(&url8);
       
   226 	}
       
   227 
       
   228 void COCSPTransportHttp::AddHttpHeadersL()
       
   229 	{
       
   230 	// Get request + it's headers
       
   231 	RStringPool stringPool = iHTTPSession().StringPool();
       
   232 	RHTTPRequest request = iHTTPTransaction.Request();
       
   233 	RHTTPHeaders headers = request.GetHeaderCollection();
       
   234 
       
   235 	// Host header: done by HTTP module
       
   236 	// Content-Length header: done by HTTP module
       
   237 
       
   238 	// Connection:close header:
       
   239 	headers.SetFieldL(stringPool.StringF(HTTP::EConnection, RHTTPSession::GetTable()), stringPool.StringF(HTTP::EClose, RHTTPSession::GetTable()));
       
   240 
       
   241 	// Content-Type header:
       
   242 	RStringF ocspRequest = stringPool.OpenFStringL(KOCSPContentTypeRequest);
       
   243 	CleanupClosePushL(ocspRequest);
       
   244 	THTTPHdrVal contentTypeVal(ocspRequest);
       
   245 	headers.SetFieldL(stringPool.StringF(HTTP::EContentType,RHTTPSession::GetTable()), contentTypeVal);
       
   246 	CleanupStack::PopAndDestroy(&ocspRequest);
       
   247 	}
       
   248 
       
   249 // From MOCSPTransport
       
   250 void COCSPTransportHttp::CancelRequest()
       
   251 	{
       
   252 	Cancel();
       
   253 	}
       
   254 
       
   255 
       
   256 // From MOCSPTransport
       
   257 TPtrC8 COCSPTransportHttp::GetResponse() const
       
   258 	{
       
   259 	__ASSERT_ALWAYS(iResponseData, Panic(KErrNotReady));
       
   260 
       
   261 	return iResponseData->Des();
       
   262 	}
       
   263 
       
   264 
       
   265 // From MHTTPDataSupplier
       
   266 TBool COCSPTransportHttp::GetNextDataPart(TPtrC8& aDataPart)
       
   267 	{
       
   268 	aDataPart.Set(iOCSPRequest);
       
   269 	return ETrue;  // This is the last chunk
       
   270 	}
       
   271 
       
   272 
       
   273 // From MHTTPDataSupplier
       
   274 void COCSPTransportHttp::ReleaseData()
       
   275 	{
       
   276 	// Nothing to do - from the OCSP side, ownership of the outgoing data is sorted already
       
   277 	}
       
   278 
       
   279 
       
   280 // From MHTTPDataSupplier
       
   281 TInt COCSPTransportHttp::OverallDataSize()
       
   282 	{
       
   283 	return iOCSPRequest.Length();
       
   284 	}
       
   285 
       
   286 
       
   287 // From MHTTPDataSupplier
       
   288 TInt COCSPTransportHttp::Reset()
       
   289 	{
       
   290 	return KErrNotSupported;
       
   291 	}
       
   292 
       
   293 // From MHTTPTransactionCallback
       
   294 void COCSPTransportHttp::MHFRunL(RHTTPTransaction aTransaction, const THTTPEvent& aEvent)
       
   295 	{
       
   296 	__ASSERT_ALWAYS(aTransaction == iHTTPTransaction, Panic(KErrArgument));
       
   297 
       
   298 	// Either ESucceeded or EFailed *will* happen
       
   299 	switch (aEvent.iStatus)
       
   300 		{
       
   301 		case THTTPEvent::EGotResponseHeaders:
       
   302 			ProcessHeadersL();
       
   303 			break;
       
   304 		case THTTPEvent::EGotResponseBodyData:
       
   305 			ProcessDataL();
       
   306 			break;
       
   307 		case THTTPEvent::EResponseComplete:
       
   308 			CheckDataCompleteL();
       
   309 			break;
       
   310 		case THTTPEvent::EFailed:
       
   311 			{
       
   312 			// Stop the timer
       
   313 			iTimer->Cancel();
       
   314 			TRequestStatus* reqstatus = &iStatus;
       
   315 			if (iServerFound)
       
   316 				{
       
   317 				User::RequestComplete(reqstatus, OCSP::KErrTransportFailure);
       
   318 				}
       
   319 			else
       
   320 				{
       
   321 				User::RequestComplete(reqstatus, OCSP::KErrServerNotFound);
       
   322 				}
       
   323 			}
       
   324 			break;
       
   325 		case THTTPEvent::ESucceeded:
       
   326 			{
       
   327 			// Stop the timer
       
   328 			iTimer->Cancel();
       
   329 			CheckDataCompleteL();
       
   330 			SetIAPL();
       
   331 			TRequestStatus* status = &iStatus;
       
   332 			User::RequestComplete(status, KErrNone);
       
   333 			}
       
   334 			break;
       
   335 		case KErrTimedOut:
       
   336 			// HTTP timed out - this really means it couldn't find the server mentioned in the url
       
   337 			// Remember this error so we pass it up correctly when we receive "THTTPEvent::EFailed" event
       
   338 			iServerFound = EFalse;
       
   339 			break;
       
   340 		default:
       
   341 			// Some other event (or error).
       
   342 			// Do nothing - HTTP module will cause EFailed or ESucceeded to occur
       
   343 			break;
       
   344 		}
       
   345 	}
       
   346 	
       
   347 void COCSPTransportHttp::SetIAPL()
       
   348 	{
       
   349 	const RStringPool strPool = iHTTPSession().StringPool();
       
   350 	RHTTPConnectionInfo connectionInfo = iHTTPSession().ConnectionInfo();
       
   351 	THTTPHdrVal hdrVal;
       
   352 	if (connectionInfo.Property(strPool.StringF(HTTP::EHttpSocketConnection, RHTTPSession::GetTable()),
       
   353 			hdrVal)) 
       
   354 		{
       
   355 		RConnection* connection =  reinterpret_cast<RConnection*>(hdrVal.Int());
       
   356 		_LIT(KIapId, "IAP\\Id");
       
   357 		User::LeaveIfError(connection->GetIntSetting(KIapId, iIap));
       
   358 		}	
       
   359 	}
       
   360 
       
   361 
       
   362 // From MHTTPTransactionCallback
       
   363 TInt COCSPTransportHttp::MHFRunError(TInt aError, RHTTPTransaction aTransaction, const THTTPEvent& /*aEvent*/)
       
   364 	{
       
   365 	__ASSERT_ALWAYS(aTransaction == iHTTPTransaction, Panic(KErrArgument));
       
   366 
       
   367 	// Process error (otherwise, we get a panic)
       
   368 	AbortTransaction(aError);
       
   369 	return KErrNone;
       
   370 	}
       
   371 
       
   372 // Methods from MCallbackTimer
       
   373 void COCSPTransportHttp::TimerRun(TInt aError)
       
   374 	{
       
   375 	if (aError == KErrNone)
       
   376 		{
       
   377 		// Handle the timeout
       
   378 		iHTTPTransaction.Cancel();
       
   379 		TRequestStatus* status = &iStatus;
       
   380 		User::RequestComplete(status, OCSP::KErrTransportTimeout);
       
   381 		}
       
   382 	else
       
   383 		{
       
   384 		// Propagate the error upward
       
   385 		TRequestStatus* status = &iStatus;
       
   386 		User::RequestComplete(status, aError);
       
   387 		}
       
   388 	}
       
   389 
       
   390 // The headers have been received - check them out
       
   391 void COCSPTransportHttp::ProcessHeadersL()
       
   392 	{
       
   393 	RHTTPResponse response = iHTTPTransaction.Response();
       
   394 	RHTTPHeaders headers = response.GetHeaderCollection();
       
   395 	RStringPool stringPool = iHTTPSession().StringPool();
       
   396 
       
   397 	// Check content type
       
   398 	RStringF ocspResponse = stringPool.OpenFStringL(KOCSPContentTypeResponse);
       
   399 	CleanupClosePushL(ocspResponse);
       
   400 	RStringF contentTypeString = stringPool.StringF(HTTP::EContentType,RHTTPSession::GetTable());
       
   401 	THTTPHdrVal contentTypeVal;
       
   402 	TInt error = headers.GetField(contentTypeString, 0, contentTypeVal);
       
   403 	if (error != KErrNone 
       
   404 		|| contentTypeVal.Type() != THTTPHdrVal::KStrFVal
       
   405 		|| contentTypeVal.StrF() != ocspResponse)
       
   406 		{
       
   407 		User::Leave(OCSP::KErrTransportFailure);
       
   408 		}
       
   409 	CleanupStack::PopAndDestroy(); // close ocspResponse
       
   410 
       
   411 	// Check content length - make descriptor ready to recieve data
       
   412 	RStringF contentLengthString = stringPool.StringF(HTTP::EContentLength,RHTTPSession::GetTable());
       
   413 	THTTPHdrVal contentLengthVal;
       
   414 	error = headers.GetField(contentLengthString, 0, contentLengthVal);
       
   415 	if (error == KErrNone
       
   416 		&& contentLengthVal.Type() == THTTPHdrVal::KTIntVal)
       
   417 		{
       
   418 		// Make descriptor ready to receive response data
       
   419 		__ASSERT_ALWAYS(iResponseData == NULL, Panic(KErrAlreadyExists));
       
   420 		iResponseLength = contentLengthVal.Int();
       
   421 		iResponseData = HBufC8::NewL(iResponseLength);
       
   422 		}
       
   423 	else
       
   424 		{
       
   425 		// No Contents-Length field in headers, or wrong data type
       
   426 		User::Leave(OCSP::KErrTransportFailure);
       
   427 		}
       
   428 	}
       
   429 
       
   430 void COCSPTransportHttp::ProcessDataL()
       
   431 	{
       
   432 	if (iResponseData)
       
   433 		{
       
   434 		// Some data has come in - copy it into our descriptor
       
   435 		MHTTPDataSupplier* body = iHTTPTransaction.Response().Body();
       
   436 		if (!body)
       
   437 			{
       
   438 			User::Leave(OCSP::KErrTransportFailure);
       
   439 			}
       
   440 
       
   441 		TPtrC8 dataChunk;
       
   442 		TBool finished = body->GetNextDataPart(dataChunk);
       
   443 		if (iResponseLength - iResponseData->Length() >= dataChunk.Length())
       
   444 			{
       
   445 			iResponseData->Des().Append(dataChunk);			
       
   446 			body->ReleaseData();
       
   447 			}
       
   448 		else
       
   449 			{
       
   450 			// Data is longer than Contents-Length header said it would be - error
       
   451 			body->ReleaseData();
       
   452 			User::Leave(OCSP::KErrTransportFailure);
       
   453 			}
       
   454 
       
   455 		if (finished)
       
   456 			{
       
   457 			CheckDataCompleteL();
       
   458 			}
       
   459 		}		
       
   460 	}
       
   461 
       
   462 void COCSPTransportHttp::CheckDataCompleteL() const
       
   463 	{
       
   464 	if (iResponseLength != iResponseData->Length())
       
   465 		{
       
   466 		User::Leave(OCSP::KErrTransportFailure);
       
   467 		}
       
   468 	}
       
   469 
       
   470 void COCSPTransportHttp::Complete(TInt aError)
       
   471 	{
       
   472 	if (iCallBack)
       
   473 		{
       
   474 		// If something went wrong, remove the dodgy response data
       
   475 		if (aError != KErrNone)
       
   476 			{
       
   477 			delete iResponseData;
       
   478 			iResponseData = NULL;
       
   479 			}
       
   480 
       
   481 		// Sets iCallBack back to NULL, so we can only do this once
       
   482 		User::RequestComplete(iCallBack, aError);
       
   483 		}
       
   484 	}
       
   485 
       
   486 void COCSPTransportHttp::RunL()
       
   487 	{
       
   488 	User::LeaveIfError(iStatus.Int());
       
   489 	switch(iState)
       
   490 		{
       
   491 	case ETransportConnectingState:
       
   492 		iState = ETransportSendRequestState;
       
   493 		iStatus = KRequestPending;
       
   494 		DoSendRequestL(*iUri, *iRequest);
       
   495 		// Start the timer (if enabled)
       
   496 		if (iTimeout > KTimeoutDisabledValue)
       
   497 			{
       
   498 			iTimer->After(iTimeout * KMillSecsToMicroSecs);
       
   499 			}
       
   500 		SetActive();
       
   501 		break;
       
   502 
       
   503 	case ETransportSendRequestState:
       
   504 		// All OK
       
   505 		Complete(KErrNone);
       
   506 		break;
       
   507 	default:
       
   508 		User::Leave(KErrNotSupported);
       
   509 		break;
       
   510 		};
       
   511 	}
       
   512 
       
   513 TInt COCSPTransportHttp::RunError(TInt aError)
       
   514 	{
       
   515 	// If we failed in the transport connection state, return a
       
   516 	// transport error. Otherwise, just propogate the error recieved.
       
   517 	if (iState == ETransportConnectingState)
       
   518 		{
       
   519 		Complete(OCSP::KErrTransportFailure);
       
   520 		}
       
   521 	else
       
   522 		{
       
   523 		Complete(aError);
       
   524 		}
       
   525 	return KErrNone;
       
   526 	}
       
   527 
       
   528 void COCSPTransportHttp::DoCancel()
       
   529 	{
       
   530 	AbortTransaction(KErrCancel);
       
   531 	Complete(KErrCancel);
       
   532 	}
       
   533 
       
   534 void COCSPTransportHttp::AbortTransaction(TInt aError)
       
   535 	{
       
   536 	switch(iState)
       
   537 		{
       
   538 		case ETransportConnectingState:
       
   539 		iHTTPSession.CancelStart();
       
   540 		break;
       
   541 
       
   542 		case ETransportSendRequestState:
       
   543 		iTimer->Cancel();
       
   544 		iHTTPTransaction.Cancel();
       
   545 
       
   546 		//DEF101099 Fix - In a very rare situation after starting invocation of this DoCancel(), the asynchronous request
       
   547 		//is getting completed normally. In that case(iStatus!=KRequestPending) no need call this request complete codes,
       
   548 		//otherwise which leads to a "Stray Signal" and ending up in a Panic - E32USER-CBase 46.
       
   549 		if(iStatus == KRequestPending)
       
   550 			{
       
   551 			// Thanks to HTTP not exporting a callback for "DoCancel()" we complete the request here
       
   552 			TRequestStatus* status = &iStatus;
       
   553 			User::RequestComplete(status, aError);
       
   554 			}	
       
   555 		break;
       
   556 		}
       
   557 	}
       
   558