diff --git a/src/CommonLib/Enums/CollectionMethod.cs b/src/CommonLib/Enums/CollectionMethod.cs
index 0b5959e1..84c8ec91 100644
--- a/src/CommonLib/Enums/CollectionMethod.cs
+++ b/src/CommonLib/Enums/CollectionMethod.cs
@@ -1,10 +1,8 @@
using System;
-namespace SharpHoundCommonLib.Enums
-{
+namespace SharpHoundCommonLib.Enums {
[Flags]
- public enum CollectionMethod
- {
+ public enum CollectionMethod {
None = 0,
Group = 1,
LocalAdmin = 1 << 1,
@@ -25,10 +23,18 @@ public enum CollectionMethod
CARegistry = 1 << 16,
DCRegistry = 1 << 17,
CertServices = 1 << 18,
+ LdapServices = 1 << 19,
+ WebClientService = 1 << 21,
+ SmbInfo = 1 << 22,
+ EventLogs = 1 << 23,
LocalGroups = DCOM | RDP | LocalAdmin | PSRemote,
- ComputerOnly = LocalGroups | Session | UserRights | CARegistry | DCRegistry,
- DCOnly = ACL | Container | Group | ObjectProps | Trusts | GPOLocalGroup | CertServices,
- Default = Group | Session | Trusts | ACL | ObjectProps | LocalGroups | SPNTargets | Container | CertServices,
- All = Default | LoggedOn | GPOLocalGroup | UserRights | CARegistry | DCRegistry
+ ComputerOnly = LocalGroups | Session | UserRights | CARegistry | DCRegistry | WebClientService | SmbInfo,
+ DCOnly = ACL | Container | Group | ObjectProps | Trusts | GPOLocalGroup | CertServices | LdapServices | SmbInfo,
+
+ Default = Group | Session | Trusts | ACL | ObjectProps | LocalGroups | SPNTargets | Container | CertServices |
+ LdapServices | SmbInfo,
+
+ All = Default | LoggedOn | GPOLocalGroup | UserRights | CARegistry | DCRegistry | WebClientService |
+ LdapServices
}
}
\ No newline at end of file
diff --git a/src/CommonLib/Enums/EventIds.cs b/src/CommonLib/Enums/EventIds.cs
new file mode 100644
index 00000000..ee93ed5c
--- /dev/null
+++ b/src/CommonLib/Enums/EventIds.cs
@@ -0,0 +1,6 @@
+namespace SharpHoundCommonLib.Enums;
+
+public class EventIds {
+ public static int LogonEvent = 4624;
+ public static int ValidateCredentialsEvent = 4776;
+}
\ No newline at end of file
diff --git a/src/CommonLib/Enums/LdapErrorCodes.cs b/src/CommonLib/Enums/LdapErrorCodes.cs
index becf71c4..c6f81b62 100644
--- a/src/CommonLib/Enums/LdapErrorCodes.cs
+++ b/src/CommonLib/Enums/LdapErrorCodes.cs
@@ -1,12 +1,12 @@
-namespace SharpHoundCommonLib.Enums
-{
- public enum LdapErrorCodes : int
- {
+namespace SharpHoundCommonLib.Enums {
+ public enum LdapErrorCodes : int {
Success = 0,
+ StrongAuthRequired = 8,
+ SaslBindInProgress = 14,
InvalidCredentials = 49,
Busy = 51,
ServerDown = 81,
LocalError = 82,
- KerberosAuthType = 83
+ KerberosAuthType = 83,
}
}
\ No newline at end of file
diff --git a/src/CommonLib/Enums/LdapOption.cs b/src/CommonLib/Enums/LdapOption.cs
new file mode 100644
index 00000000..a0403253
--- /dev/null
+++ b/src/CommonLib/Enums/LdapOption.cs
@@ -0,0 +1,12 @@
+namespace SharpHoundCommonLib.Enums {
+ public enum LdapOption : int {
+ Ssl = 0x0A,
+ ProtocolVersion = 0x11,
+ ResultCode = 0x31,
+ ServerError = 0x33,
+ ServerCertificate = 0x81,
+ Sign = 0x95,
+ Encrypt = 0x96,
+ Timeout = 0x5002,
+ }
+}
\ No newline at end of file
diff --git a/src/CommonLib/Enums/LdapOptionValue.cs b/src/CommonLib/Enums/LdapOptionValue.cs
new file mode 100644
index 00000000..16e5fb9a
--- /dev/null
+++ b/src/CommonLib/Enums/LdapOptionValue.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SharpHoundCommonLib.Enums {
+ public enum LdapOptionValue : int {
+ Off = 0,
+ On = 1,
+ Version3 = 3,
+ };
+}
\ No newline at end of file
diff --git a/src/CommonLib/Enums/LdapSupportedSaslMechanisms.cs b/src/CommonLib/Enums/LdapSupportedSaslMechanisms.cs
new file mode 100644
index 00000000..37736c70
--- /dev/null
+++ b/src/CommonLib/Enums/LdapSupportedSaslMechanisms.cs
@@ -0,0 +1,14 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace SharpHoundCommonLib.Enums {
+ public static class LdapSupportedSaslMechanisms {
+ public const string GSSAPI = "GSSAPI";
+ public const string GSS_SPNEGO = "GSS-SPNEGO";
+ public const string EXTERNAL = "EXTERNAL";
+ public const string DIGEST_MD5 = "DIGEST_MD5";
+ }
+}
\ No newline at end of file
diff --git a/src/CommonLib/Extensions.cs b/src/CommonLib/Extensions.cs
index be102fd9..e582b273 100644
--- a/src/CommonLib/Extensions.cs
+++ b/src/CommonLib/Extensions.cs
@@ -186,7 +186,7 @@ public static string LdapValue(this Guid s)
///
///
public static bool IsComputerCollectionSet(this CollectionMethod methods) {
- const CollectionMethod test = CollectionMethod.ComputerOnly | CollectionMethod.LoggedOn;
+ const CollectionMethod test = CollectionMethod.ComputerOnly | CollectionMethod.LoggedOn | CollectionMethod.SmbInfo | CollectionMethod.WebClientService;
return (methods & test) != 0;
}
diff --git a/src/CommonLib/Impersonate.cs b/src/CommonLib/Impersonate.cs
index f6e03aae..5c8ba694 100644
--- a/src/CommonLib/Impersonate.cs
+++ b/src/CommonLib/Impersonate.cs
@@ -86,11 +86,11 @@ public class Impersonator : IDisposable {
///
/// Begins impersonation with the given credentials, Logon type and Logon provider.
///
- /// Name of the user.
- /// Name of the domain.
- /// The password. param >
- ///< param name="logonType">Type of the logon.
- /// The logon provider. param >
+ /// Name of the user.
+ /// Name of the domain.
+ /// The password.
+ ///Type of the logon.
+ /// The logon provider.
public Impersonator(string userName, string domainName, string password, LogonType logonType,
LogonProvider logonProvider) {
Impersonate(userName, domainName, password, logonType, logonProvider);
@@ -125,10 +125,11 @@ public void Dispose() {
///
/// Name of the user.
/// Name of the domain.
- /// The password. param >
- ///< param name="logonType">Type of the logon.
+ /// The password.
+ ///Type of the logon.
/// The logon provider. param >
- public void Impersonate(string userName, string domainName, string password, LogonType logonType = LogonType.LOGON32_LOGON_INTERACTIVE,
+ public void Impersonate(string userName, string domainName, string password,
+ LogonType logonType = LogonType.LOGON32_LOGON_INTERACTIVE,
LogonProvider logonProvider = LogonProvider.LOGON32_PROVIDER_DEFAULT) {
UndoImpersonation();
@@ -149,14 +150,11 @@ public void Impersonate(string userName, string domainName, string password, Log
ref logonTokenDuplicate) != 0) {
var wi = new WindowsIdentity(logonTokenDuplicate);
wi.Impersonate(); // discard the returned identity context (which is the context of the application pool)
- }
- else
+ } else
throw new Win32Exception(Marshal.GetLastWin32Error());
- }
- else
+ } else
throw new Win32Exception(Marshal.GetLastWin32Error());
- }
- finally {
+ } finally {
if (logonToken != IntPtr.Zero)
Win32NativeMethods.CloseHandle(logonToken);
diff --git a/src/CommonLib/LdapConnectionPool.cs b/src/CommonLib/LdapConnectionPool.cs
index 5ec804e5..1073f960 100644
--- a/src/CommonLib/LdapConnectionPool.cs
+++ b/src/CommonLib/LdapConnectionPool.cs
@@ -16,10 +16,10 @@
using SharpHoundRPC.NetAPINative;
namespace SharpHoundCommonLib {
- internal class LdapConnectionPool : IDisposable{
+ internal class LdapConnectionPool : IDisposable {
private readonly ConcurrentBag _connections;
private readonly ConcurrentBag _globalCatalogConnection;
- private readonly SemaphoreSlim _semaphore;
+ private readonly SemaphoreSlim _semaphore = null;
private readonly string _identifier;
private readonly string _poolIdentifier;
private readonly LdapConfig _ldapConfig;
@@ -45,7 +45,7 @@ public LdapConnectionPool(string identifier, string poolIdentifier, LdapConfig c
// //If MaxConcurrentQueries is 0, we'll just disable the semaphore entirely
// _semaphore = null;
// }
-
+
_identifier = identifier;
_poolIdentifier = poolIdentifier;
_ldapConfig = config;
@@ -53,14 +53,16 @@ public LdapConnectionPool(string identifier, string poolIdentifier, LdapConfig c
_portScanner = scanner ?? new PortScanner();
_nativeMethods = nativeMethods ?? new NativeMethods();
}
-
- private async Task<(bool Success, LdapConnectionWrapper ConnectionWrapper, string Message)> GetLdapConnection(bool globalCatalog) {
+
+ private async Task<(bool Success, LdapConnectionWrapper ConnectionWrapper, string Message)> GetLdapConnection(
+ bool globalCatalog) {
if (globalCatalog) {
return await GetGlobalCatalogConnectionAsync();
}
+
return await GetConnectionAsync();
}
-
+
public async IAsyncEnumerable> Query(LdapQueryParameters queryParameters,
[EnumeratorCancellation] CancellationToken cancellationToken = new()) {
var setupResult = await SetupLdapQuery(queryParameters);
@@ -86,11 +88,14 @@ public async IAsyncEnumerable> Query(LdapQueryParam
SearchResponse response = null;
while (!cancellationToken.IsCancellationRequested) {
//Grab our semaphore here to take one of our query slots
- if (_semaphore != null){
- _log.LogTrace("Query entering semaphore with {Count} remaining for query {Info}", _semaphore.CurrentCount, queryParameters.GetQueryInfo());
+ if (_semaphore != null) {
+ _log.LogTrace("Query entering semaphore with {Count} remaining for query {Info}",
+ _semaphore.CurrentCount, queryParameters.GetQueryInfo());
await _semaphore.WaitAsync(cancellationToken);
- _log.LogTrace("Query entered semaphore with {Count} remaining for query {Info}", _semaphore.CurrentCount, queryParameters.GetQueryInfo());
+ _log.LogTrace("Query entered semaphore with {Count} remaining for query {Info}",
+ _semaphore.CurrentCount, queryParameters.GetQueryInfo());
}
+
try {
_log.LogTrace("Sending ldap request - {Info}", queryParameters.GetQueryInfo());
response = (SearchResponse)connectionWrapper.Connection.SendRequest(searchRequest);
@@ -117,7 +122,8 @@ public async IAsyncEnumerable> Query(LdapQueryParam
* since non-paged queries do not require same server connections
*/
queryRetryCount++;
- _log.LogDebug("Query - Attempting to recover from ServerDown for query {Info} (Attempt {Count})", queryParameters.GetQueryInfo(), queryRetryCount);
+ _log.LogDebug("Query - Attempting to recover from ServerDown for query {Info} (Attempt {Count})",
+ queryParameters.GetQueryInfo(), queryRetryCount);
ReleaseConnection(connectionWrapper, true);
for (var retryCount = 0; retryCount < MaxRetries; retryCount++) {
@@ -148,7 +154,8 @@ public async IAsyncEnumerable> Query(LdapQueryParam
* The expectation is that given enough time, the server should stop being busy and service our query appropriately
*/
busyRetryCount++;
- _log.LogDebug("Query - Executing busy backoff for query {Info} (Attempt {Count})", queryParameters.GetQueryInfo(), busyRetryCount);
+ _log.LogDebug("Query - Executing busy backoff for query {Info} (Attempt {Count})",
+ queryParameters.GetQueryInfo(), busyRetryCount);
var backoffDelay = GetNextBackoff(busyRetryCount);
await Task.Delay(backoffDelay, cancellationToken);
} catch (LdapException le) {
@@ -168,9 +175,11 @@ public async IAsyncEnumerable> Query(LdapQueryParam
} finally {
// Always release our semaphore to prevent deadlocks
if (_semaphore != null) {
- _log.LogTrace("Query releasing semaphore with {Count} remaining for query {Info}", _semaphore.CurrentCount, queryParameters.GetQueryInfo());
+ _log.LogTrace("Query releasing semaphore with {Count} remaining for query {Info}",
+ _semaphore.CurrentCount, queryParameters.GetQueryInfo());
_semaphore.Release();
- _log.LogTrace("Query released semaphore with {Count} remaining for query {Info}", _semaphore.CurrentCount, queryParameters.GetQueryInfo());
+ _log.LogTrace("Query released semaphore with {Count} remaining for query {Info}",
+ _semaphore.CurrentCount, queryParameters.GetQueryInfo());
}
}
@@ -197,7 +206,7 @@ public async IAsyncEnumerable> Query(LdapQueryParam
yield return LdapResult.Ok(new SearchResultEntryWrapper(entry));
}
}
-
+
public async IAsyncEnumerable> PagedQuery(LdapQueryParameters queryParameters,
[EnumeratorCancellation] CancellationToken cancellationToken = new()) {
var setupResult = await SetupLdapQuery(queryParameters);
@@ -225,11 +234,14 @@ public async IAsyncEnumerable> PagedQuery(LdapQuery
LdapResult tempResult = null;
while (!cancellationToken.IsCancellationRequested) {
- if (_semaphore != null){
- _log.LogTrace("PagedQuery entering semaphore with {Count} remaining for query {Info}", _semaphore.CurrentCount, queryParameters.GetQueryInfo());
+ if (_semaphore != null) {
+ _log.LogTrace("PagedQuery entering semaphore with {Count} remaining for query {Info}",
+ _semaphore.CurrentCount, queryParameters.GetQueryInfo());
await _semaphore.WaitAsync(cancellationToken);
- _log.LogTrace("PagedQuery entered semaphore with {Count} remaining for query {Info}", _semaphore.CurrentCount, queryParameters.GetQueryInfo());
+ _log.LogTrace("PagedQuery entered semaphore with {Count} remaining for query {Info}",
+ _semaphore.CurrentCount, queryParameters.GetQueryInfo());
}
+
SearchResponse response = null;
try {
_log.LogTrace("Sending paged ldap request - {Info}", queryParameters.GetQueryInfo());
@@ -247,15 +259,15 @@ public async IAsyncEnumerable> PagedQuery(LdapQuery
}
} catch (LdapException le) when (le.ErrorCode == (int)LdapErrorCodes.ServerDown) {
/*
- * A ServerDown exception indicates that our connection is no longer valid for one of many reasons.
- * We'll want to release our connection back to the pool, but dispose it. We need a new connection,
- * and because this is not a paged query, we can get this connection from anywhere.
- *
- * We use queryRetryCount here to prevent an infinite retry loop from occurring
- *
- * Release our connection in a faulted state since the connection is defunct.
- * Paged queries require a connection to be made to the same server which we started the paged query on
- */
+ * A ServerDown exception indicates that our connection is no longer valid for one of many reasons.
+ * We'll want to release our connection back to the pool, but dispose it. We need a new connection,
+ * and because this is not a paged query, we can get this connection from anywhere.
+ *
+ * We use queryRetryCount here to prevent an infinite retry loop from occurring
+ *
+ * Release our connection in a faulted state since the connection is defunct.
+ * Paged queries require a connection to be made to the same server which we started the paged query on
+ */
if (serverName == null) {
_log.LogError(
"PagedQuery - Received server down exception without a known servername. Unable to generate new connection\n{Info}",
@@ -263,9 +275,11 @@ public async IAsyncEnumerable> PagedQuery(LdapQuery
ReleaseConnection(connectionWrapper, true);
yield break;
}
-
- _log.LogDebug("PagedQuery - Attempting to recover from ServerDown for query {Info} (Attempt {Count})", queryParameters.GetQueryInfo(), queryRetryCount);
-
+
+ _log.LogDebug(
+ "PagedQuery - Attempting to recover from ServerDown for query {Info} (Attempt {Count})",
+ queryParameters.GetQueryInfo(), queryRetryCount);
+
ReleaseConnection(connectionWrapper, true);
for (var retryCount = 0; retryCount < MaxRetries; retryCount++) {
var backoffDelay = GetNextBackoff(retryCount);
@@ -293,7 +307,8 @@ public async IAsyncEnumerable> PagedQuery(LdapQuery
* The expectation is that given enough time, the server should stop being busy and service our query appropriately
*/
busyRetryCount++;
- _log.LogDebug("PagedQuery - Executing busy backoff for query {Info} (Attempt {Count})", queryParameters.GetQueryInfo(), busyRetryCount);
+ _log.LogDebug("PagedQuery - Executing busy backoff for query {Info} (Attempt {Count})",
+ queryParameters.GetQueryInfo(), busyRetryCount);
var backoffDelay = GetNextBackoff(busyRetryCount);
await Task.Delay(backoffDelay, cancellationToken);
} catch (LdapException le) {
@@ -306,9 +321,11 @@ public async IAsyncEnumerable> PagedQuery(LdapQuery
queryParameters);
} finally {
if (_semaphore != null) {
- _log.LogTrace("PagedQuery releasing semaphore with {Count} remaining for query {Info}", _semaphore.CurrentCount, queryParameters.GetQueryInfo());
+ _log.LogTrace("PagedQuery releasing semaphore with {Count} remaining for query {Info}",
+ _semaphore.CurrentCount, queryParameters.GetQueryInfo());
_semaphore.Release();
- _log.LogTrace("PagedQuery released semaphore with {Count} remaining for query {Info}", _semaphore.CurrentCount, queryParameters.GetQueryInfo());
+ _log.LogTrace("PagedQuery released semaphore with {Count} remaining for query {Info}",
+ _semaphore.CurrentCount, queryParameters.GetQueryInfo());
}
}
@@ -351,7 +368,7 @@ public async IAsyncEnumerable> PagedQuery(LdapQuery
pageControl.Cookie = pageResponse.Cookie;
}
}
-
+
private async Task SetupLdapQuery(LdapQueryParameters queryParameters) {
var result = new LdapQuerySetupResult();
var (success, connectionWrapper, message) =
@@ -392,10 +409,10 @@ public async IAsyncEnumerable> RangedRetrieval(string distinguish
yield return Result.Fail(connectionResult.Message);
yield break;
}
-
+
var index = 0;
var step = 0;
-
+
//Start by using * as our upper index, which will automatically give us the range size
var currentRange = $"{attributeName};range={index}-*";
var complete = false;
@@ -414,7 +431,7 @@ public async IAsyncEnumerable> RangedRetrieval(string distinguish
yield return Result.Fail("Failed to create search request");
yield break;
}
-
+
var queryRetryCount = 0;
var busyRetryCount = 0;
@@ -422,22 +439,28 @@ public async IAsyncEnumerable> RangedRetrieval(string distinguish
while (!cancellationToken.IsCancellationRequested) {
SearchResponse response = null;
- if (_semaphore != null){
- _log.LogTrace("RangedRetrieval entering semaphore with {Count} remaining for query {Info}", _semaphore.CurrentCount, queryParameters.GetQueryInfo());
+ if (_semaphore != null) {
+ _log.LogTrace("RangedRetrieval entering semaphore with {Count} remaining for query {Info}",
+ _semaphore.CurrentCount, queryParameters.GetQueryInfo());
await _semaphore.WaitAsync(cancellationToken);
- _log.LogTrace("RangedRetrieval entered semaphore with {Count} remaining for query {Info}", _semaphore.CurrentCount, queryParameters.GetQueryInfo());
+ _log.LogTrace("RangedRetrieval entered semaphore with {Count} remaining for query {Info}",
+ _semaphore.CurrentCount, queryParameters.GetQueryInfo());
}
+
try {
response = (SearchResponse)connectionWrapper.Connection.SendRequest(searchRequest);
} catch (LdapException le) when (le.ErrorCode == (int)ResultCode.Busy && busyRetryCount < MaxRetries) {
busyRetryCount++;
- _log.LogDebug("RangedRetrieval - Executing busy backoff for query {Info} (Attempt {Count})", queryParameters.GetQueryInfo(), busyRetryCount);
+ _log.LogDebug("RangedRetrieval - Executing busy backoff for query {Info} (Attempt {Count})",
+ queryParameters.GetQueryInfo(), busyRetryCount);
var backoffDelay = GetNextBackoff(busyRetryCount);
await Task.Delay(backoffDelay, cancellationToken);
} catch (LdapException le) when (le.ErrorCode == (int)LdapErrorCodes.ServerDown &&
queryRetryCount < MaxRetries) {
queryRetryCount++;
- _log.LogDebug("RangedRetrieval - Attempting to recover from ServerDown for query {Info} (Attempt {Count})", queryParameters.GetQueryInfo(), queryRetryCount);
+ _log.LogDebug(
+ "RangedRetrieval - Attempting to recover from ServerDown for query {Info} (Attempt {Count})",
+ queryParameters.GetQueryInfo(), queryRetryCount);
ReleaseConnection(connectionWrapper, true);
for (var retryCount = 0; retryCount < MaxRetries; retryCount++) {
var backoffDelay = GetNextBackoff(retryCount);
@@ -472,9 +495,11 @@ public async IAsyncEnumerable> RangedRetrieval(string distinguish
LdapResult.Fail($"Caught unrecoverable exception: {e.Message}", queryParameters);
} finally {
if (_semaphore != null) {
- _log.LogTrace("RangedRetrieval releasing semaphore with {Count} remaining for query {Info}", _semaphore.CurrentCount, queryParameters.GetQueryInfo());
+ _log.LogTrace("RangedRetrieval releasing semaphore with {Count} remaining for query {Info}",
+ _semaphore.CurrentCount, queryParameters.GetQueryInfo());
_semaphore.Release();
- _log.LogTrace("RangedRetrieval released semaphore with {Count} remaining for query {Info}", _semaphore.CurrentCount, queryParameters.GetQueryInfo());
+ _log.LogTrace("RangedRetrieval released semaphore with {Count} remaining for query {Info}",
+ _semaphore.CurrentCount, queryParameters.GetQueryInfo());
}
}
@@ -526,13 +551,13 @@ public async IAsyncEnumerable> RangedRetrieval(string distinguish
ReleaseConnection(connectionWrapper);
}
-
+
private static TimeSpan GetNextBackoff(int retryCount) {
return TimeSpan.FromSeconds(Math.Min(
MinBackoffDelay.TotalSeconds * Math.Pow(BackoffDelayMultiplier, retryCount),
MaxBackoffDelay.TotalSeconds));
}
-
+
private bool CreateSearchRequest(LdapQueryParameters queryParameters,
LdapConnectionWrapper connectionWrapper, out SearchRequest searchRequest) {
string basePath;
@@ -543,7 +568,7 @@ private bool CreateSearchRequest(LdapQueryParameters queryParameters,
if (CallDsGetDcName(queryParameters.DomainName, out var info) && info != null) {
tempPath = Helpers.DomainNameToDistinguishedName(info.Value.DomainName);
connectionWrapper.SaveContext(queryParameters.NamingContext, basePath);
- } else if (LdapUtils.GetDomain(queryParameters.DomainName,_ldapConfig, out var domainObject)) {
+ } else if (LdapUtils.GetDomain(queryParameters.DomainName, _ldapConfig, out var domainObject)) {
tempPath = Helpers.DomainNameToDistinguishedName(domainObject.Name);
} else {
searchRequest = null;
@@ -559,8 +584,9 @@ private bool CreateSearchRequest(LdapQueryParameters queryParameters,
connectionWrapper.SaveContext(queryParameters.NamingContext, basePath);
}
-
- if (string.IsNullOrWhiteSpace(queryParameters.SearchBase) && !string.IsNullOrWhiteSpace(queryParameters.RelativeSearchBase)) {
+
+ if (string.IsNullOrWhiteSpace(queryParameters.SearchBase) &&
+ !string.IsNullOrWhiteSpace(queryParameters.RelativeSearchBase)) {
basePath = $"{queryParameters.RelativeSearchBase},{basePath}";
}
@@ -579,7 +605,7 @@ private bool CreateSearchRequest(LdapQueryParameters queryParameters,
return true;
}
-
+
private bool CallDsGetDcName(string domainName, out NetAPIStructs.DomainControllerInfo? info) {
if (DCInfoCache.TryGetValue(domainName.ToUpper().Trim(), out info)) return info != null;
@@ -607,7 +633,7 @@ private bool CallDsGetDcName(string domainName, out NetAPIStructs.DomainControll
if (!success) {
return (false, null, message);
}
-
+
connectionWrapper = connection;
}
@@ -641,39 +667,40 @@ public void ReleaseConnection(LdapConnectionWrapper connectionWrapper, bool conn
if (!connectionFaulted) {
if (connectionWrapper.GlobalCatalog) {
_globalCatalogConnection.Add(connectionWrapper);
+ } else {
+ _connections.Add(connectionWrapper);
}
- else {
- _connections.Add(connectionWrapper);
- }
- }
- else {
+ } else {
connectionWrapper.Connection.Dispose();
}
}
-
+
public void Dispose() {
while (_connections.TryTake(out var wrapper)) {
wrapper.Connection.Dispose();
}
}
- private async Task<(bool Success, LdapConnectionWrapper Connection, string Message)> CreateNewConnection(bool globalCatalog = false) {
+ private async Task<(bool Success, LdapConnectionWrapper Connection, string Message)> CreateNewConnection(
+ bool globalCatalog = false) {
try {
if (!string.IsNullOrWhiteSpace(_ldapConfig.Server)) {
return CreateNewConnectionForServer(_ldapConfig.Server, globalCatalog);
}
if (CreateLdapConnection(_identifier.ToUpper().Trim(), globalCatalog, out var connectionWrapper)) {
- _log.LogDebug("Successfully created ldap connection for domain: {Domain} using strategy 1. SSL: {SSl}", _identifier, connectionWrapper.Connection.SessionOptions.SecureSocketLayer);
+ _log.LogDebug(
+ "Successfully created ldap connection for domain: {Domain} using strategy 1. SSL: {SSl}",
+ _identifier, connectionWrapper.Connection.SessionOptions.SecureSocketLayer);
return (true, connectionWrapper, "");
}
-
+
string tempDomainName;
-
+
var dsGetDcNameResult = _nativeMethods.CallDsGetDcName(null, _identifier,
(uint)(NetAPIEnums.DSGETDCNAME_FLAGS.DS_FORCE_REDISCOVERY |
- NetAPIEnums.DSGETDCNAME_FLAGS.DS_RETURN_DNS_NAME |
- NetAPIEnums.DSGETDCNAME_FLAGS.DS_DIRECTORY_SERVICE_REQUIRED));
+ NetAPIEnums.DSGETDCNAME_FLAGS.DS_RETURN_DNS_NAME |
+ NetAPIEnums.DSGETDCNAME_FLAGS.DS_DIRECTORY_SERVICE_REQUIRED));
if (dsGetDcNameResult.IsSuccess) {
tempDomainName = dsGetDcNameResult.Value.DomainName;
@@ -684,7 +711,7 @@ public void Dispose() {
_identifier, tempDomainName);
return (true, connectionWrapper, "");
}
-
+
var server = dsGetDcNameResult.Value.DomainControllerName.TrimStart('\\');
var result =
@@ -696,7 +723,7 @@ public void Dispose() {
return (true, result.connection, "");
}
}
-
+
if (!LdapUtils.GetDomain(_identifier, _ldapConfig, out var domainObject) || domainObject.Name == null) {
//If we don't get a result here, we effectively have no other ways to resolve this domain, so we'll just have to exit out
_log.LogDebug(
@@ -705,8 +732,9 @@ public void Dispose() {
_excludedDomains.Add(_identifier);
return (false, null, "Unable to get domain object for further strategies");
}
+
tempDomainName = domainObject.Name.ToUpper().Trim();
-
+
if (!tempDomainName.Equals(_identifier, StringComparison.OrdinalIgnoreCase) &&
CreateLdapConnection(tempDomainName, globalCatalog, out connectionWrapper)) {
_log.LogDebug(
@@ -714,7 +742,7 @@ public void Dispose() {
_identifier, tempDomainName);
return (true, connectionWrapper, "");
}
-
+
var primaryDomainController = domainObject.PdcRoleOwner.Name;
var portConnectionResult =
await CreateLDAPConnectionWithPortCheck(primaryDomainController, globalCatalog);
@@ -724,7 +752,7 @@ public void Dispose() {
_identifier, primaryDomainController);
return (true, portConnectionResult.connection, "");
}
-
+
foreach (DomainController dc in domainObject.DomainControllers) {
portConnectionResult =
await CreateLDAPConnectionWithPortCheck(dc.Name, globalCatalog);
@@ -742,27 +770,28 @@ public void Dispose() {
return (false, null, "All attempted connections failed");
}
-
- private (bool Success, LdapConnectionWrapper Connection, string Message ) CreateNewConnectionForServer(string identifier, bool globalCatalog = false) {
+
+ private (bool Success, LdapConnectionWrapper Connection, string Message ) CreateNewConnectionForServer(
+ string identifier, bool globalCatalog = false) {
if (CreateLdapConnection(identifier, globalCatalog, out var serverConnection)) {
return (true, serverConnection, "");
}
return (false, null, $"Failed to create ldap connection for {identifier}");
}
-
+
private bool CreateLdapConnection(string target, bool globalCatalog,
out LdapConnectionWrapper connection) {
var baseConnection = CreateBaseConnection(target, true, globalCatalog);
if (TestLdapConnection(baseConnection, out var result)) {
- connection = new LdapConnectionWrapper(baseConnection, result.SearchResultEntry, globalCatalog, _poolIdentifier);
+ connection = new LdapConnectionWrapper(baseConnection, result.SearchResultEntry, globalCatalog,
+ _poolIdentifier);
return true;
}
try {
baseConnection.Dispose();
- }
- catch {
+ } catch {
//this is just in case
}
@@ -773,43 +802,42 @@ private bool CreateLdapConnection(string target, bool globalCatalog,
baseConnection = CreateBaseConnection(target, false, globalCatalog);
if (TestLdapConnection(baseConnection, out result)) {
- connection = new LdapConnectionWrapper(baseConnection, result.SearchResultEntry, globalCatalog, _poolIdentifier);
+ connection = new LdapConnectionWrapper(baseConnection, result.SearchResultEntry, globalCatalog,
+ _poolIdentifier);
return true;
}
try {
baseConnection.Dispose();
- }
- catch {
+ } catch {
//this is just in case
}
connection = null;
return false;
}
-
+
private LdapConnection CreateBaseConnection(string directoryIdentifier, bool ssl,
bool globalCatalog) {
_log.LogDebug("Creating connection for identifier {Identifier}", directoryIdentifier);
var port = globalCatalog ? _ldapConfig.GetGCPort(ssl) : _ldapConfig.GetPort(ssl);
var identifier = new LdapDirectoryIdentifier(directoryIdentifier, port, false, false);
var connection = new LdapConnection(identifier) { Timeout = new TimeSpan(0, 0, 5, 0) };
-
+
//These options are important!
connection.SessionOptions.ProtocolVersion = 3;
//Referral chasing does not work with paged searches
connection.SessionOptions.ReferralChasing = ReferralChasingOptions.None;
if (ssl) connection.SessionOptions.SecureSocketLayer = true;
-
+
if (_ldapConfig.DisableSigning || ssl) {
connection.SessionOptions.Signing = false;
connection.SessionOptions.Sealing = false;
- }
- else {
+ } else {
connection.SessionOptions.Signing = true;
connection.SessionOptions.Sealing = true;
}
-
+
if (_ldapConfig.DisableCertVerification)
connection.SessionOptions.VerifyServerCertificate = (_, _) => true;
@@ -839,10 +867,10 @@ private bool TestLdapConnection(LdapConnection connection, out LdapConnectionTes
try {
//Attempt an initial bind. If this fails, likely auth is invalid, or its not a valid target
connection.Bind();
- }
- catch (LdapException e) {
+ } catch (LdapException e) {
//TODO: Maybe look at this and find a better way?
- if (e.ErrorCode is (int)LdapErrorCodes.InvalidCredentials or (int)ResultCode.InappropriateAuthentication) {
+ if (e.ErrorCode is (int)LdapErrorCodes.InvalidCredentials
+ or (int)ResultCode.InappropriateAuthentication) {
connection.Dispose();
throw new LdapAuthenticationException(e);
}
@@ -850,8 +878,7 @@ private bool TestLdapConnection(LdapConnection connection, out LdapConnectionTes
testResult.Message = e.Message;
testResult.ErrorCode = e.ErrorCode;
return false;
- }
- catch (Exception e) {
+ } catch (Exception e) {
testResult.Message = e.Message;
return false;
}
@@ -864,8 +891,7 @@ private bool TestLdapConnection(LdapConnection connection, out LdapConnectionTes
SearchScope.Base, null);
response = (SearchResponse)connection.SendRequest(searchRequest);
- }
- catch (LdapException e) {
+ } catch (LdapException e) {
/*
* If we can't send the initial search request, its unlikely any other search requests will work so we will immediately return false
*/
@@ -880,7 +906,7 @@ private bool TestLdapConnection(LdapConnection connection, out LdapConnectionTes
* across external trusts with kerberos authentication without Forest Search Order properly configured.
* Either way, this connection isn't useful for us because we're not going to get data, so return false
*/
-
+
connection.Dispose();
throw new NoLdapDataException();
}
@@ -895,15 +921,14 @@ private class LdapConnectionTestResult {
public IDirectoryObject SearchResultEntry { get; set; }
public int ErrorCode { get; set; }
}
-
+
private async Task<(bool success, LdapConnectionWrapper connection)> CreateLDAPConnectionWithPortCheck(
string target, bool globalCatalog) {
if (globalCatalog) {
if (await _portScanner.CheckPort(target, _ldapConfig.GetGCPort(true)) || (!_ldapConfig.ForceSSL &&
await _portScanner.CheckPort(target, _ldapConfig.GetGCPort(false))))
return (CreateLdapConnection(target, true, out var connection), connection);
- }
- else {
+ } else {
if (await _portScanner.CheckPort(target, _ldapConfig.GetPort(true)) || (!_ldapConfig.ForceSSL &&
await _portScanner.CheckPort(target, _ldapConfig.GetPort(false))))
return (CreateLdapConnection(target, true, out var connection), connection);
@@ -911,7 +936,7 @@ await _portScanner.CheckPort(target, _ldapConfig.GetPort(false))))
return (false, null);
}
-
+
private SearchRequest CreateSearchRequest(string distinguishedName, string ldapFilter,
SearchScope searchScope,
string[] attributes) {
diff --git a/src/CommonLib/LdapProducerQueryGenerator.cs b/src/CommonLib/LdapProducerQueryGenerator.cs
index 0d6580f8..25c6cb9b 100644
--- a/src/CommonLib/LdapProducerQueryGenerator.cs
+++ b/src/CommonLib/LdapProducerQueryGenerator.cs
@@ -9,11 +9,12 @@ public class LdapProducerQueryGenerator {
public static GeneratedLdapParameters GenerateDefaultPartitionParameters(CollectionMethod methods) {
var filter = new LdapFilter();
var properties = new List();
-
+
properties.AddRange(CommonProperties.BaseQueryProps);
properties.AddRange(CommonProperties.TypeResolutionProps);
- if (methods.HasFlag(CollectionMethod.ObjectProps) || methods.HasFlag(CollectionMethod.ACL) || methods.HasFlag(CollectionMethod.Container)) {
+ if (methods.HasFlag(CollectionMethod.ObjectProps) || methods.HasFlag(CollectionMethod.ACL) ||
+ methods.HasFlag(CollectionMethod.Container)) {
filter = filter.AddComputers().AddDomains().AddUsers().AddContainers().AddGPOs().AddOUs().AddGroups();
if (methods.HasFlag(CollectionMethod.Container)) {
@@ -39,14 +40,14 @@ public static GeneratedLdapParameters GenerateDefaultPartitionParameters(Collect
if (methods.HasFlag(CollectionMethod.Trusts)) {
properties.AddRange(CommonProperties.DomainTrustProps);
}
-
+
if (methods.HasFlag(CollectionMethod.GPOLocalGroup))
properties.AddRange(CommonProperties.GPOLocalGroupProps);
if (methods.HasFlag(CollectionMethod.SPNTargets))
properties.AddRange(CommonProperties.SPNTargetProps);
- if (methods.HasFlag(CollectionMethod.DCRegistry))
+ if (methods.IsComputerCollectionSet())
properties.AddRange(CommonProperties.ComputerMethodProps);
if (methods.HasFlag(CollectionMethod.SPNTargets)) {
@@ -79,16 +80,16 @@ public static GeneratedLdapParameters GenerateDefaultPartitionParameters(Collect
properties.AddRange(CommonProperties.GPOLocalGroupProps);
}
- if (methods.HasFlag(CollectionMethod.DCRegistry)) {
+ if (methods.HasFlag(CollectionMethod.DCRegistry) || methods.HasFlag(CollectionMethod.LdapServices)) {
filter = filter.AddComputers(CommonFilters.DomainControllers);
properties.AddRange(CommonProperties.ComputerMethodProps);
}
-
+
if (methods.HasFlag(CollectionMethod.Group)) {
filter = filter.AddGroups();
properties.AddRange(CommonProperties.GroupResolutionProps);
}
-
+
return new GeneratedLdapParameters {
Filter = filter,
Attributes = properties.Distinct().ToArray()
@@ -98,7 +99,7 @@ public static GeneratedLdapParameters GenerateDefaultPartitionParameters(Collect
public static GeneratedLdapParameters GenerateConfigurationPartitionParameters(CollectionMethod methods) {
var filter = new LdapFilter();
var properties = new List();
-
+
properties.AddRange(CommonProperties.BaseQueryProps);
properties.AddRange(CommonProperties.TypeResolutionProps);
@@ -106,9 +107,8 @@ public static GeneratedLdapParameters GenerateConfigurationPartitionParameters(C
methods.HasFlag(CollectionMethod.Container) || methods.HasFlag(CollectionMethod.CertServices)) {
filter = filter.AddContainers().AddConfiguration().AddCertificateTemplates().AddCertificateAuthorities()
.AddEnterpriseCertificationAuthorities().AddIssuancePolicies();
-
- if (methods.HasFlag(CollectionMethod.ObjectProps))
- {
+
+ if (methods.HasFlag(CollectionMethod.ObjectProps)) {
properties.AddRange(CommonProperties.ObjectPropsProps);
}
diff --git a/src/CommonLib/LdapUtils.cs b/src/CommonLib/LdapUtils.cs
index a9166aff..a805d54d 100644
--- a/src/CommonLib/LdapUtils.cs
+++ b/src/CommonLib/LdapUtils.cs
@@ -182,7 +182,7 @@ public IAsyncEnumerable> PagedQuery(LdapQueryParame
} catch {
//pass
}
-
+
return (false, Label.Base);
}
@@ -227,7 +227,7 @@ public IAsyncEnumerable> PagedQuery(LdapQueryParame
} catch {
//pass
}
-
+
return (false, Label.Base);
}
@@ -361,7 +361,7 @@ public IAsyncEnumerable> PagedQuery(LdapQueryParame
} catch {
//pass
}
-
+
return (false, string.Empty);
}
@@ -904,7 +904,6 @@ public async Task IsDomainController(string computerObjectId, string domai
_unresolvablePrincipals.Add(distinguishedName);
return (false, default);
}
-
}
public async Task<(bool Success, string DSHeuristics)> GetDSHueristics(string domain, string dn) {
@@ -961,7 +960,7 @@ public async IAsyncEnumerable GetWellKnownPrincipalOutput() {
yield return entdc;
}
}
-
+
private async IAsyncEnumerable GetEnterpriseDCGroups() {
var grouped = new ConcurrentDictionary>(StringComparer.OrdinalIgnoreCase);
var forestSidToName = new ConcurrentDictionary(StringComparer.OrdinalIgnoreCase);
@@ -985,6 +984,7 @@ await GetDomainSidFromDomainName(forestName) is (true, var forestDomainSid)) {
if (!forestSidToName.TryGetValue(f.Key, out var forestName)) {
continue;
}
+
var group = new Group { ObjectIdentifier = $"{forestName}-S-1-5-9" };
group.Properties.Add("name", $"ENTERPRISE DOMAIN CONTROLLERS@{forestName}".ToUpper());
group.Properties.Add("domainsid", f.Key);
@@ -1203,6 +1203,15 @@ await utils.GetDomainNameFromSid(objectIdentifier) is (true, var domainName)) {
return (true, res);
}
+ res.ObjectType = await ComputeLabel(directoryObject, objectIdentifier, domain, utils);
+
+ directoryObject.TryGetProperty(LDAPProperties.SAMAccountName, out var samAccountName);
+ res.DisplayName = ComputeDisplayName(directoryObject, domain, res.ObjectType, samAccountName);
+ return (true, res);
+ }
+
+ private static async Task