using System; using System.Collections.Generic; using System.ComponentModel; using System.DirectoryServices; using System.DirectoryServices.AccountManagement; using System.IO; using System.Linq; using System.Net; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.AccessControl; using System.Security.Principal; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using C4IT.Logging; using static C4IT.Logging.cLogManager; namespace LiamAD { public class cActiveDirectoryBase { private cADLogonInfo privLogonInfo = null; public PrincipalContext adContext = null; public DirectoryEntry directoryEntry = null; public Exception LastException { get; private set; } = null; public string LastErrorMessage { get; private set; } = null; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ResetError() { LastException = null; LastErrorMessage = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetErrorException(string Action, Exception E, LogLevels lev = LogLevels.Error) { LastException = E; LastErrorMessage = Action + ": " + E.Message; cLogManager.LogEntry(Action, lev); } private async Task privLogonAsync(cADLogonInfo LogonInfo) { try { //TODO: remove dummy delay? await Task.Delay(0); ResetError(); adContext = new PrincipalContext(ContextType.Domain, LogonInfo.Domain, LogonInfo.User, new NetworkCredential("", LogonInfo.UserSecret).Password); var ldapPath = $"LDAP://{LogonInfo.Domain}/{LogonInfo.TargetGroupPath}"; directoryEntry = new DirectoryEntry { Path = ldapPath, Username = LogonInfo.User, Password = new NetworkCredential(LogonInfo.User, LogonInfo.UserSecret).Password, AuthenticationType = AuthenticationTypes.Secure | AuthenticationTypes.Sealing }; return adContext != null; } catch (Exception E) { SetErrorException("exception error while ad login", E, LogLevels.Debug); cLogManager.LogException(E, LogLevels.Debug); } return false; } private async Task privRelogon() { if (privLogonInfo == null) return false; var RetVal = await privLogonAsync(privLogonInfo); return RetVal; } public async Task LogonAsync(cADLogonInfo LogonInfo) { var RetVal = await privLogonAsync(LogonInfo); if (RetVal == true) privLogonInfo = LogonInfo; return RetVal; } internal AuthorizationRuleCollection GetAccessControlList(string path) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { DirectoryInfo dADir = new DirectoryInfo(path); var dAACL = dADir.GetAccessControl(); return dAACL.GetAccessRules(true, false, typeof(System.Security.Principal.SecurityIdentifier)); } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } internal string resolveSid(string sid) { try { return new System.Security.Principal.SecurityIdentifier(sid).Translate(typeof(System.Security.Principal.NTAccount)).ToString(); } catch (Exception E) { LogException(E); return null; } } internal async Task RequestSecurityGroupsListAsync(string groupFilter) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { await Task.Delay(0); var Result = privRequestSecurityGroupsListAsync(groupFilter); if (Result != null) { var RetVal = new cADCollectionBase(Result.Count); foreach (var Entry in Result) { var res = new cSecurityGroupResult(Entry); RetVal.Add(res); } return RetVal; } } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } return null; } private List privRequestSecurityGroupsListAsync(string groupFilter = null, string rawLDAPFilter = null) { ResetError(); List securityGroups = new List(); if (String.IsNullOrEmpty(privLogonInfo.TargetGroupPath) || (string.IsNullOrEmpty(groupFilter) && string.IsNullOrEmpty(rawLDAPFilter))) { return new List(); } try { var ctx = adContext; var entry = directoryEntry; using (DirectorySearcher dSearch = new DirectorySearcher(entry)) { dSearch.Filter = string.IsNullOrEmpty(rawLDAPFilter) ? "(&(" + groupFilter + ")(objectClass=group))" : rawLDAPFilter; dSearch.PageSize = 100000; SearchResultCollection sr = dSearch.FindAll(); if (sr.Count > 0) { foreach (SearchResult k in sr) { var sid = new SecurityIdentifier(k.Properties["objectSid"][0] as byte[], 0).Value; var dn = k.Properties["distinguishedname"][0].ToString(); // Initialisieren Sie die managedBy-SID als null string managedBySid = null; // Prüfen, ob das managedBy-Attribut existiert und nicht null ist if (k.Properties.Contains("managedBy") && k.Properties["managedBy"].Count > 0) { // managedBy-DN erhalten string managedByDn = k.Properties["managedBy"][0].ToString(); // Erstellen eines DirectoryEntry-Objekts für den managedBy-DN using (DirectoryEntry managedByEntry = new DirectoryEntry($"LDAP://{managedByDn}")) { if (managedByEntry.Properties.Contains("objectSid") && managedByEntry.Properties["objectSid"].Count > 0) { // SID des managedBy-Objekts erhalten managedBySid = new SecurityIdentifier(managedByEntry.Properties["objectSid"][0] as byte[], 0).Value; } } } // Neues SecurityGroup-Objekt erstellen cSecurityGroupResult group = new cSecurityGroupResult() { ID = sid, Path = dn, DisplayName = k.Properties["Name"][0].ToString(), Description = k.Properties.Contains("Description") ? k.Properties["Description"][0].ToString() : string.Empty, Scope = (GroupScope)GroupPrincipal.FindByIdentity(ctx, IdentityType.Sid, sid).GroupScope, ManagedBySID = managedBySid }; securityGroups.Add(group); } } } } catch (Exception e) { cLogManager.LogException(e); return new List(securityGroups); } return securityGroups; } public class cADCollectionBase : SortedList { public cADCollectionBase() { } public cADCollectionBase(int n) : base(n) { } public void Add(cADResultBase adr) { if (!this.ContainsKey(adr.ID)) this.Add(adr.ID, adr); } } public class cSecurityGroupResult : cADResultBase { public cSecurityGroupResult() { } public cSecurityGroupResult(cADResultBase b) : base(b) { this.ManagedBySID = (b as cSecurityGroupResult)?.ManagedBySID; } public GroupScope Scope { get; internal set; } public string ManagedBySID { get; internal set; } } public class cADUserResult : cADResultBase { public string GivenName { get; internal set; } public string SurName { get; internal set; } public string UserPrincipalName { get; internal set; } public string Email { get; internal set; } public cADUserResult() { } public cADUserResult(cADResultBase b) : base(b) { } public cADUserResult(Principal Result) : base(Result) { UserPrincipalName = Result.UserPrincipalName; } public GroupScope Scope { get; internal set; } } public class cADResultBase { public string ID { get; set; } = null; public string DisplayName { get; set; } = null; public string Path { get; set; } = null; public DateTime CreatedDate { get; set; } = DateTime.MinValue; public string Description { get; set; } = null; public cADResultBase() { } public cADResultBase(cADResultBase Result) { if (Result == null) return; ID = Result.ID; DisplayName = Result.DisplayName; Description = Result.Description; Path = Result.Path; } public cADResultBase(Principal Result) { if (Result == null) return; ID = Result.Sid.ToString(); DisplayName = Result.DisplayName; Path = Result.DistinguishedName; } } /// /// Asynchrones Abrufen des managedBy-Attributs einer AD-Gruppe anhand ihrer SID. /// Gibt den Distinguished Name (DN) des Managers zurück. /// /// Die SID der AD-Gruppe. /// Der DN des Managers oder null, falls nicht gefunden. public async Task GetManagedByDnAsync(string dn) { try { // Simuliert einen asynchronen Aufruf await Task.Yield(); using (var group = GroupPrincipal.FindByIdentity(adContext, IdentityType.DistinguishedName, dn)) { if (group == null) { cLogManager.LogEntry($"Gruppe mit dn {dn} nicht gefunden."); return null; } // Zugriff auf das underlying DirectoryEntry, um das managedBy-Attribut zu lesen var directoryEntry = group.GetUnderlyingObject() as DirectoryEntry; if (directoryEntry != null && directoryEntry.Properties.Contains("managedBy")) { var managedByValue = directoryEntry.Properties["managedBy"].Value as string; if (!string.IsNullOrEmpty(managedByValue)) { return managedByValue; } else { cLogManager.LogEntry($"managedBy-Attribut für Gruppe mit dn {dn} ist leer."); } } else { cLogManager.LogEntry($"Gruppe mit dn {dn} hat kein managedBy-Attribut."); } } } catch (Exception ex) { cLogManager.LogException(ex); } return null; } /// /// Asynchrones Abrufen der Mitglieder des Managers einer AD-Gruppe anhand des groupSid. /// Verwendet den DN des Managers. /// /// Die SID der AD-Gruppe. /// Eine cADCollectionBase mit den Mitgliedern oder null. public async Task GetManagedByMembersAsync(string groupSid) { try { var managedByDn = await GetManagedByDnAsync(groupSid); if (!string.IsNullOrEmpty(managedByDn)) { return await GetMembersByDnAsync(managedByDn); } else { cLogManager.LogEntry($"Keine gültige managedBy DN für Gruppe mit SID {groupSid} gefunden."); } } catch (Exception ex) { cLogManager.LogException(ex); } return null; } /// /// Asynchrones Abrufen der Mitglieder einer AD-Gruppe anhand des Distinguished Name (DN). /// /// Der Distinguished Name der AD-Gruppe. /// Eine cADCollectionBase mit den Mitgliedern oder null. public async Task GetMembersByDnAsync(string dn) { try { // Simuliert einen asynchronen Aufruf await Task.Yield(); var result = privGetMembersByDnAsync(dn).ToList(); if (result != null && result.Any()) { var retVal = new cADCollectionBase(result.Count); foreach (var entry in result) { var res = new cADUserResult(entry); if (!string.IsNullOrEmpty(res.Path)) retVal.Add(res); } return retVal; } else { cLogManager.LogEntry($"Keine Mitglieder für Gruppe mit DN {dn} gefunden."); } } catch (Exception e) { cLogManager.LogException(e); } return null; } /// /// Interne Methode zum Abrufen der Mitglieder einer AD-Gruppe anhand des Distinguished Name (DN). /// /// Der Distinguished Name der AD-Gruppe. /// Eine PrincipalSearchResult mit den Mitgliedern oder null. private PrincipalSearchResult privGetMembersByDnAsync(string dn) { try { using (var group = GroupPrincipal.FindByIdentity(adContext, IdentityType.DistinguishedName, dn)) { if (group == null) { cLogManager.LogEntry($"Gruppe mit DN {dn} nicht gefunden."); return null; } else { return group.GetMembers(true); } } } catch (Exception e) { cLogManager.LogException(e); } return null; } /// /// Asynchrones Abrufen der Mitglieder einer AD-Gruppe anhand ihrer SID. /// Diese Methode bleibt unverändert und kann weiterhin verwendet werden. /// /// Die SID der AD-Gruppe. /// Eine cADCollectionBase mit den Mitgliedern oder null. internal async Task GetMembersAsync(string sid) { try { // Simuliert einen asynchronen Aufruf await Task.Yield(); var result = privGetMembersAsync(sid).ToList(); if (result != null && result.Any()) { var retVal = new cADCollectionBase(result.Count); foreach (var entry in result) { var res = new cADUserResult(entry); if (!string.IsNullOrEmpty(res.Path)) retVal.Add(res); } return retVal; } else { cLogManager.LogEntry($"Keine Mitglieder für Gruppe mit SID {sid} gefunden."); } } catch (Exception e) { cLogManager.LogException(e); } return null; } /// /// Interne Methode zum Abrufen der Mitglieder einer AD-Gruppe anhand ihrer SID. /// Diese Methode bleibt unverändert und kann weiterhin verwendet werden. /// /// Die SID der AD-Gruppe. /// Eine PrincipalSearchResult mit den Mitgliedern oder null. private PrincipalSearchResult privGetMembersAsync(string sid) { try { using (var group = GroupPrincipal.FindByIdentity(adContext, IdentityType.Sid, sid)) { if (group == null) { cLogManager.LogEntry($"Gruppe mit SID {sid} nicht gefunden."); return null; } else { return group.GetMembers(true); } } } catch (Exception e) { cLogManager.LogException(e); } return null; } } }