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: March 19, 2015 @ 1:00 p.m. ET / 10:00 a.m. PT The 2015 Enterprise Mobile Application Survey asked 250 mobility professionals what their biggest mobile challenges are, how many employees they are equipping with mobile apps, and their methods for driving value with mobility. Join Dan Woods, Editor and CTO of CITO Research, and Alan Murray, SVP of Products at Apperian, as they break down the results of this survey and discuss how enterprises are using mobile application management and private …

  • On-demand Event Event Date: February 12, 2015 The evolution of systems engineering with the SysML modeling language has resulted in improved requirements specification, better architectural definition, and better hand-off to downstream engineering. Agile methods have proven successful in the software domain, but how can these methods be applied to systems engineering? Check out this webcast and join Bruce Powel Douglass, author of Real-Time Agility, as he discusses how agile methods have had a tremendous …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date