LDAP query for Active Directory User's Primary Group
When querying AD (Active Directory) with LDAP (Light-weight Directory Access Protocol), it is possible to easily retrieve a lot of attribute information about a user. However, it is not obvious or straight-forward to understand how to find out the Primary Group that a user belongs to. Whilst you can get a list of groups from the 'memberOf' attribute on the 'user' object class, or even find groups by their members through the 'member' attribute of the 'group' object class, these lists do not include information about the User's Primary Group.
The 'user' Class, does provide a 'primaryGroupID' attribute, however the 'group' class does not contain a matching attribute that you can directly search against. However there is a trick! After quite some Googling and pulling information together from various sources, I have managed to achieve this, and to make it easier for everyone else, I am publishing a complete example.
PS. If you need to find a good LDAP querying and browse tool for examining the objects in AD LDAP, then I found Apache Directory Studio to be absolutely fantastic.
Basically, every object class in AD LDAP has an 'objectSID' identifier. This SID (Security Identifier) is a binary identifier provided as a byte array.
The binary expression of a SID has the following format:
byte[0] - Revision Level byte[1] - count of Sub-Authorities byte[2-7] - 48 bit Authority (big-endian) ...and then n Sub-Authorities, 32 bits each (little-endian) e.g. - [1,5,0,0,0,0,0,5,21,0,0,0,37,-20,73,58,97,-107,0,-80,109,-55,112,10,47,-24,5,0]
The last sub-authority of a SID is known as the RID (Relative Identifier), and it is this RID that differentiates objects from within the same domain. This basically means that by replacing the RID in an SID you can generate the SID for a different object. The 'primaryGroupID' attribute from the 'user' class is a RID. So, we can take the SID of the user, and replace the RID part with the primary group id, we can then lookup the group in LDAP using this SID as the key.
A binary SID can be decoded into a string, which is both easier to understand and can also be used for subsequent queries within AD LDAP. The specifics of the SID string format can be found here.
The string expression of a SID has the following format:
“S-{Revision}-{Authority}-{SubAuthority1}-{SubAuthority2}...-{SubAuthorityN}” e.g. - “S-1-5-21-977923109-2952828257-175163757-387119”
I based my code for decoding a binary expression of a SID into a string expression on the code found here but I have tried to simplify and improve on the approach.
Java Code for decoding Binary a SID into a String SID:
/** * The binary data is in the form: * byte[0] - revision level * byte[1] - count of sub-authorities * byte[2-7] - 48 bit authority (big-endian) * and then count x 32 bit sub authorities (little-endian) * * The String value is: S-Revision-Authority-SubAuthority[n]... * * Based on code from here - http://forums.oracle.com/forums/thread.jspa?threadID=1155740&tstart=0 */ public static String decodeSID(byte[] sid) { final StringBuilder strSid = new StringBuilder("S-"); // get version final int revision = sid[0]; strSid.append(Integer.toString(revision)); //next byte is the count of sub-authorities final int countSubAuths = sid[1] & 0xFF; //get the authority long authority = 0; //String rid = ""; for(int i = 2; i <= 7; i++) { authority |= ((long)sid[i]) << (8 * (5 - (i - 2))); } strSid.append("-"); strSid.append(Long.toHexString(authority)); //iterate all the sub-auths int offset = 8; int size = 4; //4 bytes for each sub auth for(int j = 0; j < countSubAuths; j++) { long subAuthority = 0; for(int k = 0; k < size; k++) { subAuthority |= (long)(sid[offset + k] & 0xFF) << (8 * k); } strSid.append("-"); strSid.append(subAuthority); offset += size; } return strSid.toString(); }
A complete Java example complete with LDAP query code is available here LDAPTest.java.
Adam Retter posted on Friday, 1st July 2011 at 22.57 (GMT+02:00)
Updated: Friday, 1st 2011 at July 22.57 (GMT+02:00)
Comments (1)
Posted by Jerome Anthony on Tuesday, 20th September 2011 at 06.51 (GMT+01:00)