diff -r 000000000000 -r 29b1cd4cb562 bluetooth/btstack/common/secman.cpp --- /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 +#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(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; iIsAuthenticationReqPending(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; iAuthenticationRequired() && !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 + (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 + (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; + }