// 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 <es_sock.h>
#include <in_sock.h>
#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
#include <ssl_internal.h>
#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(self);
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(self);
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<RSocket>(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 );
TBuf<32> tempBuf;
tempBuf.Copy(aProtocol);
tempBuf.UpperCase();
TInt ret = tempBuf.Compare(KProtocolVerSSL30);
if (!ret)
{
iRecordComposer->SetVersion(&KSSL3_0);
}
else
{
iRecordComposer->SetVersion(&KTLS1_0);
}
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<TTLSCipherSuite> 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<TDialogMode> 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<const CX509Certificate*> 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<MSoPskKeyHandler *> 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<TTLSCipherSuite> 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; outerLoop<aCiphers.Length(); outerLoop+=2 )
{
valueIsSet = EFalse;
for (TInt innerLoop=0; innerLoop<cipherList.Count();++innerLoop)
{
if( aCiphers[outerLoop] == cipherList[innerLoop].iHiByte
&& aCiphers[outerLoop+1] == cipherList[innerLoop].iLoByte )
{
// the suite is valid, so add it to the list of proposed ciphers
proposedCiphers.Append(aCiphers[outerLoop]);
proposedCiphers.Append(aCiphers[outerLoop+1]);
valueIsSet = 1;
}
if (valueIsSet)
break;
} // inner 'for' statement.
} // outer 'for' statement.
cipherList.Close(); // Close the array and free its memory.
// If no valid ciphers are received from the client, all available cipher suites
// returned by the protocol will be used
if ( proposedCiphers.Length() == 0 )
{
LOG(Log::Printf(_L("SetAvailableCipherSuites() - No valid ciphers received from client"));)
returnValue = KErrNotSupported;
}
return returnValue;
}
TInt CTlsConnection::SetClientCert(const CX509Certificate& /*aCert*/)
/**
* Sets the client certificate to use.
* In client mode, this method will set the certificate that will be used if a
* server requests one.
* Note that this method is NOT supported by the current implementation. Client
* Certificates are stored by the Security subsystem and it chooses the appropriate
* Client certificate to use based on the Server's preference list.
*
* @param aCert A reference to the certificate to use.
* @return Any one of the system error codes, or KErrNone on success.
*/
{
LOG(Log::Printf(_L("CTlsConnection::SetClientCert()"));)
return KErrNotSupported;
}
TInt CTlsConnection::SetClientCertMode(const TClientCertMode /*aClientCertMode*/)
/**
* Sets the client certificate mode.
* This method only applies to Server mode operation (which is not supported by the
* current implementation). In client mode, no action will be performed and
* KErrNotSupported will be returned by the Protocol.
*
* @param aClientCertMode The client certificate mode to use.
* @return Any one of the system error codes, or KErrNone on success.
*/
{
LOG(Log::Printf(_L("CTlsConnection::SetClientCertMode()"));)
return KErrNotSupported;
}
TInt CTlsConnection::SetDialogMode(const TDialogMode aDialogMode)
/**
* Sets the untrusted certificate dialog mode.
* It determines if a dialog is displayed when an untrusted certificate is received.
* The default behaviour is for the dialog to be set to EDialogModeAttended (this
* is set in the construction of a CTlsConnection object).
* A client can either set the dialog mode directly by calling this method, or by
* calling CTlsConnection::SetOpt() with an appropriate option value.
*
* @param aDialogMode The dialog mode to use.
* @return Any one of the system error codes, or KErrNone on success.
*/
{
LOG(Log::Printf(_L("CTlsConnection::SetDialogMode()"));)
// This method must ensure that the dialog mode passed in is part of the
// TDialogMode enum or has the value EDialogModeUnattended/EDialogModeAttended.
// Otherwise, it must return KErrArgument
TInt ret = KErrNone;
switch(aDialogMode)
{
case EDialogModeUnattended:
case EDialogModeAttended:
iDialogMode = aDialogMode;
break;
default: //-- wrong mode
LOG(Log::Printf(_L("SetDialogMode() - Unknown dialog mode, default setting (Attended mode) being used"));)
return KErrArgument;
};
if ( iTlsProvider )
{
iTlsProvider->Attributes()->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<const TInt *>(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<MSoPskKeyHandler *> pskConfigPkg
if(aOption.Length() < sizeof(MSoPskKeyHandler *))
{
return KErrArgument;
}
TPckgBuf<MSoPskKeyHandler *> pskConfigPkg;
MSoPskKeyHandler *handler = *reinterpret_cast<MSoPskKeyHandler * const *>(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<CDesC8Array * const *>(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
LOG(Log::Printf(_L("CTlsConnection::OnCompletion() aStateMachine->LastError() %d"), aStateMachine->LastError() );)
if ( iHandshake != aStateMachine )
{
return EFalse;
}
else
{
if (aStateMachine->LastError() != KErrEof)
{
}
else
{
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 );
LOG(Log::Printf(_L("CTlsConnection::OnCompletion() - AppData->ResumeL Last Error %d"), aStateMachine->LastError() );)
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));
LOG(Log::Printf(_L(" Create the Data state machines so that the user can send/receive data") );)
//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 );
LOG(Log::Printf(_L("CTlsConnection::OnCompletion() AppData::NewL Last Error %d"), aStateMachine->LastError() );)
//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<KKeyingLabelMaxSize> 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;
}