C# | Search AD entry by ObjectSid using Novell Directory Ldap Nuget package
SID stands for security identifier, a unique string that Windows Server automatically assigns to each computer, user and group in order to mark and clearly distinguish them.
Windows SID Format : SIDs always follow the same structure, with values separated by dashes:
Novell Directory Ldap
Novell Directory Ldap nuget package allows you to connect to the active directory in order to perform CRUD actions on all AD objects, among other Users Groups and computers. it support both platforms windows and linux, so even if you are under kubernetes clusters on docker containers you will be able to manage AD objects.
C# code practical case
This exemple is based on a project that was created based on .Net 6 under Visual studio 2022 and with a docker support to manage Ad Objects within a linux docker container. The nuget package version that was used are the following :
1
<PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="3.6.0" />
When we will process a search within the AD for a dedicated entry we will use a string ObjectSid instead of a bytes value. To do that we will create a helper method that will return the string value based on the input ObjectSid bytes value as bellow :
1. Add Helper method to convert ObjectSID Byte value in string format
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public static string ConvertSidByteValueToStringValue(Byte[] sidBytes)
{
short sSubAuthorityCount = 0;
StringBuilder strSid = new StringBuilder();
strSid.Append("S-");
try
{
// Add SID revision.
strSid.Append(sidBytes[0].ToString());
sSubAuthorityCount = Convert.ToInt16(sidBytes[1]);
// Next six bytes are SID authority value.
if (sidBytes[2] != 0 || sidBytes[3] != 0)
{
string strAuth = String.Format("0x{0:2x}{1:2x}{2:2x}{3:2x}{4:2x}{5:2x}",
(Int16)sidBytes[2],
(Int16)sidBytes[3],
(Int16)sidBytes[4],
(Int16)sidBytes[5],
(Int16)sidBytes[6],
(Int16)sidBytes[7]);
strSid.Append("-");
strSid.Append(strAuth);
}
else
{
Int64 iVal = sidBytes[7] +
(sidBytes[6] << 8) +
(sidBytes[5] << 16) +
(sidBytes[4] << 24);
strSid.Append("-");
strSid.Append(iVal.ToString());
}
// Get sub authority count...
int idxAuth = 0;
for (int i = 0; i < sSubAuthorityCount; i++)
{
idxAuth = 8 + i * 4;
UInt32 iSubAuth = BitConverter.ToUInt32(sidBytes, idxAuth);
strSid.Append("-");
strSid.Append(iSubAuth.ToString());
}
}
catch (Exception ex)
{
Trace.TraceWarning(ex.Message);
throw;
}
return strSid.ToString();
}
This method will get ObjectSid information parts from the array of Bytes input value as bellow :
1
2
3
4
byte[0] - Revision Level
byte[1] - count of Sub-Authorities
byte[2 - 7] - 48 bit Authority(big-endian)
byte[8 +] - n Sub-Authorities, 32 bits
And after that we return a String ObjectSID on the format bellow :
1
S-{Revision}-{Authority}-{ SubAuthority1}-{ SubAuthority2}...-{ SubAuthorityN}
2. Call Active Directory to retrieve User informations based on objectSid string value
Adding Novell directive
1
2
using Novell.Directory.Ldap;
.....
Adding the search method to get the User entry object
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/// Exemple of Dn Value : OU=User,OU=Accounts,DC=USA,DC=NY
/// Exemple of objectSidStringValue Value : S-1-5-15-420314761-778715008-4547327-1845947
/// Exemple of objectCategory Value : User or Group or Computer
/// Exemple of OutputProps Values : "description","lastLogon","email","name", "login"
public List<Attributes<string, string>>? GetLdapEntryByObjectSid(string Dn, string objectSidStringValue, string objectCategory, string[] OutputProps)
{
var ldapConnectionOptions = new LdapConnectionOptions();
ldapConnectionOptions.UseSsl();
var ldapConx = new LdapConnection(ldapConnectionOptions);
List<Attributes<string, string>> listAttributes = new List<Attributes<string, string>>();
string Filter = $"(&(objectSid={objectSidStringValue})(objectCategory={objectCategory}))";
if (!string.IsNullOrEmpty(Filter))
{
var searchQuery = ldapConx.Search(Dn, Novell.Directory.Ldap.LdapConnection.ScopeSub, Filter, OutputProps, false);
if (searchQuery != null)
{
while (searchQuery.HasMore())
{
LdapEntry nextEntry = null;
nextEntry = searchQuery.Next();
//SID Case
if (OutputProps.Contains("objectSid"))
{
//Get Sid Property Bytes value to be converted into string clear value
var objectSid = nextEntry.GetAttributeSet().FirstOrDefault(e => e.Key.Equals("objectSid"));
if (objectSid.Value != null)
{
var sidStringFormat = ConvertSidByteValueToStringValue(objectSid.Value.ByteValue);
listAttributes.Add(new Attributes<string, string>(objectSid.Key, sidStringFormat));
}
}
listAttributes.AddRange(nextEntry.GetAttributeSet()
.Where(e => !e.Key.Equals("objectSid"))
.Select(e => new Attributes<string, string>(e.Key, e.Value.StringValue))
.ToList());
}
}
}
return listAttributes;
}
The GetLdapEntryByObjectSid method will process a searchQuery within ldap based on the objectSid filter and also the objectCategory in this example User filter. As mentioned the objectSid within Active directory are persisted as a byte so we will call our Helper method to convert it into a string value:
1
var sidStringFormat = ConvertSidByteValueToStringValue(objectSid.Value.ByteValue);
The returned list of properties will contains all properties as string readable values, so we can use them also to process a new search based on other property.
Links : Novell Directory - GitHub repository Novell Directory - Nuget package