diff -r 000000000000 -r af10295192d8 networksecurity/tls/protocol/tlsconnection.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/networksecurity/tls/protocol/tlsconnection.cpp Tue Jan 26 15:23:49 2010 +0200 @@ -0,0 +1,1505 @@ +// 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 Connection source file. +// Describes the implementation of a secure (SSL/TLS) connection. +// +// + +/** + @file +*/ + +#include "tlsconnection.h" +#include "recordprotocolevents.h" +#include "tlshandshake.h" +#include "applicationdata.h" +#include +#include + +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include +#endif + + +EXPORT_C MSecureSocket* CTlsConnection::NewL(RSocket& aSocket, const TDesC& aProtocol) +/** + * Creates and initialises a new CTlsConnection object. + * + * @param aSocket is a reference to an already open and connected socket. + * @param aProtocol is a descriptor containing the name of the protocol (SSL3.0 or + * TLS1.0) the application specified when the secure socket was created. + * @return A pointer to a newly created Secure socket object. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::NewL(RSocket,Protocol)"));) + CTlsConnection* self = new(ELeave) CTlsConnection(); + LOG(Log::Printf(_L("self %x - %x"), self, (TUint)self + sizeof( CTlsConnection ));) + +#ifdef _DEBUG + TInt nBlock; + LOG(Log::Printf(_L("RHeap::Size(), RHeap::Size() - RHeap::Available() %d, %d"), User::Heap().Size(), User::Heap().Size() - User::Heap().Available( nBlock ) );) +#endif + + CleanupStack::PushL(self); + self->ConstructL(aSocket, aProtocol); + CleanupStack::Pop(); + return self; +} + +EXPORT_C MSecureSocket* CTlsConnection::NewL(MGenericSecureSocket& aSocket, const TDesC& aProtocol) +/** + * Creates and initialises a new CTlsConnection object. + * + * @param aSocket is a reference to socket like object derived from MGenericSecureSocket. + * @param aProtocol is a descriptor containing the name of the protocol (SSL3.0 or + * TLS1.0) the application specified when the secure socket was created. + * @return A pointer to a newly created Secure socket object. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::NewL(GenericSocket,Protocol)"));) + CTlsConnection* self = new(ELeave) CTlsConnection(); + LOG(Log::Printf(_L("self %x - %x"), self, (TUint)self + sizeof( CTlsConnection ));) + +#ifdef _DEBUG + TInt nBlock; + LOG(Log::Printf(_L("RHeap::Size(), RHeap::Size() - RHeap::Available() %d, %d"), User::Heap().Size(), User::Heap().Size() - User::Heap().Available( nBlock ) );) +#endif + + CleanupStack::PushL(self); + self->ConstructL(aSocket, aProtocol); + CleanupStack::Pop(); + return self; +} + +EXPORT_C void CTlsConnection::UnloadDll(TAny* /*aPtr*/) +/** + Function called prior to unloading DLL. + Does nothing in this implementation but is needed to be exported. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::UnloadDll()"));) +} + +CTlsConnection::~CTlsConnection() +/** + * Destructor. + * The user should ensure that the connection has been closed before destruction, + * as there is no check for any pending asynch event here (apart from the panic + * in ~CActive). + */ +{ + LOG(Log::Printf(_L("CTlsConnection::~CTlsConnection()"));) + +#ifdef _DEBUG + TInt nBlock; + LOG(Log::Printf(_L("RHeap::Size(), RHeap::Size() - RHeap::Available() %d, %d"), User::Heap().Size(), User::Heap().Size() - User::Heap().Available( nBlock ) );) +#endif + DeleteStateMachines(); + + delete iRecordParser; //don't change the order of the deletion (see ~CRecordParser & CRecordParser:;Reset) + delete iRecordComposer; + delete iGenericSocket; + delete iClientCert; + delete iServerCert; +#ifdef _DEBUG + LOG(Log::Printf(_L("RHeap::Size(), RHeap::Size() - RHeap::Available() %d, %d"), User::Heap().Size(), User::Heap().Size() - User::Heap().Available( nBlock ) );) +#endif + delete iTlsProvider; + delete iTlsSession; +#ifdef _DEBUG + LOG(Log::Printf(_L("RHeap::Size(), RHeap::Size() - RHeap::Available() %d, %d"), User::Heap().Size(), User::Heap().Size() - User::Heap().Available( nBlock ) );) +#endif +} + +CTlsConnection::CTlsConnection() : CActive( EPriorityHigh ) +/** + * Constructor . + * Sets the Active object priority. + */ +{ +} + +void CTlsConnection::ConstructL(RSocket& aSocket, const TDesC& aProtocol) +/** + * Two-phase constructor. + * Called by CTlsConnection::NewL() to initialise all the + * CTlsConnection objects (bar the State machines). It also sets the + * protocol for the connection. The Provider interface is created and the Session + * interface pointer is set to NULL (as no session currently exists). + * The dialog mode for the connection is set to Attended mode (default) and the current + * cipher suite is set to [0x00],[0x00]. + * + * @param aSocket is a reference to an already open and connected socket. + * @param aProtocol is a descriptor containing the name of the protocol (SSL3.0 or + * TLS1.0) the application specified when the secure socket was created. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::ConstructL(RSocket,Protocol)"));) + + CActiveScheduler::Add(this); + + iTlsProvider = CTLSProvider::ConnectL(); // Set up Security/crypto interfaces + + User::LeaveIfError( SetProtocol(aProtocol) ); + iTlsProvider->Attributes()->iCurrentCipherSuite.iLoByte = 0x00; + iTlsProvider->Attributes()->iCurrentCipherSuite.iHiByte = 0x00; + iTlsProvider->Attributes()->iDialogNonAttendedMode = EFalse; + iDialogMode = EDialogModeAttended; + + iGenericSocket = new(ELeave)CGenericSecureSocket(aSocket); + + iRecordParser = new(ELeave)CRecordParser( *iGenericSocket, *iTlsProvider ); + LOG(Log::Printf(_L("iRecordParser %x - %x"), iRecordParser, (TUint)iRecordParser + sizeof( CRecordParser ));) + iRecordComposer = new(ELeave)CRecordComposer( *iGenericSocket, *iTlsProvider ); + LOG(Log::Printf(_L("iRecordComposer %x - %x"), iRecordComposer, (TUint)iRecordComposer + sizeof( CRecordComposer ));) + +#ifdef _DEBUG + TInt nBlock; + LOG(Log::Printf(_L("RHeap::Size(), RHeap::Size() - RHeap::Available() %d, %d"), User::Heap().Size(), User::Heap().Size() - User::Heap().Available( nBlock ) );) +#endif +} + +void CTlsConnection::ConstructL(MGenericSecureSocket& aSocket, const TDesC& aProtocol) +/** + * Two-phase constructor. + * Called by CTlsConnection::NewL() to initialise all the + * CTlsConnection objects (bar the State machines). It also sets the + * protocol for the connection. The Provider interface is created and the Session + * interface pointer is set to NULL (as no session currently exists). + * The dialog mode for the connection is set to Attended mode (default) and the current + * cipher suite is set to [0x00],[0x00]. + * + * @param aSocket is a reference to socket like object derived from MGenericSecureSocket. + * @param aProtocol is a descriptor containing the name of the protocol (SSL3.0 or + * TLS1.0) the application specified when the secure socket was created. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::ConstructL(GenericSocket,Protocol)"));) + + CActiveScheduler::Add(this); + + iTlsProvider = CTLSProvider::ConnectL(); // Set up Security/crypto interfaces + + User::LeaveIfError( SetProtocol(aProtocol) ); + iTlsProvider->Attributes()->iCurrentCipherSuite.iLoByte = 0x00; + iTlsProvider->Attributes()->iCurrentCipherSuite.iHiByte = 0x00; + iTlsProvider->Attributes()->iDialogNonAttendedMode = EFalse; + iDialogMode = EDialogModeAttended; + + iRecordParser = new(ELeave)CRecordParser( aSocket, *iTlsProvider ); + LOG(Log::Printf(_L("iRecordParser %x - %x"), iRecordParser, (TUint)iRecordParser + sizeof( CRecordParser ));) + iRecordComposer = new(ELeave)CRecordComposer( aSocket, *iTlsProvider ); + LOG(Log::Printf(_L("iRecordComposer %x - %x"), iRecordComposer, (TUint)iRecordComposer + sizeof( CRecordComposer ));) +#ifdef _DEBUG + TInt nBlock; + LOG(Log::Printf(_L("RHeap::Size(), RHeap::Size() - RHeap::Available() %d, %d"), User::Heap().Size(), User::Heap().Size() - User::Heap().Available( nBlock ) );) +#endif +} + +void CTlsConnection::RunL() +{ + LOG(Log::Printf(_L("CTlsConnection::RunL()"));) +#ifdef _DEBUG + TInt nBlock; + LOG(Log::Printf(_L("RHeap::Size(), RHeap::Size() - RHeap::Available() %d, %d"), User::Heap().Size(), User::Heap().Size() - User::Heap().Available( nBlock ) );) +#endif + + CActiveScheduler::Stop(); +} + +void CTlsConnection::DoCancel() +{ +} + + +// MSecureSocket interface +TInt CTlsConnection::AvailableCipherSuites( TDes8& aCiphers ) +/** + * Retrieves the list of cipher suites that are available to use + * for handshake negotiation. + * Cipher suites are returned in two byte format as is specified in the SSL/TLS + * specifications, e.g. [0x00][0x03]. + * + * @param aCiphers A reference to a descriptor which will contain a list of cipher suites. + * @return Any one of the system error codes, or KErrNone on success. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::AvailableCipherSuites()"));) + + if ( !iTlsProvider ) + { + return KErrNotReady; + } + RArray cipherList; + TRAPD(ret,iTlsProvider->CipherSuitesL(cipherList, iStatus)); + if ( ret != KErrNone ) + return ret; + + SetActive(); + CActiveScheduler::Start(); + + if ( iStatus.Int() != KErrNone ) + { + LOG(Log::Printf(_L("Error retrieving the available cipher suites %d"), iStatus.Int() );) + return iStatus.Int(); + } + + // Each cipher suite contains 2 TUint8 elements. Check the length of the user's + // descriptor, copy the available cipher suites into it and close the array. + if ( aCiphers.MaxLength() < (cipherList.Count() * 2) ) + return KErrOverflow; + + for (TInt loop = 0; loop < cipherList.Count(); ++loop) + { + aCiphers.Append( cipherList[loop].iHiByte ); + aCiphers.Append( cipherList[loop].iLoByte ); + } + + cipherList.Close(); + return KErrNone; +} + +void CTlsConnection::CancelAll() +/** + * Cancels all outstanding operations. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::CancelAll()"));) + CancelAll( KErrNone ); +} + +void CTlsConnection::CancelHandshake() +/** + * Cancels an outstanding handshake operation. It is equivalent to + * a CancelAll() call. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::CancelHandshake()"));) + CancelAll( KErrNone ); +} + +void CTlsConnection::CancelRecv() +/** + * Cancels any outstanding read data operation. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::CancelRecv()"));) + if ( iRecvAppData ) + iRecvAppData->Cancel( KErrNone ); //no alert sent ? maybe we should send user abort + //but then it'll became be asynchronous fn // @todo - Cancels have not been tested +} + +void CTlsConnection::CancelSend() +/** + * Cancels any outstanding send data operation. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::CancelSend()"));) + if ( iSendAppData ) + iSendAppData->Cancel( KErrNone ); //no alert sent ? maybe we should send user abort + //but then it'll became be asynchronous fn // @todo +} + +const CX509Certificate* CTlsConnection::ClientCert() +/** + * Returns a pointer to the current client certificate if a Server has + * requested one. If there is no suitable client certificate available, a NULL pointer + * will be returned. + * A client certificate (if available) can only be returned after the negotiation + * is complete. + * + * @return A pointer to the client certificate, or NULL if none exists or is yet + * available. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::ClientCert()"));) + + if (!iTlsProvider || !iTlsProvider->TlsSessionPtr()) + { + LOG(Log::Printf(_L("The Client certificate is not yet available()"));) + return NULL; + } + else + { + if ( !iClientCert ) + { + iTlsProvider->TlsSessionPtr()->ClientCertificate(iClientCert, iStatus); + + SetActive(); + CActiveScheduler::Start(); + } + return iClientCert; + } +} + +TClientCertMode CTlsConnection::ClientCertMode() +/** + * Returns the current client certificate mode. This is used when the + * socket is acting as a server, and determines if a client certificate is requested. + * This method is not supported as this implementation only acts in Client mode. + * + * The closest value that the TClientCertMode enumeration provides that supports this + * is EClientCertModeIgnore. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::ClientCertMode() - Not Supported"));) + return EClientCertModeIgnore; +} + +void CTlsConnection::Close() +/** + * Closes the secure connection. + * All outstanding operations are cancelled, the internal state machines are deleted + * and the socket is closed. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::Close()"));) + //OK cancel with closure alert sent (=> asynch call) and delete the statemachines + CancelAll( KErrNone /*KErrSSLAlertCloseNotify*/ ); //it's possible to make it asynch + //(send an alert) but.... + DeleteStateMachines(); + iRecordParser->Socket().Close(); + iRecordComposer->SetVersion( NULL ); + + ResetCryptoAttributes(); +} + +TInt CTlsConnection::CurrentCipherSuite( TDes8& aCipherSuite ) +/** + * Retrieves the current cipher suite in use. + * Cipher suites are returned in two byte format as is specified in the SSL/TLS + * specifications, i.e. [0x??][0x??]. + * + * This method can only return the current cipher suite when the Server has proposed one + * to use (i.e., anytime after the Server Hello has been received). Hence, it will only + * have a valid value after the Handshake negotiation has completed. If called before + * handshake negotiation, it will have the value of the NULL cipher, [0x00][0x00]. + * + * @param aCipherSuite A reference to a descriptor at least 2 bytes long. + * @return Any one of the system error codes, or KErrNone on success. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::CurrentCipherSuite()"));) + + if ( !iTlsProvider ) + { + return KErrNotReady; + } + if ( aCipherSuite.MaxLength() < 2 ) + { + LOG(Log::Printf(_L("CurrentCipherSuite() - Descriptor should be at least 2 bytes long"));) + return KErrOverflow; + } + + aCipherSuite.SetLength(2); // A cipher suite has a 2 byte length. + aCipherSuite[0] = iTlsProvider->Attributes()->iCurrentCipherSuite.iHiByte; + aCipherSuite[1] = iTlsProvider->Attributes()->iCurrentCipherSuite.iLoByte; + + return KErrNone; +} + +TDialogMode CTlsConnection::DialogMode() +/** + * Returns the current dialog mode. + * + * @return The current dialog mode. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::DialogMode()"));) + return iDialogMode; +} + +void CTlsConnection::FlushSessionCache() +/** + * This method does NOT flush the session cache (as this is device-wide). As such, its + * interpretation has changed from the pre-Zephyr TLS implementation. + * + * It is now used as an indication that the client does not intend to reuse an existing + * session. As such it sets a flag which is called during handshake negotiation which + * indicates whether a new session or existing session will be used. + * The other choice for implementation of this method will be: + * 1) Call TLS Provider's GetSession() API to retrieve the session information. + * 2) Call TLS Provider's ClearSessionCache() API (with the retrieved session information) + * to remove the particular session from the session cache. Both these APIs are asynchronous. + * + * Note that there is no means of indicating the success or failure of this operation + * to the client. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::FlushSessionCache()"));) + if ( iTlsProvider ) + { + TTLSSessionNameAndID sessionNameAndID; + GetServerAddrInfo( sessionNameAndID.iServerName ); + TRAPD(ret,iTlsProvider->ClearSessionCacheL( sessionNameAndID, iStatus )); + if (KErrNone!=ret) + { + LOG(Log::Printf(_L("CTlsConnection: ClearSessionCacheL error: %d"),ret)); + return; + } + SetActive(); + CActiveScheduler::Start(); + } +} + +TInt CTlsConnection::GetOpt(TUint aOptionName,TUint aOptionLevel,TDes8& aOption) +/** + * Gets a Socket option. + * + * @param aOptionName An unsigned integer constant which identifies an option. + * @param aOptionLevel An unsigned integer constant which identifies the level of an option. + * @param aOption Option value packaged in a descriptor. + * @return KErrNone if successful, otherwise another of the system-wide error codes. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::GetOpt() method - descriptor Option"));) + if ( !iTlsProvider || !iTlsProvider->Attributes()) + { + return KErrNotReady; + } + + switch(aOptionLevel) + { + case KSolInetSSL: + { + switch (aOptionName) + { + case KSoCurrentCipherSuite: + { + // Call the API method that implements the functionality. + LOG(Log::Printf(_L("Option name: KSoCurrentCipherSuite"))); + return CurrentCipherSuite(aOption); + } + case KSoAvailableCipherSuites: + { + // Call the API method that implements the functionality. + LOG(Log::Printf(_L("Option name: KSoAvailableCipherSuites"))); + return AvailableCipherSuites(aOption); + } + case KSoDialogMode: + { + // Call the API method that implements the functionality. + LOG(Log::Printf(_L("Option name: KSoDialogMode"))); + + TDialogMode mode = DialogMode(); + TPckgBuf packedMode(mode); // Package the object into a descriptor + + if ( aOption.MaxLength() < packedMode.Length() ) + return KErrOverflow; + + aOption.Copy(packedMode); + return KErrNone; + } + case KSoSSLServerCert: + { + // Call the API method that implements the functionality. + LOG(Log::Printf(_L("Option name: KSoSSLServerCert"))); + + const CX509Certificate* cert = ServerCert(); + TPckgBuf packedCert(cert); // Package the pointer into a descriptor + + if ( aOption.MaxLength() < packedCert.Length() ) + return KErrOverflow; + + aOption.Copy( packedCert ); + return KErrNone; + } + case KSoUseSSLv2Handshake: + { + /* + This option is no longer supported, but returning KErrNotSupported + or any other error code will result in a BC break. + Hence we return KErrNone untill the break gets approved by SCB + */ + return KErrNone; + } + case KSoKeyingMaterial: + { + /* + Performs key generation as per RFC2716 + (PPP EAP TLS Authentication Protocol) section 3.5 + */ + return GetKeyingMaterial(aOption); + } + case KSoEnableNullCiphers: + { + *((TInt *)aOption.Ptr()) = iTlsProvider->Attributes()->iAllowNullCipherSuites; + return KErrNone; + } + + case KSoPskConfig: + { + CTlsCryptoAttributes *attrs = iTlsProvider->Attributes(); + MSoPskKeyHandler *handler = NULL; + if(attrs->iPskConfigured) + { + handler = attrs->iPskKeyHandler; + } + if(aOption.Length() < sizeof(MSoPskKeyHandler *)) + { + return KErrArgument; + } + // aOption must be a descriptor wrapped arround a MSoPskKeyHandler pointer + // For example TPckgBuf pskConfigPkg + *((MSoPskKeyHandler **)aOption.Ptr()) = handler; + return KErrNone; + } + default: + { + LOG(Log::Printf(_L("Unknown option name passed in."))); + return KErrNotSupported; + } + } // switch (name) + } // KSolInetSSL + default: + { + // Call the RSocket options directly + return iRecordComposer->Socket().GetOpt(aOptionName, aOptionLevel, aOption); + } + } // switch (aOptionLevel) +} + +TInt CTlsConnection::GetOpt(TUint aOptionName,TUint aOptionLevel,TInt& aOption) +/** + * Gets a Socket option. + * + * @param aOptionName An integer constant which identifies an option. + * @param aOptionLevel An integer constant which identifies the level of an option. + * @param aOption Option value as an integer. + * @return KErrNone if successful, otherwise another of the system-wide error codes. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::GetOpt() method - integer Option"));) + + TPtr8 optionDes( (TUint8*)&aOption, sizeof(TInt), sizeof(TInt) ); + return GetOpt(aOptionName, aOptionLevel, optionDes); +} + +TInt CTlsConnection::Protocol(TDes& aProtocol) +/** + * Returns the Protocol version in use. A minimum descriptor size of 8 is + * defined for the protocol name (a maximum of 32 is specified in the Secure Socket interface). + * + * This method can only return the agreed/negotiated Protocol anytime when the handshake + * negotiation has completed. If called before this, the value returned is the protocol + * version proposed by the user. + * + * @param aProtocol A reference to a descriptor containing the protocol name in use. + * @return An Integer value indicating the outcome of the function call. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::Protocol()"));) + if ( !iTlsProvider ) + { + return KErrNotReady; + } + + // Ensure that the descriptor size passed in reaches our minimum requirement of KProtocolDescMinSize (i.e., 8) + if (aProtocol.MaxSize() < KProtocolDescMinSize) + return KErrOverflow; + + TInt ret = KErrNone; + + CTlsCryptoAttributes& cryptoAttributes = *iTlsProvider->Attributes(); + //check whether any protocol's been negotiated yet. if no return the proposed protocol + const TTLSProtocolVersion& tlsVersion = cryptoAttributes.iNegotiatedProtocol.iMajor ? + cryptoAttributes.iNegotiatedProtocol : cryptoAttributes.iProposedProtocol; + if (tlsVersion == KSSL3_0) + { + aProtocol.Copy(KProtocolVerSSL30); // SSL 3.0 + } + else if (tlsVersion == KTLS1_0) + { + aProtocol.Copy(KProtocolVerTLS10); // TLS 1.0 + } + else + { + ret = KErrGeneral; // Unknown protocol version + LOG(Log::Printf(_L("CTlsConnection::Protocol() Unknown protocol error %d"), ret );) + } + + return ret; +} + +void CTlsConnection::Recv(TDes8& aDesc, TRequestStatus & aStatus) +/** + * Receives data from the socket. + * It is an asynchronous method, and will complete when the descriptor has been filled. + * Only one Recv or RecvOneOrMore operation can be outstanding at any time. + * + * @param aDesc A descriptor where data read will be placed. + * @param aStatus On completion, will contain an error code: see the system-wide error + * codes. Note that KErrEof indicates that a remote connection is closed, and that no + * more data is available for reading. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::Recv()"));) + + aDesc.Zero(); + if ( RecvData( aDesc, aStatus ) ) + iRecvAppData->SetSockXfrLength( NULL ); +} + +void CTlsConnection::RecvOneOrMore(TDes8& aDesc, TRequestStatus& aStatus, TSockXfrLength& aLen) +/** + * Receives data from the socket. + * It is an asynchronous call, and will complete when at least one byte has been read. + * Only one Recv or RecvOneOrMore operation can be outstanding at any time. + * + * @param aDesc A descriptor where data read will be placed. + * @param aStatus On completion, will contain an error code: see the system-wide error + * codes. Note that KErrEof indicates that a remote connection is closed, and that no + * more data is available for reading. + * @param aLen On return, a length which indicates how much data was read. This is + * the same as the length of the returned aDesc. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::RecvOneOrMore()"));) + + if ( RecvData( aDesc, aStatus ) ) + iRecvAppData->SetSockXfrLength( &aLen() ); +} + +void CTlsConnection::RenegotiateHandshake(TRequestStatus& aStatus) +/** + * Initiates a renegotiation of the secure connection. + * It is an asynchronous method that completes when renegotiation is complete. + * The Client can initiate handshake renegotiation or it can receive a re-negotiation request + * from a remote server. + * Note that the User should cancel any data transfer or wait for its completion before + * attempting to re-negotiate. + * + * @param aStatus On completion, will contain an error code: see the system-wide error + * codes. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::RenegotiateHandshake()"));) + + TRequestStatus* pStatus = &aStatus; + + // Renegotiation can only happen in data mode + if ( !IsInDataMode() ) + { + User::RequestComplete( pStatus, KErrNotReady ); + return; + } + + // Renegotiation is already taking place or client is tx-ing or rx-ing data + if ( IsReNegotiating() || iSendAppData->ClientStatus() || iRecvAppData->ClientStatus() ) + { + User::RequestComplete( pStatus, KErrInUse ); + return; + } + + StartRenegotiation( pStatus ); +} + +void CTlsConnection::Send(const TDesC8& aDesc, TRequestStatus& aStatus) +/** + * Sends data over the socket. + * Only one Send operation can be outstanding at any time. + * + * @param aDesc A constant descriptor containing the data to be sent. + * @param aStatus On completion, will contain an error code: see the system-wide + * error codes. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::Send() - Descriptor only"));) + + if ( SendData( aDesc, aStatus ) ) + iSendAppData->SetSockXfrLength( NULL ); +} + +void CTlsConnection::Send(const TDesC8& aDesc, TRequestStatus& aStatus, TSockXfrLength& aLen) +/** + * Sends data over the socket. + * Only one Send operation can be outstanding at any time. + * + * @param aDesc A constant descriptor. + * @param aStatus On completion, will contain an error code: see the system-wide + * error codes. + * @param aLen Filled in with amount of data sent before completion + */ +{ + LOG(Log::Printf(_L("CTlsConnection::Send() - Descriptor and Length"));) + + if ( SendData( aDesc, aStatus ) ) + iSendAppData->SetSockXfrLength( &aLen() ); +} + +const CX509Certificate* CTlsConnection::ServerCert() +/** + * Returns a pointer to the current server certificate. + * The returned certificate will be the certificate for the remote server. It is + * obtained via the TLS Provider API. + * + * A server certificate (if available) can only be returned only after the + * negotiation has reached a stage at which one has been received and verified. + * + * @return A pointer to the Server's certificate. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::ServerCert()"));) + + if ( !iTlsProvider || !iTlsProvider->TlsSessionPtr()) + { + LOG(Log::Printf(_L("The Server certificate is not yet available()"));) + return NULL; + } + else + { + if ( !iServerCert ) + { + iTlsProvider->TlsSessionPtr()->ServerCertificate(iServerCert, iStatus); + + SetActive(); + CActiveScheduler::Start(); + + if ( iStatus.Int() != KErrNone ) + { + LOG(Log::Printf(_L("Error retrieving the Server certificate %d"), iStatus.Int() );) + } + } + return iServerCert; + } +} + +TInt CTlsConnection::SetAvailableCipherSuites(const TDesC8& aCiphers) +/** + * A client can be involved in the Handshake negotiation with the remote server by + * specifying which cipher suites it wants to use in the negotiation. + * The client should first call AvailableCipherSuites() to retrieve all the supported + * cipher suites. This method can then be used to specify a subset which it wants to + * use. + * The list of cipher suites supplied in a descriptor to the protocol MUST be in two + * byte format, i.e. [0x??][0x??]. The order of suites is important, and so they should + * be listed with the preferred suites first. + * A client does NOT have to call/use this method. In this instance, the preference + * order of the cipher suites will be set by the TLS Provider. + * + * @param aCiphers A descriptor containing the list of ciphers suites to use. + * @return Any one of the system error codes, or KErrNone on success. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::SetAvailableCipherSuites()"));) + if ( !iTlsProvider ) + { + return KErrNotReady; + } + // Get the available cipher suites from the Provider. + RArray cipherList; + TRAPD(ret,iTlsProvider->CipherSuitesL(cipherList, iStatus)); + if ( ret != KErrNone ) + return ret; + + SetActive(); +// TRequestStatus* p=&iStatus; +// User::RequestComplete( p, KErrNone ); + CActiveScheduler::Start(); + + // Cycle through the client's list of ciphers. + // Ensure that values in the clients list are in the list of available ciphers. + TBool valueIsSet; // Break out of the inner loop once the value is set. + TInt returnValue = iStatus.Int(); + TDes8& proposedCiphers = iTlsProvider->Attributes()->iProposedCiphers; + proposedCiphers.Zero(); + for ( TInt outerLoop=0; outerLoopAttributes()->iDialogNonAttendedMode = (iDialogMode == EDialogModeUnattended); + } + + return ret; +} + +TInt CTlsConnection::SetOpt(TUint aOptionName,TUint aOptionLevel, const TDesC8& aOption) +/** + * Sets a Socket option. + * + * @param aOption Option value packaged in a descriptor. + * @param aOptionName An integer constant which identifies an option. + * @param aOptionLevel An integer constant which identifies the level of an option + * (an option level groups related options together). + * @return Any one of the system error codes, or KErrNone on success. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::SetOpt() method - descriptor Option"));) + TInt ret=KErrNotSupported; + + if ( !iTlsProvider || !iTlsProvider->Attributes()) + { + return KErrNotReady; + } + + switch(aOptionLevel) + { + case KSolInetSSL: // This is the only supported option level in SSL/TLS + { + switch (aOptionName) + { + case KSoSSLDomainName: + { + LOG(Log::Printf(_L("Option name: KSoSSLDomainName"))); + iTlsProvider->Attributes()->idomainName.Copy(aOption); + // @todo Create a CX509Name object and set in the TTlsCryptoAttribs + // structure. Note that this structure has to updated to take a + // CX509**** object. The iProposedCiphers buffer is still not correct. + // It must also have a iProposedProtocol and iNegotiatedProtocol. + ret = KErrNone; + break; + } + case KSoDialogMode: + { + // Call the API method that implements the functionality. + LOG(Log::Printf(_L("Option name: KSoDialogMode"))); + + TDialogMode dialogMode = (TDialogMode) ( *(TUint*)aOption.Ptr() ); + ret = SetDialogMode(dialogMode); + + break; + } + case KSoUseSSLv2Handshake: + { + /* + This option is no longer supported, but returning KErrNotSupported + or any other error code will result in a BC break. + Hence we return KErrNone untill the break gets approved by SCB + */ + ret = KErrNone; + break; + } + case KSoEnableNullCiphers: + { + TInt option = *reinterpret_cast(aOption.Ptr()); + iTlsProvider->Attributes()->iAllowNullCipherSuites = (option != 0); + ret = KErrNone; + break; + } + case KSoPskConfig: + { + /* + Set the PSK Key Exchange configuration. + aOption is a TDesC8 wrapper around a MSoPskKeyHandler pointer + */ + // aOption must be a descriptor wrapped arround a MSoPskKeyHandler pointer + // For example TPckgBuf pskConfigPkg + if(aOption.Length() < sizeof(MSoPskKeyHandler *)) + { + return KErrArgument; + } + TPckgBuf pskConfigPkg; + MSoPskKeyHandler *handler = *reinterpret_cast(aOption.Ptr()); + + CTlsCryptoAttributes *attrs = iTlsProvider->Attributes(); + attrs->iPskConfigured = (handler!=0); + attrs->iPskKeyHandler = handler; + ret = KErrNone; + break; + } + case KSoServerNameIndication: + { + /* + * Set the list of server names to be passed to the server in the ClientHello as described in + * RFC3546 "Server Name Indication". + * aOption is a TDesC8 wrapper around a CDesC8Array pointer. + */ + if(aOption.Length() < sizeof(CDesC8Array *)) + { + return KErrArgument; + } + CDesC8Array *serverNames = *reinterpret_cast(aOption.Ptr()); + + CTlsCryptoAttributes *attrs = iTlsProvider->Attributes(); + delete attrs->iServerNames; + attrs->iServerNames = serverNames; + + ret = KErrNone; + break; + } + default: + break; + } // KSolInetSSL + + break; + } + default: // Not a supported SSL option, call RSocket::SetOpt directly + { + LOG(Log::Printf(_L("Default option level (not supported by protocol)"))); + ret = iRecordComposer->Socket().SetOpt(aOptionName, aOptionLevel, aOption); + + break; + } // Default 'level' statement + } // switch statement + + return ret; +} + +TInt CTlsConnection::SetOpt(TUint aOptionName,TUint aOptionLevel,TInt aOption) +/** + * Sets a Socket option. calls the SetOpt() method defined above. + * + * @param aOption Option value as an integer + * @param aOptionName An integer constant which identifies an option. + * @param aOptionLevel An integer constant which identifies level of an option (an + * option level groups related options together. + * @return Any one of the system error codes, or KErrNone on success. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::SetOpt() method - integer Option"));) + + TPtr8 optionDes( (TUint8*)&aOption, sizeof(TInt), sizeof(TInt) ); + return SetOpt(aOptionName, aOptionLevel, optionDes); +} + +TInt CTlsConnection::SetProtocol(const TDesC& aProtocol) +/** + * Sets the Secure socket protocol version (SSL v3.0 or TLS v1.0) to + * use in the Handshake negotiation. It also initially sets the negotiated protocol + * to the requested protocol. A maximum length of 32 is specified in the Secure Socket + * interface for the protocol version. + * + * + * @param aProtocol is a reference to a descriptor containing the protocol version to use. + * @return Any one of the system error codes, or KErrNone on success. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::SetProtocol()"));) + + if ( !iTlsProvider ) + { + return KErrNotReady; + } + // Convert the Protocol value to upper case before doing a comparison + TBuf<32> tempBuf; + tempBuf.Copy(aProtocol); + tempBuf.UpperCase(); + + TInt ret = tempBuf.Compare(KProtocolVerSSL30); + if ( ret == 0 ) + { + iTlsProvider->Attributes()->iProposedProtocol = KSSL3_0; + } + else + { + ret = tempBuf.Compare(KProtocolVerTLS10); + if ( ret == 0 ) + { + iTlsProvider->Attributes()->iProposedProtocol = KTLS1_0; + } + else + return KErrNotSupported; + } + return KErrNone; +} + +TInt CTlsConnection::SetServerCert(const CX509Certificate& /*aCert*/) +/** + * Reserved for future work, always returns KErrNotSupported. + * + * @param aCert The certificate to use. + * @return Any one of the system error codes, or KErrNone on success. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::SetServerCert()"));) + return KErrNotSupported; +} + +void CTlsConnection::StartClientHandshake(TRequestStatus& aStatus) +/** + * Starts a client request and initiates a handshake + * with the remote server. + * Configuration retrieval happens during construction of the CTlsConnection object, + * which progresses the connection into the Idle state. + * + * @param aStatus On completion, any one of the system error codes, or KErrNone + * on success (handshake negotiation complete). + */ +{ + LOG(Log::Printf(_L("CTlsConnection::StartClientHandshake()"));) + TRequestStatus* pStatus = &aStatus; + + if ( !IsIdle() ) // The connection must be in the Idle state + { + User::RequestComplete( pStatus, KErrInUse ); + return; + } + + StartClientHandshakeStateMachine( pStatus ); +} + +void CTlsConnection::StartServerHandshake(TRequestStatus& aStatus) +/** + * Start acting as a server and listen for a handshake from the remote client. + * This is an asynchronous call, and will only complete when a client completes the + * handshake, or if it fails. + * Normally, the socket passed in will usually have been previously used in a call to + * Accept() on a listening socket, but this is not required. + * Note that this implementation does not support Server mode operation, so this method + * is NOT supported. + * + * @param aStatus On completion, any one of the system error codes, or KErrNone on success. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::StartServerHandshake()"));) + TRequestStatus* pStatus = &aStatus; + + User::RequestComplete( pStatus, KErrNotSupported ); +} + + + +//MStateMachineNotify interface +TBool CTlsConnection::OnCompletion( CStateMachine* aStateMachine ) +/** + * Called only when negotiation or renegotiation has completed. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::OnCompletion()"));) +#ifdef _DEBUG + TInt nBlock; + LOG(Log::Printf(_L("RHeap::Size(), RHeap::Size() - RHeap::Available() %d, %d"), User::Heap().Size(), User::Heap().Size() - User::Heap().Available( nBlock ) );) +#endif + + iRecordParser->IgnoreAppData( 0 ); + __ASSERT_DEBUG( !aStateMachine->SuspendRequest(), TlsPanic(ETlsPanicStateMachineStopped) ); + if ( aStateMachine->LastError() != KErrNone ) + {//user will be notified after return from this fn + if ( iHandshake != aStateMachine ) + { + return EFalse; + } + else + {//delete data path in case it's re-negotiation what's failed + delete iSendAppData; + iSendAppData = NULL; + delete iRecvAppData; + iRecvAppData = NULL; + ResetCryptoAttributes(); + } + } + else + {//from now on we propose the alrady negotiated protocol untill the connection is closed + iTlsProvider->Attributes()->iProposedProtocol = iTlsProvider->Attributes()->iNegotiatedProtocol; + if ( IsReNegotiating() ) + { + //resume the SM statuses + TRAPD(ret, iSendAppData->ResumeL(); + iRecvAppData->ResumeL( *this ) ); + if ( ret != KErrNone ) + {//something went completely wrong + //set last error so that the user will be notified after return from this fn + aStateMachine->SetLastError( ret ); + delete iSendAppData; + iSendAppData = NULL; + delete iRecvAppData; + iRecvAppData = NULL; + } + else + { + __ASSERT_DEBUG( !iSendAppData->IsActive(), TlsPanic(ETlsPanicAlreadyActive)); + if ( iSendAppData->ClientStatus() ) //has the SM finished? + {//no => start it again to resume the task + iSendAppData->Start( iSendAppData->ClientStatus(), this ); + } + //recv SM is active when re-negotiation started via HelloRequest + if ( !iRecvAppData->IsActive() && iRecvAppData->ClientStatus() ) + {//not active and not finished => start it again to resume the the task + iRecvAppData->Start( iRecvAppData->ClientStatus(), this ); + } + } + } + else if ( !IsInDataMode() ) + { + // Create the Data state machines so that the user can send/receive data + __ASSERT_DEBUG( !iRecvAppData && !iSendAppData, TlsPanic(ETlsPanicStateMachineAlreadyExists)); + + //don't change the order see CRecvAppData::ResumeL + TRAPD( ret, iSendAppData = CSendAppData::NewL( *iRecordComposer ); + iRecvAppData = CRecvAppData::NewL( *this ) ); + if ( ret != KErrNone ) + {//something went completely wrong + //set last error so that the user will be notified after return from this fn + aStateMachine->SetLastError( ret ); + //delete what may have been created + delete iRecvAppData; + iRecvAppData = 0; + delete iSendAppData; + iSendAppData = 0; + } + } + else + { + // Must be one of the Application data SM + __ASSERT_DEBUG( iRecvAppData == aStateMachine || + iSendAppData == aStateMachine, TlsPanic(ETlsPanicInvalidStateMachine)); + return EFalse; // Don't want to delete either of them. + } + } + __ASSERT_DEBUG( iHandshake == aStateMachine, TlsPanic(ETlsPanicInvalidStateMachine)); + iHandshake = 0; + return ETrue; // Delete the Handshake state machine. +} + + +// Internal functions + +TBool CTlsConnection::IsIdle() const +/** + * Returns 'True' if a connection is idle. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::IsIdle()"));) + return !iHandshake && !iSendAppData; +} + + +TBool CTlsConnection::SendData( const TDesC8& aDesc, TRequestStatus& aStatus ) +/** + * Starts the Application data transmission state machine, + * which sends data to a remote Server. + * + * @param aDesc Reference to the user's descriptor (data buffer) + * @param aClientStatus TRequestStatus object that completes when data transmission + * is finished. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::SendData()"));) + + TRequestStatus* pStatus = &aStatus; + if ( !iSendAppData ) + { + User::RequestComplete( pStatus, KErrNotReady ); + return EFalse; + } + else if ( iSendAppData->ClientStatus() ) + { + User::RequestComplete( pStatus, KErrInUse ); + return EFalse; + } + else if ( IsReNegotiating() ) + { + iSendAppData->SetUserData( (TDesC8*)&aDesc ); + iSendAppData->SetClientStatus( &aStatus ); + //and wait for re-negotiation to finish (see CTlsConnection::OnCompletion) + } + else + { + iRecordComposer->SetUserData( (TDesC8*)&aDesc ); + iRecordComposer->ResetCurrentPos(); + iSendAppData->Start( &aStatus, this ); + } + + return ETrue; +} + +TBool CTlsConnection::RecvData( TDes8& aDesc, TRequestStatus& aStatus ) +/** + * Starts the Application data reception state machine, + * which receives data from a remote Server. + * + * @param aDesc Reference to the user's descriptor (data buffer) + * @param aClientStatus TRequestStatus object that completes when data reception is + * finished. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::RecvData()"));) + TBool result = EFalse; + + TRequestStatus* pStatus = &aStatus; + + if ( !iRecvAppData ) + User::RequestComplete( pStatus, KErrNotReady ); + else if ( iRecvAppData->ClientStatus() || IsReNegotiating() ) + User::RequestComplete( pStatus, KErrInUse ); + else + { + iRecordParser->SetUserData( &aDesc ); + iRecordParser->SetUserMaxLength( aDesc.MaxLength() ); + iRecvAppData->Start( &aStatus, this ); + result = ETrue; + } + + return result; +} + +void CTlsConnection::StartClientHandshakeStateMachine(TRequestStatus* aStatus) +/** + * Creates and starts the Handshake negotiation state machine, + * which initiates negotiations with the remote Server. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::StartClientHandshakeStateMachine()"));) + delete iClientCert; + iClientCert = NULL; + delete iServerCert; + iServerCert = NULL; + + // Assert that a Handshake negotiation object doesn't exist and that a request + // status object is valid. + __ASSERT_DEBUG( !iHandshake, TlsPanic(ETlsPanicStateMachineAlreadyExists) ); + __ASSERT_DEBUG( aStatus, TlsPanic(ETlsPanicInvalidStatus)); + + TRAPD( ret, iHandshake = CHandshake::NewL(*this) ); + if ( ret != KErrNone ) + User::RequestComplete(aStatus, ret); + else + { + TRAP(ret, iHandshake->StartL(aStatus, this)); + if (ret!=KErrNone) + { + delete iHandshake; + iHandshake = NULL; + User::RequestComplete(aStatus, ret); + } + } + + + +#ifdef _DEBUG + TInt nBlock; + LOG(Log::Printf(_L("RHeap::Size(), RHeap::Size() - RHeap::Available() %d, %d"), User::Heap().Size(), User::Heap().Size() - User::Heap().Available( nBlock ) );) +#endif +} + +void CTlsConnection::ResetCryptoAttributes() +{//don't change the order of the calls see (see ~CRecordParser & CRecordParser::Reset) + LOG(Log::Printf(_L("CTlsConnection::ResetCryptoAttributes()"));) + iRecordComposer->Reset(); + iRecordParser->Reset(); +// iRecordParser->SetTlsProvider( NULL ); +// iRecordComposer->SetTlsProvider( NULL ); + if ( iTlsProvider ) + { + TRAPD(ret, iTlsProvider->ReConnectL()); // Set up Security/crypto interfaces + delete iTlsSession; + iTlsSession = NULL; + if ( ret ) + { + delete iTlsProvider; + iTlsProvider = NULL; + iRecordParser->SetTlsProvider( iTlsProvider ); + iRecordComposer->SetTlsProvider( iTlsProvider ); + } + } +} + +void CTlsConnection::StartRenegotiation( TRequestStatus* aStatus ) +/** + * Starts Handshake renegotiation. + * + * It suspends the Application Data (transmission and reception) state machines + * and restarts the Handshake negotiation state m/c. + * It also creates a new TLS Provider object to access security services. This is + * necessary as a new cryptographic token might be selected to create new key material. + * The session cache is flushed as session reuse should not take place (ensure that entirely + * new key material is generated). + */ +{ + LOG(Log::Printf(_L("CTlsConnection::StartRenegotiation()"));) + //User::RequestComplete( aStatus, KErrNotSupported ); //not until we have CTlsProvider::Close() + + iSendAppData->Suspend(); + iRecvAppData->Suspend(); + iRecordParser->IgnoreAppData( 1 ); + StartClientHandshakeStateMachine( aStatus ); +} + +void CTlsConnection::DeleteStateMachines() +/** + * Deletes the connections' state machines. + */ +{ + LOG(Log::Printf(_L("CTlsConnection::DeleteStateMachines()"));) + + delete iHandshake; + iHandshake = NULL; + + delete iRecvAppData; + iRecvAppData = NULL; + + delete iSendAppData; + iSendAppData = NULL; +} + +void CTlsConnection::CancelAll( TInt aError ) +/** + * Cancels all outstanding operations. It is called by the Secure socket API, + * CTlsConnection::CancelAll(). + */ +{ + LOG(Log::Printf(_L("CTlsConnection::CancelAll()"));) + + if ( iRecvAppData ) + { + iRecvAppData->Cancel( KErrNone ); + } + + if ( iSendAppData ) + { + // Send an alert if it is not going to be sent by iHandshake + iSendAppData->Cancel( iHandshake ? KErrNone : aError ); + } + + if ( iHandshake ) + { + iHandshake->Cancel( aError ); + __ASSERT_DEBUG(!iHandshake, TlsPanic(ETlsPanicStateMachineAlreadyExists)); + } +} + +void CTlsConnection::GetServerAddrInfo( TTLSServerAddr& serverInfo ) + { + LOG(Log::Printf(_L("CTlsConnection::GetServerAddrInfo()"));) + // Find out if there is an existing stored session for the RSocket object. + TSockAddr sockAddr; // Endpoint address for the socket object, TBuf<8> + iRecordComposer->Socket().RemoteName( sockAddr ); + TInetAddr inetAddr( sockAddr ); + if ( sockAddr.Family() != KAfInet6 ) + { + inetAddr.ConvertToV4Mapped(); + } + + serverInfo.iAddress.Copy(TPtr8( (TUint8*)(&inetAddr.Ip6Address().u.iAddr8[0]), sizeof( TIp6Addr ), sizeof( TIp6Addr ))); + serverInfo.iPort = TUint16(sockAddr.Port()); //see TTLSServerAddr definition + LOG(Log::Printf(_L("CTlsConnection::GetServerAddrInfo() port: %d"), serverInfo.iPort );) + LOG(RFileLogger::HexDump(KSSLLogDir,KSSLLogFileName,EFileLoggingModeAppend, NULL, NULL, serverInfo.iAddress.Ptr(), serverInfo.iAddress.Length() )); + } + +TInt CTlsConnection::GetKeyingMaterial(TDes8& aKeyingMaterial) +/* +Performs key generation as per RFC2716 (PPP EAP TLS Authentication Protocol) section 3.5 +*/ + { + if(iTlsSession == NULL) + { + LOG(Log::Printf(_L("iTlsSession needs to be created to call this function") );) + return KErrNotReady; + } + + if(aKeyingMaterial.Length()>KKeyingLabelMaxSize) + { + LOG(Log::Printf(_L("Supplied Descriptor is too large. Size=%d Maximum=%d"), aKeyingMaterial.Length(), KKeyingLabelMaxSize);) + return KErrArgument; + } + + TBuf8 keyingLabel; + keyingLabel.Copy(aKeyingMaterial); + + aKeyingMaterial.Zero(); + TInt err = iTlsSession->KeyDerivation(keyingLabel,iTlsSession->Attributes()->iMasterSecretInput, aKeyingMaterial); + + if(err == KErrNone && aKeyingMaterial.Length()<=0) + { + LOG(Log::Printf(_L("Failed to derive keys"));) + err = KErrGeneral; + } + + return err; + }