using System; using System.Collections.Generic; using System.DirectoryServices.AccountManagement; using System.IO; using System.Linq; using System.Reflection; using System.Security.AccessControl; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; using C4IT.Logging; using C4IT.Matrix42.ServerInfo; using LiamNtfs; using static C4IT.Logging.cLogManager; using static LiamNtfs.cActiveDirectoryBase; namespace C4IT.LIAM { public static class LiamInitializer { static public cLiamProviderBase CreateInstance(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData) { return new cLiamProviderNtfs(LiamConfiguration, ProviderData); } } public class cLiamProviderNtfs : cLiamProviderBase { public static Guid nftsModuleId = new Guid("77e213a1-6517-ea11-4881-000c2980fd94"); public readonly cNtfsBase ntfsBase = new cNtfsBase(); public readonly cActiveDirectoryBase activeDirectoryBase = new cActiveDirectoryBase(); //public readonly bool WithoutPrivateFolders = true; public cLiamProviderNtfs(cLiamConfiguration LiamConfiguration, cLiamProviderData ProviderData) : base(LiamConfiguration, ProviderData) { this.ReplaceNtfsCustomTags(); } public override async Task LogonAsync() { if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(nftsModuleId)) { LogEntry($"Error: License not valid", LogLevels.Error); return false; } return await LogonAsync(true); } private void ReplaceNtfsCustomTags() { foreach (var namingConvention in NamingConventions) { String key = null; String value = null; key = "GROUPTYPEPOSTFIX"; if (namingConvention.AccessRole == eLiamAccessRoles.Owner) { value = CustomTags["Filesystem_GroupOwnerTag"]; } else if (namingConvention.AccessRole == eLiamAccessRoles.Write) { value = CustomTags["Filesystem_GroupWriteTag"]; } else if (namingConvention.AccessRole == eLiamAccessRoles.Read) { value = CustomTags["Filesystem_GroupReadTag"]; } else if (namingConvention.AccessRole == eLiamAccessRoles.Traverse) { value = CustomTags["Filesystem_GroupTraverseTag"]; } if (!String.IsNullOrEmpty(key) && !String.IsNullOrEmpty(value)) { namingConvention.DescriptionTemplate = namingConvention.DescriptionTemplate.Replace($"{{{{{key}}}}}", value); namingConvention.NamingTemplate = namingConvention.NamingTemplate.Replace($"{{{{{key}}}}}", value); namingConvention.Wildcard = namingConvention.Wildcard.Replace($"{{{{{key}}}}}", value); } value = null; key = "SCOPE"; if (namingConvention.Scope == eLiamAccessRoleScopes.DomainLocal) { value = CustomTags["Filesystem_GroupDomainLocalTag"]; } else if (namingConvention.Scope == eLiamAccessRoleScopes.Global) { value = CustomTags["Filesystem_GroupGlobalTag"]; } if (!String.IsNullOrEmpty(key) && !String.IsNullOrEmpty(value)) { namingConvention.DescriptionTemplate = namingConvention.DescriptionTemplate.Replace($"{{{{{key}}}}}", value); namingConvention.NamingTemplate = namingConvention.NamingTemplate.Replace($"{{{{{key}}}}}", value); namingConvention.Wildcard = namingConvention.Wildcard.Replace($"{{{{{key}}}}}", value); } } } public async Task LogonAsync(bool force = false) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { var LI = new cNtfsLogonInfo() { Domain = Domain, User = Credential?.Identification, UserSecret = Credential?.Secret, TargetNetworkName = RootPath, TargetGroupPath = this.GroupPath }; var RetVal = await ntfsBase.LogonAsync(LI) && await activeDirectoryBase.LogonAsync(LI); return RetVal; } catch (Exception E) { LogException(E); } finally { LogMethodEnd(CM); } return false; } public override async Task> getDataAreasAsync(int Depth = -1) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(nftsModuleId)) { LogEntry($"Error: License not valid", LogLevels.Error); return new List(); } if (!await LogonAsync()) return null; if (string.IsNullOrEmpty(this.RootPath)) return null; var DataAreas = new List(); var rootpathSplit = this.RootPath.Split(new string[] { "\\" }, StringSplitOptions.RemoveEmptyEntries); cLiamNtfsShare share = null; cLiamNtfsFolder NtfsRootFolder = null; switch (rootpathSplit.Length) { case 0: case 1: return null; case 2: { share = new cLiamNtfsShare(this, new cNtfsResultShare() { DisplayName = rootpathSplit.Last(), Path = RootPath, Level = 0 }); DataAreas.Add(share); break; } default: { NtfsRootFolder = new cLiamNtfsFolder(this, null, null, new cNtfsResultFolder() { DisplayName = rootpathSplit.Last(), Path = RootPath, Level = 0 }); DataAreas.Add(NtfsRootFolder); break; } } var DAL = await ntfsBase.RequestFoldersListAsync(this.RootPath, Depth); if (DAL == null) return null; foreach (var Entry in DAL) { if (!string.IsNullOrEmpty(this.DataAreaRegEx) && !Regex.Match(Entry.Value.DisplayName, this.DataAreaRegEx).Success) continue; var Folder = new cLiamNtfsFolder(this, share, NtfsRootFolder, (cNtfsResultFolder)Entry.Value); DataAreas.Add(Folder); } return DataAreas; } catch (Exception E) { LogException(E); } finally { LogMethodEnd(CM); } return null; } public override async Task LoadDataArea(string UID) { //TODO implement LoadDataArea var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { await Task.Delay(0); if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(nftsModuleId)) { LogEntry($"Error: License not valid", LogLevels.Error); return null; } var splt = UID.Split(System.IO.Path.DirectorySeparatorChar); var name = Path.GetDirectoryName(UID); switch (splt.Length) { case 0: case 1: return null; case 2: { return new cLiamNtfsShare(this, new cNtfsResultShare() { DisplayName = name, Path = UID, Level = getDepth(UID) }); } default: { return new cLiamNtfsFolder(this, null, null, new cNtfsResultFolder() { DisplayName = name, Path = UID, Level = getDepth(UID) }); } } } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } public override async Task> getSecurityGroupsAsync(string groupFilter) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { if (!cC4ITLicenseM42ESM.Instance.IsValid || !cC4ITLicenseM42ESM.Instance.Modules.ContainsKey(nftsModuleId)) { LogEntry($"Error: License not valid", LogLevels.Error); return new List(); } if (!await LogonAsync()) return null; if (string.IsNullOrEmpty(this.GroupPath)) return null; var SecurityGroups = new List(); var SGL = await activeDirectoryBase.RequestSecurityGroupsListAsync(groupFilter); if (SGL == null) return null; foreach (var Entry in SGL) { if (!string.IsNullOrEmpty(this.GroupRegEx) && !Regex.Match(Entry.Value.DisplayName, this.GroupRegEx).Success) continue; var SecurityGroup = new cLiamAdGroup(this, (cSecurityGroupResult)Entry.Value); SecurityGroups.Add(SecurityGroup); } return SecurityGroups; } catch (Exception E) { LogException(E); } finally { LogMethodEnd(CM); } return null; } public int getDepth(string path) { return getDepth(this.RootPath, path); } public static int getDepth(DirectoryInfo root, DirectoryInfo folder) { var rootDepth = root.FullName.TrimEnd(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar).Length; var folderDepth = folder.FullName.TrimEnd(Path.DirectorySeparatorChar).Split(Path.DirectorySeparatorChar).Length; return folderDepth - rootDepth; } public static int getDepth(string root, string folder) { return getDepth(new DirectoryInfo(root), new DirectoryInfo(folder)); } public override string GetLastErrorMessage() { throw new NotImplementedException(); } } public class cLiamNtfsShare : cLiamDataAreaBase { public new readonly cLiamProviderNtfs Provider = null; private readonly cNtfsResultBase Share = null; public cLiamNtfsShare(cLiamProviderNtfs Provider, cNtfsResultBase Share) : base(Provider) { this.Provider = Provider; this.Share = Share; this.DisplayName = Share.Path.Split('\\').Last(); this.TechnicalName = Share.Path; this.UID = cLiamNtfsFolder.GetUniqueDataAreaID(Share.Path); this.Level = Share.Level; this.DataType = eLiamDataAreaTypes.NtfsShare; this.SupportsOwners = false; this.SupportsPermissions = false; } internal async Task> getFolders() { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { await Task.Delay(0); var RetVal = new List(0); return RetVal; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } public override async Task> getChildrenAsync(int Depth = -1) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { await Task.Delay(0); var RetVal = new List(); return RetVal; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } } public class cLiamAdGroup : cLiamDataAreaBase { public new readonly cLiamProviderNtfs Provider = null; public readonly string dn = null; public readonly string scope = null; public override Task> getChildrenAsync(int Depth = -1) { throw new NotImplementedException(); } public cLiamAdGroup(cLiamProviderNtfs Provider, cSecurityGroupResult secGroup) : base(Provider) { this.UID = secGroup.ID; this.TechnicalName = secGroup.DisplayName; this.Provider = Provider; this.dn = secGroup.Path; this.scope = secGroup.Scope.ToString(); } } public class cLiamNtfsFolder : cLiamDataAreaBase { public new readonly cLiamProviderNtfs Provider = null; public readonly cLiamNtfsShare Share = null; public readonly cLiamNtfsFolder NtfsRootFolder = null; public string OwnerGroupIdentifier = "S-1-0-0"; public string WriteGroupIdentifier = "S-1-0-0"; public string ReadGroupIdentifier = "S-1-0-0"; public string TraverseGroupIdentifier = "S-1-0-0"; public cLiamNtfsFolder(cLiamProviderNtfs Provider, cLiamNtfsShare share, cLiamNtfsFolder ntfsRootFolder, cNtfsResultFolder NtfsFolder) : base(Provider) { var ntfsParent = NtfsFolder.Parent; this.Provider = Provider; this.NtfsRootFolder = ntfsRootFolder; this.Share = share; this.TechnicalName = NtfsFolder.Path; this.UID =GetUniqueDataAreaID(NtfsFolder.Path); this.DisplayName = new DirectoryInfo(NtfsFolder.Path).Name; this.Level = NtfsFolder.Level; this.DataType = eLiamDataAreaTypes.NtfsFolder; this.SupportsPermissions = true; this.CreatedDate = NtfsFolder.CreatedDate; if (ntfsParent != null) { this.ParentUID = GetUniqueDataAreaID(ntfsParent.Path); } else if (this.Level == 1) { this.ParentUID = GetUniqueDataAreaID(this.Provider.RootPath); } assignPermissionGroups(Provider, NtfsFolder); } public override async Task> GetOwnersAsync() { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { return await GetMembersAsync(true); } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } private async Task> GetMembersAsync(bool owners) { var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { var AD = this.Provider?.activeDirectoryBase; if (AD == null) { LogEntry($"Could not get ad class from Provider for folder '{this.TechnicalName}'", LogLevels.Warning); return null; } cADCollectionBase lstMembers; this.OwnerGroupIdentifier = this.OwnerRef ?? this.OwnerGroupIdentifier; if (owners && !String.IsNullOrEmpty(this.OwnerGroupIdentifier)) lstMembers = await AD.GetMembersAsync(this.OwnerGroupIdentifier); else lstMembers = null; if (lstMembers == null) { LogEntry($"Could not get owner list for folder '{this.TechnicalName}'", LogLevels.Warning); return null; } var RetVal = new List(lstMembers.Count); LogEntry($"Owners for folder found: {lstMembers.Count}", LogLevels.Debug); foreach (var MemberEntry in lstMembers.Values) { var User = new cLiamUserInfo() { DisplayName = MemberEntry.DisplayName, UserPrincipalName = (MemberEntry as cADUserResult).UserPrincipalName, SID = MemberEntry.ID }; RetVal.Add(User); } return RetVal; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } private async void assignPermissionGroups(cLiamProviderNtfs Provider, cNtfsResultFolder NtfsFolder) { var ACLs = Provider.activeDirectoryBase.GetAccessControlList(NtfsFolder.Path); var ownerNamingConvention = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Owner && (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGP && i.Scope == eLiamAccessRoleScopes.Global || Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP && i.Scope == eLiamAccessRoleScopes.DomainLocal)); var writeNamingConvention = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Write && (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGP && i.Scope == eLiamAccessRoleScopes.Global || Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP && i.Scope == eLiamAccessRoleScopes.DomainLocal)); var readNamingConvention = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Read && (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGP && i.Scope == eLiamAccessRoleScopes.Global || Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP && i.Scope == eLiamAccessRoleScopes.DomainLocal)); var traverseNamingConvention = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Traverse); foreach (FileSystemAccessRule rule in ACLs) { //skip ACLs for user "everyone" if (rule.IdentityReference.Value == "S-1-1-0") continue; GroupPrincipal grp = GroupPrincipal.FindByIdentity(Provider.activeDirectoryBase.adContext, IdentityType.Sid, rule.IdentityReference.Value); if (grp != null) { DefaultLogger.LogEntry(LogLevels.Debug, $"Try matching: {grp.Name}"); if (Regex.IsMatch(grp.SamAccountName, ownerNamingConvention.Wildcard, RegexOptions.IgnoreCase)) { this.OwnerGroupIdentifier = rule.IdentityReference.Value; if (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP) { var ldapFilter = String.Format("memberOf={0}", grp.DistinguishedName); var res = await Provider.activeDirectoryBase.RequestSecurityGroupsListAsync(ldapFilter); var ownerNamingConventionGlobal = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Owner && i.Scope == eLiamAccessRoleScopes.Global); foreach (var memberItem in res) { var SecurityGroup = new cLiamAdGroup(this.Provider, (cSecurityGroupResult)memberItem.Value); if (Regex.IsMatch( SecurityGroup.TechnicalName,ownerNamingConventionGlobal.Wildcard, RegexOptions.IgnoreCase)) this.OwnerGroupIdentifier = SecurityGroup.UID; } } } else if (Regex.IsMatch(grp.SamAccountName, writeNamingConvention.Wildcard, RegexOptions.IgnoreCase)) { WriteGroupIdentifier = rule.IdentityReference.Value; if (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP) { var ldapFilter = String.Format("memberOf={0}", grp.DistinguishedName); var res = await Provider.activeDirectoryBase.RequestSecurityGroupsListAsync(ldapFilter); var writeNamingConventionGlobal = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Write && i.Scope == eLiamAccessRoleScopes.Global); foreach (var memberItem in res) { var SecurityGroup = new cLiamAdGroup(this.Provider, (cSecurityGroupResult)memberItem.Value); if (Regex.IsMatch(SecurityGroup.TechnicalName, writeNamingConventionGlobal.Wildcard, RegexOptions.IgnoreCase)) this.WriteGroupIdentifier = SecurityGroup.UID; } } } else if (Regex.IsMatch(grp.SamAccountName, readNamingConvention.Wildcard, RegexOptions.IgnoreCase)) { ReadGroupIdentifier = rule.IdentityReference.Value; if (Provider.GroupStrategy == eLiamGroupStrategies.Ntfs_AGDLP) { var ldapFilter = String.Format("memberOf={0}", grp.DistinguishedName); var res = await Provider.activeDirectoryBase.RequestSecurityGroupsListAsync(ldapFilter); var readNamingConventionGlobal = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Read && i.Scope == eLiamAccessRoleScopes.Global); foreach (var memberItem in res) { var SecurityGroup = new cLiamAdGroup(this.Provider, (cSecurityGroupResult)memberItem.Value); if (Regex.IsMatch(SecurityGroup.TechnicalName, readNamingConventionGlobal.Wildcard, RegexOptions.IgnoreCase)) this.ReadGroupIdentifier = SecurityGroup.UID; } } } else if (Regex.IsMatch(grp.SamAccountName, traverseNamingConvention.Wildcard, RegexOptions.IgnoreCase)) { TraverseGroupIdentifier = rule.IdentityReference.Value; } else DefaultLogger.LogEntry(LogLevels.Debug, $"No match for: {grp.Name}"); } } } public static string GetUniqueDataAreaID(string fullPath) { LogMethodBegin(MethodBase.GetCurrentMethod()); try { var md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); var utf8 = new System.Text.UTF8Encoding(); var hash = BitConverter.ToString(md5.ComputeHash(utf8.GetBytes(fullPath))); hash = hash.ToLower().Replace("-", ""); return hash; } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); throw; } finally { LogMethodEnd(MethodBase.GetCurrentMethod()); } } public string GetUniqueDataAreaID() { return GetUniqueDataAreaID(this.TechnicalName); } public override async Task> getChildrenAsync(int Depth = 1) { //TODO implement getChildrenAsync var CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); try { await Task.Delay(0); var DataAreas = new List(); return DataAreas; } catch (Exception E) { LogException(E); return null; } finally { LogMethodEnd(CM); } } } }