Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

IDirectoryObject Refactor #135

Merged
merged 16 commits into from
Jul 22, 2024
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 9 additions & 10 deletions src/CommonLib/ConnectionPoolManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,23 @@
namespace SharpHoundCommonLib {
public class ConnectionPoolManager : IDisposable{
private readonly ConcurrentDictionary<string, LdapConnectionPool> _pools = new();
private readonly LDAPConfig _ldapConfig;
private readonly LdapConfig _ldapConfig;
private readonly string[] _translateNames = { "Administrator", "admin" };
private readonly ConcurrentDictionary<string, string> _resolvedIdentifiers = new(StringComparer.OrdinalIgnoreCase);
private readonly ILogger _log;
private readonly PortScanner _portScanner;

public ConnectionPoolManager(LDAPConfig config, ILogger log = null, PortScanner scanner = null) {
public ConnectionPoolManager(LdapConfig config, ILogger log = null, PortScanner scanner = null) {
_ldapConfig = config;
_log = log ?? Logging.LogProvider.CreateLogger("ConnectionPoolManager");
_portScanner = scanner ?? new PortScanner();
}

public void ReleaseConnection(LdapConnectionWrapper connectionWrapper, bool connectionFaulted = false) {
//I dont think this is possible, but at least account for it
if (connectionWrapper == null) {
return;
}
//I don't think this is possible, but at least account for it
if (!_pools.TryGetValue(connectionWrapper.PoolIdentifier, out var pool)) {
_log.LogWarning("Could not find pool for {Identifier}", connectionWrapper.PoolIdentifier);
connectionWrapper.Connection.Dispose();
Expand Down Expand Up @@ -84,11 +87,8 @@ private bool GetDomainSidFromDomainName(string domainName, out string domainSid)
if (Cache.GetDomainSidMapping(domainName, out domainSid)) return true;

try {
var entry = new DirectoryEntry($"LDAP://{domainName}");
//Force load objectsid into the object cache
entry.RefreshCache(new[] { "objectSid" });
var sid = entry.GetSid();
if (sid != null) {
var entry = new DirectoryEntry($"LDAP://{domainName}").ToDirectoryObject();
if (entry.TryGetSecurityIdentifier(out var sid)) {
Cache.AddDomainSidMapping(domainName, sid);
domainSid = sid;
return true;
Expand All @@ -100,8 +100,7 @@ private bool GetDomainSidFromDomainName(string domainName, out string domainSid)

if (LdapUtils.GetDomain(domainName, _ldapConfig, out var domainObject))
try {
domainSid = domainObject.GetDirectoryEntry().GetSid();
if (domainSid != null) {
if (domainObject.GetDirectoryEntry().ToDirectoryObject().TryGetSecurityIdentifier(out domainSid)) {
Cache.AddDomainSidMapping(domainName, domainSid);
return true;
}
Expand Down
134 changes: 0 additions & 134 deletions src/CommonLib/DirectoryEntryExtensions.cs

This file was deleted.

188 changes: 188 additions & 0 deletions src/CommonLib/DirectoryObjects/DirectoryEntryWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
using System;
using System.Collections.Generic;
using System.DirectoryServices;
using System.Security.Cryptography.X509Certificates;
using System.Security.Principal;
using System.Text;

namespace SharpHoundCommonLib;

public class DirectoryEntryWrapper : IDirectoryObject {
private readonly DirectoryEntry _entry;

public DirectoryEntryWrapper(DirectoryEntry entry) {
_entry = entry;
}

public bool TryGetDistinguishedName(out string value) {
return TryGetProperty(LDAPProperties.DistinguishedName, out value);
}

private bool CheckCache(string propertyName) {
try {
if (!_entry.Properties.Contains(propertyName))
_entry.RefreshCache(new[] { propertyName });

return _entry.Properties.Contains(propertyName);
}
catch {
return false;
}
}

public bool TryGetProperty(string propertyName, out string value) {
value = string.Empty;
if (!CheckCache(propertyName)) {
return false;
}

var s = _entry.Properties[propertyName].Value;
value = s switch {
string st => st,
int i => i.ToString(),
_ => null
};

return value != null;
}

public bool TryGetByteProperty(string propertyName, out byte[] value) {
value = Array.Empty<byte>();
if (!CheckCache(propertyName)) {
return false;
}

var prop = _entry.Properties[propertyName].Value;
if (prop is not byte[] b) return false;
value = b;
return true;
}

public bool TryGetArrayProperty(string propertyName, out string[] value) {
value = Array.Empty<string>();
if (!CheckCache(propertyName)) {
return false;
}

var dest = new List<string>();
foreach (var val in _entry.Properties[propertyName]) {
if (val is string s) {
dest.Add(s);
}
}
rvazarkar marked this conversation as resolved.
Show resolved Hide resolved

value = dest.ToArray();
return true;
}

public bool TryGetByteArrayProperty(string propertyName, out byte[][] value) {
value = Array.Empty<byte[]>();
if (!CheckCache(propertyName)) {
return false;
}

var raw = _entry.Properties[propertyName].Value;
if (raw is not byte[][] b) {
return false;
}
value = b;
return true;
}

public bool TryGetIntProperty(string propertyName, out int value) {
value = 0;
if (!CheckCache(propertyName)) return false;
rvazarkar marked this conversation as resolved.
Show resolved Hide resolved

if (!TryGetProperty(propertyName, out var s)) {
return false;
}

return int.TryParse(s, out value);
}

public bool TryGetCertificateArrayProperty(string propertyName, out X509Certificate2[] value) {
value = Array.Empty<X509Certificate2>();
if (!TryGetByteArrayProperty(propertyName, out var bytes)) {
return false;
}

if (bytes.Length == 0) {
return true;
}

var result = new List<X509Certificate2>();

foreach (var b in bytes) {
try {
var cert = new X509Certificate2(b);
result.Add(cert);
}
catch {
//pass
}
}

value = result.ToArray();
return true;
}

public bool TryGetSecurityIdentifier(out string securityIdentifier) {
securityIdentifier = string.Empty;
if (!CheckCache(LDAPProperties.ObjectSID)) {
return false;
}

var raw = _entry.Properties[LDAPProperties.ObjectSID][0];
try {
securityIdentifier = raw switch {
byte[] b => new SecurityIdentifier(b, 0).ToString(),
string st => new SecurityIdentifier(Encoding.ASCII.GetBytes(st), 0).ToString(),
_ => default
};

return securityIdentifier != default;
}
catch {
return false;
}
}

public bool TryGetGuid(out string guid) {
guid = string.Empty;
if (!TryGetByteProperty(LDAPProperties.ObjectGUID, out var raw)) {
return false;
}

try {
guid = new Guid(raw).ToString().ToUpper();
return true;
} catch {
return false;
}
}

public string GetProperty(string propertyName) {
CheckCache(propertyName);
return _entry.Properties[propertyName].Value as string;
}

public byte[] GetByteProperty(string propertyName) {
CheckCache(propertyName);
return _entry.Properties[propertyName].Value as byte[];
}

public int PropertyCount(string propertyName) {
if (!CheckCache(propertyName)) {
return 0;
}

var prop = _entry.Properties[propertyName];
return prop.Count;

}

public IEnumerable<string> PropertyNames() {
foreach (var property in _entry.Properties.PropertyNames)
yield return property.ToString().ToLower();
}
}
Loading
Loading