diff --git a/LiamNtfs/C4IT.LIAM.Ntfs.cs b/LiamNtfs/C4IT.LIAM.Ntfs.cs index b8f6aa1..4af4b0c 100644 --- a/LiamNtfs/C4IT.LIAM.Ntfs.cs +++ b/LiamNtfs/C4IT.LIAM.Ntfs.cs @@ -1350,72 +1350,110 @@ namespace C4IT.LIAM var traverseNamingConvention = Provider.NamingConventions.First(i => i.AccessRole == eLiamAccessRoles.Traverse); foreach (FileSystemAccessRule rule in ACLs) { - if (rule.IdentityReference.Value == "S-1-1-0") + var aclSid = rule.IdentityReference.Value; + if (aclSid == "S-1-1-0") continue; - GroupPrincipal grp = GroupPrincipal.FindByIdentity(Provider.activeDirectoryBase.adContext, IdentityType.Sid, rule.IdentityReference.Value); + GroupPrincipal grp = GroupPrincipal.FindByIdentity(Provider.activeDirectoryBase.adContext, IdentityType.Sid, aclSid); if (grp == null) - continue; - - DefaultLogger.LogEntry(LogLevels.Debug, $"Try matching: {grp.Name}"); - if (Regex.IsMatch(grp.SamAccountName, ownerNamingConvention.Wildcard, RegexOptions.IgnoreCase)) { - this.OwnerGroupIdentifier = rule.IdentityReference.Value; + DefaultLogger.LogEntry(LogLevels.Debug, $"ACL SID '{aclSid}' on '{path}' could not be resolved to an AD group. Naming convention matching skipped."); + continue; + } + + var samAccountName = grp.SamAccountName ?? string.Empty; + if (string.IsNullOrWhiteSpace(samAccountName)) + { + DefaultLogger.LogEntry(LogLevels.Debug, $"ACL SID '{aclSid}' on '{path}' resolved to '{grp.Name}', but no sAMAccountName is available. Naming convention matching skipped."); + continue; + } + + if (Regex.IsMatch(samAccountName, ownerNamingConvention.Wildcard, RegexOptions.IgnoreCase)) + { + this.OwnerGroupIdentifier = aclSid; + DefaultLogger.LogEntry(LogLevels.Debug, $"ACL SID '{aclSid}' on '{path}' resolved to '{samAccountName}' and matched Owner naming convention '{ownerNamingConvention.Wildcard}'."); 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); + var matchedGlobalGroup = false; - foreach (var memberItem in res) + foreach (var memberItem in res ?? new cADCollectionBase()) { var SecurityGroup = new cLiamAdGroup(this.Provider, (cSecurityGroupResult)memberItem.Value); if (Regex.IsMatch(SecurityGroup.TechnicalName, ownerNamingConventionGlobal.Wildcard, RegexOptions.IgnoreCase)) + { this.OwnerGroupIdentifier = SecurityGroup.UID; + matchedGlobalGroup = true; + DefaultLogger.LogEntry(LogLevels.Debug, $"AGDLP Owner ACL group '{samAccountName}' resolved to global group '{SecurityGroup.TechnicalName}' with SID '{SecurityGroup.UID}'."); + } } + + if (!matchedGlobalGroup) + DefaultLogger.LogEntry(LogLevels.Debug, $"AGDLP Owner ACL group '{samAccountName}' matched, but no nested global group matched naming convention '{ownerNamingConventionGlobal.Wildcard}'. Keeping ACL SID '{aclSid}'."); } } - else if (Regex.IsMatch(grp.SamAccountName, writeNamingConvention.Wildcard, RegexOptions.IgnoreCase)) + else if (Regex.IsMatch(samAccountName, writeNamingConvention.Wildcard, RegexOptions.IgnoreCase)) { - this.WriteGroupIdentifier = rule.IdentityReference.Value; + this.WriteGroupIdentifier = aclSid; + DefaultLogger.LogEntry(LogLevels.Debug, $"ACL SID '{aclSid}' on '{path}' resolved to '{samAccountName}' and matched Write naming convention '{writeNamingConvention.Wildcard}'."); 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); + var matchedGlobalGroup = false; - foreach (var memberItem in res) + foreach (var memberItem in res ?? new cADCollectionBase()) { var SecurityGroup = new cLiamAdGroup(this.Provider, (cSecurityGroupResult)memberItem.Value); if (Regex.IsMatch(SecurityGroup.TechnicalName, writeNamingConventionGlobal.Wildcard, RegexOptions.IgnoreCase)) + { this.WriteGroupIdentifier = SecurityGroup.UID; + matchedGlobalGroup = true; + DefaultLogger.LogEntry(LogLevels.Debug, $"AGDLP Write ACL group '{samAccountName}' resolved to global group '{SecurityGroup.TechnicalName}' with SID '{SecurityGroup.UID}'."); + } } + + if (!matchedGlobalGroup) + DefaultLogger.LogEntry(LogLevels.Debug, $"AGDLP Write ACL group '{samAccountName}' matched, but no nested global group matched naming convention '{writeNamingConventionGlobal.Wildcard}'. Keeping ACL SID '{aclSid}'."); } } - else if (Regex.IsMatch(grp.SamAccountName, readNamingConvention.Wildcard, RegexOptions.IgnoreCase)) + else if (Regex.IsMatch(samAccountName, readNamingConvention.Wildcard, RegexOptions.IgnoreCase)) { - this.ReadGroupIdentifier = rule.IdentityReference.Value; + this.ReadGroupIdentifier = aclSid; + DefaultLogger.LogEntry(LogLevels.Debug, $"ACL SID '{aclSid}' on '{path}' resolved to '{samAccountName}' and matched Read naming convention '{readNamingConvention.Wildcard}'."); 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); + var matchedGlobalGroup = false; - foreach (var memberItem in res) + foreach (var memberItem in res ?? new cADCollectionBase()) { var SecurityGroup = new cLiamAdGroup(this.Provider, (cSecurityGroupResult)memberItem.Value); if (Regex.IsMatch(SecurityGroup.TechnicalName, readNamingConventionGlobal.Wildcard, RegexOptions.IgnoreCase)) + { this.ReadGroupIdentifier = SecurityGroup.UID; + matchedGlobalGroup = true; + DefaultLogger.LogEntry(LogLevels.Debug, $"AGDLP Read ACL group '{samAccountName}' resolved to global group '{SecurityGroup.TechnicalName}' with SID '{SecurityGroup.UID}'."); + } } + + if (!matchedGlobalGroup) + DefaultLogger.LogEntry(LogLevels.Debug, $"AGDLP Read ACL group '{samAccountName}' matched, but no nested global group matched naming convention '{readNamingConventionGlobal.Wildcard}'. Keeping ACL SID '{aclSid}'."); } } - else if (Regex.IsMatch(grp.SamAccountName, traverseNamingConvention.Wildcard, RegexOptions.IgnoreCase)) + else if (Regex.IsMatch(samAccountName, traverseNamingConvention.Wildcard, RegexOptions.IgnoreCase)) { - this.TraverseGroupIdentifier = rule.IdentityReference.Value; + this.TraverseGroupIdentifier = aclSid; + DefaultLogger.LogEntry(LogLevels.Debug, $"ACL SID '{aclSid}' on '{path}' resolved to '{samAccountName}' and matched Traverse naming convention '{traverseNamingConvention.Wildcard}'."); } else { - DefaultLogger.LogEntry(LogLevels.Debug, $"No match for: {grp.Name}"); + DefaultLogger.LogEntry(LogLevels.Debug, $"ACL SID '{aclSid}' on '{path}' resolved to '{samAccountName}', but did not match Owner/Write/Read/Traverse naming conventions."); } } }