Monday, July 30, 2007

Finding an Active Directory User by Their Security Identitfier (SID)

Problem: You need to store a unique identifier for a Windows user in your application database that is not subject to change. Their NT Login Name is potentially volatile, so you choose to store the NT Security Identifier, the SID. You need to look up certain details per user in the Directory when the user logs into your application, rather than storing duplicate data in your membership table(s). Since you are only storing the SID, you must perform this lookup by obtaining the SID of the authenticated principal and looking it up in your membership table(s).

Sounds fairly straightforward, until you discover and/or realize that security identifiers for Active Directory user objects are not stored using the Security Descriptor Definition Language format (i.e. SecurityIdentifier.ToString(), e.g. "S-1-5-9-..."). The SID of a Windows principal is stored as a binary value in the directory. Constructing a LDAP filter to find the appropriate user's directory entry means understanding:

  • how the LDAP specification represents binary values as strings,

  • which property of a user directory entry contains the Windows SID

  • how to obtain the binary representation of SID from a SecurityIdentifier object

  • how to apply string formatting codes to a byte value in order to obtain a hex string

Fortunately, dear reader, I have done all the leg work on this one. This article is a continuation of a loosely-coupled series of articles giving you the know-how required, without specifying exactly how, to build a dual-mode authentication (Windows/Forms Auth) membership architecture for your ASP.NET applications.

First off, the RFC says that LDAP formats binary value in filter strings by delimiting each byte with a backslash ("\") character and writing the hexadecimal value of the byte. So, we need to:

  1. obtain the binary value of the SID

  2. convert each byte to this string format

  3. append it to our LDAP filter

//(1) assuming a SecurityIdentifer sid;
byte[] sidBytes = new byte[sid.BinaryLength];
sid.GetBinaryForm(sidBytes, 0);

//(2) assuming a StringBuilder sb;
foreach(byte b in sidBytes)
//formats a byte using a two-digit hex value representation

//(3) to be used in an LDAP query
String.Format("(&(objectClass="user")(objectSid="{0}"))", sb.ToString());

I hope this helps!