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

Create unit tests for TestConnection #126

Closed
wants to merge 5 commits into from
Closed
Show file tree
Hide file tree
Changes from all 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
33 changes: 33 additions & 0 deletions src/CommonLib/DomainControllerCacheKey.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
namespace SharpHoundCommonLib
{
public class DomainControllerCacheKey
{
public string DomainName;
public int Port;
public bool GlobalCatalog;

protected bool Equals(DomainControllerCacheKey other)
{
return DomainName == other.DomainName && Port == other.Port && GlobalCatalog == other.GlobalCatalog;
}

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((DomainControllerCacheKey)obj);
}

public override int GetHashCode()
{
unchecked
{
var hashCode = (DomainName != null ? DomainName.GetHashCode() : 0);
hashCode = (hashCode * 397) ^ Port;
hashCode = (hashCode * 397) ^ GlobalCatalog.GetHashCode();
return hashCode;
}
}
}
}
15 changes: 15 additions & 0 deletions src/CommonLib/DomainWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.DirectoryServices.Protocols;

namespace SharpHoundCommonLib
{
public class DomainWrapper
{
public string DomainSID { get; set; }
public string DomainFQDN { get; set; }
public string DomainSearchBase { get; set; }
public string DomainConfigurationPath { get; set; }
public string DomainNetbiosName { get; set; }
}
}
3 changes: 2 additions & 1 deletion src/CommonLib/Enums/LdapErrorCodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ public enum LdapErrorCodes : int
Success = 0,
Busy = 51,
ServerDown = 81,
LocalError = 82
LocalError = 82,
KerberosAuthType = 83
}
}
12 changes: 12 additions & 0 deletions src/CommonLib/Exceptions/LdapAuthenticationException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;
using System.DirectoryServices.Protocols;

namespace SharpHoundCommonLib.Exceptions
{
public class LdapAuthenticationException : Exception
{
public LdapAuthenticationException(LdapException exception) : base("Error authenticating to LDAP", exception)
{
}
}
}
13 changes: 13 additions & 0 deletions src/CommonLib/Exceptions/LdapConnectionException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using System;
using System.DirectoryServices.Protocols;

namespace SharpHoundCommonLib.Exceptions
{
public class LdapConnectionException : Exception
{
public LdapConnectionException(LdapException innerException) : base("Failed during ldap connection tests", innerException)
{

}
}
}
9 changes: 9 additions & 0 deletions src/CommonLib/Exceptions/NoLdapDataException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System;

namespace SharpHoundCommonLib.Exceptions
{
public class NoLdapDataException : Exception
{

}
}
79 changes: 79 additions & 0 deletions src/CommonLib/Extensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using SharpHoundCommonLib.Enums;
using SharpHoundCommonLib.LDAPQueries;
using SearchScope = System.DirectoryServices.Protocols.SearchScope;

namespace SharpHoundCommonLib
{
Expand All @@ -23,6 +25,83 @@ static Extensions()
Log = Logging.LogProvider.CreateLogger("Extensions");
}

public class LdapConnectionTestResult
{
public bool Success { get; set; }
public LdapException Exception { get; set; }
public DomainWrapper DomainInfo { get; set; }
public string ServerName { get; set; }

public LdapConnectionTestResult(bool success, LdapException e, DomainWrapper info, string server)
{
Success = success;
Exception = e;
DomainInfo = info;
ServerName = server;
}
}

public static LdapConnectionTestResult TestConnection(this LdapConnection connection)
{
try
{
//Attempt an initial bind. If this fails, likely auth is invalid, or its not a valid target
connection.Bind();
}
catch (LdapException e)
{
return new LdapConnectionTestResult(false, e, null, null);
}

try
{
//Do an initial search request to get the rootDSE
//This ldap filter is equivalent to (objectclass=*)
var searchRequest = new SearchRequest("", new LDAPFilter().AddAllObjects().GetFilter(),
SearchScope.Base, null);
searchRequest.Controls.Add(new SearchOptionsControl(SearchOption.DomainScope));

var response = (SearchResponse)connection.SendRequest(searchRequest);
if (response == null)
{
return new LdapConnectionTestResult(false, null, null, null);
}

if (response.Entries.Count == 0)
{
connection.Dispose();
return new LdapConnectionTestResult(false, new LdapException((int)LdapErrorCodes.KerberosAuthType), null, null);
}

var entry = response.Entries[0];
var baseDN = entry.GetProperty(LDAPProperties.PrimaryNamingContext).ToUpper().Trim();
var configurationDN = entry.GetProperty(LDAPProperties.ConfigurationNamingContext).ToUpper().Trim();
var domainname = Helpers.DistinguishedNameToDomain(baseDN).ToUpper().Trim();
var servername = entry.GetProperty(LDAPProperties.ServerName);
var compName = servername.Substring(0, servername.IndexOf(',')).Substring(3).Trim();
var fullServerName = $"{compName}.{domainname}".ToUpper().Trim();

return new LdapConnectionTestResult(true, null, new DomainWrapper
{
DomainConfigurationPath = configurationDN,
DomainSearchBase = baseDN,
DomainFQDN = domainname
}, fullServerName);
}
catch (LdapException e)
{
try
{
connection.Dispose();
}
catch
{
//pass
}
return new LdapConnectionTestResult(false, e, null, null);
}
}

internal static async Task<List<T>> ToListAsync<T>(this IAsyncEnumerable<T> items)
{
var results = new List<T>();
Expand Down
12 changes: 9 additions & 3 deletions src/CommonLib/LDAPConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,20 @@ public class LDAPConfig
public string Password { get; set; } = null;
public string Server { get; set; } = null;
public int Port { get; set; } = 0;
public bool SSL { get; set; } = false;
public bool ForceSSL { get; set; } = false;
public bool DisableSigning { get; set; } = false;
public bool DisableCertVerification { get; set; } = false;
public AuthType AuthType { get; set; } = AuthType.Kerberos;

public int GetPort()
//Returns the port for connecting to LDAP. Will always respect a user's overridden config over anything else
public int GetPort(bool ssl)
{
return Port == 0 ? SSL ? 636 : 389 : Port;
if (Port != 0)
{
return Port;
}

return ssl ? 636 : 389;
}
}
}
36 changes: 36 additions & 0 deletions src/CommonLib/LDAPConnectionCacheKey.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
namespace SharpHoundCommonLib
{
public class LDAPConnectionCacheKey
{
public bool GlobalCatalog { get; }
public string Domain { get; }
public string Server { get; set; }

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);
}
}
}
}
5 changes: 5 additions & 0 deletions src/CommonLib/LDAPProperties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,5 +69,10 @@ public static class LDAPProperties
public const string CertificateTemplates = "certificatetemplates";
public const string CrossCertificatePair = "crosscertificatepair";
public const string Flags = "flags";
public const string PrimaryNamingContext = "primarynamingcontext";
public const string ConfigurationNamingContext = "configurationnamingcontext";
public const string NetbiosName = "netbiosName";
public const string DnsRoot = "dnsroot";
public const string ServerName = "servername";
}
}
Loading
Loading