--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/bluetooth/btstack/common/secman.cpp Fri Jan 15 08:13:17 2010 +0200
@@ -0,0 +1,1272 @@
+// 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 "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("secman");
+#endif
+
+#pragma warning (disable: 4355) //'this' in base init list - is OK for our usage
+
+static const TInt KBTSecManAccessRequesterArrayGranularity = 4;
+static const TInt KBTSecManNotifierRequesterArrayGranularity = 4;
+
+
+//------------------------------------------------------------------------//
+//class CBTSecMan
+//------------------------------------------------------------------------//
+void Panic(TBTSecPanic aPanic)
+ {
+ LOG_FUNC
+ User::Panic(KBTSecPanic, aPanic);
+ }
+
+CBTSecMan* CBTSecMan::NewL()
+ {
+ LOG_FUNC
+ CBTSecMan* self = CBTSecMan::NewLC();
+ CleanupStack::Pop(); //self
+ return self;
+ }
+
+CBTSecMan* CBTSecMan::NewLC()
+ {
+ LOG_FUNC
+ CBTSecMan* self = new(ELeave) CBTSecMan();
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ return self;
+ }
+
+CBTSecMan::CBTSecMan()
+ : iAccessRequesters(KBTSecManAccessRequesterArrayGranularity),
+ iNotifierRequesters(KBTSecManNotifierRequesterArrayGranularity)
+ {
+ LOG_FUNC
+ }
+
+void CBTSecMan::ConstructL()
+ {
+ LOG_FUNC
+ }
+
+void CBTSecMan::SetPhysicalLinksMgr(const CPhysicalLinksManager& aConnectionsMgr)
+ {
+ LOG_FUNC
+ iPhysicalLinksManager = &const_cast<CPhysicalLinksManager&>(aConnectionsMgr);
+ }
+
+CPhysicalLinksManager& CBTSecMan::ConnectionsManager() const
+ {
+ LOG_FUNC
+ return *iPhysicalLinksManager;
+ }
+
+
+CBTSecMan::~CBTSecMan()
+ {
+ LOG_FUNC
+
+ iAccessRequesters.ResetAndDestroy();
+ iAccessRequesters.Close();
+
+ iNotifierRequesters.ResetAndDestroy();
+ iNotifierRequesters.Close();
+ }
+
+void CBTSecMan::AccessRequestL(const TBTServiceSecurity& aSecurity,
+ const TBTServiceSecurityPerDevice* const aOverride,
+ const TBTDevAddr& aBDAddr,
+ MAccessRequestResponseHandler& aRequester)
+/**
+Handle an access request...
+Create a new CBTAccessRequester object to handle the request.
+**/
+ {
+ LOG_FUNC
+ // find the baseband this SAP is running on
+ CPhysicalLink& con = *iPhysicalLinksManager->FindPhysicalLink(aBDAddr);
+ CBTAccessRequester* p = CBTAccessRequester::NewLC(con, aSecurity,
+ aOverride,
+ aRequester,
+ *this);
+ User::LeaveIfError(iAccessRequesters.Append(p));
+ CleanupStack::Pop(); //clean up of p now handled by iAccessRequesters
+ // Try to start- it may not happen (depends on if device retreived from registry
+ p->DoRequest();
+
+ }
+
+void CBTSecMan::CancelRequest(MAccessRequestResponseHandler& aRequester)
+ {
+ LOG_FUNC
+ // search through access requesters to find correct one
+ LOG1(_L("sec\tCBTSecMan::CancelRequest from SAP 0x%08x"), &aRequester)
+ TInt count = iAccessRequesters.Count();
+
+ for (TInt i=(count-1); i>=0; i--)
+ {
+ CBTAccessRequester* requester = iAccessRequesters[i];
+ if(&requester->ServiceRequester() == &aRequester)
+ {
+ // should assert that aRequester is the same as in AccessRequester?
+ LOG(_L("sec\tRemoving AccessRequester..."))
+ iAccessRequesters.Remove(i);
+ delete requester;
+ break;
+ }
+ }
+ }
+
+void CBTSecMan::GetPassKeyLengthAndOriginator(const TBTDevAddr& aAddr, TUint& aPasskeyMinLength,
+ TBool& aLocallyInitiatedAuthentication)
+
+/**
+If authorisation request was initiated locally it will return true and will
+return the passkey minimal length requred by user
+**/
+ {
+ LOG_FUNC
+
+ TUint tmpPasskeyLength = 0;
+ TBool locallyInitiated = EFalse;
+ TInt count = iAccessRequesters.Count();
+
+ if (count == 0)
+ {
+ aLocallyInitiatedAuthentication = EFalse;
+ return;
+ }
+
+ // find all pending AccessRequesters for given BTAddr and find maximum of PasskeyMinLength
+
+ for (TInt i=0; i<count;i++)
+ {
+ CBTAccessRequester* requester = iAccessRequesters[i];
+ if (requester->IsAuthenticationReqPending(aAddr, tmpPasskeyLength))
+ {
+ locallyInitiated = ETrue;
+ if (aPasskeyMinLength < tmpPasskeyLength)
+ {
+ aPasskeyMinLength = tmpPasskeyLength;
+ }
+ }
+ }
+
+ aLocallyInitiatedAuthentication = locallyInitiated;
+ }
+
+void CBTSecMan::AuthenticationInProgress()
+/**
+When authorisation request was sent to HW, HCI will notify SecMan
+**/
+ {
+ LOG_FUNC
+
+ // find first pending AccessRequesters and set AuthenticationInProgress flag
+ for (TInt i=0; i<iAccessRequesters.Count(); i++)
+ {
+ CBTAccessRequester* requester = iAccessRequesters[i];
+
+ if (requester->AuthenticationRequired() && !requester->AuthenticationInProgress())
+ {
+ requester->SetAuthenticationInProgress();
+ break;
+ }
+ }
+ }
+
+void CBTSecMan::AccessRequestComplete(CBTAccessRequester* aAccessRequester, TInt aResult)
+/**
+The access request has been fully completed.
+Delete the CBTAccessRequester that was handling the request.
+**/
+ {
+ LOG_FUNC
+
+ TInt count = iAccessRequesters.Count();
+
+ ASSERT_DEBUG(count);
+
+ // find the originating service *now*
+ MAccessRequestResponseHandler& service = const_cast<MAccessRequestResponseHandler&>
+ (aAccessRequester->ServiceRequester());
+
+ // clean up the AccessRequester object
+ TInt i;
+ for (i=(count-1); i>=0; i--)
+ {
+ //find aRequester in iAccessRequesters and delete it
+ if(iAccessRequesters[i] == aAccessRequester)
+ {
+ iAccessRequesters.Remove(i);
+ delete aAccessRequester;
+ aAccessRequester = NULL;
+ break;
+ }
+ }
+ //compress the array if needs be
+ if ((count!=0) && (i!=count))
+ {
+ iAccessRequesters.GranularCompress();
+ }
+
+ // now tell the service
+ service.AccessRequestComplete(aResult);
+ }
+
+
+void CBTSecMan::AddNotifierRequestToQueL(CSecNotifierRequester& aRequest)
+/**
+Add notifier request to front of queue. If there are no other requests already in the queue,
+initiate this request.
+**/
+ {
+ LOG_FUNC
+ TInt count = iNotifierRequesters.Count();
+ User::LeaveIfError(iNotifierRequesters.Insert(&aRequest,0)); //add to front of queue since requests are taken from the back
+ if (count == 0) //ok since count was calculated before we inserted the new element
+ {
+ iActiveNotifierRequester = &aRequest;
+ aRequest.DoRequest();
+ }
+ else
+ {
+ LOG(_L("Secman: Request NOT initiated, should start later..."));
+ }
+ }
+
+void CBTSecMan::RemoveNotifierRequestFromQue(CSecNotifierRequester& aRequest)
+/**
+Remove the request from the queue. If aRequest is the currently active request then we can activate
+the next one in the queue. Otherwise, aRequest is being deleted prematurely and we must simply
+remove it from the array.
+**/
+ {
+ LOG_FUNC
+ LOG(_L("sec\tCBTSecMan - removing notifier request from queue"));
+ TInt count = iNotifierRequesters.Count();
+ TInt found = 0;
+ for (TInt i=(count-1); i>=0; i--)
+ {
+ if (iNotifierRequesters[i] == &aRequest)
+ {
+ found++;
+ iNotifierRequesters.Remove(i);
+ }
+ }
+ __ASSERT_DEBUG(found, User::Panic(KBTSecPanic, EBTSecBadNotifierArray));
+
+ if (&aRequest == iActiveNotifierRequester)
+ {
+ //start the next request if there is one...
+ count = iNotifierRequesters.Count();
+ if (count > 0)
+ {
+ LOG(_L("sec\tCBTSecMan - auto-starting next notifier request from queue"));
+ iActiveNotifierRequester = iNotifierRequesters[count-1];
+ iActiveNotifierRequester->DoRequest();
+ }
+ }
+ }
+
+
+CSecNotifierRequester::CSecNotifierRequester(CBTSecMan& aSecMan)
+: CActive(EPriorityStandard),
+ iInquiryMgr(aSecMan.ConnectionsManager().LinkManagerProtocol().InquiryMgr()),
+ iSecMgr(aSecMan)
+ {
+ LOG_FUNC
+ }
+
+void CSecNotifierRequester::ConstructL(const TBTDevAddr& aAddr)
+ {
+ LOG_FUNC
+ User::LeaveIfError(iNotifier.Connect());
+
+ // find the name at this stage for this device - may not be there yet
+ iDeviceName = iInquiryMgr.DeviceNameFromCache(aAddr);
+
+ if (!iDeviceName || iDeviceName->Length() ==0)
+ {
+ // cache didn't have name - so we'll ask for it as a HR action
+ TRAP_IGNORE(iHR = iInquiryMgr.NewHostResolverL());
+ iHRNameRecord = new TNameRecord;
+
+ // ignore error - only an optimisation - don't want to leave if there's
+ // a problem doing this optimisation
+ if (iHR && iHRNameRecord)
+ {
+ iHR->SetNotify(this);
+
+ TInquirySockAddr i;
+ i.SetAction(KHostResName);
+ i.SetBTAddr(aAddr);
+ iHRNameRecord->iAddr = i;
+ iHR->GetByAddress(*iHRNameRecord);
+ }
+ }
+
+ iDevAddr = aAddr;
+
+ iSecMgr.AddNotifierRequestToQueL(*this);
+ iIsAddedToNotifierQue = ETrue;
+ }
+
+CSecNotifierRequester::~CSecNotifierRequester()
+ {
+ LOG_FUNC
+ Cancel();
+
+ //remove ourself from the notifier que if we're still on it.
+ if (iIsAddedToNotifierQue)
+ {
+ iSecMgr.RemoveNotifierRequestFromQue(*this);
+ iIsAddedToNotifierQue = EFalse;
+ }
+
+ delete iHR;
+ delete iHRNameRecord;
+ delete iUpdateNotifierAO;
+
+ iNotifier.Close();
+ }
+
+void CSecNotifierRequester::QueryComplete(TInt aErr)
+ {
+ LOG_FUNC
+ if ( (aErr==KErrNone) && (iHRNameRecord!=NULL) )
+ {
+ // now have device name - update notifiers
+ // we do have a copy of the name - but it is now wide :-|
+ // and also we have iDeviceName that is still NULL, so best bet is
+ // to just set our pointer and use the cache one (which we *know* is there!)
+ TBTDevAddr a = TBTSockAddr::Cast(iHRNameRecord->iAddr).BTAddr();
+ iDeviceName = iInquiryMgr.DeviceNameFromCache(a);
+ DoUpdateNotifier();
+ }
+
+ delete iHRNameRecord;
+ iHRNameRecord = NULL;
+ }
+
+//------------------------------------------------------------------------//
+//class CBTPinRequester
+//------------------------------------------------------------------------//
+
+
+CBTPinRequester* CBTPinRequester::NewL(CPhysicalLink& aParent,
+ MPINCodeResponseHandler& aRequester,
+ CBTSecMan& aSecMan,
+ TUint aPasskeyMinLength,
+ TBool aInternallyInitiated)
+ {
+ LOG_FUNC
+ CBTPinRequester* s = CBTPinRequester::NewLC(aParent, aRequester, aSecMan,
+ aPasskeyMinLength, aInternallyInitiated);
+ CleanupStack::Pop();
+ return s;
+ }
+
+CBTPinRequester* CBTPinRequester::NewLC(CPhysicalLink& aParent,
+ MPINCodeResponseHandler& aRequester,
+ CBTSecMan& aSecMan,
+ TUint aPasskeyMinLength,
+ TBool aInternallyInitiated)
+ {
+ LOG_FUNC
+ CBTPinRequester* s = new(ELeave) CBTPinRequester(aParent, aRequester, aSecMan,
+ aPasskeyMinLength, aInternallyInitiated);
+ CleanupStack::PushL(s);
+ s->ConstructL(aParent.BDAddr());
+ return s;
+ }
+
+CBTPinRequester::CBTPinRequester(CPhysicalLink& aParent,
+ MPINCodeResponseHandler& aRequester,
+ CBTSecMan& aSecMan,
+ TUint aPasskeyMinLength,
+ TBool aInternallyInitiated) :
+ CSecNotifierRequester(aSecMan),
+ iParent(aParent),
+ iRequester(aRequester),
+ iSecMan(aSecMan),
+ iPasskeyMinLength(aPasskeyMinLength),
+ iInternallyInitiated(aInternallyInitiated)
+ {
+ LOG_FUNC
+ // a lot of inlines
+ CActiveScheduler::Add(this);
+ }
+
+CBTPinRequester::~CBTPinRequester()
+ {
+ LOG_FUNC
+ Cancel();
+ }
+
+
+void CBTPinRequester::DoUpdateNotifier()
+ {
+ LOG_FUNC
+ if(IsActive())
+ {
+ if(!iUpdateNotifierAO)
+ {
+ //Create a new CSecNotifierUpdateAO object
+ TRAP_IGNORE(iUpdateNotifierAO = CSecNotifierUpdateAO::NewL(iNotifier, KBTManPinNotifierUid));
+ }
+
+ if( (iUpdateNotifierAO) && (!iUpdateNotifierAO->IsActive()) )
+ {
+ TBTNotifierUpdateParamsPckg pckg;
+ if(iDeviceName)
+ {
+ TRAPD(err, pckg().iName = BTDeviceNameConverter::ToUnicodeL(*iDeviceName));
+ pckg().iResult = err; // Error code can be KErrNone
+ if (err!=KErrNone)
+ {
+ pckg().iName = KNullDesC;
+ }
+ }
+ else
+ {
+ pckg().iName = KNullDesC;
+ pckg().iResult = KErrNotFound;
+ }
+
+ iUpdateNotifierAO->DoUpdate(pckg);
+ }
+ }
+ }
+
+void CBTPinRequester::DoRequest()
+/**
+It's our turn...start the RNotifier plugin that deals with authorisation.
+**/
+ {
+ LOG_FUNC
+ ASSERT_DEBUG(iDevAddr == iParent.BDAddr());
+
+ iPasskeyParamsPckg().iBDAddr = iDevAddr;
+ if (iDeviceName)
+ {
+ TRAPD(err, iPasskeyParamsPckg().iName = BTDeviceNameConverter::ToUnicodeL(*iDeviceName));
+ if (err!=KErrNone)
+ {
+ iPasskeyParamsPckg().iName = KNullDesC;
+ }
+ }
+ else
+ {
+ iPasskeyParamsPckg().iName = KNullDesC;
+ }
+ iPasskeyParamsPckg().iPasskeyMinLength = iPasskeyMinLength;
+ iPasskeyParamsPckg().iLocallyInitiated = iInternallyInitiated;
+
+ iNotifier.StartNotifierAndGetResponse(iStatus, KBTManPinNotifierUid, iPasskeyParamsPckg, iPassKey);
+ SetActive();
+ }
+
+void CBTPinRequester::FriendlyNameRetrieved(const TDesC& /*aName*/, TInt /*aResult*/)
+ {
+ LOG_FUNC
+ // do nothing for now
+ }
+
+
+void CBTPinRequester::DoCancel()
+ {
+ LOG_FUNC
+ iNotifier.CancelNotifier(KBTManPinNotifierUid);
+ if (iUpdateNotifierAO)
+ {
+ iUpdateNotifierAO->Cancel();
+ }
+ }
+
+void CBTPinRequester::RunL()
+ {
+ LOG_FUNC
+ //got a PIN or error, so finish off: unload the plugin
+ iNotifier.CancelNotifier(KBTManPinNotifierUid);
+
+ //remove ourself from the notifier que, allowing the next notifier to be activated
+ iSecMan.RemoveNotifierRequestFromQue(*this);
+ iIsAddedToNotifierQue = EFalse;
+
+ ASSERT_DEBUG(iPasskeyParamsPckg().iBDAddr == iParent.BDAddr());
+ if (iStatus.Int())
+ {
+ // it failed - be unpairable
+ iRequester.PINCodeRequestNegativeReply((iParent.BDAddr()));
+ }
+ else
+ {
+ // got a PIN
+ iRequester.PINCodeRequestReply(iParent.BDAddr(),iPassKey);
+ iParent.SetPassKey(iPassKey);
+ iParent.PinRequestSent();
+ iParent.DeleteLinkKeyL(); //only delete link key when user has entered PIN
+ }
+ // in either case tell parent as we're done.
+ iParent.PinRequestComplete();
+ }
+
+#ifdef __FLOG_ACTIVE
+TInt CBTPinRequester::RunError(TInt aError)
+#else
+TInt CBTPinRequester::RunError(TInt /*aError*/)
+#endif
+ {
+ LOG_FUNC
+ ASSERT_DEBUG(iPasskeyParamsPckg().iBDAddr == iParent.BDAddr());
+ LOG1(_L("sec\tCBTPinRequester::RunError(%d)"), aError);
+ iRequester.PINCodeRequestNegativeReply(iParent.BDAddr());
+ iParent.PinRequestComplete();
+ return KErrNone;
+ }
+
+//------------------------------------------------------------------------//
+//class CBTAuthorisor
+//------------------------------------------------------------------------//
+
+CBTAuthorisor* CBTAuthorisor::NewL(CBTAccessRequester& aParent, TUid aServiceUID)
+ {
+ LOG_FUNC
+ CBTAuthorisor* s = CBTAuthorisor::NewLC(aParent, aServiceUID);
+ CleanupStack::Pop();
+ return s;
+ }
+
+CBTAuthorisor* CBTAuthorisor::NewLC(CBTAccessRequester& aParent, TUid aServiceUID)
+ {
+ LOG_FUNC
+ CBTAuthorisor* s = new(ELeave) CBTAuthorisor(aParent, aServiceUID);
+ CleanupStack::PushL(s);
+ s->ConstructL(aParent.DeviceAddress());
+ return s;
+ }
+
+CBTAuthorisor::CBTAuthorisor(CBTAccessRequester& aAccessRequester, TUid aServiceUID) :
+ CSecNotifierRequester(aAccessRequester.SecMan()),iAccessRequester(aAccessRequester)
+ {
+ LOG_FUNC
+ iAuthorisationParamsPckg().iUid = aServiceUID;
+ CActiveScheduler::Add(this);
+ }
+
+CBTAuthorisor::~CBTAuthorisor()
+ {
+ LOG_FUNC
+ Cancel();
+ }
+
+
+void CBTAuthorisor::DoUpdateNotifier()
+ {
+ LOG_FUNC
+ if(IsActive())
+ {
+ if(!iUpdateNotifierAO)
+ {
+ //Create a new CSecNotifierUpdateAO object
+ TRAP_IGNORE(iUpdateNotifierAO = CSecNotifierUpdateAO::NewL(iNotifier, KBTManAuthNotifierUid));
+ }
+
+ if( (iUpdateNotifierAO) && (!iUpdateNotifierAO->IsActive()) )
+ {
+ TBTNotifierUpdateParamsPckg pckg;
+ if(iDeviceName)
+ {
+ TRAPD(err, pckg().iName = BTDeviceNameConverter::ToUnicodeL(*iDeviceName));
+ pckg().iResult = err; // Error code can be KErrNone
+ if (err!=KErrNone)
+ {
+ pckg().iName = KNullDesC;
+ }
+ }
+ else
+ {
+ pckg().iName = KNullDesC;
+ pckg().iResult = KErrNotFound;
+ }
+
+ iUpdateNotifierAO->DoUpdate(pckg);
+ }
+ }
+ }
+
+void CBTAuthorisor::DoRequest()
+/**
+Start the RNotifier plugin that deals with authorisation.
+**/
+ {
+ LOG_FUNC
+ TInt err(KErrNone);
+
+ if (iDeviceName)
+ {
+ TRAP(err, iAuthorisationParamsPckg().iName = BTDeviceNameConverter::ToUnicodeL(*iDeviceName));
+ if (err!=KErrNone)
+ {
+ iAuthorisationParamsPckg().iName = KNullDesC;
+ }
+ }
+ else
+ {
+ iAuthorisationParamsPckg().iName = KNullDesC;
+ }
+ iAuthorisationParamsPckg().iBDAddr = iDevAddr;
+
+ iNotifier.StartNotifierAndGetResponse(iStatus, KBTManAuthNotifierUid, iAuthorisationParamsPckg, iResultPckg);
+ SetActive();
+ }
+
+
+void CBTAuthorisor::DoCancel()
+ {
+ LOG_FUNC
+
+ iNotifier.CancelNotifier(KBTManAuthNotifierUid);
+
+ }
+
+void CBTAuthorisor::RunL()
+ {
+ LOG_FUNC
+ //unload the plugin
+ iNotifier.CancelNotifier(KBTManAuthNotifierUid);
+ //remove ourself from the notifier que, allowing the next notifier to be activated
+ iAccessRequester.SecMan().RemoveNotifierRequestFromQue(*this);
+ iIsAddedToNotifierQue = EFalse;
+ //check for errors + notify owner of completion
+ LOG1(_L("sec\tCBTAuthorisor::RunL(): iStatus = %d"), iStatus.Int());
+ if (iStatus.Int()!=KErrNone)
+ {
+ //error
+ iAccessRequester.CompleteRequest(iStatus.Int());
+ }
+ else
+ {
+ iAccessRequester.AuthorisationComplete(iResultPckg());
+ }
+ }
+
+TInt CBTAuthorisor::RunError(TInt aError)
+ {
+ LOG_FUNC
+ //will never get called as our RunL doesn't leave.
+ LOG1(_L("sec\tCBTAuthorisor::RunError(%d)"), aError);
+ return aError;
+ }
+
+
+/**
+ 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,
+ CBTSecMan& aParent)
+ {
+ LOG_FUNC
+ CBTAccessRequester* s = new(ELeave) CBTAccessRequester(aConnection, aSecurityRequired,
+ aOverride, aRequester, aParent);
+ CleanupStack::PushL(s);
+ s->ConstructL();
+ return s;
+ }
+
+
+CBTAccessRequester::CBTAccessRequester(CPhysicalLink& aConnection,
+ const TBTServiceSecurity& aServiceSecurity,
+ const TBTServiceSecurityPerDevice* const aOverride,
+ MAccessRequestResponseHandler& aRequester,
+ CBTSecMan& aParent) :
+ iRequester(aRequester),
+ iSecMan(aParent),
+ iBaseband(aConnection),
+ iServiceRequirements(aServiceSecurity),
+ iOverride(aOverride),
+ iIsSubscribedToConnection(EFalse),
+ iDeviceRetrievedFromRegistry(EFalse),
+ iQueLink(this),
+ iAuthenticationInProgress(EFalse)
+ {
+ LOG_FUNC
+ // try to get name for UI dialogs
+ SetDeviceName();
+ }
+
+void CBTAccessRequester::ConstructL()
+ {
+ LOG_FUNC
+ LOG2(_L("sec\tCBTAccessRequester 0x%08x constructed; sizeof %d"), this, sizeof(*this));
+ }
+
+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 (iBaseband.BDAddr());
+ }
+
+void CBTAccessRequester::DoRequest()
+/**
+Subscribe to the baseband link notifier to find out about the link state.
+This will kick off the state machine.
+**/
+ {
+ LOG_FUNC
+ LOG1(_L("sec\tAccessRequester 0x%08x DoRequest"),this);
+ iBaseband.SubscribeLinkObserver(*this);
+ iIsSubscribedToConnection = ETrue;
+
+ // if the link is already up (e.g. we're SecMode 2 for L2CAP/RFCOMM) then proceed
+ if (iBaseband.IsConnected())
+ {
+ TBTBasebandEventNotification event(ENotifyPhysicalLinkUp);
+ PhysicalLinkChange(event); // kick statemachine
+ }
+ // else wait until it is retrieved
+ }
+
+void CBTAccessRequester::CompleteRequest(TInt aResult)
+ {
+ LOG_FUNC
+ LOG2(_L("sec\tAccessRequester 0x%08x CompleteRequest, result %d"),this,aResult);
+ iSecMan.AccessRequestComplete(this, aResult);
+ }
+
+CBTAccessRequester::~CBTAccessRequester()
+ {
+ LOG_FUNC
+ LOG1(_L("sec\tAccessRequester 0x%08x Destroying"),this);
+ if (iIsSubscribedToConnection)
+ {
+ iBaseband.UnsubscribeLinkObserver(*this);
+ }
+ delete iAuthorisor;
+ }
+
+
+void CBTAccessRequester::NewStateL()
+/**
+This access request has entered a new state. Work out what we should do next.
+**/
+ {
+ LOG_FUNC
+
+ // We should ensure that at each iteration through the state machine the requirements
+ // are up-to-date. This is needed as the physical link may have changed
+ // some internal state e.g. as a result of the Registry having been modified.
+ iRequirements = OverallRequirements(iServiceRequirements, iBaseband.RemoteDevice());
+
+#ifdef _DEBUG
+ _LIT(KRequestNone, "Not Request. ");
+ _LIT(KRequestPending, "Request Pending. ");
+ _LIT(KRequestFailed, "Request Failed. ");
+ _LIT(KRequestComplete, "Request Complete.");
+
+ TBuf<20> iStateName[4] =
+ {
+ KRequestNone(),
+ KRequestPending(),
+ KRequestFailed(),
+ KRequestComplete(),
+ };
+
+ TBTDevAddr addr = iBaseband.BDAddr();
+ LOG3(_L("sec\tCBTAccessRequester::NewStateL(Authorised[%S] Authenticated[%S] Encrypted[%S])"),
+ &iStateName[iState.AuthorisationState()],
+ &iStateName[iState.AuthenticationState()],
+ &iStateName[iState.EncryptionState()]);
+ LOG6(_L("\t\taddr[0x%02x%02x%02x%02x%02x%02x]"),
+ addr[0],addr[1],addr[2],addr[3],addr[4],addr[5]);
+ LOG4(_L("\t\tRequirements: a[%d] p[%d] e[%d] d[%d]"), iRequirements.AuthenticationRequired(),
+ iRequirements.AuthorisationRequired(), iRequirements.EncryptionRequired(), iRequirements.Denied());
+#endif
+
+ if (iRequirements.AuthenticationRequired() && iRequirements.PasskeyMinLength() &&
+ iBaseband.Authenticated() )
+ {
+ LOG(_L("sec\tCheck min passkey length")) // for remotly initiated connection
+ TBTPinCode passKey = iBaseband.PassKey();
+ if ( passKey().iLength < iRequirements.PasskeyMinLength())
+ {
+ // the remote device is authenticated, but longer passkey is required
+ LOG(_L("sec\tCBTAccessRequester::Complete(ACCESS DENIED) Passkey min length requirement is longer, then current used for authentication"));
+ CompleteRequest(EBTSecManAccessDenied);
+ return;
+ }
+ }
+
+ //1. Check for any failures or for "Denied" in iRequirements
+ //If anything that was required has failed, complete the request with EBTManAccessDenied and delete this.
+ if ((iState.AuthenticationState() == TBTAccessRequestState::ERequestFailed && iRequirements.AuthenticationRequired())
+ || (iState.EncryptionState() == TBTAccessRequestState::ERequestFailed && iRequirements.EncryptionRequired())
+ || (iState.AuthorisationState() == TBTAccessRequestState::ERequestFailed && iRequirements.AuthorisationRequired())
+ || iRequirements.Denied())
+ {
+ LOG(_L("sec\tCBTAccessRequester::Complete(ACCESS DENIED)"));
+ CompleteRequest(EBTSecManAccessDenied);
+ }
+ //2. Check for authentication
+ else if (iRequirements.AuthenticationRequired() &&
+ !iBaseband.Authenticated() && !iBaseband.Encrypted())
+ {
+ // might have been encrypted by remote side - so no need to reauthenticate
+ // this makes us more interoperable with controllers that don't honour erratum E2244
+ LOG(_L("sec\tAuthentication required..."))
+ //if we don't have a pending authentication request, start authentication.
+ if (iState.AuthenticationState() != TBTAccessRequestState::ERequestPending)
+ {
+ LOG(_L("sec\tStarting Authentication..."));
+ iState.SetAuthenticationState(TBTAccessRequestState::ERequestPending);
+ User::LeaveIfError(iBaseband.Authenticate());
+ }
+ }
+ //3. Check for encryption
+ else if (iRequirements.EncryptionRequired() && !iBaseband.Encrypted())
+ {
+ LOG(_L("sec\tEncryption required..."))
+
+ if (!iBaseband.IsEncryptionDisabledForRoleSwitch())
+ {
+ //if we don't have a pending encryption request, start encryption.
+ if (iState.EncryptionState() != TBTAccessRequestState::ERequestPending)
+ {
+ LOG(_L("sec\tStarting Encryption..."))
+ iState.SetEncryptionState(TBTAccessRequestState::ERequestPending);
+ User::LeaveIfError(iBaseband.ChangeEncryption(EPointToPointEncryption));
+ }
+ }
+ }
+ //4. Check for authorisation
+ else if (iRequirements.AuthorisationRequired() &&
+ (iState.AuthorisationState() == TBTAccessRequestState::ERequestNone || iState.AuthorisationState() == TBTAccessRequestState::ERequestPending))
+ {
+ //if we don't have a pending authorisation request, start authorisation.
+ LOG(_L("sec\tAuthorisation required..."))
+ if (iState.AuthorisationState() != TBTAccessRequestState::ERequestPending)
+ {
+ LOG(_L("sec\tStarting Authorisation..."))
+ __ASSERT_ALWAYS(!iAuthorisor, User::Panic(KBTSecPanic,EBTSecAuthorisationRequestAlreadyExists));
+ iAuthorisor = CBTAuthorisor::NewL(*this, iServiceRequirements.Uid());
+ iState.SetAuthorisationState(TBTAccessRequestState::ERequestPending);
+ }
+ }
+ //5. Before EBTManAccessGranted, doublecheck all the checks were done
+ else
+ {
+ TBool authenticationComplete;
+ TBool encryptionComplete;
+ TBool authorisationComplete;
+
+ authenticationComplete = !iRequirements.AuthenticationRequired() ||
+ (iRequirements.AuthenticationRequired() &&
+ (iBaseband.Authenticated() || iBaseband.Encrypted()));
+
+
+
+ encryptionComplete = !iRequirements.EncryptionRequired() ||
+ (iRequirements.EncryptionRequired() && iBaseband.Encrypted());
+
+
+ authorisationComplete = !iRequirements.AuthorisationRequired() ||
+ (iRequirements.AuthorisationRequired() && iState.AuthorisationState()==TBTAccessRequestState::ERequestComplete);
+
+
+ if (authenticationComplete && encryptionComplete && authorisationComplete)
+ {
+ LOG(_L("sec\tEBTManAccessGranted"))
+ CompleteRequest(EBTSecManAccessGranted);
+ }
+ else
+ {
+ LOG(_L("sec\tCBTAccessRequester::Complete(ACCESS DENIED) during double check"));
+ CompleteRequest(EBTSecManAccessDenied);
+ }
+ }
+ }
+
+TBTAccessRequirements CBTAccessRequester::OverallRequirements(const TBTServiceSecurity& aServiceSecurity,
+ const TBTNamelessDevice& aDevice)
+/**
+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.
+**/
+ {
+ LOG_FUNC
+ //Create the initial stab at the access requirements from the requirements of this service...
+ TBTAccessRequirements req;
+ req.SetAuthentication(aServiceSecurity.AuthenticationRequired());
+ 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(_L("sec\tDevice has global security settings"));
+ TBTDeviceSecurity devSec = aDevice.GlobalSecurity();
+ if (devSec.Banned())
+ req.SetDenied(ETrue);
+ if (devSec.Encrypt())
+ req.SetEncryption(ETrue);
+ if (devSec.NoAuthenticate())
+ req.SetAuthentication(EFalse);
+ 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(_L("sec\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.NoAuthenticate())
+ req.SetAuthentication(EFalse);
+ if (servSec.NoAuthorise())
+ req.SetAuthorisation(EFalse);
+ if (servSec.PasskeyMinLength() && servSec.PasskeyMinLength() > req.PasskeyMinLength() )
+ req.SetPasskeyMinLength(servSec.PasskeyMinLength());
+ }
+
+ // if only encryption is required, then must need authentication
+ if (req.EncryptionRequired())
+ {
+ req.SetAuthentication(ETrue);
+ }
+
+ return req;
+ }
+
+void CBTAccessRequester::FriendlyNameRetrieved(const TDesC& /*aName*/, TInt /*aResult*/)
+
+ {
+ LOG_FUNC
+ // do nothing for now
+ }
+
+void CBTAccessRequester::DeviceRetrieved(const TBTNamelessDevice& aDevice, TInt aError)
+/**
+ A device has been retrieved from the registry
+ If it's for us we need to continue the access request based on the overrides it has
+
+ This object chooses not to keep a copy of the device, but notes its availability
+**/
+ {
+ LOG_FUNC
+ if (aDevice.Address() != iBaseband.BDAddr())
+ {
+ return; // wasn't for us
+ }
+
+ TInt err = aError;
+
+ iDeviceRetrievedFromRegistry = ETrue;
+
+ if (err == KErrNone || err == KErrNotFound)
+ {
+ // proceed with the security check...
+ iRequirements = OverallRequirements(iServiceRequirements, aDevice);
+ }
+
+ if (err!=KErrNone)
+ {
+ CompleteRequest(err);
+ }
+ }
+
+
+void CBTAccessRequester::AuthorisationComplete(TBool aResult)
+/**
+We have a new authorisation state.
+**/
+ {
+ LOG_FUNC
+ LOG1(_L("sec\tCBTAccessRequester::AuthorisationComplete(%d)"), aResult);
+ if (aResult)
+ {
+ iState.SetAuthorisationState(TBTAccessRequestState::ERequestComplete);
+ }
+ else
+ {
+ iState.SetAuthorisationState(TBTAccessRequestState::ERequestFailed);
+ }
+ TRAPD(err,NewStateL());
+ if (err)
+ {
+ CompleteRequest(err);
+ }
+ }
+
+
+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
+ }
+
+void CBTAccessRequester::PhysicalLinkChange(const TBTBasebandEventNotification & aEvent)
+ {
+ LOG_FUNC
+ if(aEvent.EventType() & (ENotifyPhysicalLinkDown | ENotifyPhysicalLinkError))
+ {
+ CompleteRequest(aEvent.ErrorCode());
+ return;
+ }
+
+ if (iState.AuthenticationState() == TBTAccessRequestState::ERequestPending &&
+ aEvent.EventType() == ENotifyAuthenticationComplete)
+ {
+ LOG(_L("sec\tAuthentication complete..."))
+
+ if(aEvent.ErrorCode() != KErrNone)
+ {
+ iState.SetAuthenticationState(TBTAccessRequestState::ERequestFailed);
+ //if Auth fail then ensure any previously stored Linkkey is removed
+ //Don't know which AccessRequester initiated this so can't complete
+ //the request with the error code, only thing to do is trap this error
+ //Deleting a LinkKey is not Critical merely cosmetic
+ LOG(_L("CPhysicalLink: Deleting link key due to failed Authentication"))
+ TRAP_IGNORE(iBaseband.DeleteLinkKeyL());
+ }
+ else
+ {
+ iState.SetAuthenticationState(TBTAccessRequestState::ERequestComplete);
+ }
+ }
+
+ if (iState.EncryptionState() == TBTAccessRequestState::ERequestPending &&
+ (aEvent.EventType() == ENotifyEncryptionChangeOn || aEvent.EventType() == ENotifyEncryptionChangeOff))
+ {
+ LOG(_L("sec\tEncryption Change complete..."))
+
+ // We can try again as 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.ErrorCode() == ELMPErrorTransactionCollision || aEvent.ErrorCode() == EDifferentTransactionCollision)
+ {
+ // This will force the state machine logic to try sending the command again
+ iState.SetEncryptionState(TBTAccessRequestState::ERequestNone);
+ }
+ else if(aEvent.ErrorCode() != KErrNone)
+ {
+
+ iState.SetEncryptionState(TBTAccessRequestState::ERequestFailed);
+ }
+ else
+ {
+ iState.SetEncryptionState(TBTAccessRequestState::ERequestComplete);
+ }
+ }
+
+ TRAPD(err,NewStateL());
+ if (err)
+ {
+ CompleteRequest(err);
+ }
+ }
+
+
+CBTSecMan& CBTAccessRequester::SecMan()
+ {
+ LOG_FUNC
+ return iSecMan;
+ }
+
+TBool CBTAccessRequester::IsAuthenticationReqPending(const TBTDevAddr& aAddr, TUint& aPasskeyMinLength)
+ {
+ LOG_FUNC
+ if (iState.AuthenticationState() == TBTAccessRequestState::ERequestPending &&
+ iAuthenticationInProgress && aAddr == iBaseband.BDAddr())
+ {
+ aPasskeyMinLength = iServiceRequirements.PasskeyMinLength();
+ return ETrue;
+ }
+ else
+ {
+ aPasskeyMinLength = 0;
+ return EFalse;
+ }
+ }
+
+TBool CBTAccessRequester::AuthenticationRequired() const
+ {
+ LOG_FUNC
+ return iRequirements.AuthenticationRequired();
+ }
+
+TBool CBTAccessRequester::AuthenticationInProgress() const
+ {
+ LOG_FUNC
+ return iAuthenticationInProgress;
+ }
+
+void CBTAccessRequester::SetAuthenticationInProgress()
+ {
+ LOG_FUNC
+ iAuthenticationInProgress = ETrue;
+ }
+
+
+ //------------------------------------------------------------------------//
+ //class CSecNotifierUpdateAO
+ //------------------------------------------------------------------------//
+
+CSecNotifierUpdateAO* CSecNotifierUpdateAO::NewL(RNotifier& aNotifier, TUid aNotifierUid)
+ {
+ LOG_FUNC
+ CSecNotifierUpdateAO* s = CSecNotifierUpdateAO::NewLC(aNotifier, aNotifierUid);
+ CleanupStack::Pop();
+ return s;
+ }
+
+CSecNotifierUpdateAO* CSecNotifierUpdateAO::NewLC(RNotifier& aNotifier, TUid aNotifierUid)
+ {
+ LOG_FUNC
+ LOG(_L("sec\tCSecNotifierUpdateAO::NewLC()"));
+ CSecNotifierUpdateAO* s = new(ELeave) CSecNotifierUpdateAO();
+ CleanupStack::PushL(s);
+ s->ConstructL(aNotifier, aNotifierUid);
+ return s;
+ }
+
+CSecNotifierUpdateAO::CSecNotifierUpdateAO()
+: CActive(EPriorityStandard)
+ {
+ LOG_FUNC
+ CActiveScheduler::Add(this);
+ }
+
+CSecNotifierUpdateAO::~CSecNotifierUpdateAO()
+ {
+ LOG_FUNC
+ Cancel();
+ }
+
+void CSecNotifierUpdateAO::ConstructL(RNotifier& aNotifier, TUid aNotifierUid)
+ {
+ LOG_FUNC
+ iNotifier = aNotifier;
+ iNotifierUid = aNotifierUid;
+ }
+
+void CSecNotifierUpdateAO::DoUpdate(const TBTNotifierUpdateParamsPckg& aPckg)
+ {
+ LOG_FUNC
+ //Retain a copy so that it does not go out of memory scope
+ iPckg = aPckg;
+
+ //we're not expecting an answer...
+ iNotifier.UpdateNotifierAndGetResponse(iStatus, iNotifierUid, iPckg, iAnswer);
+ SetActive();
+ }
+
+void CSecNotifierUpdateAO::RunL()
+ {
+ LOG_FUNC
+ //We can't do anything if an error is returned - just make sure we haven't done anything stupid...
+ __ASSERT_DEBUG((iStatus==KErrNone)||(iStatus==KErrNoMemory)||(iStatus==KErrNotReady), User::Panic(KBTSecPanic, EBTSecBadNotifierUpdate));
+ }
+
+void CSecNotifierUpdateAO::DoCancel()
+ {
+ LOG_FUNC
+ LOG(_L("sec\tCSecNotifierUpdateAO::DoCancel()"));
+ iNotifier.CancelNotifier(iNotifierUid); // no other API on Notifier to just cancel the update; but typically we'll want to cancel the whole notifier at this point(?)
+ }
+
+TInt CSecNotifierUpdateAO::RunError(TInt aError)
+ {
+ LOG_FUNC
+ LOG1(_L("sec\tCSecNotifierUpdateAO::RunError(%d)"), aError);
+
+ #ifndef __FLOGGING__
+ aError += 0; //Remove Compiler warning
+ #endif
+
+ return KErrNone;
+ }