Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Active Directory Group Membership Checking in .Net 4.5

I have an ASP.Net MVC application using Windows Authentication, and I am checking group membership for security on controller actions.

Simple as it sounds, I've found no other Question that can resolve the problem I am experiencing.

First Attempt: [Authorize]

The classic method is to simply slap an Authorize data annotation attribute on the controller action and go to town:

[Authorize(Roles = @"domain\groupName1")]

No dice. I am prompted for credentials. Usually this means something is wrong with the Windows Authentication configuration but it's setup fine: (1) HttpContext.User is a WindowsPrincipal object, and (2) I confirmed another known group name works.

Second Attempt: IsInRole()

The next step taken was to go a more old fashioned route and use IPrincipal.IsInRole(), and again, one returns false, the other true.

var wp = (WindowsPrincipal)User;

// false
var inGroup1 = wp.IsInRole(@"domain\groupName1");
// true
var inGroup2 = wp.IsInRole(@"domain\groupName2");

Stumped... so I hit up my systems nerds and we double check everything. User is a group member? Yes. Group name is spelled correctly? Yes. The next step was to snag the SID.

Third Attempt: Search Identity's Group Collection

In my controller I check the WindowsIdentity and look through the group collection for the SID of the troublesome group:

var wi = (WindowsIdentity)wp.Identity;
var group = wi.Groups.SingleOrDefault(g => g.Value == "group1-sidValue");

The group variable is the SecurityIdentifier object. Because it is not null, we can be certain that this current user is a member of the group that both the [Authorize()] or IsInRole() attempts fail to confirm.

Fourth Attempt: DirectoryServices.AccountManagement

At this point, I'm going nuts and add reference to the AccountManagement APIs. I search the domain context for the GroupPrincipal by both name and SID:

   var pc = new PrincipalContext(ContextType.Domain, "domain");
   var gp1byName = GroupPrincipal.FindByIdentity(pc, "groupName1")
   var gp1bySid = GroupPrincipal.FindByIdentity(pc, IdentityType.Sid, "group1-sidValue");

Both group principal variables are ripe with the same object, and I verified through a watch variable that the principal's Members collection contains a UserPrincipal object with the same SID as the current WindowsPrincipal on HttpContext.

Question:

What in the hell have I missed here? Why would both role checking methodologies fail when it is plain and clear through object exploration that the user is a valid member of this given group?

The fact that one group checks fine and the other does not seems the most strange part at this point.

like image 300
one.beat.consumer Avatar asked Oct 30 '12 20:10

one.beat.consumer


1 Answers

Answer:

Essentially it's translation issues between WindowsIdentity and NTAccount (both of these System.Security.Principal) and lastly, the actual Active Directory entry.

When validating a WindowsIdentity against AD, if you want to use anything other than the Sam or the Sid, you will need to use System.DirectoryServices.AccountManagement.

Caveat: In .Net 4.5 the security principals include Claims but that's out of context.


Long Explanation:

In a Windows Authenticated web application, HttpContext.User is a WindowsPrincipal object wrapping an underlying WindowsIdentity.

WindowsIdentity has for most intents and purposes only two properties with which the authenticated user can be identified: Name and User.

These properties translate to two properties on the identity's corresponding AD account entry:

WindowsIdentity.Name = SamAccountName

WindowsIdentity.User = SID

The [Authorize] filter attribute ultimately calls IsInRole(string role) on the underlying principal... and the IsInRole() string overload instantiates an NTAccount with the role (the "SamAccountName" in an AD entry).

This explains the failure in #1 and #2 above.

To authorize the HttpContext.User against anything but his/her Sid or SamAccountName, you'll need DirectoryServices.AccountManagement or classic LDAP.

like image 167
one.beat.consumer Avatar answered Oct 03 '22 06:10

one.beat.consumer