networksecurity/tls/protocol/AlertProtocolEvents.cpp
changeset 0 af10295192d8
child 23 425d8f4f7fa5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/networksecurity/tls/protocol/AlertProtocolEvents.cpp	Tue Jan 26 15:23:49 2010 +0200
@@ -0,0 +1,246 @@
+// Copyright (c) 2003-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:
+// SSL3.0 and TLS1.0 Alert protocol source file.
+// Describes the implementation of the Alert protocol (send and receive)
+// events classes
+// 
+//
+
+/**
+ @file AlertProtocolEvents.cpp
+*/
+
+#include "AlertProtocolEvents.h"
+#include "tlsrecorditem.h"
+#include "recordprotocolevents.h"
+#include "tlshandshake.h"
+#include "applicationdata.h"
+#include <sslerr.h>
+
+// @todo There must be a means of notifying the TLS Provider when a fatal 
+// alert happens (as this invalidates the session; i.e., no new connections
+// can subsequently be made with that session id). Can ClearSessionCache be 
+// used but identifying only one session (as opposed to all a client's 
+// sessions)?
+
+//TLS specific error <-> alert code mapping
+struct TTLSErrorAlertCodeMap 
+{
+   TInt iTLSErrorCode;
+   TInt iAlertCode;
+};
+
+const TUint KWarningAlertsCount = 2;
+
+enum ETLSAlertCode {
+   EAlertclose_notify = 0, 
+
+   EAlertunexpected_message = 10,
+   EAlertbad_record_mac = 20,
+   EAlertdecryption_failed = 21,
+   EAlertrecord_overflow = 22,
+   EAlertdecompression_failure = 30,
+   EAlerthandshake_failure = 40,
+   EAlertbad_certificate = 42,
+   EAlertunsupported_certificate = 43,
+   EAlertcertificate_revoked = 44,
+   EAlertcertificate_expired = 45,
+   EAlertcertificate_unknown = 46,
+   EAlertillegal_parameter = 47,
+   EAlertunknown_ca = 48,
+   EAlertaccess_denied = 49,
+   EAlertdecode_error = 50,
+   EAlertdecrypt_error = 51,
+   EAlertexport_restriction = 60,
+   EAlertprotocol_version = 70,
+   EAlertinsufficient_security = 71,
+   EAlertinternal_error = 80,
+   EAlertuser_canceled = 90,
+   EAlertno_renegotiation = 100
+};
+
+const TTLSErrorAlertCodeMap glbTLSErrorAlertCodeMap[] = 
+{//the first two alerts are warnings the rest is fatal
+   {KErrSSLAlertCloseNotify, 0},          //close_notify(0), 
+   {KErrSSLAlertNoRenegotiation,100},     //no_renegotiation(100),
+
+   {KErrSSLAlertUnexpectedMessage,10},    //unexpected_message(10),
+   {KErrSSLAlertBadRecordMac,20},         //bad_record_mac(20),
+   {KErrSSLAlertDecryptionFailed,21},     //decryption_failed(21),
+   {KErrSSLAlertRecordOverflow,22},       //record_overflow(22),
+   {KErrSSLAlertDecompressionFailure,30}, //decompression_failure(30),
+   {KErrSSLAlertHandshakeFailure,40},     //handshake_failure(40),
+   {KErrSSLAlertBadCertificate,42},       //bad_certificate(42),
+   {KErrSSLAlertUnsupportedCertificate,43},//unsupported_certificate(43),
+   {KErrSSLAlertCertificateRevoked,44},   //certificate_revoked(44),
+   {KErrSSLAlertCertificateExpired,45},   //certificate_expired(45),
+   {KErrSSLAlertCertificateUnknown,46},   //certificate_unknown(46),
+   {KErrSSLAlertIllegalParameter,47},     //illegal_parameter(47),
+   {KErrSSLAlertUnknownCA,48},            //unknown_ca(48),
+   {KErrSSLAlertAccessDenied,49},         //access_denied(49),
+   {KErrSSLAlertDecodeError,50},          //decode_error(50),
+   {KErrSSLAlertDecryptError,51},         //decrypt_error(51),
+   {KErrSSLAlertExportRestriction,60},    //export_restriction(60),
+   {KErrSSLAlertProtocolVersion,70},      //protocol_version(70),
+   {KErrSSLAlertInsufficientSecurity,71}, //insufficient_security(71),
+   {KErrSSLAlertInternalError,80},        //internal_error(80),
+   {KErrSSLAlertUserCanceled,90}         //user_canceled(90),
+};
+//the error which apparently don't map to any alert
+//		KErrSSLAlertAccessDenied:
+//		KErrSSLAlertDecodeError:	
+//		KErrSSLAlertDecryptError:	
+
+CAsynchEvent* CSendAlert::ProcessL( TRequestStatus& aStatus )
+{
+	// @todo The processing code can be put into a separate function (code reuse/reduce bloat).
+	// @todo Ensure that alerts are set up correctly (i.e., the code that should cause an 
+	// alert to be sent is set up correctly.
+
+	TInt error = iStateMachine->LastError();
+	LOG(Log::Printf(_L("CSendAlert::ProcessL(). Error value = %d"), error ));
+   iAlertMsg.SetLength( 0 );
+	
+	switch ( error )
+	{
+		case KErrEof:
+			{	// CTlsConnection::Recv() completion status when there is no more data to 
+				// receive. This is not a SSL/TLS error.
+				TRequestStatus* p=&aStatus;
+				User::RequestComplete( p, KErrNone );
+				
+				return NULL; //stop the state machine
+			}
+		case KErrSSLAlertCloseNotify:
+			{
+				//Upon sending the close_notify from server report KErrEof to the application 
+				//to be intact with existing behaviour.	
+				iStateMachine->SetLastError( KErrEof );
+				iAlertMsg.Append( EAlertWarning );
+				iAlertMsg.Append( EAlertclose_notify );
+				iRecordComposer.SetNext( this );
+            break;
+			}
+	    case KErrDisconnected:
+			{
+			/* Fix for the TLS Client hang issue DEF130128.
+			 * Server might have disconnected the TLS connection. 
+			 * Terminate the state machine */
+				TRequestStatus* p=&aStatus;
+				User::RequestComplete(p, KErrDisconnected);
+
+				return NULL; //stop the state machine
+			}
+      case KErrArgument:
+         {
+            iStateMachine->SetLastError( KErrSSLAlertIllegalParameter );
+				iAlertMsg.Append( EAlertFatal );
+				iAlertMsg.Append( EAlertillegal_parameter );
+				iRecordComposer.SetNext( NULL );
+            break;
+         }
+		case KErrCancel:
+			{// A user_canceled alert should be followed by a close_notify alert. So this
+			 // event will be the next one to be processed. 
+				iRecordComposer.SetNext( NULL );
+				iAlertMsg.Append( EAlertWarning );
+				iAlertMsg.Append( EAlertclose_notify );
+				iRecordComposer.SetNext( NULL );
+            break;
+			}
+		default:			
+			{	//find the matching alert code to send
+            TUint nIndex = 0;
+            while ( nIndex < sizeof( glbTLSErrorAlertCodeMap )/sizeof( glbTLSErrorAlertCodeMap[0] ) &&
+               glbTLSErrorAlertCodeMap[nIndex].iTLSErrorCode != error )
+               {
+               nIndex++;
+               }
+				// Set the message content and its record type.
+            iAlertMsg.Append( nIndex >= KWarningAlertsCount ? EAlertFatal : EAlertWarning );
+				iAlertMsg.Append( nIndex < sizeof( glbTLSErrorAlertCodeMap )/sizeof( glbTLSErrorAlertCodeMap[0] ) ?
+               glbTLSErrorAlertCodeMap[nIndex].iAlertCode : EAlertunexpected_message );
+            if ( iAlertMsg[0] == EAlertFatal )
+            {
+				   iRecordComposer.SetNext( NULL );
+            }
+            break;
+			}
+
+	} //switch
+
+	iRecordComposer.SetUserData( &iAlertMsg );
+	iRecordComposer.SetRecordType( ETlsAlertContentType );
+	iRecordComposer.ResetCurrentPos();
+	return iRecordComposer.ProcessL( aStatus );
+}
+
+CAsynchEvent* CRecvAlert::ProcessL( TRequestStatus& aStatus )
+{
+	// Get the Alert message contents from the Record Parser's iPtrHBuf (this descriptor is
+	// always set to point to the decrypted (when necessary) data.
+	TPtr8 alertMsg( NULL, 0 );
+	alertMsg.Set( iRecordParser.PtrHBuf() ); 
+   User::LeaveIfError( alertMsg.Length() != KAlertMsgLength ? KErrSSLAlertUnexpectedMessage : KErrNone );
+	TUint8 alertLevel = alertMsg[0];
+	TUint8 alertDesc = alertMsg[1];
+	LOG(Log::Printf(_L("CRecvAlert::ProcessL(). Alert level = %d"), alertLevel ));
+	LOG(Log::Printf(_L("CRecvAlert::ProcessL(). Alert description = %d"), alertDesc ));
+
+	TRequestStatus* p=&aStatus;
+	User::RequestComplete( p, KErrNone );
+	if ( alertLevel == EAlertWarning )
+	   {// In all circumstances, when a warning alert is received, we carry on as normal.
+		// There is no need to set the next event as this will be unchanged from normal
+		// operation. For a Close_notify alert, we must send one in response.
+		// So the next event will be CSendAlert sending a close-notify alert.
+      if ( alertDesc == EAlertclose_notify )
+         {
+         iStateMachine->SetLastError( KErrSSLAlertCloseNotify );
+         return &iSendAlert;
+         }
+      else if ( alertDesc != EAlertno_renegotiation )
+         {
+         return &iRecordParser;
+         }
+      else if ( iRecordParser.ReadActive() )
+         {//we must be in data mode already to receive this alert
+      //if alertDesc == EAlertno_renegotiation for the moment it means that re-negotiation completed successfully
+         return NULL;
+         }
+      alertDesc = EAlertillegal_parameter;
+      }
+	//Complete the request immediately and close the connection.
+   //and set the statemachine error code to return to the client
+   TUint nIndex = 0;
+   while ( nIndex < sizeof( glbTLSErrorAlertCodeMap )/sizeof( glbTLSErrorAlertCodeMap[0] ) &&
+      glbTLSErrorAlertCodeMap[nIndex].iAlertCode != alertDesc )
+      {
+      nIndex++;
+      }
+   iStateMachine->SetLastError( nIndex < sizeof( glbTLSErrorAlertCodeMap )/sizeof( glbTLSErrorAlertCodeMap[0] ) ?
+      glbTLSErrorAlertCodeMap[nIndex].iTLSErrorCode : KErrSSLAlertIllegalParameter );
+
+	return NULL;
+}
+
+TBool CRecvAlert::AcceptRecord( TInt aRecordType ) const
+/**
+ * This method accepts an Alert event. Alerts are always accepted.
+ */
+{
+	LOG(Log::Printf(_L("CRecvAlert::AcceptRecord()\n"));)  
+	return aRecordType == ETlsAlertContentType; 
+}
+