I translated the following methods from Java, for use in C#, over a year ago (sometime in early 2022, or late 2021). Maybe there's better versions of the same elsewhere; I dunno. If this is the best that you - o weary traveller of the internet - could find, then I hope you find them useful for your purposes.
You know SQL injection attacks? Well, there's LDAP injection attacks, too.
The following method escapes values which you're tossing into LDAP search strings.
Example usage: var filter = $"(sAMAccountName={EncodeFilterValue(username)})";
// from https://github.com/apache/directory-ldap-api/blob/master/ldap/model/src/main/java/org/apache/directory/api/ldap/model/filter/FilterEncoder.java
// licensed under https://www.apache.org/licenses/LICENSE-2.0
public static string EncodeFilterValue(string value)
{
var sb = new StringBuilder();
var escaped = false;
var hexPair = false;
var hex = '\0';
foreach (var c in value)
{
switch (c)
{
case '*':
if (escaped)
{
sb.Append("\\5C");
if (hexPair)
{
sb.Append(hex);
hexPair = false;
}
escaped = false;
}
sb.Append("\\2A");
break;
case '(':
if (escaped)
{
sb.Append("\\5C");
if (hexPair)
{
sb.Append(hex);
hexPair = false;
}
escaped = false;
}
sb.Append("\\28");
break;
case ')':
if (escaped)
{
sb.Append("\\5C");
if (hexPair)
{
sb.Append(hex);
hexPair = false;
}
escaped = false;
}
sb.Append("\\29");
break;
case '\0':
if (escaped)
{
sb.Append("\\5C");
if (hexPair)
{
sb.Append(hex);
hexPair = false;
}
escaped = false;
}
sb.Append("\\00");
break;
case '\\':
if (escaped)
{
sb.Append("\\5C");
escaped = false;
}
else
{
escaped = true;
hexPair = false;
}
break;
case >= '0' and <= '9':
case >= 'a' and <= 'f':
case >= 'A' and <= 'F':
if (escaped)
{
if (hexPair)
{
sb.Append('\\').Append(hex).Append(c);
escaped = false;
hexPair = false;
}
else
{
hexPair = true;
hex = c;
}
}
else
{
sb.Append(c);
}
break;
default:
if (escaped)
{
sb.Append("\\5C");
if (hexPair)
{
sb.Append(hex);
hexPair = false;
}
escaped = false;
}
sb.Append(c);
break;
}
}
if (escaped)
{
sb.Append("\\5C");
}
return sb.ToString();
}
Again: you must be wary of LDAP injection attacks. Never trust user input.
// from https://github.com/apache/directory-ldap-api/blob/master/ldap/model/src/main/java/org/apache/directory/api/ldap/model/name/Rdn.java
// licensed under https://www.apache.org/licenses/LICENSE-2.0
public static string EncodeDistinguishedName(string value)
{
var sb = new StringBuilder();
for (var i = 0; i < value.Length; i++)
{
var c = value[i];
switch (c)
{
case ' ':
if (i > 0 && i < value.Length - 1)
{
sb.Append(c);
}
else
{
sb.Append('\\');
sb.Append(c);
}
break;
case '#':
if (i != 0)
sb.Append(c);
else
{
sb.Append('\\');
sb.Append(c);
}
break;
case '"':
case '+':
case ',':
case ';':
case '=':
case '<':
case '>':
case '\\':
sb.Append('\\');
sb.Append(c);
break;
case (char)0x7F:
sb.Append("\\7F");
break;
case >= (char)0x00 and <= (char)0x0F:
sb.Append("\\0");
sb.Append(HEX_CHAR[c & 0x0F]);
break;
case >= (char)0x10 and <= (char)0x1F:
sb.Append("\\1");
sb.Append(HEX_CHAR[c & 0x0F]);
break;
default:
sb.Append(c);
break;
}
}
return sb.ToString();
}
private static readonly char[] HEX_CHAR = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
You may sometimes ask a library for a SID, and get a byte[]
, but you're probably used to thinking of SIDs as string
s. This method turns a byte[]
SID into a string
.
//From https://ldapwiki.com/wiki/ObjectSID
public static string DecodeSid(byte[] sid)
{
string result = "S-";
// byte(0) - revision level
result += sid[0].ToString();
// byte(1) - count of sub-authorities
int countSubAuths = sid[1] & 0xFF;
// byte(2-7) - 48 bit authority
long authority = 0;
for (int i = 2; i <= 7; i++)
{
authority |= (long)sid[i] << 8 * (5 - (i - 2));
}
result += $"-{authority:X}";
// iterate all the sub-auths and then countSubAuths x 32 bit sub authorities
int offset = 8;
int size = 4;
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;
}
result += $"-{subAuthority}";
offset += size;
}
return result;
}
256 is the MS-stated/official max length for a SID; in practice they never get longer than 184 characters.
If you're storing SIDs in a DB, play it safe and just give 'em 256 characters. Allowing +72 characters doesn't actually cost you anything, and MS says SIDs can be longer. (Maybe Windows 12, or whatever, will introduce longer SIDs. Who knows.)