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
    }
    

Managed C++: Determining User Security Roles

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 - MSFT

I am a Program Manager and Content Strategist for the Microsoft MSDN Online team managing the Windows Vista and Visual C++ developer centers. Before being employed at Microsoft, I was awarded MVP status for the Visual C++ product. A 20+ year veteran of programming with various languages - C++, C, Assembler, RPG III/400, PL/I, etc. - I've also written many technical books (Inside C#, Extending MFC Applications with the .NET Framework, Visual C++.NET Bible, etc.) and 100+ online articles.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: August 20, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT When you look at natural user interfaces as a developer, it isn't just fun and games. There are some very serious, real-world usage models of how things can help make the world a better place – things like Intel® RealSense™ technology. Check out this upcoming eSeminar and join the panel of experts, both from inside and outside of Intel, as they discuss how natural user interfaces will likely be getting adopted in a wide variety …

  • Event Date: April 15, 2014 The ability to effectively set sales goals, assign quotas and territories, bring new people on board and quickly make adjustments to the sales force is often crucial to success--and to the field experience! But for sales operations leaders, managing the administrative processes, systems, data and various departments to get it all right can often be difficult, inefficient and manually intensive. Register for this webinar and learn how you can: Align sales goals, quotas and …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds