DEV Community

Hassan BOLAJRAF
Hassan BOLAJRAF

Posted on

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:

Image description

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 :

<PackageReference Include="Novell.Directory.Ldap.NETStandard" Version="3.6.0" />
Enter fullscreen mode Exit fullscreen mode

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

 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();
        }
Enter fullscreen mode Exit fullscreen mode

This method will get ObjectSid information parts from the array of Bytes input value as bellow :

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 
Enter fullscreen mode Exit fullscreen mode

And after that we return a String ObjectSID on the format bellow :

S-{Revision}-{Authority}-{ SubAuthority1}-{ SubAuthority2}...-{ SubAuthorityN}
Enter fullscreen mode Exit fullscreen mode

2. Call Active Directory to retrieve User informations based on objectSid string value

Adding Novell directive

using Novell.Directory.Ldap;
.....
Enter fullscreen mode Exit fullscreen mode

Adding the search method to get the User entry object

/// 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;
        }

Enter fullscreen mode Exit fullscreen mode

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:

 var sidStringFormat = ConvertSidByteValueToStringValue(objectSid.Value.ByteValue);
Enter fullscreen mode Exit fullscreen mode

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

Top comments (0)