diff --git a/src/CommonLib/DCConnectionCache.cs b/src/CommonLib/DCConnectionCache.cs deleted file mode 100644 index 36e1e82b..00000000 --- a/src/CommonLib/DCConnectionCache.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System.Collections.Concurrent; -using System.DirectoryServices.Protocols; - -namespace SharpHoundCommonLib -{ - public class DCConnectionCache - { - private readonly ConcurrentDictionary _ldapConnectionCache; - - public DCConnectionCache() - { - _ldapConnectionCache = new ConcurrentDictionary(); - } - - public bool TryGet(string key, bool isGlobalCatalog, out LdapConnectionWrapperNew connection) - { - var cacheKey = GetKey(key, isGlobalCatalog); - return _ldapConnectionCache.TryGetValue(cacheKey, out connection); - } - - public LdapConnectionWrapperNew AddOrUpdate(string key, bool isGlobalCatalog, LdapConnectionWrapperNew connection) - { - var cacheKey = GetKey(key, isGlobalCatalog); - return _ldapConnectionCache.AddOrUpdate(cacheKey, connection, (_, existingConnection) => - { - existingConnection.Connection.Dispose(); - return connection; - }); - } - - public LdapConnectionWrapperNew TryAdd(string key, bool isGlobalCatalog, LdapConnectionWrapperNew connection) - { - var cacheKey = GetKey(key, isGlobalCatalog); - return _ldapConnectionCache.AddOrUpdate(cacheKey, connection, (_, existingConnection) => - { - connection.Connection.Dispose(); - return existingConnection; - }); - } - - private LDAPConnectionCacheKey GetKey(string key, bool isGlobalCatalog) - { - return new LDAPConnectionCacheKey(key.ToUpper().Trim(), isGlobalCatalog); - } - - private class LDAPConnectionCacheKey - { - public string Domain { get; } - public bool GlobalCatalog { get; } - - public LDAPConnectionCacheKey(string domain, bool globalCatalog) - { - GlobalCatalog = globalCatalog; - Domain = domain; - } - - protected bool Equals(LDAPConnectionCacheKey other) - { - return GlobalCatalog == other.GlobalCatalog && Domain == other.Domain; - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((LDAPConnectionCacheKey)obj); - } - - public override int GetHashCode() - { - unchecked - { - return (GlobalCatalog.GetHashCode() * 397) ^ (Domain != null ? Domain.GetHashCode() : 0); - } - } - } - } -} \ No newline at end of file diff --git a/src/CommonLib/ILdapUtilsNew.cs b/src/CommonLib/ILdapUtilsNew.cs index de90c441..8bb57820 100644 --- a/src/CommonLib/ILdapUtilsNew.cs +++ b/src/CommonLib/ILdapUtilsNew.cs @@ -42,5 +42,7 @@ IAsyncEnumerable> RangedRetrieval(string distinguishedName, public Task IsDomainController(string computerObjectId, string domainName); public Task<(bool Success, TypedPrincipal Principal)> LookupDistinguishedName(string distinguishedName); + public void AddDomainController(string domainControllerSID); + IAsyncEnumerable GetWellKnownPrincipalOutput(); } } \ No newline at end of file diff --git a/src/CommonLib/LdapUtilsNew.cs b/src/CommonLib/LdapUtilsNew.cs index 54f2ea81..0132207d 100644 --- a/src/CommonLib/LdapUtilsNew.cs +++ b/src/CommonLib/LdapUtilsNew.cs @@ -29,6 +29,7 @@ public class LdapUtilsNew : ILdapUtilsNew { //This cache is indexed by domain sid private readonly ConcurrentDictionary _dcInfoCache = new(); private static readonly ConcurrentDictionary DomainCache = new(); + private static readonly ConcurrentDictionary DomainControllers = new(); private static readonly ConcurrentDictionary DomainToForestCache = new(StringComparer.OrdinalIgnoreCase); @@ -165,6 +166,7 @@ await _connectionPool.GetLdapConnection(domain, _log.LogError( "RangedRetrieval - Failed to get a new connection after ServerDown for path {Path}", distinguishedName); + _connectionPool.ReleaseConnection(connectionWrapper); tempResult = Result.Fail( "RangedRetrieval - Failed to get a new connection after ServerDown."); @@ -172,15 +174,23 @@ await _connectionPool.GetLdapConnection(domain, } } catch (LdapException le) { + if (le.ErrorCode is (int)LdapErrorCodes.ServerDown) { + _connectionPool.ReleaseConnection(connectionWrapper, true); + } + else { + _connectionPool.ReleaseConnection(connectionWrapper); + } tempResult = Result.Fail( $"Caught unrecoverable ldap exception: {le.Message} (ServerMessage: {le.ServerErrorMessage}) (ErrorCode: {le.ErrorCode})"); } catch (Exception e) { + _connectionPool.ReleaseConnection(connectionWrapper); tempResult = Result.Fail($"Caught unrecoverable exception: {e.Message}"); } //If we have a tempResult set it means we hit an error we couldn't recover from, so yield that result and then break out of the function + //We handle connection release in the relevant exception blocks if (tempResult != null) { yield return tempResult; yield break; @@ -201,6 +211,7 @@ await _connectionPool.GetLdapConnection(domain, } if (complete) { + _connectionPool.ReleaseConnection(connectionWrapper); yield break; } @@ -210,6 +221,7 @@ await _connectionPool.GetLdapConnection(domain, } else { //I dont know what can cause a RR to have multiple entries, but its nothing good. Break out + _connectionPool.ReleaseConnection(connectionWrapper); yield break; } } @@ -1382,5 +1394,38 @@ public async Task IsDomainController(string computerObjectId, string domai } } } + + public void AddDomainController(string domainControllerSID) + { + DomainControllers.TryAdd(domainControllerSID, new byte()); + } + + public async IAsyncEnumerable GetWellKnownPrincipalOutput() { + foreach (var wkp in SeenWellKnownPrincipals) + { + WellKnownPrincipal.GetWellKnownPrincipal(wkp.Value.WkpId, out var principal); + OutputBase output = principal.ObjectType switch + { + Label.User => new User(), + Label.Computer => new Computer(), + Label.Group => new OutputTypes.Group(), + Label.GPO => new GPO(), + Label.Domain => new OutputTypes.Domain(), + Label.OU => new OU(), + Label.Container => new Container(), + Label.Configuration => new Container(), + _ => throw new ArgumentOutOfRangeException() + }; + + output.Properties.Add("name", $"{principal.ObjectIdentifier}@{wkp.Value.DomainName}".ToUpper()); + if (await GetDomainSidFromDomainName(wkp.Value.DomainName) is (true, var sid)) { + output.Properties.Add("domainsid", sid); + } + + output.Properties.Add("domain", wkp.Value.DomainName.ToUpper()); + output.ObjectIdentifier = wkp.Key; + yield return output; + } + } } } \ No newline at end of file diff --git a/src/CommonLib/SearchResultEntryWrapperNew.cs b/src/CommonLib/SearchResultEntryWrapperNew.cs deleted file mode 100644 index b031d892..00000000 --- a/src/CommonLib/SearchResultEntryWrapperNew.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System.Collections.Generic; -using System.DirectoryServices.Protocols; -using System.Security.Cryptography.X509Certificates; -using SharpHoundCommonLib.Enums; - -namespace SharpHoundCommonLib { - public class SearchResultEntryWrapperNew : ISearchResultEntry { - private readonly SearchResultEntry _entry; - - public string DistinguishedName => _entry.DistinguishedName; - public ResolvedSearchResult ResolveBloodHoundInfo() { - throw new System.NotImplementedException(); - } - - public string GetProperty(string propertyName) { - throw new System.NotImplementedException(); - } - - public byte[] GetByteProperty(string propertyName) { - throw new System.NotImplementedException(); - } - - public string[] GetArrayProperty(string propertyName) { - throw new System.NotImplementedException(); - } - - public byte[][] GetByteArrayProperty(string propertyName) { - throw new System.NotImplementedException(); - } - - public bool GetIntProperty(string propertyName, out int value) { - throw new System.NotImplementedException(); - } - - public X509Certificate2[] GetCertificateArrayProperty(string propertyName) { - throw new System.NotImplementedException(); - } - - public string GetObjectIdentifier() { - throw new System.NotImplementedException(); - } - - public bool IsDeleted() { - throw new System.NotImplementedException(); - } - - public Label GetLabel() { - throw new System.NotImplementedException(); - } - - public string GetSid() { - throw new System.NotImplementedException(); - } - - public string GetGuid() { - throw new System.NotImplementedException(); - } - - public int PropCount(string prop) { - throw new System.NotImplementedException(); - } - - public IEnumerable PropertyNames() { - throw new System.NotImplementedException(); - } - - public bool IsMSA() { - throw new System.NotImplementedException(); - } - - public bool IsGMSA() { - throw new System.NotImplementedException(); - } - - public bool HasLAPS() { - throw new System.NotImplementedException(); - } - } -} \ No newline at end of file