diff -r 000000000000 -r 33413c0669b9 vpnengine/ikev1lib/src/ikev1dialog.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/vpnengine/ikev1lib/src/ikev1dialog.cpp Thu Dec 17 09:14:51 2009 +0200 @@ -0,0 +1,665 @@ +/* +* Copyright (c) 2005-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: CIkeDialog class implementation +* +*/ + +#include + +#include "ikev1dialog.h" +#include "dhparameters.h" +#include "ikedebug.h" +#include "ikev1pluginsession.h" +#include "ikev1crypto.h" +#include "ikev1filesdef.h" + + +CIkev1Dialog::CIkev1Dialog( MIkeDebug& aDebug ) + : CActive( EPriorityStandard ), + iDebug( aDebug ) +{ + CActiveScheduler::Add(this); //Added to the Active Scheduler +} + +CIkev1Dialog::~CIkev1Dialog() +{ + DEBUG_LOG(_L("CIkev1Dialog destructed")); + + DeQueueDialog(this); + + if(iTimeout) + { + iTimeout->Cancel(); + } + Cancel(); // Dialog itself + + delete iTimeout; + delete iInputData; + + iFs.Close(); +} + +void CIkev1Dialog::PurgeDialogQueue(CIkev1Dialog* aQueuedDialog) +{ + CIkev1Dialog* NextDialog; + while ( aQueuedDialog ) { + NextDialog = aQueuedDialog->iNext; + delete aQueuedDialog; + aQueuedDialog = NextDialog; + } +} + +void CIkev1Dialog::DoCancel() +{ + iNotifier.CancelNotifier(KUidVpnDialogNotifier); + iNotifier.Close(); + DEBUG_LOG(_L("CIkev1Dialog::DoCancel() OK")); +} + +void CIkev1Dialog::ConstructL(CIkev1PluginSession* aPluginSession, CIkev1Dialog* *aToQueAnchor) +{ + User::LeaveIfError(iFs.Connect()); + iTimeout = new (ELeave) CDialogTimeout( iDebug ); + iTimeout->ConstructL(this); + iToQueAnchor = aToQueAnchor; + iPluginSession = aPluginSession; +} + +CIkev1Dialog* CIkev1Dialog::NewL( CIkev1PluginSession* aPluginSession, + CIkev1Dialog** aToQueAnchor, + MIkeDebug& aDebug ) +{ + CIkev1Dialog* Dialog = new (ELeave) CIkev1Dialog( aDebug ); + Dialog->ConstructL( aPluginSession, aToQueAnchor ); + + return Dialog; +} + +void CIkev1Dialog::StoreUserNameL(TPtr8 aUserName) +{ +/*-------------------------------------------------------------------- + * + * Store specified user name into cache file (used as init value in + * the next user name specific dialog). + * User name shall be encrypted (DES) before stored into cache file. + * + *---------------------------------------------------------------------*/ + + + if (aUserName.Length() == 0) + { + User::Leave(KErrArgument); + } + + // + // Allocate buffer for file header and encrypted key + // + + HBufC8* HeaderBfr = HBufC8::NewLC(aUserName.Length() + sizeof(TUserNameFileHdr) + 32); + + TUserNameFileHdr* FileHeader = (TUserNameFileHdr*)HeaderBfr->Ptr(); + // + // Get random data values for salt and IV. + // + TPtr8 ptr((TUint8*)FileHeader, sizeof(TUserNameFileHdr)); + ptr.SetLength(sizeof(TUserNameFileHdr)); + TRandom::RandomL(ptr); + + FileHeader->iFileId = USER_NAME_FILE_ID; + // + // Build encryption key from just created salt data and fixed + // secret passphrase using MD5 hash + // + TBuf8<16> EncryptionKey; + TPtr8 SaltPtr((TUint8*)FileHeader->iSalt, 8, 8); + User::LeaveIfError(CIkev1Dialog::BuildEncryptionKey(SaltPtr, EncryptionKey)); + // + // Encrypt user name data with just created key. + // Because DES is used as encryption algorithm, the eight first + // octets of created encryption octets is used as encryption key. + // + TInt EncrLth = 0; + EncrLth = SymmetricCipherL((TUint8*)aUserName.Ptr(), + ((TUint8*)FileHeader + sizeof(TUserNameFileHdr)), + aUserName.Length(), FileHeader->iIV, (TUint8*)EncryptionKey.Ptr(), ETrue); + if ( EncrLth ) + { + // + // Write encrypted data into user name file + // + RFile NameFile; + + TBuf<128> Ppath; + User::LeaveIfError(iFs.PrivatePath(Ppath)); + + Ppath.Append(USER_NAME_CACHE_FILE); + TInt err = iFs.CreatePrivatePath(EDriveC); + if (err != KErrNone && + err != KErrAlreadyExists) + { + User::Leave(err); + } + User::LeaveIfError(NameFile.Replace(iFs, Ppath, EFileShareAny|EFileWrite)); + + TPtrC8 EncryptedData((TUint8*)FileHeader, sizeof(TUserNameFileHdr) + EncrLth); + + NameFile.Write(EncryptedData); + NameFile.Close(); + } + + CleanupStack::PopAndDestroy(); // Delete encryption buffer +} + +/*-------------------------------------------------------------------- + * + * Asynchronous dialog is completed. + * + *---------------------------------------------------------------------*/ +void CIkev1Dialog::RunL() +{ + TInt delete_obj = 1; + HBufC8 *un_bfr = NULL; + HBufC8 *pw_bfr = NULL; + CIkev1Dialog* NextDialog = iNext; + + iNotifier.CancelNotifier(KUidVpnDialogNotifier); + iNotifier.Close(); + + if ( iStatus.Int() == KErrNone ) + { + if ( iCallback ) + { + TIPSecDialogOutput& resp = iResponseBuf(); + un_bfr = ConvertPwdToOctetString(resp.iOutBuf); + pw_bfr = ConvertPwdToOctetString(resp.iOutBuf2); + } + } + + if ( iCallback ) + { + TInt err; + TRAP(err, delete_obj = iCallback->DialogCompleteL(this, iUserInfo, + un_bfr, //User name + pw_bfr, //Password + NULL)); //domain + delete un_bfr; + delete pw_bfr; + if ( err != KErrNone ) + delete_obj = 1; + } + + if ( delete_obj ) + { + delete this; + } + + // + // Start a dialog from wait queue if there is some + // + if ( NextDialog ) + NextDialog->StartDialogL(); +} + + +/*-------------------------------------------------------------------- + * + * Get user name and password data for Legacy authentication + * This is a synchronous dialog which does NOT convert user name and + * password data into the 8-bit ASCII text + * + *---------------------------------------------------------------------*/ +TInt CIkev1Dialog::GetSyncUNPWDialog(TDes& aUserName, TDes& aPassword) +{ +TIPSecDialogOutput output; + + + TIPSecDialogInfo dialog_input(TKMDDialog::EUserPwd, 0); + + TPckgBuf InfoBuf(dialog_input);//package it in appropriate buf + + TPckgBuf ResponseBuf(output);//create the buf to receive the response + + TInt status = LauchSyncDialog(InfoBuf, ResponseBuf); + if ( status == KErrNone ) { + TIPSecDialogOutput& resp = ResponseBuf(); + aUserName = resp.iOutBuf; + aPassword = resp.iOutBuf2; + } + + return status; +} + +/*-------------------------------------------------------------------- + * + * Get user name and password data for Legacy authentication + * This is a synchronous dialog which does NOT convert user name and + * password data into the 8-bit ASCII text + * Uses username cache + * + *---------------------------------------------------------------------*/ +TInt CIkev1Dialog::GetSyncUNPWCacheDialog(TDes& aUserName, TDes& aPassword) +{ + TInt status = KErrGeneral; + TIPSecDialogOutput output; + + TIPSecDialogInfo dialog_input(TKMDDialog::EUserPwd, 0); + + iInputData = CreateDialogInput(dialog_input, ETrue);// TRUE = Use user name cache + + TPckgBuf ResponseBuf(output);//create the buf to receive the response + + if ( iInputData ) + status = LauchSyncDialog((TPckgBuf&)*iInputData, ResponseBuf); + + if ( status == KErrNone ) { + TIPSecDialogOutput& resp = ResponseBuf(); + aUserName = resp.iOutBuf; + aPassword = resp.iOutBuf2; + } + + return status; +} + +void CIkev1Dialog::ShowErrorDialogL(TInt aDialogText, TAny *aUserInfo, MIkeDialogComplete* aCallback ) +{ + iDialogType = TNoteDialog::EInfo; + iUserInfo = aUserInfo; + iCallback = aCallback; // For asynchronous dialog RunL + + TIPSecDialogInfo dialog_input(TNoteDialog::EInfo, aDialogText); + iInputData = CreateDialogInput(dialog_input, EFalse);// FALSE = Do not use user name cache + if ( iInputData ) + LaunchDialogL(); //launch the dialog +} + +/*-------------------------------------------------------------------- + * + * Get user name and password data for Legacy authentication + * + *---------------------------------------------------------------------*/ +void CIkev1Dialog::GetAsyncUNPWDialogL(TAny *aUserInfo, MIkeDialogComplete* aCallback) +{ + DEBUG_LOG2(_L("CIkev1Dialog::GetAsyncUNPWDialogL(), aUserInfo = %x, aCallback = %x"), aUserInfo, aCallback); + + iDialogType = TKMDDialog::EUserPwd; + iUserInfo = aUserInfo; + iCallback = aCallback; // For asynchronous dialog RunL + + TIPSecDialogInfo dialog_input(TKMDDialog::EUserPwd, 0); + iInputData = CreateDialogInput(dialog_input, ETrue);// TRUE = Use user name cache + if ( iInputData ) + LaunchDialogL(); //launch the dialog +} + +/*-------------------------------------------------------------------- + * + * Get user name and Secure ID pin data for Legacy authentication + * + *---------------------------------------------------------------------*/ +void CIkev1Dialog::GetAsyncSecureidDialogL(TAny *aUserInfo, MIkeDialogComplete* aCallback) +{ + DEBUG_LOG2(_L("CIkev1Dialog::GetAsyncSecureidDialogL(), aUserInfo = %x, aCallback = %x"), aUserInfo, aCallback); + + iDialogType = TKMDDialog::ESecurIdPin; + iUserInfo = aUserInfo; + iCallback = aCallback; // For asynchronous dialog RunL + + TIPSecDialogInfo dialog_input(TKMDDialog::ESecurIdPin, 0); + iInputData = CreateDialogInput(dialog_input, ETrue);// TRUE = Use user name cache + if ( iInputData ) + LaunchDialogL(); //launch the dialog +} + +/*-------------------------------------------------------------------- + * + * Get user name and Secure ID next pin data for Legacy authentication + * + *---------------------------------------------------------------------*/ +void CIkev1Dialog::GetAsyncSecureNextPinDialogL(TAny *aUserInfo, MIkeDialogComplete* aCallback) +{ + DEBUG_LOG2(_L("CIkev1Dialog::GetAsyncSecureNextPinDialogL(), aUserInfo = %x, aCallback = %x"), aUserInfo, aCallback); + + iDialogType = TKMDDialog::ESecurIdNextPin; + iUserInfo = aUserInfo; + iCallback = aCallback; // For asynchronous dialog RunL + + TIPSecDialogInfo dialog_input(TKMDDialog::ESecurIdNextPin, 0); + iInputData = CreateDialogInput(dialog_input, ETrue);// TRUE = Use user name cache + if ( iInputData ) + LaunchDialogL(); //launch the dialog +} + + +/*-------------------------------------------------------------------- + * + * For future use (for challenge/response type Legacy authentication) + * + *---------------------------------------------------------------------*/ +void CIkev1Dialog::GetAsyncUNAMEDialog(TAny* /*aUserInfo*/, MIkeDialogComplete* /*aCallback*/) +{ +} + +void CIkev1Dialog::GetAsyncRespDialog(TPtr8 /*aChallenge*/, TAny* /*aUserInfo*/, MIkeDialogComplete* /*aCallback*/) +{ +} + + +/////////////////////////////////////////////////////////////////////////////// +// +// Private methods +// +/////////////////////////////////////////////////////////////////////////////// +HBufC8* CIkev1Dialog::CreateDialogInput(TIPSecDialogInfo& aDialogInfo, TBool aUserNameCache) +{ + // + // Create dialog input data buffer. Concatenate cached user name + // string into input data if requested and if cached name exists + // + HBufC8* DialogInput; + HBufC8* UserName = NULL; + TInt UserNameLth = 0; + TPckgBuf infoBuf(aDialogInfo); + + if ( aUserNameCache ) { + UserName = GetUserNameFromFile(); + if ( UserName ) + UserNameLth = UserName->Length(); + } + + DialogInput = HBufC8::New(sizeof(TIPSecDialogInfo) + UserNameLth); + if ( DialogInput ) { + DialogInput->Des().Copy(infoBuf); + if ( UserName ) { + DialogInput->Des().Append(UserName->Des()); + delete UserName; + } + } + + return DialogInput; +} + +void CIkev1Dialog::LaunchDialogL() +{ + // + // Launch the dialog if there is no dialog already going + // + if ( QueueDialog(this) == 1 ) + { + StartDialogL(); + } +} + +void CIkev1Dialog::StartDialogL() +{ + // + // Start an asynchronous dialog + // + User::LeaveIfError(iNotifier.Connect()); + iNotifier.StartNotifierAndGetResponse(iStatus, + KUidVpnDialogNotifier, + (TPckgBuf&)(*iInputData), + iResponseBuf); + SetActive(); +} + + +TInt CIkev1Dialog::LauchSyncDialog(const TDesC8& aInput, TDes8& aOutput) +{ + RNotifier notifier; + TInt err = notifier.Connect(); + if(err != KErrNone) + { + return err; + } + + TRequestStatus status; + notifier.StartNotifierAndGetResponse(status, KUidVpnDialogNotifier, aInput, aOutput); + User::WaitForRequest( status ); + + notifier.CancelNotifier(KUidVpnDialogNotifier); + notifier.Close(); + + return status.Int(); +} + +TInt CIkev1Dialog::QueueDialog(CIkev1Dialog* aDialog) +{ + TInt DialogCount = 1; + aDialog->iNext = NULL; + CIkev1Dialog* QueuedDialog = *aDialog->iToQueAnchor; + + if ( QueuedDialog ) + { + DialogCount ++; + while ( QueuedDialog->iNext ) { + QueuedDialog = QueuedDialog->iNext; + DialogCount ++; + } + QueuedDialog->iNext = aDialog; + } + else *aDialog->iToQueAnchor = aDialog; + + return DialogCount; +} + +void CIkev1Dialog::DeQueueDialog(CIkev1Dialog* aDialog) +{ + CIkev1Dialog* PreviousDialog = NULL; + CIkev1Dialog* QueuedDialog = *aDialog->iToQueAnchor; + + while ( QueuedDialog ) { + if ( QueuedDialog == aDialog ) { + if ( PreviousDialog ) + PreviousDialog->iNext = QueuedDialog->iNext; + else *aDialog->iToQueAnchor = QueuedDialog->iNext; + } + PreviousDialog = QueuedDialog; + QueuedDialog = QueuedDialog->iNext; + } +} + +HBufC8* CIkev1Dialog::GetUserNameFromFile() +{ +/*-------------------------------------------------------------------- + * + * Get user name default value from encrypted cache file + * + *---------------------------------------------------------------------*/ + // + // Allocate buffer for file header and encrypted key + // + HBufC8* UserNameBfr = NULL; + RFile UserNameFile; + if ( UserNameFile.Open(iFs, USER_NAME_CACHE_FILE, EFileRead) == KErrNone ) { + TInt FileSize = 0; + UserNameFile.Size(FileSize); + if ( (FileSize > 0) && (FileSize < 256) ) { + HBufC8* FileData = HBufC8::New(FileSize); + if ( FileData ) { + // + // Read encrypted file data into the allocated buffer. + // + TPtr8 FileDataPtr(FileData->Des()); + if ( UserNameFile.Read(FileDataPtr) == KErrNone ) { + // + // Build decryption key and decrypt user name data. + // Both salt data needed in key generation and IV + // value required in decryption are found from + // encrypted file header + // + TUserNameFileHdr* FileHeader = (TUserNameFileHdr*)FileData->Ptr(); + if ( FileHeader->iFileId == USER_NAME_FILE_ID ) { + TBuf8<16> DecryptionKey; + TPtr8 SaltPtr((TUint8*)FileHeader->iSalt, 8, 8); + if ( CIkev1Dialog::BuildEncryptionKey(SaltPtr, DecryptionKey) ) { + TInt EncrLth = FileSize - sizeof(TUserNameFileHdr); + TUint8* UserNameRawPtr = (TUint8*)FileHeader + sizeof(TUserNameFileHdr); + TInt err; + TRAP(err, EncrLth = SymmetricCipherL(UserNameRawPtr, UserNameRawPtr, EncrLth, + FileHeader->iIV, (TUint8*)DecryptionKey.Ptr(), EFalse)); + if ( (err == KErrNone) && EncrLth ) { + // + // Allocate a HBufC8 for decrypted user name + // + UserNameBfr = HBufC8::New(EncrLth); + if ( UserNameBfr ) + UserNameBfr->Des().Copy(UserNameRawPtr, EncrLth); + } + } + } + } + delete FileData; + } + } + } + + UserNameFile.Close(); + return UserNameBfr; +} + + +HBufC8 *CIkev1Dialog::ConvertPwdToOctetString(TDesC &aUnicodeBfr) +{ +/*-------------------------------------------------------------------- + * + * Convert password from Unicode string to 8-bit octet string + * + *---------------------------------------------------------------------*/ + HBufC8 *octet_data = HBufC8::New(aUnicodeBfr.Length()); + if ( octet_data ) { + TPtr8 ptr8(octet_data->Des()); + ptr8.Copy(aUnicodeBfr); + } + return octet_data; +} + + + +TBool CIkev1Dialog::BuildEncryptionKey(const TDesC8& aSalt, TDes8& aEncryptionKey) +{ +/*-------------------------------------------------------------------- + * + * Build encryption key for user name data cipher. + * The encryption key is created as follows: + * DH group 5 (MODP 1536) prime is used as passphrase seed so + * that MODP_1536_PRIME_LENGTH/4 octets of seed is taken from prime + * starting from position MODP_1536_PRIME_LENGTH/2. + * The specified salt is concatenated with that data. + * The MD5 hash over that shall be the encryption key (max key then + * 128 bits) + * + *---------------------------------------------------------------------*/ + // + // Allocate buffer for key seed data + // + HBufC8* SeedDataBfr = HBufC8::New(aSalt.Length() + MODP_1536_PRIME_LENGTH/4); + if ( !SeedDataBfr ) + return EFalse; + + TPtr8 SeedDataPtr(SeedDataBfr->Des()); + TPtrC8 PassPhrasePtr((TUint8 *)&MODP_1536_PRIME[MODP_1536_PRIME_LENGTH/2], + MODP_1536_PRIME_LENGTH/4); + SeedDataPtr.Copy(PassPhrasePtr); + SeedDataPtr.Append(aSalt); + + TInt err; + TRAP(err, MD5HashL(SeedDataPtr, aEncryptionKey)); + + delete SeedDataBfr; + + if ( err == KErrNone ) + return ETrue; + else return EFalse; + +} + + + +/**------------------------------------------------------- + * + * CDialogTimeout class + * This timeout class used to check user dialog displayed + * shall be completed in reasonable time (now 90 seconds). + * This class is used the following way: + * -- When a CIkev1Dialog class object is constructed one + * CDialogTimeout object is constructed as well. These + * objects are linked together. + * -- If user dialog completes normally (within 90 seconds) + * CDialogTimeout is cancelled in CIkev1Dialog.RunL(). + * -- If timeout expires, CIkev1Dialog is completed via CDialogTimeout.RunL() + * + *--------------------------------------------------------*/ +CDialogTimeout::CDialogTimeout( MIkeDebug& aDebug ) + : CTimer( EPriorityStandard ), + iDebug( aDebug ) +{ + CActiveScheduler::Add(this); //Adds itself to the scheduler only the first time +} + +CDialogTimeout::~CDialogTimeout() +{ + DEBUG_LOG(_L("CDialogTimeout destructed")); + if (IsActive()) + Cancel(); +} + +void CDialogTimeout::ConstructL(CIkev1Dialog *aDialog) +{ + CTimer::ConstructL(); + iDialog = aDialog; + After(90*1000000); //Start dialog timer +} + +void CDialogTimeout::DoCancel() +{ + DEBUG_LOG(_L("CDialogTimeout cancelled")); + CTimer::DoCancel(); +} + +void CDialogTimeout::RunL() +{ + DEBUG_LOG(_L("CKmdDialog timeout occurred")); + TInt delete_dialog = 1; + CIkev1Dialog* NextDialog = iDialog->NextDialog(); + MIkeDialogComplete* Callback = iDialog->Callback(); + + if ( Callback ) + { + TInt err; + DEBUG_LOG2(_L("Calling DialogCompleteL(), UserInfo = %x, Callback = %x"), (TUint32)iDialog->UserInfo(), (TUint32)Callback); + TRAP(err, delete_dialog = Callback->DialogCompleteL(iDialog, + iDialog->UserInfo(), + NULL, //User name + NULL, //Password + NULL)); //domain + DEBUG_LOG2(_L("DialogCompleteL() completed, err = %d, delete_dialog = %d"), err, delete_dialog); + if ( err != KErrNone ) + delete_dialog = 1; + } + if ( delete_dialog ) + { + delete iDialog; + iDialog = NULL; + } + + // + // Start a dialog from wait queue if there is some + // + if ( NextDialog ) + { + DEBUG_LOG(_L("Next dialog started from dialog timer")); + NextDialog->StartDialogL(); + } +} +