SecurityManaged C++: Determining User Security Roles

Managed C++: Determining User Security Roles

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

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).

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, then 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
    }
    

Testing for Inclusion in One of Multiple Groups

If the code you’re attempting to execute can be executed by someone belonging to any of multiple groups, you can use the PrincipalPermission::Union method to join these groups and then call the PrincipalPermission::Demand method, which will throw an exception only if the user doesn’t belong to any of those groups. Here’s an example of this using code from the previous section. I’ve bolded the changes:

try
{
  AppDomain* dom = AppDomain::CurrentDomain;
  dom->SetPrincipalPolicy(PrincipalPolicy::WindowsPrincipal);

  WindowsIdentity* identity = WindowsIdentity::GetCurrent();

  PrincipalPermission* permissions = new PrincipalPermission(identity->Name, "Administrators");
  PrincipalPermission* permissionsPU = new PrincipalPermission(identity->Name, "PowerUsers");
  permissions->Union(permissionsPU);
  permissions->Demand();

  //... run code that requires the checked-for rights
}
catch(Security::SecurityException* ex)
{
  // ex->Message will contain the exact error message
}

Security for the Rest of Us

Not many of us are security gurus like Keith Brown (my favorite author and trainer on the subject). However, with these past two articles, you can perform some very basic security-rights verification without having to become an expert on Windows security. If you do wish to learn more, I would highly recommend any of Keith’s books on the subject.

About the Author

Tom Archer owns his own training company, Archer Consulting Group, which specializes in educating and mentoring .NET programmers and providing project management consulting. If you would like to find out how the Archer Consulting Group can help you reduce development costs, get your software to market faster, and increase product revenue, contact Tom through his Web site.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories