diff -r 000000000000 -r af10295192d8 networksecurity/tls/protocol/tlshandshake.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/networksecurity/tls/protocol/tlshandshake.cpp Tue Jan 26 15:23:49 2010 +0200 @@ -0,0 +1,473 @@ +// 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 Handshake Negotiation source file. +// Describes the implementation of a Handshake Negotiation state machine. +// +// + +/** + @file +*/ + +#include "tlshandshakeitem.h" +#include "AlertProtocolEvents.h" +#include "handshakereceiveevents.h" +#include "handshaketransmitevents.h" +#include "recordprotocolevents.h" +#include "changecipherevents.h" +#include "tlsconnection.h" +#include "tlshandshake.h" + +void CHandshakeHeader::ComposeHeader( ETlsHandshakeMessage aHandshakeMessage, TInt aLength ) +/** + * This method composes a handshake header. + */ +{ + LOG(Log::Printf(_L("CHandshakeHeader::ComposeHeader()"));) + + __ASSERT_DEBUG( iPtr8 != NULL, TlsPanic(ETlsPanicNullPointerToHandshakeHeaderBuffer) ); + iPtr8[KTlsHandshakeTypeOffset] = static_cast(aHandshakeMessage); + + TBigEndian value; + value.SetValue( iPtr8 + KTlsHandshakeLengthOffset, KTlsHandshakeBodyLength, aLength ); +} + +// +CHandshake* CHandshake::NewL(CTlsConnection& aTlsConnection) +/** + * This method creates a Handshake negotiation object. + */ +{ + LOG(Log::Printf(_L("CHandshake::NewL()"));) + + CHandshake* self = new(ELeave) CHandshake(aTlsConnection); + LOG(Log::Printf(_L("self %x - %x"), self, (TUint)self + sizeof( CHandshake ));) + CleanupStack::PushL(self); + self->ConstructL(aTlsConnection); + CleanupStack::Pop(); + return self; +} + +CHandshake::~CHandshake() +/** + * Destructor. + * Destroys the list of handshake messages to transmit, the hash objects and the + * 'Send Alert' object + */ +{ + LOG(Log::Printf(_L("CHandshake::~CHandshake()"));) + + DestroyTxList(); + delete iSHA1Verify; + delete iMD5Verify; + delete iSendAlert; + delete iServerCert; +} + +void CHandshake::DestroyTxList() +{ + LOG(Log::Printf(_L("CHandshake::DestroyTxList() of transmitted handshake messages"));) + + CTlsEvent* listItem; + + iTxListIter.SetToFirst(); + while ( (listItem = iTxListIter++) != NULL ) + { + iTransmitList.Remove(*listItem); + delete listItem; + }; +} +void CHandshake::ConstructL(CTlsConnection& aTlsConnection) +/** + * Standard 2 phase construction. + * This method creates and returns pointers to Hash objects; + * Re-constructs the record parser and composer objects (setting them up + * specifically for Handshake negotiation); + * Creates an Alert object (for sending Alerts) and + * Initiates transmission of the first asynchronous event, the Client Hello message. + * + * @param aTlsConnection The connection that initiated the handshake negotiation. + */ +{ + LOG(Log::Printf(_L("CHandshake::ConstructL()"));) + + iSHA1Verify = CSHA1::NewL(); + iMD5Verify = CMD5::NewL(); + + iSendAlert = new(ELeave)CSendAlert( *this, aTlsConnection.RecordComposer() ); + LOG(Log::Printf(_L("iSendAlert %x - %x"), SendAlert(), (TUint)iSendAlert + sizeof( CSendAlert ));) + aTlsConnection.RecordParser().ReConstructL( this, TPtr8(NULL, 0), *iSendAlert ); + aTlsConnection.RecordComposer().ReConstructL( this, 0 ); + iActiveEvent = InitiateTransmitL(); // pointer to the 1st event/message to transmit +} + +void CHandshake::StartL( TRequestStatus* aClientStatus, MStateMachineNotify* aStateMachineNotify ) +/** + * This method starts the handshake negotiation state machine by calling the + * Start() method of the base state machine class (CStateMachine). + * + * @param aClientStatus Pointer to a TRequestStatus object that completes when + * handshake negotiation is completed. + * @param aStateMachineNotify Pointer to a MStateMachineNotify interface object. + */ +{ + LOG(Log::Printf(_L("CHandshake::StartL()"));) + + // Obtain a reference to a Record parser object and use its Handshake parser + // to set the user's data descriptor and its length (CRecordParser::iUserData + // and iUserMaxLength). + CRecordParser& RecordParser = iTlsConnection.RecordParser(); + CHandshakeParser* pParser = RecordParser.HandshakeParser(); + pParser->SetMessageAsUserDataL( KTlsHandshakeHeaderSize ); + + CStateMachine::Start( aClientStatus, (CAsynchEvent*)iSendAlert, aStateMachineNotify ); +} + +CTlsEvent* CHandshake::InitiateReceiveL() +/** + * This method creates a list of Handshake messages that are expected from + * the Server. + */ +{ + LOG(Log::Printf(_L("CHandshake::InitiateReceiveL()"));) + + CTLSProvider& tlsProvider = iTlsConnection.TlsProvider(); + + CRecordParser& recordParser = iTlsConnection.RecordParser(); + CHandshakeParser* pParser = recordParser.HandshakeParser(); + __ASSERT_DEBUG( pParser != 0, TlsPanic(ETlsPanicNullPointerToHandshakeRecordParser)); + __ASSERT_DEBUG( History() != 0, TlsPanic(ETlsPanicNullStateMachineHistory)); + + pParser->DestroyRxList(); + CHandshakeReceive* rxItem; + + if ( History() & ETlsFinishedRecv ) // Handshake negotiation is complete + { + return NULL; + } + + if ( History() == ETlsClientHelloSent ) // and nothing else has happened + { + rxItem = new(ELeave)CServerHello( tlsProvider, *this, recordParser ); + //coverity[leave_without_push] + LOG(Log::Printf(_L("CServerHello %x - %x"), rxItem, (TUint)rxItem + sizeof( CServerHello ));) + pParser->AddToList( *rxItem ); + } + else if ( History() & ETlsFullHandshake ) // Full handshake + { + if ( History() & ETlsServerHelloDoneRecv) // Server hello done received + { + rxItem = new(ELeave)CRecvFinished( tlsProvider, *this, recordParser ); + //coverity[leave_without_push] + LOG(Log::Printf(_L("CRecvFinished %x - %x"), rxItem, (TUint)rxItem + sizeof( CRecvFinished ));) + pParser->AddToList( *rxItem ); + } + else // Start of the handshake + { + if ( (History() & ETlsUsingPskKeyExchange) == 0) + { + rxItem = new(ELeave)CServerCertificate( tlsProvider, *this, recordParser ); + //coverity[leave_without_push] + LOG(Log::Printf(_L("CServerCertificate %x - %x"), rxItem, (TUint)rxItem + sizeof( CServerCertificate ));) + pParser->AddToList( *rxItem ); + } + + rxItem = new(ELeave)CServerKeyExch( tlsProvider, *this, recordParser ); + //coverity[leave_without_push] + LOG(Log::Printf(_L("CServerKeyExch %x - %x"), rxItem, (TUint)rxItem + sizeof( CServerKeyExch ));) + pParser->AddToList( *rxItem ); + + if ( (History() & ETlsUsingPskKeyExchange) == 0) + { + rxItem = new(ELeave)CCertificateReq( tlsProvider, *this, recordParser ); + //coverity[leave_without_push] + LOG(Log::Printf(_L("CCertificateReq %x - %x"), rxItem, (TUint)rxItem + sizeof( CCertificateReq ));) + pParser->AddToList( *rxItem ); + } + + rxItem = new(ELeave)CServerHelloDone( tlsProvider, *this, recordParser ); + //coverity[leave_without_push] + LOG(Log::Printf(_L("CServerHelloDone %x - %x"), rxItem, (TUint)rxItem + sizeof( CServerHelloDone ));) + pParser->AddToList( *rxItem ); + } + } + else if ( History() & ETlsAbbreviatedHandshake ) // Abbreviated handshake + { + rxItem = new(ELeave)CRecvFinished( tlsProvider, *this, recordParser ); + //coverity[leave_without_push] + LOG(Log::Printf(_L("CRecvFinished %x - %x"), rxItem, (TUint)rxItem + sizeof( CRecvFinished ));) + pParser->AddToList( *rxItem ); + } + + return &recordParser; +} + +CTlsEvent* CHandshake::InitiateTransmitL() +/** + * This method initiates transmission of SSL/TLS events. + * It destroys the current list of events/messages and checks its current + * position in terms of the state machine's history. + * It also gets references to the TLS provider interface and the Record composer + * object. (It needs access to cryptography services for the message items and it + * needs a record composer to build a TLS record to transmit) + * + * This method is called whenever the protocol has to transmit messages/events, + * i.e., at the start of Handshake negotiations and the start of Client authentication. + */ +{ + LOG(Log::Printf(_L("CHandshake::InitiateTransmitL()"));) + + DestroyTxList(); + + // Handshake negotiation is complete + if ( History() & ETlsFinishedSent ) + return NULL; + + CTLSProvider& tlsProvider = iTlsConnection.TlsProvider(); + CRecordComposer& recordComposer = iTlsConnection.RecordComposer(); + CTlsEvent* txItem; + + if ( History() == ETlsHandshakeStart ) + { + // Create a ClientHello message, initialise it and add it to the Transmit list + txItem = new(ELeave)CClientHello( tlsProvider, *this, recordComposer ); + //coverity[leave_without_push] + LOG(Log::Printf(_L("CClientHello %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));) + AddToList( *txItem ); + } + else if ( (History() & ETlsClientKeyExchSent) || (History() & ETlsAbbreviatedHandshake) ) + { + // As fixed DH is not supported, the Certificate Verify message is always sent, + // providing a non-NULL Client Certificate message is sent. + if ( CertificateVerifyReqd() ) + { + txItem = new(ELeave)CCertificateVerify( tlsProvider, *this, recordComposer ); + //coverity[leave_without_push] + LOG(Log::Printf(_L("CCertificateVerify %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));) + AddToList( *txItem ); + } + + txItem = new(ELeave)CSendChangeCipherSpec( tlsProvider, *this, recordComposer ); + //coverity[leave_without_push] + LOG(Log::Printf(_L("CSendChangeCipherSpec %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));) + AddToList( *txItem ); + + txItem = new(ELeave)CSendFinished( tlsProvider, *this, recordComposer ); + //coverity[leave_without_push] + LOG(Log::Printf(_L("CSendFinished %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));) + AddToList( *txItem ); + } + else + { + if ( History() & ETlsCertificateReqRecv ) + { + txItem = new(ELeave)CClientCertificate( tlsProvider, *this, recordComposer ); + //coverity[leave_without_push] + LOG(Log::Printf(_L("CClientCertificate %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));) + AddToList( *txItem ); + } + txItem = new(ELeave)CClientKeyExch( tlsProvider, *this, recordComposer ); + //coverity[leave_without_push] + LOG(Log::Printf(_L("CClientKeyExch %x - %x"), txItem, (TUint)txItem + sizeof( CClientHello ));) + AddToList( *txItem ); + } + + iTxListIter.SetToFirst(); + return NextTxEvent(); +} + +void CHandshake::SetNegotiatedVersion( const TTLSProtocolVersion* aTlsVersion ) +{ + iTlsConnection.RecordComposer().SetVersion( aTlsVersion ); +} + +void CHandshake::UpdateVerify( TDesC8& aMessage ) +/** + * When the first Finished message is computed, it is necessary to copy the iMD5Verify + * and iSHA1Verify to new CMD5 & CSHA1 to see all ::Final and be able to update the members + * to compute the second Finished message. + */ +{ + LOG(Log::Printf(_L("CHandshake::UpdateVerify()"));) + + iMD5Verify->Update( aMessage ); + iSHA1Verify->Update( aMessage ); +} + +void CHandshake::DoCancel() + { + LOG(Log::Printf(_L("CHandshake::DoCancel()"));) + + if ( iTlsConnection.TlsSession() ) + { + iTlsConnection.TlsSession()->CancelRequest(); + } + + iTlsConnection.RecordComposer().CancelAll(); + iTlsConnection.RecordParser().CancelAll(); + } + +void CHandshake::Cancel(TInt aLastError) + { + LOG(Log::Printf(_L("CHandshake::Cancel(TInt)"));) + CStateMachine::Cancel(aLastError); + if (!iErrorEvent) + { + OnCompletion(); // This will cause the instance to be deleted + } + } + +void CHandshake::AddToList( CTlsEvent& aMsgItem ) +{ + LOG(Log::Printf(_L("CHandshake::AddToList()"));) + + TxMessageList().AddLast(aMsgItem); +} + +void CHandshake::GetServerAddrInfo( TTLSServerAddr& serverInfo ) +{ + iTlsConnection.GetServerAddrInfo( serverInfo ); +} + +CTLSSession*& CHandshake::TlsSession() +{ + return iTlsConnection.TlsSession(); +} + +void CHandshake::ResetCryptoAttributes() +{ + iTlsConnection.ResetCryptoAttributes(); +} + +TBool CHandshake::SessionReUse() const +/** + * This method returns a boolean which indicates whether a session should be reused. + */ +{ + LOG(Log::Printf(_L("CHandshake::SessionReUse()"));) + return iTlsConnection.SessionReUse(); +} + +CExtensionNode::CExtensionNode(CItemBase* aNext) : + CConstItem(aNext, KTlsExtensionTypeLength) +{ +} + +CExtensionNode::~CExtensionNode() +{ +} + +TInt CExtensionNode::ExtensionLength() + /** + For ParseL (internalisation) to work correctly this function must return the total extension length. + Any derived class must override this function by calling this one and then adding on its extra members. + Unfortunately we can not walk the record linked list to perform this calculation because GetItemLength + is not virtual (and not define in CItemBase). + */ +{ + return GetItemLength(); +} + + +CKnownExtensionNode::CKnownExtensionNode() : + CExtensionNode(&iOpaqueData), + iOpaqueData(NULL, KTlsExtensionLength) +{ +} + +void CKnownExtensionNode::ConstructOpaqueDataWrapperL(CItemBase* aNext) +{ + iOpaqueData.AddNodeL(aNext); +} + +CKnownExtensionNode::~CKnownExtensionNode() +{ + // Must not delete the opaque data because it points to members of a class derived from this one... + iOpaqueData.DropNodes(); +} + +CCompoundList::~CCompoundList() + { + TInt nodeCount = iRecords.Count(); + for(TInt i=0; i