11using System ;
2- using System . Net ;
32using System . DirectoryServices . Protocols ;
43using TheSprayer . Models ;
54using System . Collections . Generic ;
1211using System . Text ;
1312using System . Collections . Concurrent ;
1413using System . IO ;
14+ using System . Net ;
15+ using System . Net . Sockets ;
1516
1617namespace TheSprayer . Services
1718{
@@ -23,13 +24,17 @@ public class ActiveDirectoryService
2324 private readonly string _domainUserPass ;
2425 private readonly string _domainController ;
2526 private readonly string _distinguishedName ;
27+ private readonly string _resolvedDomainController ;
28+ private readonly LdapDirectoryIdentifier _ldapIdentifier ;
2629
2730 public ActiveDirectoryService ( string domain , string domainUser , string domainUserPass , string domainController )
2831 {
2932 _domain = domain ;
3033 _domainUser = domainUser ;
3134 _domainUserPass = domainUserPass ;
3235 _domainController = domainController ;
36+ _resolvedDomainController = ResolveDomainController ( domainController ) ;
37+ _ldapIdentifier = new LdapDirectoryIdentifier ( _resolvedDomainController , 389 , false , false ) ;
3338
3439 var splitDomain = _domain . Split ( '.' ) ;
3540 _distinguishedName = "" ;
@@ -206,12 +211,25 @@ public List<ActiveDirectoryUser> GetAllDomainUsers(IEnumerable<string> userSamAc
206211 // Loop through every response entry
207212 foreach ( SearchResultEntry entry in searchResponse . Entries )
208213 {
209- var sid = entry . Attributes . Contains ( "objectSid" ) && entry . Attributes [ "objectSid" ] . Count > 0
210- ? new SecurityIdentifier ( ( byte [ ] ) entry . Attributes [ "objectSid" ] [ 0 ] , 0 ) . Value
211- : null ;
214+ string sid = null ;
215+ if ( entry . Attributes . Contains ( "objectSid" ) && entry . Attributes [ "objectSid" ] . Count > 0 )
216+ {
217+ if ( OperatingSystem . IsWindows ( ) )
218+ {
219+ sid = new SecurityIdentifier ( ( byte [ ] ) entry . Attributes [ "objectSid" ] [ 0 ] , 0 ) . Value ;
220+ }
221+ }
212222
213223 var badPwdCount = entry . Attributes . GetIfExists < int ? > ( "badPwdCount" ) ;
214224 var lastBadPwdTime = entry . Attributes . GetIfExists < DateTime ? > ( "badPasswordTime" ) ;
225+
226+ // Sometimes badPasswordTime is just "0" which means never
227+ if ( lastBadPwdTime == null )
228+ {
229+ var intEntry = entry . Attributes . GetIfExists < int ? > ( "badPasswordTime" ) ;
230+ lastBadPwdTime = intEntry == 0 ? entry . Attributes . GetIfExists < DateTime ? > ( "createTimeStamp" ) : null ;
231+ }
232+
215233 if ( badPwdCount == null || lastBadPwdTime == null )
216234 {
217235 var sam = entry . Attributes . GetIfExists ( "sAMAccountName" ) ;
@@ -580,7 +598,8 @@ public static bool ShouldSprayUser(ActiveDirectoryUser user, PasswordPolicy defa
580598
581599 public bool TryValidateCredentials ( string username , string password )
582600 {
583- using var connection = new LdapConnection ( new LdapDirectoryIdentifier ( _domainController , 389 , false , false ) ) ;
601+ using var connection = new LdapConnection ( _ldapIdentifier ) ;
602+ connection . SessionOptions . ReferralChasing = ReferralChasingOptions . All ;
584603 connection . Credential = new NetworkCredential ( username , password , _domain ) ;
585604
586605 try
@@ -596,12 +615,51 @@ public bool TryValidateCredentials(string username, string password)
596615
597616 private LdapConnection CreateLdapConnection ( )
598617 {
599- var connection = new LdapConnection ( new LdapDirectoryIdentifier ( _domainController , 389 , false , false ) ) ;
618+ var connection = new LdapConnection ( _ldapIdentifier ) ;
619+ connection . SessionOptions . ReferralChasing = ReferralChasingOptions . All ;
600620 if ( ! string . IsNullOrWhiteSpace ( _domainUser ) && ! string . IsNullOrWhiteSpace ( _domainUserPass ) )
601621 {
602622 connection . Credential = new NetworkCredential ( _domainUser , _domainUserPass , _domain ) ;
603623 }
604624 return connection ;
605625 }
626+
627+ private static string ResolveDomainController ( string domainController )
628+ {
629+ if ( string . IsNullOrWhiteSpace ( domainController ) )
630+ {
631+ throw new ArgumentException ( "Domain controller must be provided" , nameof ( domainController ) ) ;
632+ }
633+
634+ if ( IPAddress . TryParse ( domainController , out _ ) )
635+ {
636+ return domainController ;
637+ }
638+
639+ try
640+ {
641+ var addresses = Dns . GetHostAddresses ( domainController )
642+ . Where ( a => a . AddressFamily == AddressFamily . InterNetwork || a . AddressFamily == AddressFamily . InterNetworkV6 )
643+ . ToArray ( ) ;
644+
645+ if ( ! addresses . Any ( ) )
646+ {
647+ ColorConsole . WriteLine ( $ "Warning: Unable to resolve domain controller { domainController } , falling back to original value.", ConsoleColor . Yellow ) ;
648+ return domainController ;
649+ }
650+
651+ if ( addresses . Length > 1 )
652+ {
653+ ColorConsole . WriteLine ( $ "Warning: Domain controller { domainController } resolves to multiple addresses; using { addresses [ 0 ] } for all operations.", ConsoleColor . Yellow ) ;
654+ }
655+
656+ return addresses [ 0 ] . ToString ( ) ;
657+ }
658+ catch ( Exception ex )
659+ {
660+ ColorConsole . WriteLine ( $ "Warning: Failed to resolve domain controller { domainController } : { ex . Message } . Using supplied value.", ConsoleColor . Yellow ) ;
661+ return domainController ;
662+ }
663+ }
606664 }
607665}
0 commit comments