Introduction
This article intends to introduce you to a new managed Mutex class, MutexSecurityNeutral
. The existing .NET framework provided System.Threading.Mutex
class has the limitation of using only one security context, the one under which it is created. Once it is created under a specific security context, it can’t be opened/created under a different user/security context. Here is a workaround for this problem - the MutexSecurityNeutral
class.
The basic idea is to create a Win32 Kernel mutex object by specifying the security descriptor with a null DACL. A null DACL in the security descriptor means “Everyone all access”. Well, I admit the fact that creating a Kernel object with a null DACL is a possible entry point for a DoS (Denial of Service) attack. It is solely up to you and the security criticality of your system to decide upon this.
Background
A short time ago, one of my friends came across an issue of getting an access-denied error while creating a mutex object in a web application. This ASP.NET – C# web application was on Windows authentication and Impersonation
set to true
. The same code worked well if Impersonation
was set to false
.
Rationale behind the issue
When an ASP.NET application runs under Windows authentication with Impersonation
set to true
, the ASP.NET worker process, aspnet_wp.exe, will be running under the user context of the logged on user. But if Impersonation
is set to false
, then the user context impersonated will always be the system user ASPNET.
So in a Windows authentication scenario with Impersonation
set to true
, when the first request comes from a user, say, domain1\user1, the mutex object will get created under the context of domain1\user1. If the next request (to inetinfo.exe, and then to aspnet_wp.exe) is from the same user, then there is no problem as the context is the same. But if the request is from a different user, say, domain1\user2, then the issue elevates. In this case, the .aspx page will be running under the context of domain1\user2. Then, the mutex object creation will fail as the security descriptor associated with the already existing mutex object is of domain\user1. A detailed elucidation of IIS, ASP.NET, and the Windows security model is outside the scope of this document. There are many articles available on CP on this topic. Also, I am in the processes of compiling a series of articles on the Windows security model and the Internet. Will keep this area updated, once it is done.
Even though .NET 2.0 comes with a set of new Win32 security model wrapper classes like System.Security.AccessControl.MutexSecurity
, the object model does not allow to add a null ACE into the DACL. One of the design goals was to prevent users from creating a security vulnerable Kernel object. Here comes the solution for this, a security neutral managed type, MutexSecurityNeutral
. This can be created under any user context as the security descriptor is initialized with a null DACL.
As an alternative, we can use the .NET lock
block as well, if there is no inter-process synchronization required.
Using the code
The class MutexSecurityNeutral
can be used just like the .NET Mutex
class. Give a reference to the MutexSecurityNeutral.dll in your project. Given below is a C# sample code which synchronizes a shared resource:
SecUtil.MutexSecurityNeutral mutexsecurityneutral =
new SecUtil.MutexSecurityNeutral("yourmutexname");
mutexsecurityneutral.WaitOne();
nSharedResource++;
mutexsecurityneutral.Done();
The MutexSecurityNeutral
implementation is shown below:
namespace SecUtil
{
public __gc class MutexSecurityNeutral
{
private:
CMutexSecurityNeutralUnmanaged*
m_CMutexSecurityNeutralUnmanaged;
String __gc* m_MutexName;
public:
MutexSecurityNeutral(String __gc* MutexName):
m_MutexName(MutexName){
m_CMutexSecurityNeutralUnmanaged = NULL;
}
~MutexSecurityNeutral(){
delete m_CMutexSecurityNeutralUnmanaged;
}
bool WaitOne(){
char __nogc* szMutexname =
static_cast<CHAR *>(Marshal::StringToHGlobalAnsi(
m_MutexName).ToPointer());
m_CMutexSecurityNeutralUnmanaged = new
CMutexSecurityNeutralUnmanaged(szMutexname);
bool bRtn= m_CMutexSecurityNeutralUnmanaged->WaitOne();
Marshal::FreeHGlobal( IntPtr((void*)szMutexname));
return bRtn;
} bool
Done(){ returnm_CMutexSecurityNeutralUnmanaged->Done();
}
};
}
The SecurityNeutralUnmanaged
has two methods:
WaitOne()
In the wait()
method, it creates an object of CMutexSecurityNeutralUnmanaged
, and passes the mutex name to the unmanaged wait()
method.
Done()
Makes a call to the unmanaged CMutexSecurityNeutralUnmanaged->Done()
method. This CMutexSecurityNeutralUnmanaged
is declared in MutexSecurityNeutral.h.
class CMutexSecurityNeutralUnmanaged
{
private:
const char* m_szMutexName;
HANDLE m_hMutex;
public:
CMutexSecurityNeutralUnmanaged(const char*
szMutexName):m_szMutexName(szMutexName){
m_hMutex = NULL;
}
~CMutexSecurityNeutralUnmanaged(){
}
bool WaitOne(){
bool rtn = false;
SECURITY_DESCRIPTOR sd;
SECURITY_ATTRIBUTES sa;
try{
if (!InitializeSecurityDescriptor(&sd,
SECURITY_DESCRIPTOR_REVISION))
return FALSE;
if (!SetSecurityDescriptorDacl(&sd, TRUE, NULL, FALSE))
return FALSE;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = &sd;
sa.bInheritHandle = FALSE;
m_hMutex = CreateMutex(&sa, true, m_szMutexName);
WaitForSingleObject( m_hMutex, 10000L);
rtn = true;
}
catch(...){
rtn = false;
}
return rtn;
}
bool Done(){
bool rtn = false;
try{
ReleaseMutex(m_hMutex);
CloseHandle(m_hMutex);
rtn = true;
}
catch(...){
rtn = false;
}
return rtn;
}
};
The SecurityNeutralUnmanaged
class has two methods:
WaitOne()
This method creates the SECURITY_DESCRIPTOR
variable and associates a null DACL to it by calling SetSecurityDescriptorDacl
. Then, it calls CreateMutex
to create the mutex object, and WaitForSingleObject
waits on the mutex handle.
Done()
This method just releases the mutex and closes the handle.
Summary
All Win32 Kernel objects are associated with a particular user/security context. Thus, in the case of a Win32 mutex object which is created under a particular user security context, it can’t be recreated/opened under a different user context. If I rephrase it, these Kernel objects have a user affinity. In a desktop application scenario, it may not be a concern as all programs generally run under the logged on user context unless otherwise it is impersonated programmatically. But if we are using mutex like Kernel objects in a web application scenario, with Impersonation
set to true
, things are different. This does make trouble.
So, as a web developer, what is the impact on you because of this Kernel object user affinity? .NET provides (both 1.1 and 2.0) the System.Threading.Mutex
class, but it can not be used under a web application with Windows Authentication and Impersonation
set to true
. The object creation will fail with an Access-denied error. As an alternative, you can use this MutexSecurityNeutral
class, but it has the security vulnerability loop hole of using a null DACL. I repeat, it is solely up to you to decide whether to use this class or not.
Revision History
- May 05, 2006 - Version 1.0 - first release.