--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/debugsrv/runmodedebug/securityserver/src/c_security_svr_server.cpp Thu Sep 02 22:05:40 2010 +0300
@@ -0,0 +1,685 @@
+// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "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:
+// Provides the debug security server server implementation.
+//
+//
+
+/**
+ @file
+ @internalTechnology
+ @released
+*/
+
+#include <e32base.h>
+#include <e32base_private.h>
+#include <rm_debug_api.h>
+#include "c_process_pair.h"
+#include "c_security_svr_session.h"
+#include "c_security_svr_server.h"
+#include "rm_debug_logging.h"
+
+using namespace Debug;
+
+/**
+Server constructor, sessions are created as ESharableSessions, meaning that
+each session will be used by at most one debug agent
+*/
+CSecuritySvrServer::CSecuritySvrServer(CActive::TPriority aActiveObjectPriority)
+ : CServer2(aActiveObjectPriority, ESharableSessions),
+ iSessionCount(0),
+ iShutdown()
+ {
+ LOG_MSG("CSecuritySvrServer::CSecuritySvrServer()\n");
+ }
+
+/**
+Standard implementation
+
+@return pointer to new CSecuritySvrServer object
+*/
+CSecuritySvrServer* CSecuritySvrServer::NewLC()
+ {
+ LOG_MSG("CSecuritySvrServer::NewLC()\n");
+
+ CSecuritySvrServer* self=new(ELeave) CSecuritySvrServer(EPriorityStandard);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ return self;
+ }
+
+/**
+Server destructor, performs cleanup for the server
+*/
+CSecuritySvrServer::~CSecuritySvrServer()
+ {
+ LOG_MSG("CSecuritySvrServer::~CSecuritySvrServer()\n");
+
+ // stop the kernel side driver
+ iKernelDriver.Close();
+ User::FreeLogicalDevice(KDebugDriverName);
+
+ //deallocate both the debug maps
+ iPassiveDebugMap.ResetAndDestroy();
+ iActiveDebugMap.ResetAndDestroy();
+ }
+
+/**
+Starts the server and constructs and starts the servers shutdown timer
+*/
+void CSecuritySvrServer::ConstructL()
+ {
+ LOG_MSG("CSecuritySvrServer::ConstructL()");
+
+ StartL(KSecurityServerName);
+ iShutdown.ConstructL();
+ iShutdown.Start();
+
+ //load the kernel driver
+ TInt err = User::LoadLogicalDevice(KDebugDriverFileName);
+ if(! ((KErrNone == err) || (KErrAlreadyExists == err)))
+ {
+ User::Leave(err);
+ }
+ //create an information object for initialising the driver
+ TRM_DebugDriverInfo driverInfo;
+ driverInfo.iUserLibraryEnd = 0;
+ User::LeaveIfError(iKernelDriver.Open(driverInfo));
+ }
+
+/**
+Creates a new session with the DSS. A version check is done to ensure that an
+up to date version of the DSS is available (according to the DA's needs).
+The device driver is loaded if necessary and a session with the server and a
+handle to the driver opened.
+
+@param aRequiredVersion the minimal version of the DSS required by the DA
+
+@return a pointer to the new sever session, or NULL if any of the
+ initialisation process failed
+*/
+CSession2* CSecuritySvrServer::NewSessionL(const TVersion& aRequiredVersion, const RMessage2& aMessage) const
+//
+// Session constructor
+//
+ {
+ LOG_ARGS("version=%d.%d.%d", aRequiredVersion.iMajor, aRequiredVersion.iMinor, aRequiredVersion.iBuild);
+
+ //assert compatible version
+ TVersion currentVersion(KDebugServMajorVersionNumber, KDebugServMinorVersionNumber, KDebugServPatchVersionNumber);
+ if(!User::QueryVersionSupported(currentVersion, aRequiredVersion))
+ {
+ LOG_MSG("Requested version not compatible with this version. Asked for %d.%d.%d but this is %d.%d.%d", aRequiredVersion.iMajor, aRequiredVersion.iMinor, aRequiredVersion.iBuild, KDebugServMajorVersionNumber, KDebugServMinorVersionNumber, KDebugServPatchVersionNumber);
+ User::Leave(KErrNotSupported);
+ }
+
+ //get the debug agent's process id
+ RThread clientThread;
+ User::LeaveIfError(aMessage.Client(clientThread));
+ CleanupClosePushL(clientThread);
+ RProcess clientProcess;
+ User::LeaveIfError(clientThread.Process(clientProcess));
+ CleanupStack::PopAndDestroy(&clientThread);
+ TProcessId processId = clientProcess.Id();
+ clientProcess.Close();
+
+ //create session
+ LOG_MSG("About to call new(ELeave) CSecuritySvrSession()");
+ CSecuritySvrSession* servSession = new(ELeave) CSecuritySvrSession(processId);
+
+ CleanupStack::PushL(servSession);
+ servSession->ConstructL();
+ CleanupStack::Pop(servSession);
+ return servSession;
+ }
+
+/**
+Manages requests from debug agents to attach to target debug processes
+
+Given the debug agent process ID and the target process name:
+(1) checks whether the pair is already in either of the debug maps, if so
+ then returns KErrAlreadyExists
+(2) if aPassive == ETrue then just add the pair to the passive map and return
+ whatever the return value of the array write was
+(3) if aPassive == EFalse then check whether the target debug process is
+ already reserved by another debug agent. If it is then return KErrInUse,
+ otherwise add the pair to the active debug map and return the status
+ value of the array write.
+
+@param aTargetProcessName original FileName of the process to attach to
+@param aDebugAgentProcessId process ID of the debug agent
+@param aPassive ETrue if wish to attach passively, EFalse otherwise
+
+@return KErrNone if successfully attached, otherwise another system wide error
+ code as above
+*/
+TInt CSecuritySvrServer::AttachProcessL(const TDesC& aTargetProcessName, const TProcessId aDebugAgentProcessId, const TBool aPassive)
+ {
+ //store the pair of values
+ LOG_MSG3("CSecuritySvrServer::AttachProcessL() 0x%lx aPassive=%d", aDebugAgentProcessId.Id(), aPassive);
+
+ CProcessPair *processPair = CProcessPair::NewL(aTargetProcessName, aDebugAgentProcessId);
+ if(processPair == NULL)
+ return KErrNoMemory;
+
+ //check whether the pair already exists in the active debug map
+ for(TInt i=0; i<iActiveDebugMap.Count(); i++)
+ {
+ // Note that the equality is of the debug agent id and the proc name
+ if(*processPair == *(iActiveDebugMap[i]))
+ {
+ //process already exists
+ LOG_MSG( " AttachProcessL() error : KErrAlreadyExists in active map\n" );
+ delete processPair;
+ return KErrAlreadyExists;
+ }
+ }
+
+ //check whether the pair already exists in the passive map
+ for(TInt i=0; i<iPassiveDebugMap.Count(); i++)
+ {
+ if(*processPair == *(iPassiveDebugMap[i]))
+ {
+ //process already exists
+ LOG_MSG( " AttachProcessL() error : KErrAlreadyExists in passive map\n" );
+ delete processPair;
+ return KErrAlreadyExists;
+ }
+ }
+
+ if(aPassive)
+ {
+ //just add the pair and return
+ TInt err = iPassiveDebugMap.Append(processPair);
+ if(err != KErrNone)
+ {
+ // couldn't add pair for some unknown reason, so delete the pair
+ LOG_MSG2( " AttachProcessL() error %d appending passive process pair", err );
+ delete processPair;
+ }
+ return err;
+ }
+ else
+ {
+ //check whether the process Id has already been reserved
+ for(TInt i=0; i<iActiveDebugMap.Count(); i++)
+ {
+ // Now check if this is already debugged, but only if its for a particular process.
+ // We can have several entries from AttachAll calls, and its ok to add them
+ if( (!processPair->ProcessNameMatches(_L("*"))) &&
+ (processPair->ProcessNameMatches(*(iActiveDebugMap[i])) ) )
+ {
+ //process already being debugged
+ LOG_MSG( " AttachProcessL() error : process already being debugged" );
+ delete processPair;
+ return KErrInUse;
+ }
+ }
+ //try to add the pair.
+ TInt err = iActiveDebugMap.Append(processPair);
+ if(err != KErrNone)
+ {
+ // couldn't add pair for some unknown reason, so delete the pair
+ LOG_MSG2( " AttachProcessL() error %d inserting active process pair", err );
+ delete processPair;
+ }
+ return err;
+ }
+ }
+
+/*
+Detach from debugging the specified process
+
+@param aTargetProcessName name of the process to detach from
+@param aDebugAgentProcessId process ID of the debug agent
+
+@return KErrNone if successfully detached, KErrNotFound if an attempt is made
+ to detach from a process which the debug agent hasn't previously attached to
+*/
+TInt CSecuritySvrServer::DetachProcess(const TDesC& aTargetProcessName, const TProcessId aDebugAgentProcessId)
+ {
+ LOG_MSG2( "CSecuritySvrServer::DetachProcess() for agent with id 0x%lx", aDebugAgentProcessId.Id() );
+
+ //check whether the pair is in the active debug map
+ for(TInt i=0; i<iActiveDebugMap.Count(); i++)
+ {
+ if(iActiveDebugMap[i]->Equals(aTargetProcessName, aDebugAgentProcessId))
+ {
+ //remove the process pair from the active debug map
+ delete iActiveDebugMap[i];
+ iActiveDebugMap.Remove(i);
+ return KErrNone;
+ }
+ }
+
+ //check whether the pair is in the passive debug map
+ for(TInt i=0; i<iPassiveDebugMap.Count(); i++)
+ {
+ if(iPassiveDebugMap[i]->Equals(aTargetProcessName, aDebugAgentProcessId))
+ {
+ //remove the process pair from the active debug map
+ delete iPassiveDebugMap[i];
+ iPassiveDebugMap.Remove(i);
+ return KErrNone;
+ }
+ }
+
+ //process pair wasn't in either map
+ return KErrNotFound;
+ }
+
+/**
+Given a debug agent process ID, removes all references to that debug agent
+from the debug maps
+
+@param aMessage message from the debug agent
+
+@return returns KErrNone if successful, another system wide error code otherwise
+*/
+void CSecuritySvrServer::DetachAllProcesses(const TProcessId aDebugAgentProcessId)
+ {
+ //check whether the debug agent process ID is in the active debug map
+ for(TInt i=iActiveDebugMap.Count()-1; i>=0; i--)
+ {
+ if(iActiveDebugMap[i]->ProcessIdMatches(aDebugAgentProcessId))
+ {
+ //remove the process pair from the active debug map
+ delete iActiveDebugMap[i];
+ iActiveDebugMap.Remove(i);
+ }
+ }
+
+ //check whether the debug agent process ID is in the passive debug map
+ for(TInt i=iPassiveDebugMap.Count()-1; i>=0; i--)
+ {
+ if(iPassiveDebugMap[i]->ProcessIdMatches(aDebugAgentProcessId))
+ {
+ //remove the process pair from the passive debug map
+ delete iPassiveDebugMap[i];
+ iPassiveDebugMap.Remove(i);
+ }
+ }
+ }
+
+/*
+Check whether the specified debug agent is attaced to the specfied target
+process.
+
+@param aTargetThreadId thread ID of a thread in the target process
+@param aMessage a message which originates with the debug agent
+@param aPassive if EFalse then checks whether the debug agent is the active
+ debugger of the target process. If ETrue then checks whether the debug
+ agent is attached to the target process, irrespective of whether it is
+ attached passively or actively
+
+@return ETrue if attached, EFalse otherwise
+*/
+TBool CSecuritySvrServer::CheckAttached(const TThreadId aTargetThreadId, const RMessage2& aMessage, const TBool aPassive)
+ {
+
+ //get a handle to the target thread
+ RThread targetThread;
+ TInt err = targetThread.Open(aTargetThreadId);
+ if(err != KErrNone)
+ {
+ return EFalse;
+ }
+
+ //get a handle to the target process
+ RProcess targetProcess;
+ err = targetThread.Process(targetProcess);
+ //finshed with the thread handle so close it
+ targetThread.Close();
+ if(err != KErrNone)
+ return EFalse;
+
+ //get the target process' file name
+ TFileName targetFileName = targetProcess.FileName();
+
+ // Tamperproofing. Ensure that the debug agent really has a superset
+ // of the target process PlatSec capabilities, as authorised
+ // by an OEM Debug Token (if any)
+
+ TSecurityInfo targetSecInfo(targetProcess);
+
+ // Now compare the capabilities, to ensure the DebugAgent has been authorised with
+ // sufficient capabilities from its OEM Debug token
+ CSecuritySvrSession* session = (CSecuritySvrSession*)aMessage.Session();
+
+ // Presume we need to check the target process is debuggable unless a valid OEM Debug token in effect?
+ if (!OEMTokenPermitsDebugL(session->GetOEMDebugCapabilities(), targetSecInfo.iCaps) )
+ {
+ // No debug token therefore check if the process is debuggable
+ err = iKernelDriver.IsDebuggable(targetProcess.Id());
+ }
+
+ //finished with the process handle so close it
+ targetProcess.Close();
+
+ if (err != KErrNone)
+ {
+ // The process was not marked as debuggable by the loader, and the OEM
+ // debug token did not override the lack of a debuggable bit.
+ // The process was not marked as debuggable by the loader
+ return EFalse;
+ }
+
+ return CheckAttachedProcess(targetFileName, aMessage, aPassive);
+ }
+
+/*
+Check whether the specified debug agent is attaced to the specfied target
+process.
+
+@param aTargetProcessId process ID of the target process
+@param aMessage a message which originates with the debug agent
+@param aPassive if EFalse then checks whether the debug agent is the active
+ debugger of the target process. If ETrue then checks whether the debug
+ agent is attached to the target process, irrespective of whether it is
+ attached passively or actively
+
+@return ETrue if attached, EFalse otherwise
+*/
+TBool CSecuritySvrServer::CheckAttached(const TProcessId aTargetProcessId, const RMessage2& aMessage, const TBool aPassive)
+ {
+ //get a handle to the target process
+ RProcess targetProcess;
+ TInt err =targetProcess.Open(aTargetProcessId);
+ if(err != KErrNone)
+ {
+ return EFalse;
+ }
+
+ //get the target process' file name
+ TFileName targetFileName = targetProcess.FileName();
+
+ // Tamperproofing. Ensure that the debug agent really has a superset
+ // of the target process PlatSec capabilities, as authorised
+ // by an OEM Debug Token (if any)
+
+ TSecurityInfo targetSecInfo(targetProcess);
+
+ // Now compare the capabilities, to ensure the DebugAgent has been authorised with
+ // sufficient capabilities from its OEM Debug token
+ CSecuritySvrSession* session = (CSecuritySvrSession*)aMessage.Session();
+
+ // Presume we need to check the target process is debuggable unless a valid OEM Debug token in effect?
+ if ( !OEMTokenPermitsDebugL(session->GetOEMDebugCapabilities(), targetSecInfo.iCaps) )
+ {
+ // No debug token therefore check if the process is debuggable
+ err = iKernelDriver.IsDebuggable(targetProcess.Id());
+ }
+
+ //finished with the process handle so close it
+ targetProcess.Close();
+
+ if (err != KErrNone)
+ {
+ return EFalse;
+ }
+
+ return CheckAttachedProcess(targetFileName, aMessage, aPassive);
+ }
+
+/*
+Check whether the specified debug agent is attaced to the specfied target
+process.
+
+@param aTargetProcessName
+@param aMessage a message which originates with the debug agent
+
+@return ETrue if attached, EFalse otherwise
+*/
+TBool CSecuritySvrServer::CheckAttachedProcess(const TDesC& aTargetProcessName, const RMessage2& aMessage, const TBool aPassive) const
+ {
+ //get the debug agent's process id
+ TProcessId clientProcessId = 0;
+ TInt err = GetProcessIdFromMessage(clientProcessId, aMessage);
+ if(err != KErrNone)
+ return EFalse;
+
+ //check permissions
+ if(aPassive)
+ return IsDebugger(aTargetProcessName, clientProcessId);
+ else
+ return IsActiveDebugger(aTargetProcessName, clientProcessId);
+ }
+
+/**
+Tests whether the debug agent is attached actively to the target debug process
+
+@param aTargetProcessName target debug process' FileName
+@param aDebugAgentProcessId process ID of a debug agent
+
+@return ETrue if the specified debug agent is actively attached to the
+ specified target debug process, EFalse otherwise
+*/
+TBool CSecuritySvrServer::IsActiveDebugger(const TDesC& aTargetProcessName, const TProcessId aDebugAgentProcessId) const
+ {
+ //check whether the pair is in the active debug map
+ for(TInt i=0; i<iActiveDebugMap.Count(); i++)
+ {
+ // If both match, which can include aTargetProcessName being "*"
+ if(iActiveDebugMap[i]->Equals(aTargetProcessName, aDebugAgentProcessId))
+ {
+ LOG_MSG2( "CSecuritySvrServer::IsActiveDebugger() for agent id 0x%lx matches name and pid",
+ aDebugAgentProcessId.Id() );
+ return ETrue;
+ }
+ // if the pid matches and the name in the map is "*", indicating that
+ // this is an attach all agent, and thus debugging
+ if( iActiveDebugMap[i]->ProcessIdMatches(aDebugAgentProcessId) &&
+ iActiveDebugMap[i]->ProcessNameMatches(_L("*")) )
+ {
+ LOG_MSG2( "CSecuritySvrServer::IsActiveDebugger() for AttachAll agent id 0x%lx",
+ aDebugAgentProcessId.Id() );
+ return ETrue;
+ }
+ }
+ //not found so return false
+ return EFalse;
+ }
+
+/**
+Tests whether the target process is being debugged
+
+@param aTargetProcessName target process' FileName
+@param aPassive indicates whether to check for the process being actively debugged,
+or passively debugged
+
+@return ETrue if the specified target process is being debugged,
+ EFalse otherwise
+*/
+TBool CSecuritySvrServer::IsDebugged(const TDesC& aTargetProcessName, const TBool aPassive) const
+ {
+ //get a reference to the appropriate list
+ const RPointerArray<CProcessPair>& map = (aPassive) ? iPassiveDebugMap : iActiveDebugMap;
+
+ //iterate through the map trying to match the aTargetProcessName
+ for(TInt i=0; i<map.Count(); i++)
+ {
+ if(map[i]->ProcessNameMatches(aTargetProcessName))
+ {
+ return ETrue;
+ }
+ }
+ return EFalse;
+ }
+
+/**
+Tests whether the debug agent is attached to the target debug process
+
+@param aTargetProcessName target debug process' FileName
+@param aDebugAgentProcessId process ID of a debug agent
+
+@return ETrue if the specified debug agent is attached to the
+ specified target debug process (regardless of whether it is attached
+ passively or actively), EFalse otherwise
+*/
+TBool CSecuritySvrServer::IsDebugger(const TDesC& aTargetProcessName, const TProcessId aDebugAgentProcessId) const
+ {
+ //check whether the pair is in the active debug map
+ if(IsActiveDebugger(aTargetProcessName, aDebugAgentProcessId))
+ return ETrue;
+
+ //check whether the pair is in the passive debug map
+ for(TInt i=0; i<iPassiveDebugMap.Count(); i++)
+ {
+ if(iPassiveDebugMap[i]->Equals(aTargetProcessName, aDebugAgentProcessId))
+ return ETrue;
+ }
+ //not found so return false
+ return EFalse;
+ }
+
+/**
+Decrements the server's count of how many sessions are connected to it and
+starts the shutdown timer if there are no sessions connected
+*/
+void CSecuritySvrServer::SessionClosed()
+ {
+ if(--iSessionCount < 1)
+ {
+ iShutdown.Start();
+ }
+ }
+
+/**
+Increments the servers count of how many sessions are connected to it and
+cancels the shutdown timer if it is running
+*/
+void CSecuritySvrServer::SessionOpened()
+ {
+ iSessionCount++;
+ iShutdown.Cancel();
+ }
+
+/**
+ Get the process id of the thread which sent aMessage
+ @param aProcessId process id of the thread which sent aMessage
+ @param aMessage message object sent by thread
+
+ @return KErrNone if aProcessId could be set, or one of the system wide error codes if not
+ */
+TInt CSecuritySvrServer::GetProcessIdFromMessage(TProcessId& aProcessId, const RMessage2& aMessage) const
+ {
+ //get the debug agent's thread
+ RThread clientThread;
+ TInt err = aMessage.Client(clientThread);
+ if(err != KErrNone)
+ {
+ return err;
+ }
+
+ //get the debug agent's process
+ RProcess clientProcess;
+ err = clientThread.Process(clientProcess);
+
+ //finished with the thread handle so close it
+ clientThread.Close();
+
+ //check if there was an error from getting the process
+ if(err != KErrNone)
+ {
+ return err;
+ }
+
+ //get the debug agent's process id
+ aProcessId = clientProcess.Id();
+
+ //finished with the process handle so close it
+ clientProcess.Close();
+
+ return KErrNone;
+ }
+
+/**
+ Helper function which determines whether the capabilities of the
+ OEM Token are sufficient to permit debug of an application.
+
+ Normally, we use the AllFiles capability as a proxy which
+ means a Debug Agent can debug non-debuggable executables,
+ provided it has a superset of the capabilities of the executable
+ to be debugged.
+
+ However, this causes the problem that all OEM Debug Tokens implicitly
+ give the power to debug an AllFiles executable, even if all that
+ is really needed is the power to debug an app with no capabilities,
+ or capabilities other than AllFiles.
+
+ To address this, we treat the AllFiles capability in a special way.
+ The AllFiles capability in a token is taken to mean that an OEM has
+ signed the token, and hence can debug other executables. But this does
+ not inclue the ability to debug an AllFiles executable. To debug an AllFiles
+ executable, the token must also have TCB.
+
+ @param aTokenCaps - The PlatSec capabilities of a token
+ @param aTargetCaps - The PlatSec capabilities of a target app to be debugged
+
+ @return ETrue if authorised for debugging, EFalse otherwise.
+
+ @leave Any system error code.
+ */
+TBool CSecuritySvrServer::OEMTokenPermitsDebugL(const TCapabilitySet aTokenCaps, const TCapabilitySet aTargetCaps)
+ {
+ LOG_MSG("CSecuritySvrSession::OEMTokenPermitsDebugL\n");
+
+ // Is the token valid - i.e. does it have AllFiles.
+ if ( !aTokenCaps.HasCapability(ECapabilityAllFiles) )
+ {
+ // Token is not valid, as it does not have AllFiles.
+ LOG_MSG("CSecuritySvrSession::OEMTokenPermitsDebugL - Token does not have AllFiles\n");
+
+ return EFalse;
+ }
+
+ // Token MUST have a strict superset of capabilities
+ if ( !aTokenCaps.HasCapabilities(aTargetCaps) )
+ {
+ // Token does not have at least all the capabilities of the target
+ LOG_MSG("CSecuritySvrSession::OEMTokenPermitsDebugL - Token does not have superset of target capabilities\n");
+
+ return EFalse;
+ }
+
+ // Special case: If the target has AllFiles, the Token must have TCB
+ if ( aTargetCaps.HasCapability(ECapabilityAllFiles) )
+ {
+ // Target has AllFiles, so does the Token have TCB?
+ if ( !aTokenCaps.HasCapability(ECapabilityTCB) )
+ {
+ // Token does not have TCB.
+ LOG_MSG("CSecuritySvrSession::OEMTokenPermitsDebugL - Token does not have TCB when target has AllFiles\n");
+
+ return EFalse;
+ }
+ }
+
+ // If we have passed all the above tests, the token permits debug
+ return ETrue;
+ }
+
+/**
+ * This looks at a debug tokens capability and ensures it is sufficient
+ * to provide access to the flash partition
+ * @param aTokenCaps Capabilties of the Token
+ * @return TBool Whether or not flash access is permitted
+ */
+TBool CSecuritySvrServer::OEMTokenPermitsFlashAccessL(const TCapabilitySet aTokenCaps)
+ {
+ //Must have TCB to access flash
+ return aTokenCaps.HasCapability(ECapabilityTCB);
+ }
+
+//eof
+