kernel/eka/debug/securityServer/src/c_security_svr_server.cpp
branchRCL_3
changeset 257 3e88ff8f41d5
child 258 880ff05ad710
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/debug/securityServer/src/c_security_svr_server.cpp	Wed Sep 01 12:34:56 2010 +0100
@@ -0,0 +1,655 @@
+// 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);
+		}
+
+	//create session
+	LOG_MSG("About to call new(ELeave) CSecuritySvrSession()");
+	CSecuritySvrSession* servSession = new(ELeave) CSecuritySvrSession();
+
+	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_MSG( "CSecuritySvrServer::AttachProcessL()\n" );
+
+	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++)
+		{
+		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 \n", err );
+			delete processPair;
+			}
+		return err;
+		}
+	else
+		{
+		//check whether the process Id has already been reserved
+		for(TInt i=0; i<iActiveDebugMap.Count(); i++)
+			{
+			if(processPair->ProcessNameMatches(*(iActiveDebugMap[i])))
+				{
+				//process already being debugged
+				LOG_MSG( "  AttachProcessL() error : process already being debugged\n" );
+				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 appending active process pair \n", 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)
+	{
+	//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(iActiveDebugMap[i]->Equals(aTargetProcessName, aDebugAgentProcessId))
+			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
+