bluetooth/btstack/secman/btaccessrequester.cpp
changeset 0 29b1cd4cb562
child 8 2b6718f05bdb
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/secman/btaccessrequester.cpp	Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,1202 @@
+// Copyright (c) 1999-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:
+//
+
+#include <bluetooth/logger.h>
+
+#include "secman.h"
+#include "secevent.h"
+#include "hostresolver.h"
+#ifdef BT_LINKMGR_V2
+#include "physicallinks.h"
+#include "physicallinksmanager.h"
+#else
+#include "PhysicalLinks.h"
+#include "PhysicalLinksManager.h"
+#endif
+
+#ifdef __FLOG_ACTIVE
+_LIT8(KLogComponent, LOG_COMPONENT_SECMAN);
+#endif
+
+#ifdef _DEBUG
+PANICCATEGORY("btaccreq");
+#endif
+
+/**
+	class CBTAccessRequester
+
+
+	The access requester handles the state machine involved in allowing accesses:
+	setting authentication, entering PINs, asking for authorisation and encrypting
+
+	This class deals only with security procedures we initiate (which could be for
+	inbound or outbound connections)
+
+*/
+
+CBTAccessRequester* CBTAccessRequester::NewLC(CPhysicalLink& aConnection,
+											  const TBTServiceSecurity& aSecurityRequired,
+											  const TBTServiceSecurityPerDevice* const aOverride,
+											  MAccessRequestResponseHandler& aRequester,
+											  TAccessType aAccessType,
+											  CBTSecMan& aParent)
+	{
+	LOG_STATIC_FUNC
+	CBTAccessRequester* s = new(ELeave) CBTAccessRequester(aConnection, aSecurityRequired, aOverride, aRequester, aAccessType, aParent);
+	CleanupStack::PushL(s);
+	s->ConstructL();
+	return s;
+	}
+
+
+CBTAccessRequester::CBTAccessRequester(CPhysicalLink& aConnection,
+									   const TBTServiceSecurity& aServiceSecurity,
+									   const TBTServiceSecurityPerDevice* const aOverride,
+									   MAccessRequestResponseHandler& aRequester,
+									   TAccessType aAccessType,
+									   CBTSecMan& aParent)
+	: iRequester(aRequester)
+	, iSecMan(aParent)
+	, iBaseband(&aConnection)
+	, iDevAddr(aConnection.BDAddr())
+	, iServiceRequirements(aServiceSecurity)
+	, iOverride(aOverride)
+	, iIsSubscribedToConnection(EFalse)
+	, iDeviceRetrievedFromRegistry(EFalse)
+	, iQueLink(this)
+	, iPrefetchQueueLink(this)
+	, iAuthenticationInProgress(EFalse)
+	, iAccessType(aAccessType)
+	, iDedicatedBondingNotAvailable(EFalse)
+	, iCurrentState(EBTUninitialised)
+	{
+	LOG_FUNC
+	// try to get name for UI dialogs
+	SetDeviceName();
+	}
+
+CBTAccessRequester::~CBTAccessRequester()
+	{
+	LOG_FUNC
+	if (iBaseband && iIsSubscribedToConnection)
+		{
+		iBaseband->UnsubscribeLinkObserver(*this);
+		}
+	iPrefetchQueueLink.Deque();
+	delete iAuthorisor;
+	delete iTimer;
+	}
+
+void CBTAccessRequester::ConstructL()
+	{
+	LOG_FUNC
+	iTimer = CAuthenticationTimer::NewL(*this);
+	SubscribeToLinkObserver();
+	}
+
+void CBTAccessRequester::SubscribeToLinkObserver()
+	{
+	LOG_FUNC
+	iBaseband->SubscribeLinkObserver(*this);
+	iIsSubscribedToConnection = ETrue;
+	if (BasebandConnected() && iBaseband->SimplePairingMode() != EPhySimplePairingUndefined)
+		{
+		TBTSecEvent event(TBTSecEvent::EPhysicalLinkUp);
+		SendEvent(event);
+		}
+	}
+
+
+//
+// Events
+//
+
+template<class XDerivedSecEventType, TBTSecEvent::TEventType XEventType>
+XDerivedSecEventType* DynamicEventCast(TBTSecEvent* aEvent)
+	{
+	if(aEvent && aEvent->Event() == XEventType)
+		{
+		return static_cast<XDerivedSecEventType*>(aEvent);
+		}
+	return NULL;
+	}
+
+TBTSecEventStart* TBTSecEventStart::Cast(TBTSecEvent* aEvent)
+	{
+	return DynamicEventCast<TBTSecEventStart, EStart>(aEvent);
+	}
+
+TBTSecEventAuthorisationComplete* TBTSecEventAuthorisationComplete::Cast(TBTSecEvent* aEvent)
+	{
+	return DynamicEventCast<TBTSecEventAuthorisationComplete, EAuthorisationComplete>(aEvent);
+	}
+
+TBTSecEventIoCapabilityResponse* TBTSecEventIoCapabilityResponse::Cast(TBTSecEvent* aEvent)
+	{
+	return DynamicEventCast<TBTSecEventIoCapabilityResponse, EIOCapsResponse>(aEvent);
+	}
+
+TBTSecEventIoCapabilityRequested* TBTSecEventIoCapabilityRequested::Cast(TBTSecEvent* aEvent)
+	{
+	return DynamicEventCast<TBTSecEventIoCapabilityRequested, EIOCapsRequested>(aEvent);
+	}
+
+TBTSecEventKeypressEntry* TBTSecEventKeypressEntry::Cast(TBTSecEvent* aEvent)
+	{
+	return DynamicEventCast<TBTSecEventKeypressEntry, EKeypressEntry>(aEvent);
+	}
+
+TBTSecEventUserConfirmationRequest* TBTSecEventUserConfirmationRequest::Cast(TBTSecEvent* aEvent)
+	{
+	return DynamicEventCast<TBTSecEventUserConfirmationRequest, EUserConfirmationRequested>(aEvent);
+	}
+
+TBTSecEventRemoteOOBDataRequest* TBTSecEventRemoteOOBDataRequest::Cast(TBTSecEvent* aEvent)
+	{
+	return DynamicEventCast<TBTSecEventRemoteOOBDataRequest, ERemoteOOBDataRequested>(aEvent);
+	}
+
+TBTSecEventPhysicalLinkUp* TBTSecEventPhysicalLinkUp::Cast(TBTSecEvent* aEvent)
+	{
+	return DynamicEventCast<TBTSecEventPhysicalLinkUp, EPhysicalLinkUp>(aEvent);
+	}
+
+TBTSecEventAuthenticationComplete* TBTSecEventAuthenticationComplete::Cast(TBTSecEvent* aEvent)
+	{
+	return DynamicEventCast<TBTSecEventAuthenticationComplete, EAuthenticationComplete>(aEvent);
+	}
+
+TBTSecEventEncryptionChangeComplete* TBTSecEventEncryptionChangeComplete::Cast(TBTSecEvent* aEvent)
+	{
+	return DynamicEventCast<TBTSecEventEncryptionChangeComplete, EEncryptionChangeComplete>(aEvent);
+	}
+
+TBTSecEventAuthenticationRequested* TBTSecEventAuthenticationRequested::Cast(TBTSecEvent* aEvent)
+	{
+	return DynamicEventCast<TBTSecEventAuthenticationRequested, EAuthenticationRequested>(aEvent);
+	}
+
+TBTSecEventRequestAuthentication* TBTSecEventRequestAuthentication::Cast(TBTSecEvent* aEvent)
+	{
+	return DynamicEventCast<TBTSecEventRequestAuthentication, ERequestAuthentication>(aEvent);
+	}
+
+TBTSecEventAuthorisationRequested* TBTSecEventAuthorisationRequested::Cast(TBTSecEvent* aEvent)
+	{
+	return DynamicEventCast<TBTSecEventAuthorisationRequested, EAuthorisationRequested>(aEvent);
+	}
+
+TBTSecEventRequestAuthorisation* TBTSecEventRequestAuthorisation::Cast(TBTSecEvent* aEvent)
+	{
+	return DynamicEventCast<TBTSecEventRequestAuthorisation, ERequestAuthorisation>(aEvent);
+	}
+
+
+
+// This function is expected to be re-entrant
+TBool CBTAccessRequester::SendEvent(TBTSecEvent& aEvent)
+	{
+	LOG_FUNC
+	LOG2(_L8("iCurrentState = %d, aEvent.Event() = %d"), iCurrentState, aEvent.Event());
+	TInt err = KErrNotFound; // Let the caller know if the event has been accepted and therefore is being handled ok.
+	err = iSecMan.StateMachine()->ProcessRequesterState(iCurrentState, *this, aEvent);
+	if(err != KErrNone)
+		{
+		__ASSERT_DEBUG(err != KErrNotFound, PANIC(KBTSecPanic, EBTSecCouldNotFindStateTransition));
+		CompleteRequest(err);
+		return EFalse;
+		}
+	return ETrue;
+	}
+
+
+void CBTAccessRequester::Ready(TBTSecEvent& aEvent)
+	{
+	LOG_FUNC
+	TBTSecEvent* event = TBTSecEventPhysicalLinkUp::Cast(&aEvent);
+	event = event ? event : TBTSecEventStart::Cast(&aEvent);
+	__ASSERT_DEBUG(event, PANIC(KBTSecPanic, EBTSecUnexpectedStateMachineEventId));
+
+	iRequirements = OverallRequirements(iServiceRequirements, iBaseband->RemoteDevice());
+		
+	if (AuthenticationRecommended())
+		{
+		LOG(_L8("\tAuthentication recommended"))
+		/* For dedicated bonding we always want to request authentication (even if we are already authenticated)
+		 * so that we can upgrade the linkkey if the new linkkey is stronger.
+		 */
+		if(iBaseband->Authenticated() && LinkKeyGoodEnough() && (iAccessType != EDedicatedBonding))
+			{
+			TBTSecEvent newevent(TBTSecEvent::EAuthenticationComplete);
+			SendEvent(newevent);
+			}
+		else
+			{
+			LOG(_L8("\tPhysical link needs to be authenticated"))
+			TBTSecEvent newevent(TBTSecEvent::ERequestAuthentication);
+			SendEvent(newevent);
+			}
+		}
+	else
+		{
+		LOG(_L8("\tAuthentication is not required or recommended"))
+		TBTSecEvent newevent(TBTSecEvent::EAuthenticationComplete);
+		SendEvent(newevent);
+		}
+	}
+
+void CBTAccessRequester::RequestAuthentication(TBTSecEvent& aEvent)
+	{
+	LOG_FUNC
+	TBTSecEventRequestAuthentication* event = TBTSecEventRequestAuthentication::Cast(&aEvent);
+	__ASSERT_DEBUG(event, User::Panic(KBTSecPanic, EBTSecUnexpectedStateMachineEventId));
+
+	// Here we issue the request if authentication is "recommended" - not neccessarily required.
+	if	(
+		AuthenticationRecommended()
+		&&
+			(
+			!iBaseband->Authenticated()
+			||
+			!LinkKeyGoodEnough()
+			||
+			iAccessType == EDedicatedBonding
+			)
+		)
+		{
+		LOG(_L8("\tAuthentication required..."));
+		LOG(_L8("\tStarting Authentication..."));
+		if(iBaseband->Authenticate(iRequirements.MitmProtection() != EMitmNotRequired) !=KErrNone)
+			{
+			// the remote device is authenticated, but longer passkey is required
+			CompleteRequest(EBTSecManAccessDenied);
+			}
+		else
+			{
+			SetAuthenticationInProgress();
+			// Pending authentication...
+			}
+		}
+	else
+		{
+		LOG(_L8("\tAuthentication not required go to next state"))
+		TBTSecEvent event(TBTSecEvent::EAuthenticationComplete);
+		SendEvent(event);
+		}
+	}
+
+void CBTAccessRequester::AuthenticationRequested(TBTSecEvent& aEvent)
+	{
+	LOG_FUNC
+	TBTSecEventAuthenticationRequested* event = TBTSecEventAuthenticationRequested::Cast(&aEvent);
+	__ASSERT_DEBUG(event, User::Panic(KBTSecPanic, EBTSecUnexpectedStateMachineEventId));
+
+	if(aEvent.Error() == KErrNone)
+		{
+		// Start guard timer?
+		iTimer->Start();
+
+		SetAuthenticationInProgress();
+		// Pending authentication...
+		}
+	else
+		{
+		CompleteRequest(EBTSecManAccessDenied);
+		}
+	}
+
+void CBTAccessRequester::AuthenticationComplete(TBTSecEvent& aEvent)
+	{
+	LOG_FUNC
+	TBTSecEventAuthenticationComplete* event = TBTSecEventAuthenticationComplete::Cast(&aEvent);
+	__ASSERT_DEBUG(event, User::Panic(KBTSecPanic, EBTSecUnexpectedStateMachineEventId));
+	
+	iTimer->Cancel();
+	
+	// Here we check that we have successfully authenticated the link before progressing to the
+	// next stage (encryption).  Here success is considered as when ...
+	if (
+		!AuthenticationRequired() // either authentication is not required...
+		|| // or authentication is required...
+			( 
+			iBaseband->Authenticated() // and the phys is already authenticated, ...
+			&&
+			LinkKeyGoodEnough() // and the link key is of a sufficient strength.
+			)
+		)
+			{
+			// Initiate encryption
+			TBTSecEvent newevent(TBTSecEvent::ERequestEncryptionChange);
+			SendEvent(newevent);
+			}
+		else if (
+				aEvent.Error() == ELMPErrorTransactionCollision
+				||
+				aEvent.Error() == EDifferentTransactionCollision
+				)
+			{
+			// Re-attempt authentication request
+			TBTSecEvent newevent(TBTSecEvent::ERequestAuthentication);
+			SendEvent(newevent);
+			}
+		else if(aEvent.Error() == ERemoteUserEndedConnection)
+			{
+			// If end user brough the connection down then we'll see a disconnection in
+			// a minute - and that will handle prefetch cases.
+			}
+		else
+			{
+			// Failed to set-up authentication conditions required by the access requester.
+			CompleteRequest(EBTSecManAccessDenied);
+			}
+	} 
+
+void CBTAccessRequester::EncryptionChangePending(TBTSecEvent& aEvent)
+	{
+	LOG_FUNC
+
+	switch(aEvent.Event())
+		{
+		case TBTSecEvent::ERequestEncryptionChange:
+			{
+			if(EncryptionRequired() && !iBaseband->Encrypted())
+				{
+				LOG(_L8("\tEncryption required..."))
+
+				if (!iBaseband->IsEncryptionDisabledForRoleSwitch())
+					{
+					TInt err = iBaseband->ChangeEncryption(EPointToPointEncryption);
+					if(err!=KErrNone)
+						{
+						CompleteRequest(EBTSecManAccessDenied);
+						}
+						// Pending encryption...
+					}
+					// Pending encryption...
+				}
+			else
+				{
+				TBTSecEvent event(TBTSecEvent::EEncryptionChangeComplete);
+				SendEvent(event);
+				}
+			}
+		break;
+		case TBTSecEvent::EEncryptionChangeRequested:
+			// start guard timer
+			iTimer->Start();
+		break;
+		default:
+			User::Panic(KBTSecPanic,EBTSecUnexpectedStateMachineEventId);
+		break;
+		}
+	}
+
+void CBTAccessRequester::EncryptionChangeComplete(TBTSecEvent& aEvent)
+	{
+	LOG_FUNC
+	TBTSecEventEncryptionChangeComplete* event = TBTSecEventEncryptionChangeComplete::Cast(&aEvent);
+	__ASSERT_DEBUG(event, User::Panic(KBTSecPanic, EBTSecUnexpectedStateMachineEventId));
+	
+	// cancel guard timer
+	iTimer->Cancel();
+	
+	// We can try again if there was a race with some other PHY modification
+	// Curiously, the firmware always return ELMPErrorTransactionCollision (0x23) for both
+	// kinds of transaction collisions (0x23,0x2a), we guard against both situations here
+	// anyway just to be safe.
+	if((aEvent.Error() == ELMPErrorTransactionCollision) || (aEvent.Error() == EDifferentTransactionCollision))
+		{	
+		// This will force the state machine logic to try sending the command again
+		TBTSecEvent newevent(TBTSecEvent::ERequestEncryptionChange);
+		SendEvent(newevent);
+		}
+	else if((aEvent.Error() == KErrNone) && 
+				(!EncryptionRequired() || // The extra checks may not be needed...
+				(EncryptionRequired() && iBaseband->Encrypted())))
+		{
+		TBTSecEvent newevent(TBTSecEvent::ERequestAuthorisation);
+		SendEvent(newevent);
+		}
+	else
+		{
+		CompleteRequest(EBTSecManAccessDenied);
+		}
+	}
+
+void CBTAccessRequester::RequestAuthorisation(TBTSecEvent& aEvent)
+	{
+	LOG_FUNC
+	TBTSecEventRequestAuthorisation* event = TBTSecEventRequestAuthorisation::Cast(&aEvent);
+	__ASSERT_ALWAYS(event, User::Panic(KBTSecPanic, EBTSecUnexpectedStateMachineEventId));
+	if (iRequirements.AuthorisationRequired())
+		{
+		LOG(_L8("\tAuthorisation required..."))
+		LOG(_L8("\tStarting Authorisation..."))
+		__ASSERT_ALWAYS(!iAuthorisor, User::Panic(KBTSecPanic,EBTSecAuthorisationRequestAlreadyExists));
+		TRAPD(err,iAuthorisor = CBTAuthorisor::NewL(*this, iSecMan, iServiceRequirements.Uid()));
+		if(err != KErrNone)
+			{
+			// the remote device is authenticated, but longer passkey is required
+			CompleteRequest(EBTSecManAccessDenied);
+			}
+			// Pending authorisation...
+		}
+	else
+		{
+		TBTSecEventAuthorisationComplete event(ETrue);
+		SendEvent(event);
+		}
+	}
+
+void CBTAccessRequester::AuthorisationRequested(TBTSecEvent& aEvent)
+	{
+	LOG_FUNC
+	TBTSecEventAuthorisationRequested* event = TBTSecEventAuthorisationRequested::Cast(&aEvent);
+	__ASSERT_ALWAYS(event, User::Panic(KBTSecPanic, EBTSecUnexpectedStateMachineEventId));
+	iTimer->Start();
+	}
+
+void CBTAccessRequester::AuthorisationComplete(TBTSecEvent& aEvent)
+	{
+	LOG_FUNC
+	TBTSecEventAuthorisationComplete* event = TBTSecEventAuthorisationComplete::Cast(&aEvent);
+	__ASSERT_DEBUG(event, User::Panic(KBTSecPanic, EBTSecUnexpectedStateMachineEventId));
+		
+	// cancel guard timer
+	iTimer->Cancel();
+	if(event->Error() != KErrNone && iRequirements.AuthorisationRequired())
+		{
+		CompleteRequest(EBTSecManAccessDenied);
+		}
+	else
+		{
+		if(event->AccessAllowed())
+			{
+			CompleteRequest(EBTSecManAccessGranted);
+			}
+		else
+			{
+			CompleteRequest(EBTSecManAccessDenied);
+			}
+		}
+	}
+
+void CBTAccessRequester::IOCapsResponse(TBTSecEvent& aEvent)
+	{
+	LOG_FUNC
+	TBTSecEventIoCapabilityResponse* event = TBTSecEventIoCapabilityResponse::Cast(&aEvent);
+	__ASSERT_ALWAYS(event, User::Panic(KBTSecPanic,EBTSecBadStateMachineEvent));
+	
+	// store/do something with the remote device's IO caps
+	iRemoteIOCapability = event->IoCapability();
+	iRemoteOOBDataPresence = event->OobDataPresent();
+	
+	// If remote device says "no bonding" when asked for dedicated
+	// bonding, remember this for sending a negative reply later
+	THCIAuthenticationRequirement authReq = event->AuthenticationRequirements();
+	if (iAccessType == EDedicatedBonding && (authReq == EMitmReqNoBonding || authReq == EMitmNotReqNoBonding))
+		{
+		iDedicatedBondingNotAvailable = ETrue;
+		}
+	}
+
+void CBTAccessRequester::IOCapsRequested(TBTSecEvent& aEvent)
+	{
+	LOG_FUNC
+	TBTSecEventIoCapabilityRequested* event = TBTSecEventIoCapabilityRequested::Cast(&aEvent);
+	__ASSERT_ALWAYS(event, User::Panic(KBTSecPanic,EBTSecBadStateMachineEvent));
+	
+	// Send local IO caps to remote device
+	
+	// Host and Controller supports SSP.
+	
+	// Send HCI_IO_Capability_Request_Reply with...
+	// BD_ADDR
+	// IO_Capability:		DisplayYesNo 
+	// OOB_Data_Present:	check secman::IsOOBDataAvailable(BD_ADDR)
+	// Authentication_Requirements:	depends on connection type (Bonding, ACL, Service)
+	// 								check secman::ConnectionType()
+	
+	// For test assume...
+	// OOB_Data_Present = EOOBDataNotPresent
+	// Authentication_Requirements = EMITMNRGB
+
+	if(iBaseband->IsPairable())
+		{
+		THCIAuthenticationRequirement authReq = EMitmNotReqGeneralBonding;
+		
+		// If MITM protection is required by a service, or the device is in paired only connections mode
+		// the require MITM protection.
+		if (iRequirements.MitmProtection() != EMitmNotRequired || iSecMan.ConnectionsManager().IsAcceptPairedOnlyMode())
+			{
+			if (iAccessType == EDedicatedBonding)
+				{
+				authReq = EMitmReqDedicatedBonding;
+				}
+			else
+				{
+				authReq = EMitmReqGeneralBonding;
+				}
+			iBaseband->SetLocalMITM(ETrue);
+			}
+		else
+			{
+			if (iAccessType == EDedicatedBonding)
+				{
+				authReq = EMitmNotReqDedicatedBonding;
+				}
+			else
+				{
+				authReq = EMitmNotReqGeneralBonding;
+				}
+			iBaseband->SetLocalMITM(EFalse);
+			}
+		
+		THCIOobDataPresence oobPresence = EOOBDataNotPresent;
+		if (iBaseband->HasRemoteOobData())
+			{
+			oobPresence = EOOBDataPresent;
+			}
+		
+		TRAPD(err, iSecMan.CommandController().IOCapabilityRequestReplyL(iBaseband->BDAddr(), EIOCapsDisplayYesNo, oobPresence, authReq));
+		if(err!=KErrNone)
+			{
+			CompleteRequest(EBTSecManAccessDenied);
+			}
+		}
+	else
+		{
+		TRAPD(err, iSecMan.CommandController().IOCapabilityRequestNegativeReplyL(iBaseband->BDAddr(), EPairingNotAllowed));
+		if(err!=KErrNone)
+			{
+			CompleteRequest(EBTSecManAccessDenied);
+			}
+		}
+	}
+
+void CBTAccessRequester::UserConfirmation(TBTSecEvent& aEvent)
+	{
+	LOG_FUNC
+	TBTSecEventUserConfirmationRequest* event = TBTSecEventUserConfirmationRequest::Cast(&aEvent);
+	__ASSERT_ALWAYS(event, User::Panic(KBTSecPanic, EBTSecBadStateMachineEvent));
+		
+	// Ignore the user confirmation request, we're unable to bond
+	// (dedicated bonding not available at both ends of the link)
+	// A negative reply is also sent in secman.cpp
+	if (UnableToBond())
+		{
+		CompleteRequest(KErrRemoteDeviceIndicatedNoBonding);
+		return;
+		}
+
+	// start guard timer...
+	iTimer->Start();
+	}
+
+void CBTAccessRequester::PasskeyEntry(TBTSecEvent& aEvent)
+	{
+	LOG_FUNC
+	//just progressing through the state machine in case of a CBTAccessRequester present.
+	//do nothing here because the action has been handled by secman
+	switch(aEvent.Event())
+		{
+		case TBTSecEvent::EPasskeyNotfication:
+			//do nothing as secman handles the processing
+		break;
+		case TBTSecEvent::EKeypressEntry:
+			{
+			TBTSecEventKeypressEntry* event = TBTSecEventKeypressEntry::Cast(&aEvent);
+			__ASSERT_ALWAYS(event, User::Panic(KBTSecPanic,EBTSecBadStateMachineEvent));
+			//do nothing as secman handles the processing
+			}
+		break;
+		default:
+			User::Panic(KBTSecPanic,EBTSecUnexpectedStateMachineEventId);
+		break;
+		}
+	}
+
+void CBTAccessRequester::RemoteOOBDataRequest(TBTSecEvent& aEvent)
+	{
+	LOG_FUNC
+	TBTSecEventRemoteOOBDataRequest* event = TBTSecEventRemoteOOBDataRequest::Cast(&aEvent);
+	__ASSERT_ALWAYS(event, User::Panic(KBTSecPanic, EBTSecBadStateMachineEvent));
+		
+	// just progressing through the state machine in case of a CBTAccessRequester present.
+	// do nothing here because the action has been handled by secman
+	}
+
+void CBTAccessRequester::SimplePairingPending(TBTSecEvent& aEvent)
+	{
+	LOG_FUNC
+	switch(aEvent.Event())
+		{
+		case TBTSecEvent::EUserConfirmationRequestComplete:
+			{
+			// restart guard timer
+			iTimer->Start();
+			}
+		break;
+		case TBTSecEvent::EKeypressComplete:
+			// restart guard timer
+			iTimer->Start();
+		break;
+		case TBTSecEvent::ERemoteOOBDataRequestComplete:
+			// restart guard timer
+			iTimer->Start();
+		break;
+		default:
+			User::Panic(KBTSecPanic,EBTSecUnexpectedStateMachineEventId);
+		break;
+		}
+	}
+
+
+void CBTAccessRequester::SetDeviceName()
+	{
+	LOG_FUNC
+	// not *that* bad - mostly inlines
+	
+	iDeviceName = iSecMan.ConnectionsManager().
+					LinkManagerProtocol().InquiryMgr().
+					  DeviceNameFromCache(iBaseband->BDAddr());
+	}
+
+
+const MAccessRequestResponseHandler& CBTAccessRequester::ServiceRequester() const
+	{
+	LOG_FUNC
+	return iRequester;
+	}
+
+const TBTDevAddr& CBTAccessRequester::DeviceAddress() const
+	{
+	LOG_FUNC
+	return iDevAddr;
+	}
+
+void CBTAccessRequester::Start()
+	{
+	LOG_FUNC
+	TBTSecEvent event(TBTSecEvent::EStart, KErrNone);
+	SendEvent(event);
+	}
+
+void CBTAccessRequester::CompleteRequest(TInt aResult)
+	{
+	LOG_FUNC
+#ifdef __FLOG_ACTIVE
+	if(aResult == EBTSecManAccessDenied)
+		{
+		LOG(_L8("\tACCESS DENIED"));
+		}
+	else if(aResult == EBTSecManAccessGranted)
+		{
+		LOG(_L8("\tACCESS GRANTED"));
+		}
+	else
+		{
+		LOG1(_L8("\tERROR (%d)"), aResult);
+		}
+#endif // __FLOG_ACTIVE
+	iSecMan.AccessRequestComplete(this, aResult);
+	}
+
+/**
+Take the access requirements of the service and compare them with the permissions given 
+to the device by the user.  This results in a list of tasks to be carried out by the 
+access requester before it may let the connection proceed.
+**/
+TBTAccessRequirements CBTAccessRequester::OverallRequirements(const TBTServiceSecurity& aServiceSecurity,
+														 const TBTNamelessDevice& aDevice)
+	{
+	LOG_FUNC
+	//Create the initial stab at the access requirements from the requirements of this service...
+	TBTAccessRequirements req;
+	req.SetAuthentication(aServiceSecurity.MitmProtection());
+	req.SetAuthorisation(aServiceSecurity.AuthorisationRequired());
+	req.SetEncryption(aServiceSecurity.EncryptionRequired());
+	req.SetDenied(aServiceSecurity.Denied());
+	req.SetPasskeyMinLength(aServiceSecurity.PasskeyMinLength());
+	
+	//Look at the global security setting of the device...
+	if (aDevice.IsValidGlobalSecurity())
+		{
+		LOG(_L8("\tDevice has global security settings"));
+		TBTDeviceSecurity devSec = aDevice.GlobalSecurity();
+		if (devSec.Banned())
+			req.SetDenied(ETrue);
+		if (devSec.Encrypt())
+			req.SetEncryption(ETrue);
+		if (devSec.MitmRequirements() == TBTDeviceSecurity::EMitmRequired)
+			req.SetAuthentication(EMitmRequired);
+		if (devSec.NoAuthorise())
+			req.SetAuthorisation(EFalse);
+		if (devSec.PasskeyMinLength() && devSec.PasskeyMinLength() > req.PasskeyMinLength())
+			req.SetPasskeyMinLength(devSec.PasskeyMinLength());
+		}
+
+	//Check to see if the device has anything specific about this service...
+
+	if (iOverride)
+		{
+		// get the device security from the override (the override points to the correct device)
+		LOG(_L8("\tSAP has overriden global security settings for device"));
+		const TBTDeviceSecurity& servSec = iOverride->DeviceSecurity();
+		if (servSec.Banned())
+			req.SetDenied(ETrue);
+		if (servSec.Encrypt())
+			req.SetEncryption(ETrue);
+		if (servSec.MitmRequirements() == TBTDeviceSecurity::EMitmRequired)
+			req.SetAuthentication(EMitmRequired);
+		if (servSec.NoAuthorise())
+			req.SetAuthorisation(EFalse);
+		if (servSec.PasskeyMinLength() && servSec.PasskeyMinLength() > req.PasskeyMinLength() )
+			req.SetPasskeyMinLength(servSec.PasskeyMinLength());
+		}
+
+	// If encryption is required then authentication is implicitly required.  The logic for 
+	// determining this is in the AuthenticationRequired function, that for any particular
+	// moment determies whether or not authentication is required.
+
+	return req;
+	}
+
+void CBTAccessRequester::PhysicalLinkChange(const TBTBasebandEventNotification & aEvent, CPhysicalLink& /*aPhysicalLink*/)
+	{
+	LOG_FUNC
+	// only forward events that secman is interested in
+	// linkup, linkdown, encryption, authentication, error
+	// Care needed: other events may harm operation of secman 
+	// and open security hole, such as ENotifySniffMode, ENotifyParkMode
+	// and ENotifyHoldMode
+	TBTPhysicalLinkStateNotifier secmanEvents = static_cast<TBTPhysicalLinkStateNotifier>
+	           (ENotifyPhysicalLinkUp |
+	            ENotifyPhysicalLinkDown |
+	            ENotifyPhysicalLinkError |
+	            ENotifyAuthenticationComplete |
+	            ENotifyEncryptionChangeOn |
+	            ENotifyEncryptionChangeOff );
+
+	if (aEvent.EventType() & secmanEvents)
+		{
+		PhysicalLinkChange(aEvent);
+		}
+	// else drop
+	}
+
+TPhysicalLinkObserverQLink& CBTAccessRequester::ObserverQLink()
+	{
+	LOG_FUNC
+	return iQueLink;
+	}
+
+void CBTAccessRequester::PhysicalLinkChange(const TBTBasebandEventNotification & aEvent)
+	{
+	LOG_FUNC;
+	if(aEvent.EventType() == ENotifyPhysicalLinkUp && iBaseband->SimplePairingMode() != EPhySimplePairingUndefined)
+		{
+		LOG(_L8("\tPhysical link up..."))
+		TBTSecEvent event(TBTSecEvent::EPhysicalLinkUp, aEvent.ErrorCode());
+		SendEvent(event);
+		}
+	else if(aEvent.EventType() == ENotifyAuthenticationComplete)
+		{
+		LOG(_L8("\tAuthentication complete..."))
+		TBTSecEvent event(TBTSecEvent::EAuthenticationComplete, aEvent.ErrorCode());
+		SendEvent(event);
+		}
+	else if((aEvent.EventType() == ENotifyEncryptionChangeOn)||(aEvent.EventType() == ENotifyEncryptionChangeOff))
+		{
+		LOG(_L8("\tEncryption Change complete..."))
+		TBTSecEvent event(TBTSecEvent::EEncryptionChangeComplete, aEvent.ErrorCode());
+		SendEvent(event);
+		}
+	else if((aEvent.EventType() == ENotifyPhysicalLinkDown))
+		{
+		LOG(_L8("\tPhysical link down...Complete(ACCESS DENIED)"));
+		LinkError(aEvent.ErrorCode());
+		}
+	else if((aEvent.EventType() == ENotifyPhysicalLinkError))
+		{
+		LOG(_L8("\tPhysical link error...Complete(ACCESS DENIED)"));
+		LinkError(aEvent.ErrorCode());
+		}
+	}
+
+TPrefetchNotifierQLink& CBTAccessRequester::MbpnQueLink()
+	{
+	LOG_FUNC
+	return iPrefetchQueueLink;
+	}
+
+void CBTAccessRequester::MbpnPrefetchComplete(TInt aError)
+	{
+	LOG_FUNC
+	if(aError == KErrNone)
+		{
+		aError = EBTSecManAccessDeferred;
+		}
+	CompleteRequest(aError);
+	}
+
+void CBTAccessRequester::LinkError(TInt aError)
+	{
+	LOG_FUNC
+	// Speculatively register for a prefetch notification
+	TInt error = iSecMan.ConnectionsManager().PrefetchMan().RegisterForPrefetching(DeviceAddress(), *this);
+
+	// Whatever we do - our CPhysicalLink representation is going to disappear
+	if (iIsSubscribedToConnection)
+		{
+		iBaseband->UnsubscribeLinkObserver(*this);
+		iIsSubscribedToConnection = EFalse;
+		}
+	if(error != KErrNone)
+		{
+		// No prefetch available, so fail the access request now.
+		CompleteRequest(EBTSecManAccessDenied);
+		return; //CompleteRequest deletes us
+		}
+	else
+		{
+		// Prefetch is available - so we need to enter an unconnected state.
+		TBTSecEvent event(TBTSecEvent::EPhysicalLinkDown, aError);
+		if(SendEvent(event))
+			{
+			// Physical link representation can be considered gone.
+			iBaseband = NULL;
+			}
+		}
+	}
+
+TBool CBTAccessRequester::IsAuthenticationReqPending(const TBTDevAddr& aAddr, TUint& aPasskeyMinLength, TBluetoothMitmProtection& aMitmLevel)
+	{
+	LOG_FUNC
+	if(aAddr == iBaseband->BDAddr() && (iCurrentState == EBTAuthenticationRequested || iCurrentState == EBTRequestAuthentication ||
+			                            iCurrentState == EBTIOCapsResponse  || 	iCurrentState == EBTIOCapsRequested ||
+			                            iCurrentState == EBTUserConfirmation || iCurrentState == EBTPasskeyEntry ||
+			                            iCurrentState == EBTRemoteOOBDataRequest || iCurrentState == EBTSimplePairingPending))
+		{
+
+		aPasskeyMinLength = iServiceRequirements.PasskeyMinLength();
+		aMitmLevel = iServiceRequirements.MitmProtection();
+		return ETrue;
+		}
+	else 
+		{
+		aPasskeyMinLength = 0;
+		aMitmLevel = EMitmNotRequired;
+		return EFalse;
+		}
+	}
+
+TBool CBTAccessRequester::AuthenticationRequired() const
+	{
+	LOG_FUNC
+	ASSERT_DEBUG(iBaseband->SimplePairingMode() != EPhySimplePairingUndefined);
+	
+	// Here we determine if authentication is required on the link.
+	TBool authenticationRequired = EFalse;
+	
+	if(iBaseband->SimplePairingMode() == EPhySimplePairingEnabled)
+		{
+		// If operating in simple pairing mode then authentication is always required
+		// due to security mode 4.
+		authenticationRequired = ETrue;
+		}
+	else
+		{
+		// Otherwise we are operating a legacy pre-v2.1 link, and we have to be more
+		// clever about whether authentication is required.
+		if(iAccessType == EGeneralBondingSecurityMode4Outgoing)
+			{
+	        // If this is the security mode 4 access requests and there is no simple pairing
+			// then don't bother - we will authenticate later when the security mode 2 access
+			// requester is used.
+			// <NOP> // authenticationRequired is already EFalse
+			}
+		else if(iRequirements.MitmProtection() != EMitmNotRequired)
+			{
+			// If we have any form of MITM specification then we should perform authentication
+			// to provide the best MITM protection we can (PIN code entry).
+			authenticationRequired = ETrue;
+			}
+		else if(iRequirements.EncryptionRequired())
+			{
+			// If encryption is required then authentication is needed to establish the
+			// encrypted link.
+			authenticationRequired = ETrue;
+			}
+		}
+	return authenticationRequired;
+	}
+
+TBool CBTAccessRequester::AuthenticationRecommended() const
+	{
+	LOG_FUNC
+	TBool authenticationRecommended = AuthenticationRequired();
+	if(!authenticationRecommended && LinkKeyGoodEnough())
+		{
+		// If the current link key is good enough then we should be able to use that
+		// if it is available.
+		if(iSecMan.ConnectionsManager().LinkManagerProtocol().IsSecureSimplePairingSupportedLocally())
+			{
+			// Of course this is only "recommended" when using controllers capable of renegotiating
+			// stronger (in terms of PIN length) link keys.  We don't want to be stuck we a
+			// low quality link key from the first service level connection (probably
+			// SDP) that the device initiates...
+			authenticationRecommended = ETrue;
+			}
+		}
+	return authenticationRecommended;
+	}
+
+TBool CBTAccessRequester::EncryptionRequired() const
+	{
+	LOG_FUNC
+	ASSERT_DEBUG(iBaseband->SimplePairingMode() != EPhySimplePairingUndefined);
+
+	// Here we determine if encryption is required on the link.
+	TBool encryptionRequired = EFalse;
+
+	if(iRequirements.EncryptionRequired())
+		{
+		// If the access request is for a dedicated bond then there is no need to perform any
+		// encryption.  If the access requester has specified encryption and dedicated bonding then
+		// this can be considered a defect.
+		__ASSERT_DEBUG(iAccessType != EDedicatedBonding, PANIC(KBTSecPanic, EBTSecEncryptionRequestForDedicatedBond));
+		encryptionRequired = ETrue; // If a specification of encryption is made explicitly then we honour it.
+		}
+	else if(iAccessType != EDedicatedBonding)
+		{
+		// Otherwise dedicated bonding is incidental...so only for general bonding accesses
+		// do we consider enabling encryption.
+		if(iBaseband->SimplePairingMode() == EPhySimplePairingEnabled)
+			{
+			// If the link is SSP capable then for security mode 4 we must encrypt the link
+			// for general bonding requests.
+			encryptionRequired = ETrue;
+			}
+		else if(iBaseband->Authenticated() && iBaseband->IsEncryptionSupported())
+			{
+			// If the link is authenticated, for security mode 4 we might as well
+			// encrypt it (assuming that it is supported).
+			encryptionRequired = ETrue;
+			}
+		}
+
+	// Ensure that if encryption is required then the state machine has already authenticated the link.
+	__ASSERT_DEBUG(!encryptionRequired || iBaseband->Authenticated(), PANIC(KBTSecPanic, EBTSecEncryptionRequiredOnUnauthenticatedLink));
+
+	return encryptionRequired;
+	}
+
+TBool CBTAccessRequester::UnableToBond() const
+	{
+	LOG_FUNC
+	return (iAccessType == EDedicatedBonding && iDedicatedBondingNotAvailable);
+	}
+
+TAccessType CBTAccessRequester::AccessType() const
+	{
+	return iAccessType;
+	}
+
+TBool CBTAccessRequester::AuthenticationInProgress() const
+	{
+	LOG_FUNC
+	return iAuthenticationInProgress;
+	}
+
+void  CBTAccessRequester::SetAuthenticationInProgress()
+	{
+	LOG_FUNC
+	iAuthenticationInProgress = ETrue;
+	}
+	
+TBool CBTAccessRequester::BasebandConnected() const
+	{
+	LOG_FUNC
+	return iBaseband && iBaseband->IsConnected();
+	}
+
+TBool CBTAccessRequester::LinkKeyGoodEnough() const
+	{
+	LOG_FUNC
+	if(!iBaseband->RemoteDevice().IsValidLinkKey())
+		{
+		// If there is no valid link key then it isn't good enough.
+		return EFalse;
+		}
+	
+	switch(iBaseband->RemoteDevice().LinkKeyType())
+		{
+	case ELinkKeyAuthenticated:
+		// An authenticated link key is the best we can do, so it is always good enough.
+		LOG(_L8("\tLink key is authenticated"))
+		return ETrue;
+		
+	case ELinkKeyUnauthenticatedUpgradable:
+		// If the link key is unauthenticated but upgradable then, only in the cases where
+		// MITM is not required is it good enough since there is the potential to be stronger. 
+		if(iRequirements.MitmProtection() == EMitmNotRequired)
+			{
+			LOG(_L8("\tLink key is unauthenticated(upgradable) and is sufficient"));
+			return ETrue;
+			}
+		else if(iSecMan.ConnectionsManager().IsAcceptPairedOnlyMode() && !iBaseband->Authenticated())
+			{
+			LOG(_L8("\tLink key is unauthenticated(upgradable) and in paired only connections"));
+			// we don't want to pair if not yet authenticated in paired only connection mode...
+			return ETrue;
+			}
+		else
+			{
+			LOG(_L8("\tLink key is unauthenticated but can be upgraded"));
+			return EFalse;
+			}
+		
+	case ELinkKeyUnauthenticatedNonUpgradable:
+		// If the link key is unauthenticated but not upgradable then it is good enough so longer
+		// as MITM protection is not required.  Unless OOB data has been received for that device
+		// in which case we can attempt to upgrade through supplied data.
+		if (iBaseband->HasRemoteOobData() && iRequirements.MitmProtection() != EMitmNotRequired)
+			{
+			LOG(_L8("\tLink key is not upgradable but OOB data is available"));
+			return EFalse;
+			}
+		else if (iRequirements.MitmProtection() != EMitmRequired)
+			{
+			LOG(_L8("\tLink key is unauthenticated(non-upgradable) and is sufficient"));
+			return ETrue;
+			}
+		else
+			{
+			LOG(_L8("\tLink key is unauthenticated but not sufficient"));
+			return EFalse;
+			}
+		
+	case ELinkKeyCombination:
+		// A combination key not suitable if the remote device is now SSP capable.  Also we
+		// determine if the key is sufficiently strong based on the length of the PIN code used.
+		if(iBaseband->SimplePairingMode() == EPhySimplePairingEnabled)
+			{
+			LOG(_L8("\tCombination key is not good enough for an SSP enabled device!"));
+			return EFalse;
+			}
+		else
+			{
+			LOG(_L("\tChecking min passkey length...")) 
+			TBTPinCode passKey = iBaseband->PassKey();
+			if(passKey().iLength >= iRequirements.PasskeyMinLength())
+				{
+				LOG(_L8("\tPIN code is of a sufficient length"));
+				return ETrue;
+				}
+			else
+				{
+				LOG(_L8("\tPIN code length is not long enough!"))
+				return EFalse;
+				}
+			}
+		
+	case ELinkKeyDebug:
+		// Debug keys are used during development of Bluetooth services, as such they are
+		// only appropriate for use if the device is in a debugging mode.
+		if(iSecMan.DebugMode())
+			{
+			LOG(_L8("\tDebug Key is acceptable for use in debug mode"));
+			return ETrue;
+			}
+		else
+			{
+			LOG(_L8("\tDebug Key being used when not in debug mode!"));
+			return EFalse;
+			}
+		}
+	LOG1(_L8("!!! Unknown link key type (%d)"), iBaseband->RemoteDevice().LinkKeyType());
+	DEBUG_PANIC_LINENUM
+	return EFalse; 
+	}
+
+void CBTAccessRequester::SetCurrentState(TBTAccessRequesterState aState)
+	{
+	LOG_FUNC
+	iCurrentState = aState;
+	}
+
+TBool CBTAccessRequester::RequirementsDenied()
+	{
+	LOG_FUNC
+	return iRequirements.Denied();
+	}
+	
+void CBTAccessRequester::AuthenticationTimerElapsed()
+	{
+	LOG_FUNC
+	LOG(_L8("\tTimer has elapsed during an authentication request... Complete(ACCESS DENIED)"));
+	CompleteRequest(EBTSecManAccessDenied);
+	}
+
+
+//
+// CBTAccessRequester::CAuthenticationTimer
+//
+	
+CBTAccessRequester::CAuthenticationTimer* CBTAccessRequester::CAuthenticationTimer::NewL(CBTAccessRequester& aObserver)
+	{
+	CAuthenticationTimer* self = new(ELeave) CAuthenticationTimer(aObserver);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+CBTAccessRequester::CAuthenticationTimer::~CAuthenticationTimer()
+	{
+	LOG_FUNC
+	Cancel();
+	}
+	
+CBTAccessRequester::CAuthenticationTimer::CAuthenticationTimer(CBTAccessRequester& aObserver)
+	: CTimer(CActive::EPriorityIdle)
+	, iObserver(aObserver)
+	{
+	LOG_FUNC
+	CActiveScheduler::Add(this);
+	}
+
+void CBTAccessRequester::CAuthenticationTimer::ConstructL()
+	{
+	LOG_FUNC
+	CTimer::ConstructL();
+	}
+	
+void CBTAccessRequester::CAuthenticationTimer::Start()
+	{	
+	LOG_FUNC
+	Cancel();
+	After(KAuthenticationRequestTimeout);
+	}
+
+void CBTAccessRequester::CAuthenticationTimer::RunL()
+	{
+	LOG_FUNC
+	iObserver.AuthenticationTimerElapsed();
+	}
+
+
+