--- /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;
+}
+