11using System ;
22using System . Collections . Generic ;
33using System . Diagnostics . CodeAnalysis ;
4+ using System . Linq ;
45using System . Runtime . CompilerServices ;
56using System . Threading . Tasks ;
7+ using CommonLibTest . Facades ;
8+ using Microsoft . Extensions . Logging ;
69using Moq ;
710using SharpHoundCommonLib ;
11+ using SharpHoundCommonLib . Enums ;
12+ using SharpHoundCommonLib . Ntlm ;
813using SharpHoundCommonLib . Processors ;
914using SharpHoundRPC ;
1015using Xunit ;
@@ -15,6 +20,8 @@ namespace CommonLibTest {
1520 public class DCLdapProcessorTest : IDisposable {
1621
1722 private readonly ITestOutputHelper _testOutputHelper ;
23+ private readonly string SEC_E_UNSUPPORTED_FUNCTION = "80090302" ;
24+ private readonly string SEC_E_BAD_BINDINGS = "80090346" ;
1825
1926 public DCLdapProcessorTest ( ITestOutputHelper testOutputHelper ) {
2027 _testOutputHelper = testOutputHelper ;
@@ -27,7 +34,7 @@ public void Dispose() {
2734 public async Task DCLdapProcessor_Scan ( ) {
2835 var mockProcessor = new Mock < DCLdapProcessor > ( It . IsAny < int > ( ) , "primary.testlab.local" , null ) ;
2936
30- mockProcessor . Setup ( x => x . Authenticate ( It . IsAny < Uri > ( ) , It . IsAny < LdapAuthOptions > ( ) ) ) . ReturnsAsync ( false ) ;
37+ mockProcessor . Setup ( x => x . Authenticate ( It . IsAny < Uri > ( ) , It . IsAny < LdapAuthOptions > ( ) , null , null ) ) . ReturnsAsync ( false ) ;
3138
3239 mockProcessor . Setup ( x => x . TestLdapPort ( ) ) . ReturnsAsync ( true ) ;
3340 mockProcessor . Setup ( x => x . TestLdapsPort ( ) ) . ReturnsAsync ( true ) ;
@@ -54,7 +61,7 @@ public async Task DCLdapProcessor_Scan() {
5461 public async Task DCLdapProcessor_Scan_Failed ( ) {
5562 var mockProcessor = new Mock < DCLdapProcessor > ( It . IsAny < int > ( ) , "primary.testlab.local" , null ) ;
5663
57- mockProcessor . Setup ( x => x . Authenticate ( It . IsAny < Uri > ( ) , It . IsAny < LdapAuthOptions > ( ) ) ) . Throws ( new Exception ( "Error" ) ) ;
64+ mockProcessor . Setup ( x => x . Authenticate ( It . IsAny < Uri > ( ) , It . IsAny < LdapAuthOptions > ( ) , null , null ) ) . Throws ( new Exception ( "Error" ) ) ;
5865
5966 mockProcessor . Setup ( x => x . TestLdapPort ( ) ) . ReturnsAsync ( true ) ;
6067 mockProcessor . Setup ( x => x . TestLdapsPort ( ) ) . ReturnsAsync ( true ) ;
@@ -83,7 +90,7 @@ public async Task DCLdapProcessor_Scan_Failed() {
8390 public async Task DCLdapProcessor_CheckScan_Timeout ( ) {
8491 var mockProcessor = new Mock < DCLdapProcessor > ( 2 , "primary.testlab.local" , null ) ;
8592
86- mockProcessor . Setup ( x => x . Authenticate ( It . IsAny < Uri > ( ) , It . IsAny < LdapAuthOptions > ( ) ) ) . ReturnsAsync ( ( ) => {
93+ mockProcessor . Setup ( x => x . Authenticate ( It . IsAny < Uri > ( ) , It . IsAny < LdapAuthOptions > ( ) , null , null ) ) . ReturnsAsync ( ( ) => {
8794 Task . Delay ( 100 ) . Wait ( ) ;
8895 return false ;
8996 } ) ;
@@ -111,7 +118,7 @@ public async Task DCLdapProcessor_CheckScan_Timeout() {
111118 public async Task DCLdapProcessor_CheckIsNtlmSigningRequired ( )
112119 {
113120 var mockProcessor = new Mock < DCLdapProcessor > ( It . IsAny < int > ( ) , "primary.testlab.local" , null ) ;
114- mockProcessor . Setup ( x => x . Authenticate ( It . IsAny < Uri > ( ) , It . IsAny < LdapAuthOptions > ( ) ) ) . ReturnsAsync ( false ) ;
121+ mockProcessor . Setup ( x => x . Authenticate ( It . IsAny < Uri > ( ) , It . IsAny < LdapAuthOptions > ( ) , null , null ) ) . ReturnsAsync ( false ) ;
115122 var processor = mockProcessor . Object ;
116123 var result = await processor . CheckIsNtlmSigningRequired ( ) ;
117124 Assert . True ( result . IsSuccess ) ;
@@ -122,11 +129,146 @@ public async Task DCLdapProcessor_CheckIsNtlmSigningRequired()
122129 public async Task DCLdapProcessor_CheckIsNtlmSigningRequired_Exception ( )
123130 {
124131 var mockProcessor = new Mock < DCLdapProcessor > ( It . IsAny < int > ( ) , "primary.testlab.local" , null ) ;
125- mockProcessor . Setup ( x => x . Authenticate ( It . IsAny < Uri > ( ) , It . IsAny < LdapAuthOptions > ( ) ) ) . Throws ( new Exception ( "Error" ) ) ;
132+ mockProcessor . Setup ( x => x . Authenticate ( It . IsAny < Uri > ( ) , It . IsAny < LdapAuthOptions > ( ) , null , null ) ) . Throws ( new Exception ( "Error" ) ) ;
126133 var processor = mockProcessor . Object ;
127134 var result = await processor . CheckIsNtlmSigningRequired ( ) ;
128135 Assert . True ( result . IsFailed ) ;
129136 Assert . Contains ( "CheckIsNtlmSigningRequired failed: System.Exception: Error" , result . Error ) ;
130137 }
138+
139+ [ Fact ]
140+ public async Task DCLdapProcessor_Authenticate_InvalidCredentialsException_SEC_E_UNSUPPORTED_FUNCTION ( )
141+ {
142+ var exception = "ErrorTest" ;
143+ var endpoint = "http://primary.testlab.local/" ;
144+ var expected = $ "LDAP endpoint '{ endpoint } ' does not support NTLM";
145+
146+ var mockLogger = new Mock < ILogger < DCLdapProcessor > > ( ) ;
147+ var mockLdapTransport = new Mock < LdapTransport > ( null , It . IsAny < Uri > ( ) ) ;
148+ mockLdapTransport . Setup ( x => x . InitializeConnectionAsync ( It . IsAny < int > ( ) ) ) . Throws ( new LdapNativeException ( "Error" , ( int ) LdapErrorCodes . InvalidCredentials , SEC_E_UNSUPPORTED_FUNCTION ) ) ;
149+ var processor = new DCLdapProcessor ( It . IsAny < int > ( ) , "primary.testlab.local" , mockLogger . Object ) ;
150+ var result = await processor . Authenticate ( new Uri ( endpoint ) , It . IsAny < LdapAuthOptions > ( ) , null , mockLdapTransport . Object ) ;
151+ Assert . False ( result ) ;
152+ mockLogger . VerifyLogContains ( LogLevel . Debug , expected ) ;
153+ }
154+
155+ [ Fact ]
156+ public async Task DCLdapProcessor_Authenticate_InvalidCredentialsException_SEC_E_BAD_BINDINGS ( )
157+ {
158+ var exception = "ErrorTest" ;
159+ var endpoint = "http://primary.testlab.local/" ;
160+ var expected = $ "Bad bindings with the LDAPS endpoint '{ endpoint } '. Server error: { SEC_E_BAD_BINDINGS } ";
161+
162+ var mockLogger = new Mock < ILogger < DCLdapProcessor > > ( ) ;
163+ var mockLdapTransport = new Mock < LdapTransport > ( null , It . IsAny < Uri > ( ) ) ;
164+ mockLdapTransport . Setup ( x => x . InitializeConnectionAsync ( It . IsAny < int > ( ) ) ) . Throws ( new LdapNativeException ( "Error" , ( int ) LdapErrorCodes . InvalidCredentials , SEC_E_BAD_BINDINGS ) ) ;
165+ var processor = new DCLdapProcessor ( It . IsAny < int > ( ) , "primary.testlab.local" , mockLogger . Object ) ;
166+ var result = await processor . Authenticate ( new Uri ( endpoint ) , It . IsAny < LdapAuthOptions > ( ) , null , mockLdapTransport . Object ) ;
167+ Assert . False ( result ) ;
168+ mockLogger . VerifyLogContains ( LogLevel . Debug , expected ) ;
169+ }
170+
171+ [ Fact ]
172+ public async Task DCLdapProcessor_Authenticate_InvalidCredentialsException_Unhandled ( )
173+ {
174+ var exception = "ErrorTest" ;
175+ var endpoint = "http://primary.testlab.local/" ;
176+ var expected = $ "Unhandled LDAP InvalidCred error code during LDAP test: SharpHoundCommonLib.Ntlm.LdapNativeException: { exception } . LDAP error code: { ( int ) LdapErrorCodes . InvalidCredentials } .";
177+
178+ var mockLogger = new Mock < ILogger < DCLdapProcessor > > ( ) ;
179+ var mockLdapTransport = new Mock < LdapTransport > ( null , It . IsAny < Uri > ( ) ) ;
180+ mockLdapTransport . Setup ( x => x . InitializeConnectionAsync ( It . IsAny < int > ( ) ) ) . Throws ( new LdapNativeException ( exception , ( int ) LdapErrorCodes . InvalidCredentials , "80090347" ) ) ;
181+ var processor = new DCLdapProcessor ( It . IsAny < int > ( ) , "primary.testlab.local" , mockLogger . Object ) ;
182+ var result = await processor . Authenticate ( new Uri ( endpoint ) , It . IsAny < LdapAuthOptions > ( ) , null , mockLdapTransport . Object ) ;
183+ Assert . False ( result ) ;
184+ mockLogger . VerifyLogContains ( LogLevel . Error , expected ) ;
185+ }
186+
187+ [ Fact ]
188+ public async Task DCLdapProcessor_Authenticate_StrongAuthRequiredException ( )
189+ {
190+ var exception = "ErrorTest" ;
191+ var endpoint = "http://primary.testlab.local/" ;
192+ var expected = $ "LDAP requires signing. Endpoint: { endpoint } ";
193+
194+ var mockLogger = new Mock < ILogger < DCLdapProcessor > > ( ) ;
195+ var mockLdapTransport = new Mock < LdapTransport > ( null , It . IsAny < Uri > ( ) ) ;
196+ mockLdapTransport . Setup ( x => x . InitializeConnectionAsync ( It . IsAny < int > ( ) ) ) . Throws ( new LdapNativeException ( exception , ( int ) LdapErrorCodes . StrongAuthRequired , null ) ) ;
197+ var processor = new DCLdapProcessor ( It . IsAny < int > ( ) , "primary.testlab.local" , mockLogger . Object ) ;
198+ var result = await processor . Authenticate ( new Uri ( endpoint ) , It . IsAny < LdapAuthOptions > ( ) , null , mockLdapTransport . Object ) ;
199+ Assert . False ( result ) ;
200+ mockLogger . VerifyLog ( LogLevel . Debug , expected ) ;
201+ }
202+
203+ [ Fact ]
204+ public async Task DCLdapProcessor_Authenticate_ServerDownException ( )
205+ {
206+ var exception = "ErrorTest" ;
207+ var endpoint = "http://primary.testlab.local/" ;
208+ var expected = $ "LDAP endpoint '{ endpoint } ' not accessible";
209+
210+ var mockLogger = new Mock < ILogger < DCLdapProcessor > > ( ) ;
211+ var mockLdapTransport = new Mock < LdapTransport > ( null , It . IsAny < Uri > ( ) ) ;
212+ mockLdapTransport . Setup ( x => x . InitializeConnectionAsync ( It . IsAny < int > ( ) ) ) . Throws ( new LdapNativeException ( exception , ( int ) LdapErrorCodes . ServerDown ) ) ;
213+ var processor = new DCLdapProcessor ( It . IsAny < int > ( ) , "primary.testlab.local" , mockLogger . Object ) ;
214+ var result = await processor . Authenticate ( new Uri ( endpoint ) , It . IsAny < LdapAuthOptions > ( ) , null , mockLdapTransport . Object ) ;
215+ Assert . False ( result ) ;
216+ mockLogger . VerifyLog ( LogLevel . Debug , expected ) ;
217+ }
218+
219+ [ Fact ]
220+ public async Task DCLdapProcessor_Authenticate_LdapUnhandledException ( )
221+ {
222+ var endpoint = "http://primary.testlab.local/" ;
223+ var exception = "ErrorTest" ;
224+ var expected = $ "Unhandled LdapException error code during LDAP test: SharpHoundCommonLib.Ntlm.LdapNativeException: { exception } . LDAP error code: { ( int ) LdapErrorCodes . LocalError } ";
225+
226+ var mockLogger = new Mock < ILogger < DCLdapProcessor > > ( ) ;
227+ var mockLdapTransport = new Mock < LdapTransport > ( null , It . IsAny < Uri > ( ) ) ;
228+ mockLdapTransport . Setup ( x => x . InitializeConnectionAsync ( It . IsAny < int > ( ) ) ) . Throws ( new LdapNativeException ( exception , ( int ) LdapErrorCodes . LocalError ) ) ;
229+ var processor = new DCLdapProcessor ( It . IsAny < int > ( ) , "primary.testlab.local" , mockLogger . Object ) ;
230+ var result = await processor . Authenticate ( new Uri ( endpoint ) , It . IsAny < LdapAuthOptions > ( ) , null , mockLdapTransport . Object ) ;
231+ Assert . False ( result ) ;
232+ mockLogger . VerifyLogContains ( LogLevel . Error , expected ) ;
233+ }
234+
235+ [ Fact ]
236+ public async Task DCLdapProcessor_Authenticate_InvalidOperationException ( )
237+ {
238+ var endpoint = "http://primary.testlab.local/" ;
239+ var exception = "Server did return a challenge" ;
240+ var expected = $ "LDAP InvalidOperationException: { exception } ";
241+
242+ var mockLogger = new Mock < ILogger < DCLdapProcessor > > ( ) ;
243+ var mockLdapTransport = new Mock < LdapTransport > ( null , It . IsAny < Uri > ( ) ) ;
244+ var mockAuthenticator = new Mock < NtlmAuthenticationHandler > ( It . IsAny < string > ( ) , null ) ;
245+ mockLdapTransport . Setup ( x => x . InitializeConnectionAsync ( It . IsAny < int > ( ) ) ) . Verifiable ( ) ;
246+ mockAuthenticator . Setup ( x => x . PerformNtlmAuthenticationAsync ( It . IsAny < INtlmTransport > ( ) ) )
247+ . Throws ( new InvalidOperationException ( exception ) ) ;
248+ var processor = new DCLdapProcessor ( It . IsAny < int > ( ) , "primary.testlab.local" , mockLogger . Object ) ;
249+ var result = await processor . Authenticate ( new Uri ( endpoint ) , It . IsAny < LdapAuthOptions > ( ) , mockAuthenticator . Object , mockLdapTransport . Object ) ;
250+ Assert . False ( result ) ;
251+ mockLogger . VerifyLog ( LogLevel . Debug , expected ) ;
252+ }
253+
254+ [ Fact ]
255+ public async Task DCLdapProcessor_Authenticate_UnhandledException ( )
256+ {
257+ var endpoint = "http://primary.testlab.local/" ;
258+ var exception = "Unhandled exception" ;
259+ var expected = $ "An unhandled error occurred during the LDAP test: System.Exception: { exception } ";
260+
261+ var mockLogger = new Mock < ILogger < DCLdapProcessor > > ( ) ;
262+ var mockLdapTransport = new Mock < LdapTransport > ( null , It . IsAny < Uri > ( ) ) ;
263+ var mockAuthenticator = new Mock < NtlmAuthenticationHandler > ( It . IsAny < string > ( ) , null ) ;
264+ mockLdapTransport . Setup ( x => x . InitializeConnectionAsync ( It . IsAny < int > ( ) ) ) . Verifiable ( ) ;
265+ mockAuthenticator . Setup ( x => x . PerformNtlmAuthenticationAsync ( It . IsAny < INtlmTransport > ( ) ) )
266+ . Throws ( new Exception ( exception ) ) ;
267+ var processor = new DCLdapProcessor ( It . IsAny < int > ( ) , "primary.testlab.local" , mockLogger . Object ) ;
268+ var result = await processor . Authenticate ( new Uri ( endpoint ) , It . IsAny < LdapAuthOptions > ( ) , mockAuthenticator . Object , mockLdapTransport . Object ) ;
269+ Assert . False ( result ) ;
270+ mockLogger . VerifyLogContains ( LogLevel . Error , expected ) ;
271+ }
272+
131273 }
132274}
0 commit comments