--- /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 <random.h>
+
+#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<TIPSecDialogInfo> InfoBuf(dialog_input);//package it in appropriate buf
+
+ TPckgBuf<TIPSecDialogOutput> 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<TIPSecDialogOutput> ResponseBuf(output);//create the buf to receive the response
+
+ if ( iInputData )
+ status = LauchSyncDialog((TPckgBuf<TIPSecDialogInfo>&)*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<TIPSecDialogInfo> 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<TIPSecDialogInfo>&)(*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();
+ }
+}
+