Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine User Active Directory Groups from Local Machine off Network

My current project requires that I validate a user against Active Directory groups. The catch is, the computer may not always be connected to the domain but users may still need to run the tool. I understand that I can't query Active Directory while I'm not connected, instead I'm trying to query the Machine SAM (MSAM).

I am having trouble determining the current user while I'm disconnected from the network. Here's what I'm using:

PrincipalContext principalctx = new PrincipalContext(ContextType.Machine);
UserPrincipal uprincipal = new UserPrincipal(principalctx);

From this point, how do I ask who is currently logged into the local machine.

When connected to the domain, I can query using UserPrincipal.Current. If I am not connected to the domain it will fail saying "The server could not be contacted". Note: this method is not available using the above code, instead I can forgo the PrincipalContext and directly query the current user. With the current user identified I can query GetGroups() and determine if they're in one of the required groups.

Also, can someone please describe the three ContextType options ApplicationDirectory, Domain, Machine. I'm afraid I don't fully understand each option and therefore may be using it incorrectly.

like image 665
StinkerPeter Avatar asked Jul 16 '13 21:07

StinkerPeter


1 Answers

From the top:

My current project requires that I validate a user against Active Directory groups. The catch is, the computer may not always be connected to the domain but users may still need to run the tool.

At this point, you must therefore accept that any enforced security is able to be bypassed by an attacker since it is entirely enforced on the client. Not exactly part of the solution, but keep it in mind.

I understand that I can't query Active Directory while I'm not connected, instead I'm trying to query the Machine SAM (MSAM).

The Security Accounts Manager only stores the local accounts (MACHINENAME\Administrator and others). It will not have domain user credentials. You are thinking of the LSA cache, which remembers the last N domain logins's credentials (where N is a number from 0 to 50 as configured by Group Policy) and the last N SID to name mappings (defaults to 128, configurable via registry). The Security Accounts Manager only stores domain accounts on a domain controller.

I am having trouble determining the current user while I'm disconnected from the network. Here's what I'm using: PrincipalContext principalctx = new PrincipalContext(ContextType.Machine); UserPrincipal uprincipal = new UserPrincipal(principalctx);

Also, can someone please describe the three ContextType options ApplicationDirectory, Domain, Machine. I'm afraid I don't fully understand each option and therefore may be using it incorrectly.

As noted above, information is not cached, but the ContextType enum can be described as follows:

From MSDN:

  • Domain: The domain store. This represents the AD DS store. (As it says, this is for domain accounts, as in LDAP Directory access of Active Directory - which would require network access)
  • ApplicationDirectory: The application directory store. This represents the AD LDS store. (AD Lightweight Directory Services (previously known as ADAM) is a smaller version of Active Directory designed to store credentials for a single application. It is not relevant to this discussion, but also uses LDAP.)
  • Machine: The computer store. This represents the SAM store. (Enumerates local accounts only)

From this point, how do I ask who is currently logged into the local machine.

You can always examine the logged on user by calling WindowsIdentity.GetCurrent(). This will return the logged in user's SID and Group SIDs, possibly cached, if the login occurred while offline.

When connected to the domain, I can query using UserPrincipal.Current. If I am not connected to the domain it will fail saying "The server could not be contacted". Note: this method is not available using the above code, instead I can forgo the PrincipalContext and directly query the current user. With the current user identified I can query GetGroups() and determine if they're in one of the required groups.

To determine group membership, check if the group's SID you want is in the identity returned by WindowsIdentity.GetCurrent. If you are not using the SID in your Access Control system, you can translate a name to an SID by calling SecurityIdentifier.Translate. You will need to translate it while online, then cache it for use offline. It is storable as either a string or as binary, so both fit well in the registry.

// while we are online, translate the Group to SID
// Obviously, administrators would be a bad example as it is a well known SID...
var admins = new NTAccount("Administrators");
var adminSid = (SecurityIdentifier)admins.Translate(typeof(SecurityIdentifier));

// store the sid as a byte array on disk somewhere 
byte[] adminSIDbytes = new byte[adminSid.BinaryLength];
adminSid.GetBinaryForm(adminSIDbytes, 0);

// at time of check, retrieve the sid and check membership
var sidToCheck = new SecurityIdentifier(adminSIDbytes, 0);
if (!wi.Groups.Contains(sidToCheck))
    throw new UnauthorizedAccessException("User is not a member of required group");
like image 120
Mitch Avatar answered Oct 29 '22 08:10

Mitch