Managed C++: Determining User Security Roles

In my previous article, “Managed C++: Retrieving User’s Windows Security Information,” I mentioned that there are times when an application can benefit from knowing specific Windows security information about a user. For example, in a recent spyware detection/removal system that I wrote, the code needed to delete certain files and, if those files were in use, mark them for deletion via the Registry. This latter part involved changing certain Registry keys that required that the user be defined in the Administrator group.

This article illustrates how to use the WindowsIdentity and WindowsPrincipal classes to test for a user’s inclusion in a specified security group and how to use the PrincipalPermission class to perform a security check against the active principal.

Determining Role

The .NET security classes enable you to determine both authentication information regarding a user and specific role information (see Figure 1).

Figure 1. User Information Regarding Authentication and Specific Roles

As Figure 1 shows, I am an Administrator on the HOMEOFFICE domain. I determined this programmatically via the WindowsIdentity and WindowsPrincipal classes by taking the following steps:

  1. Include the necessary namespace:
    using namespace System::Security::Principal;
  2. Obtain the WindowsIdentity object associated with the current user:
    WindowsIdentity* identity = WindowsIdentity::GetCurrent();
  3. Create a WindowsPrincipal object based on the WindowsIdentity object. The WindowsPrincipal object contains information regarding the current user’s group membership(s):
    WindowsPrincipal* principal = new WindowsPrincipal(identity);
  4. Call the WindowsPrincipal::IsInRole method, passing it either a string representing the role you are verifying or any of the members of the WindowsBuiltInRole enumeration type:
    bool isAdmin = principal->IsInRole(WindowsBuiltInRole::Administrator);

Using the PrincipalPermissions Object

Another way to check for the inclusion of a user in a security group is by using the PrincipalPermission class, which allows you to perform a security check against the active principal:

  1. Include the necessary namespaces
    using namespace System::Security::Permissions;
    using namespace System::Threading;
    
  2. Call the current domain’s SetPrincipal method, passing to it the desired principal policy. Calling this method dictates how principal and identity objects should be attached to a thread if the thread attempts to bind to a principal. In most cases, you’ll pass the PrincipalPolicy::WindowsPrincipal enumeration member value so that operating system groups are mapped to security roles. Do this in situations where the code is making role-based security demands:
    AppDomain* dom = AppDomain::CurrentDomain;
    dom->SetPrincipalPolicy(PrincipalPolicy::WindowsPrincipal);
    
  3. Obtain the user’s name via the current WindowsIdentity object:
    WindowsIdentity* identity = WindowsIdentity::GetCurrent();
  4. Instantiate a PrincipalPermissions object. When constructing this type, you must pass both the name of the user (the reason for the previous step) and the security group name. (Note that you cannot pass a WindowsBuiltInRole enumeration value, such as WindowsBuiltInRole::Administrator, here.)
    PrincipalPermission* permissions
      = new PrincipalPermission(identity->Name, "Administrators");
    
  5. In a try block, call the PrincipalPermission::Demand method before attempting to call code that is specific to a given user group’s permissions. If the user does not belong in the group specified in the constructor of the PrincipalPermission object, a Security::SecurityException will be thrown. Therefore, placing a call to the Demand method at the top of a try block that then continues to security-specific code enables you to gracefully handle scenarios in which the user doesn’t have the necessary security privileges to run the intended code:
    try
    {
      permissions->Demand();
    
      //... run code that requires the checked-for rights
    }
    catch(Security::SecurityException* ex)
    {
      // ex->Message will contain the exact error message
    }
    

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read