Click here to Skip to main content
15,881,882 members
Articles / Programming Languages / C++/CLI
Article

A security neutral mutex class for the managed platform

Rate me:
Please Sign up or sign in to vote.
3.93/5 (6 votes)
5 May 20064 min read 38.8K   174   6   6
An article on a security neutral mutex class that can be used on any managed platform.

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:

C#
SecUtil.MutexSecurityNeutral mutexsecurityneutral =
         new SecUtil.MutexSecurityNeutral("yourmutexname");

mutexsecurityneutral.WaitOne();
nSharedResource++;
mutexsecurityneutral.Done();

The MutexSecurityNeutral implementation is shown below:

MC++
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.

MC++
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.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMy vote of 1 Pin
span23721-Apr-10 19:44
span23721-Apr-10 19:44 
GeneralGood idea, but Doesn't work cross process [modified] Pin
LightingToGo31-Mar-07 10:57
LightingToGo31-Mar-07 10:57 
It's very helpful that you identified this problem in your article. There are many common needs for a cross-user Mutex and it saves time to know .NET is normally not capable of this.

However the whole point of a Mutex is that it can be inter-process (cross-process), and your implementation breaks this functionality.

So what we need is a cross-user Mutex that your code provides, while retaining the cross-process capability that .NET provides.

In order to do this you need to use "OpenMutex" that provides that magic.

The easiest way to do this is to add a new static method to your managed class called OpenExisting that calls through to OpenMutex.

Secondly, your class breaks source compatibility of the .NET Mutex for no good reason - why "Done" instead or "WaitOne"? Structuring the class similar to the .NET version would not only maximize ease of use, but would provide source level compatibility at the same time.

My final feedback is that the C++ code is not compatible with the latest version of Managed C++, and requires the "clr /OldSyntax" switch. The older syntax you use is deprecated so it would be nice to remove this switch.

Thank you for your contribution, it helped me realize the problem I was having and surely has helped others.

ecards

modified on Sunday, August 24, 2008 12:15 PM

GeneralRe: Good idea, but Doesn't work cross process Pin
Member 2362228-Aug-07 10:21
Member 2362228-Aug-07 10:21 
GeneralResource Leak Pin
viceroy9-May-06 3:49
viceroy9-May-06 3:49 
GeneralNuetral Pin
HobbitCoder7-May-06 6:49
HobbitCoder7-May-06 6:49 
GeneralRe: Nuetral Pin
Milton Karimbekallil8-May-06 5:11
Milton Karimbekallil8-May-06 5:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.