From 2a47e52a3bd2db6664da76124ae983631c3bde6b Mon Sep 17 00:00:00 2001 From: rvazarkar Date: Tue, 16 Jan 2024 11:32:06 -0500 Subject: [PATCH 01/13] fix: properly parse collection methods for cert services objects --- src/Producers/LdapProducer.cs | 6 +++++- src/Runtime/ObjectProcessors.cs | 37 +++++++++++++++++---------------- 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/Producers/LdapProducer.cs b/src/Producers/LdapProducer.cs index 7ee79ab..9d1ff5b 100644 --- a/src/Producers/LdapProducer.cs +++ b/src/Producers/LdapProducer.cs @@ -31,6 +31,11 @@ public override async Task Produce() var log = Context.Logger; var utils = Context.LDAPUtils; + if (string.IsNullOrEmpty(ldapData.Filter.GetFilter())) + { + return; + } + if (Context.Flags.CollectAllProperties) { log.LogDebug("CollectAllProperties set. Changing LDAP properties to *"); @@ -83,7 +88,6 @@ await OutputChannel.Writer.WriteAsync(new Domain Context.Logger.LogTrace("Producer wrote {DistinguishedName} to channel", searchResult.DistinguishedName); } } - } /// diff --git a/src/Runtime/ObjectProcessors.cs b/src/Runtime/ObjectProcessors.cs index 20881cf..c2fcfa6 100644 --- a/src/Runtime/ObjectProcessors.cs +++ b/src/Runtime/ObjectProcessors.cs @@ -561,21 +561,22 @@ private async Task ProcessRootCA(ISearchResultEntry entry, ResolvedSearc ret.Properties.Add("name", resolvedSearchResult.DisplayName); ret.Properties.Add("distinguishedname", entry.DistinguishedName.ToUpper()); ret.Properties.Add("domainsid", resolvedSearchResult.DomainSid); + - if ((_methods & ResolvedCollectionMethod.ACL) != 0) + if ((_methods & ResolvedCollectionMethod.ACL) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) { ret.Aces = _aclProcessor.ProcessACL(resolvedSearchResult, entry).ToArray(); ret.IsACLProtected = _aclProcessor.IsACLProtected(entry); ret.Properties.Add("isaclprotected", ret.IsACLProtected); } - if ((_methods & ResolvedCollectionMethod.ObjectProps) != 0) + if ((_methods & ResolvedCollectionMethod.ObjectProps) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) { var props = LDAPPropertyProcessor.ReadRootCAProperties(entry); ret.Properties.Merge(props); } - if ((_methods & ResolvedCollectionMethod.Container) != 0) + if ((_methods & ResolvedCollectionMethod.Container) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) { ret.ContainedBy = _containerProcessor.GetContainingObject(entry.DistinguishedName); } @@ -595,20 +596,20 @@ private async Task ProcessAIACA(ISearchResultEntry entry, ResolvedSearchR ret.Properties.Add("distinguishedname", entry.DistinguishedName.ToUpper()); ret.Properties.Add("domainsid", resolvedSearchResult.DomainSid); - if ((_methods & ResolvedCollectionMethod.ACL) != 0) + if ((_methods & ResolvedCollectionMethod.ACL) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) { ret.Aces = _aclProcessor.ProcessACL(resolvedSearchResult, entry).ToArray(); ret.IsACLProtected = _aclProcessor.IsACLProtected(entry); ret.Properties.Add("isaclprotected", ret.IsACLProtected); } - if ((_methods & ResolvedCollectionMethod.ObjectProps) != 0) + if ((_methods & ResolvedCollectionMethod.ObjectProps) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) { var props = LDAPPropertyProcessor.ReadAIACAProperties(entry); ret.Properties.Merge(props); } - if ((_methods & ResolvedCollectionMethod.Container) != 0) + if ((_methods & ResolvedCollectionMethod.Container) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) { ret.ContainedBy = _containerProcessor.GetContainingObject(entry.DistinguishedName); } @@ -628,14 +629,14 @@ private async Task ProcessEnterpriseCA(ISearchResultEntry entry, R ret.Properties.Add("distinguishedname", entry.DistinguishedName.ToUpper()); ret.Properties.Add("domainsid", resolvedSearchResult.DomainSid); - if ((_methods & ResolvedCollectionMethod.ACL) != 0) + if ((_methods & ResolvedCollectionMethod.ACL) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) { ret.Aces = _aclProcessor.ProcessACL(resolvedSearchResult, entry).ToArray(); ret.IsACLProtected = _aclProcessor.IsACLProtected(entry); ret.Properties.Add("isaclprotected", ret.IsACLProtected); } - if ((_methods & ResolvedCollectionMethod.ObjectProps) != 0) + if ((_methods & ResolvedCollectionMethod.ObjectProps) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) { var props = LDAPPropertyProcessor.ReadEnterpriseCAProperties(entry); ret.Properties.Merge(props); @@ -644,15 +645,15 @@ private async Task ProcessEnterpriseCA(ISearchResultEntry entry, R ret.EnabledCertTemplates = _certAbuseProcessor.ProcessCertTemplates(entry.GetArrayProperty(LDAPProperties.CertificateTemplates), resolvedSearchResult.Domain).ToArray(); } - if ((_methods & ResolvedCollectionMethod.Container) != 0) + if ((_methods & ResolvedCollectionMethod.Container) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) { ret.ContainedBy = _containerProcessor.GetContainingObject(entry.DistinguishedName); } // Collect properties from CA server registry - bool cASecurityCollected = false; - bool enrollmentAgentRestrictionsCollected = false; - bool isUserSpecifiesSanEnabledCollected = false; + var cASecurityCollected = false; + var enrollmentAgentRestrictionsCollected = false; + var isUserSpecifiesSanEnabledCollected = false; var caName = entry.GetProperty(LDAPProperties.Name); var dnsHostName = entry.GetProperty(LDAPProperties.DNSHostName); if ((_methods & ResolvedCollectionMethod.CARegistry) != 0 && caName != null && dnsHostName != null) @@ -695,14 +696,14 @@ private async Task ProcessNTAuthStore(ISearchResultEntry entry, Res ret.Properties.Add("distinguishedname", entry.DistinguishedName.ToUpper()); ret.Properties.Add("domainsid", resolvedSearchResult.DomainSid); - if ((_methods & ResolvedCollectionMethod.ACL) != 0) + if ((_methods & ResolvedCollectionMethod.ACL) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) { ret.Aces = _aclProcessor.ProcessACL(resolvedSearchResult, entry).ToArray(); ret.IsACLProtected = _aclProcessor.IsACLProtected(entry); ret.Properties.Add("isaclprotected", ret.IsACLProtected); } - if ((_methods & ResolvedCollectionMethod.ObjectProps) != 0) + if ((_methods & ResolvedCollectionMethod.ObjectProps) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) { var props = LDAPPropertyProcessor.ReadNTAuthStoreProperties(entry); @@ -715,7 +716,7 @@ private async Task ProcessNTAuthStore(ISearchResultEntry entry, Res ret.Properties.Merge(props); } - if ((_methods & ResolvedCollectionMethod.Container) != 0) + if ((_methods & ResolvedCollectionMethod.Container) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) { ret.ContainedBy = _containerProcessor.GetContainingObject(entry.DistinguishedName); } @@ -735,20 +736,20 @@ private async Task ProcessCertTemplate(ISearchResultEntry entry, R ret.Properties.Add("distinguishedname", entry.DistinguishedName.ToUpper()); ret.Properties.Add("domainsid", resolvedSearchResult.DomainSid); - if ((_methods & ResolvedCollectionMethod.ACL) != 0) + if ((_methods & ResolvedCollectionMethod.ACL) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) { ret.Aces = _aclProcessor.ProcessACL(resolvedSearchResult, entry).ToArray(); ret.IsACLProtected = _aclProcessor.IsACLProtected(entry); ret.Properties.Add("isaclprotected", ret.IsACLProtected); } - if ((_methods & ResolvedCollectionMethod.ObjectProps) != 0) + if ((_methods & ResolvedCollectionMethod.ObjectProps) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) { var certTemplatesProps = LDAPPropertyProcessor.ReadCertTemplateProperties(entry); ret.Properties.Merge(certTemplatesProps); } - if ((_methods & ResolvedCollectionMethod.Container) != 0) + if ((_methods & ResolvedCollectionMethod.Container) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) { ret.ContainedBy = _containerProcessor.GetContainingObject(entry.DistinguishedName); } From 28361f398096fd0aa9c729baf98baa8349f8f9e7 Mon Sep 17 00:00:00 2001 From: rvazarkar Date: Tue, 16 Jan 2024 13:59:37 -0500 Subject: [PATCH 02/13] fix: dcregistry should only pull domain controllers --- src/Producers/BaseProducer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Producers/BaseProducer.cs b/src/Producers/BaseProducer.cs index 08e0610..99a671a 100644 --- a/src/Producers/BaseProducer.cs +++ b/src/Producers/BaseProducer.cs @@ -130,7 +130,7 @@ protected LDAPData CreateDefaultNCData() if ((methods & ResolvedCollectionMethod.DCRegistry) != 0) { - query = query.AddComputers(); + query = query.AddComputers(CommonFilters.DomainControllers); props.AddRange(CommonProperties.ComputerMethodProps); } } From 3808c5a536b8d11c27e81714285c48b8297c5b79 Mon Sep 17 00:00:00 2001 From: rvazarkar Date: Tue, 16 Jan 2024 14:17:06 -0500 Subject: [PATCH 03/13] fix: properly set includeACL for cert services --- src/Producers/LdapProducer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Producers/LdapProducer.cs b/src/Producers/LdapProducer.cs index 9d1ff5b..05fb15c 100644 --- a/src/Producers/LdapProducer.cs +++ b/src/Producers/LdapProducer.cs @@ -76,7 +76,7 @@ await OutputChannel.Writer.WriteAsync(new Domain foreach (var searchResult in Context.LDAPUtils.QueryLDAP(ldapData.Filter.GetFilter(), SearchScope.Subtree, ldapData.Props.Distinct().ToArray(), cancellationToken, domain.Name, adsPath: Context.SearchBase, - includeAcl: (Context.ResolvedCollectionMethods & ResolvedCollectionMethod.ACL) != 0)) + includeAcl: (Context.ResolvedCollectionMethods & ResolvedCollectionMethod.ACL) != 0 || (Context.ResolvedCollectionMethods & ResolvedCollectionMethod.CertServices) != 0)) { var l = searchResult.DistinguishedName.ToLower(); if (l.Contains("cn=domainupdates,cn=system")) @@ -114,7 +114,7 @@ public override async Task ProduceConfigNC() foreach (var searchResult in Context.LDAPUtils.QueryLDAP(configNcData.Filter.GetFilter(), SearchScope.Subtree, configNcData.Props.Distinct().ToArray(), cancellationToken, domain.Name, adsPath: configAdsPath, - includeAcl: (Context.ResolvedCollectionMethods & ResolvedCollectionMethod.ACL) != 0)) + includeAcl: (Context.ResolvedCollectionMethods & ResolvedCollectionMethod.ACL) != 0 || (Context.ResolvedCollectionMethods & ResolvedCollectionMethod.CertServices) != 0)) { await Channel.Writer.WriteAsync(searchResult, cancellationToken); Context.Logger.LogTrace("Producer wrote {DistinguishedName} to channel", searchResult.DistinguishedName); From 1d0bd51c7c4cacbcf3dea434da0150f76d206d5e Mon Sep 17 00:00:00 2001 From: rvazarkar Date: Tue, 16 Jan 2024 14:17:39 -0500 Subject: [PATCH 04/13] fix: don't include ACLs in default NC for cert services --- src/Producers/LdapProducer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Producers/LdapProducer.cs b/src/Producers/LdapProducer.cs index 05fb15c..2dff79b 100644 --- a/src/Producers/LdapProducer.cs +++ b/src/Producers/LdapProducer.cs @@ -76,7 +76,7 @@ await OutputChannel.Writer.WriteAsync(new Domain foreach (var searchResult in Context.LDAPUtils.QueryLDAP(ldapData.Filter.GetFilter(), SearchScope.Subtree, ldapData.Props.Distinct().ToArray(), cancellationToken, domain.Name, adsPath: Context.SearchBase, - includeAcl: (Context.ResolvedCollectionMethods & ResolvedCollectionMethod.ACL) != 0 || (Context.ResolvedCollectionMethods & ResolvedCollectionMethod.CertServices) != 0)) + includeAcl: (Context.ResolvedCollectionMethods & ResolvedCollectionMethod.ACL) != 0)) { var l = searchResult.DistinguishedName.ToLower(); if (l.Contains("cn=domainupdates,cn=system")) From ca7f5e1e7c4c49a00744e61903f8fac28577535b Mon Sep 17 00:00:00 2001 From: rvazarkar Date: Wed, 17 Jan 2024 14:01:53 -0500 Subject: [PATCH 05/13] chore: remove highvalue tag as its superceded by admin_tier_0 --- src/Runtime/ObjectProcessors.cs | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/src/Runtime/ObjectProcessors.cs b/src/Runtime/ObjectProcessors.cs index c2fcfa6..dfa4aef 100644 --- a/src/Runtime/ObjectProcessors.cs +++ b/src/Runtime/ObjectProcessors.cs @@ -108,7 +108,6 @@ private async Task ProcessUserObject(ISearchResultEntry entry, ret.Properties.Add("name", resolvedSearchResult.DisplayName); ret.Properties.Add("distinguishedname", entry.DistinguishedName.ToUpper()); ret.Properties.Add("domainsid", resolvedSearchResult.DomainSid); - ret.Properties.Add("highvalue", false); ret.Properties.Add("samaccountname", entry.GetProperty(LDAPProperties.SAMAccountName)); if ((_methods & ResolvedCollectionMethod.ACL) != 0) @@ -172,7 +171,6 @@ private async Task ProcessComputerObject(ISearchResultEntry entry, ret.Properties.Add("name", resolvedSearchResult.DisplayName); ret.Properties.Add("distinguishedname", entry.DistinguishedName.ToUpper()); ret.Properties.Add("domainsid", resolvedSearchResult.DomainSid); - ret.Properties.Add("highvalue", false); ret.Properties.Add("samaccountname", entry.GetProperty(LDAPProperties.SAMAccountName)); var hasLaps = entry.HasLAPS(); @@ -322,7 +320,6 @@ private Group ProcessGroupObject(ISearchResultEntry entry, ret.Properties.Add("name", resolvedSearchResult.DisplayName); ret.Properties.Add("distinguishedname", entry.DistinguishedName.ToUpper()); ret.Properties.Add("domainsid", resolvedSearchResult.DomainSid); - ret.Properties.Add("highvalue", IsHighValueGroup(resolvedSearchResult.ObjectId)); ret.Properties.Add("samaccountname", entry.GetProperty(LDAPProperties.SAMAccountName)); if ((_methods & ResolvedCollectionMethod.ACL) != 0) @@ -356,30 +353,6 @@ private Group ProcessGroupObject(ISearchResultEntry entry, return ret; } - private bool IsHighValueGroup(string objectId) - { - // TODO: replace w/ a more definitive/centralized list - var suffixes = new string[] - { - "-512", - "-516", - "-519", - "S-1-5-32-544", - "S-1-5-32-548", - "S-1-5-32-549", - "S-1-5-32-550", - "S-1-5-32-551", - }; - foreach (var suffix in suffixes) - { - if (objectId.EndsWith(suffix)) - { - return true; - } - } - return false; - } - private async Task ProcessDomainObject(ISearchResultEntry entry, ResolvedSearchResult resolvedSearchResult) { @@ -392,7 +365,6 @@ private async Task ProcessDomainObject(ISearchResultEntry entry, ret.Properties.Add("name", resolvedSearchResult.DisplayName); ret.Properties.Add("distinguishedname", entry.DistinguishedName.ToUpper()); ret.Properties.Add("domainsid", resolvedSearchResult.DomainSid); - ret.Properties.Add("highvalue", true); if ((_methods & ResolvedCollectionMethod.ACL) != 0) { @@ -440,7 +412,6 @@ private GPO ProcessGPOObject(ISearchResultEntry entry, ret.Properties.Add("name", resolvedSearchResult.DisplayName); ret.Properties.Add("distinguishedname", entry.DistinguishedName.ToUpper()); ret.Properties.Add("domainsid", resolvedSearchResult.DomainSid); - ret.Properties.Add("highvalue", false); if ((_methods & ResolvedCollectionMethod.ACL) != 0) { @@ -474,7 +445,6 @@ private async Task ProcessOUObject(ISearchResultEntry entry, ret.Properties.Add("name", resolvedSearchResult.DisplayName); ret.Properties.Add("distinguishedname", entry.DistinguishedName.ToUpper()); ret.Properties.Add("domainsid", resolvedSearchResult.DomainSid); - ret.Properties.Add("highvalue", false); if ((_methods & ResolvedCollectionMethod.ACL) != 0) { @@ -522,7 +492,6 @@ private Container ProcessContainerObject(ISearchResultEntry entry, ret.Properties.Add("name", resolvedSearchResult.DisplayName); ret.Properties.Add("distinguishedname", entry.DistinguishedName.ToUpper()); ret.Properties.Add("domainsid", resolvedSearchResult.DomainSid); - ret.Properties.Add("highvalue", false); if ((_methods & ResolvedCollectionMethod.Container) != 0) ret.ContainedBy = _containerProcessor.GetContainingObject(entry.DistinguishedName); From 2e1cd9f3df1907b36beb8ea85500769a34ab68ba Mon Sep 17 00:00:00 2001 From: rvazarkar Date: Wed, 17 Jan 2024 14:29:42 -0500 Subject: [PATCH 06/13] fix: add reconcile tag where necessary --- src/Producers/LdapProducer.cs | 5 ++--- src/Runtime/CollectionTask.cs | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/Producers/LdapProducer.cs b/src/Producers/LdapProducer.cs index 2dff79b..82a456c 100644 --- a/src/Producers/LdapProducer.cs +++ b/src/Producers/LdapProducer.cs @@ -109,6 +109,8 @@ public override async Task ProduceConfigNC() if (!configurationNCsCollected.Contains(configAdsPath)) { Context.Logger.LogInformation("Beginning LDAP search for {Domain} Configuration NC", domain.Name); + // Ensure we only collect the Configuration NC once per forest + configurationNCsCollected.Add(configAdsPath); //Do a basic LDAP search and grab results foreach (var searchResult in Context.LDAPUtils.QueryLDAP(configNcData.Filter.GetFilter(), SearchScope.Subtree, @@ -119,9 +121,6 @@ public override async Task ProduceConfigNC() await Channel.Writer.WriteAsync(searchResult, cancellationToken); Context.Logger.LogTrace("Producer wrote {DistinguishedName} to channel", searchResult.DistinguishedName); } - - // Ensure we only collect the Configuration NC once per forest - configurationNCsCollected.Add(configAdsPath); } else { diff --git a/src/Runtime/CollectionTask.cs b/src/Runtime/CollectionTask.cs index bfc7f78..e4403ee 100644 --- a/src/Runtime/CollectionTask.cs +++ b/src/Runtime/CollectionTask.cs @@ -22,6 +22,7 @@ public class CollectionTask private readonly OutputWriter _outputWriter; private readonly BaseProducer _producer; private readonly List _taskPool = new(); + private const string EnterpriseDCSuffix = "S-1-5-9"; public CollectionTask(IContext context) { @@ -82,7 +83,21 @@ internal async Task StartCollection() _log.LogInformation("Consumers finished, closing output channel"); foreach (var wkp in _context.LDAPUtils.GetWellKnownPrincipalOutput(_context.DomainName)) + { + if (wkp.ObjectIdentifier.EndsWith(EnterpriseDCSuffix)) + { + if (wkp is Group g && g.Members.Length == 0) + { + continue; + } + } + else + { + wkp.Properties["reconcile"] = false; + } await _outputChannel.Writer.WriteAsync(wkp); + } + _outputChannel.Writer.Complete(); _compStatusChannel?.Writer.Complete(); From 7b71960cea98000856de947c114dc874a529600a Mon Sep 17 00:00:00 2001 From: rvazarkar Date: Wed, 17 Jan 2024 14:35:53 -0500 Subject: [PATCH 07/13] fix: only collect caregistry if flag is set --- src/Runtime/ObjectProcessors.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Runtime/ObjectProcessors.cs b/src/Runtime/ObjectProcessors.cs index dfa4aef..19e1453 100644 --- a/src/Runtime/ObjectProcessors.cs +++ b/src/Runtime/ObjectProcessors.cs @@ -619,7 +619,9 @@ private async Task ProcessEnterpriseCA(ISearchResultEntry entry, R ret.ContainedBy = _containerProcessor.GetContainingObject(entry.DistinguishedName); } - // Collect properties from CA server registry + if ((_methods & ResolvedCollectionMethod.CARegistry) != 0) + { + // Collect properties from CA server registry var cASecurityCollected = false; var enrollmentAgentRestrictionsCollected = false; var isUserSpecifiesSanEnabledCollected = false; @@ -648,7 +650,8 @@ private async Task ProcessEnterpriseCA(ISearchResultEntry entry, R ret.Properties.Add("casecuritycollected", cASecurityCollected); ret.Properties.Add("enrollmentagentrestrictionscollected", enrollmentAgentRestrictionsCollected); ret.Properties.Add("isuserspecifiessanenabledcollected", isUserSpecifiesSanEnabledCollected); - + } + return ret; } From 1dea05ae86e8b2ec7b2e828bb7bef3c2cb83b274 Mon Sep 17 00:00:00 2001 From: rvazarkar Date: Thu, 18 Jan 2024 12:36:16 -0500 Subject: [PATCH 08/13] fix: add proper props to cert services --- src/Producers/BaseProducer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Producers/BaseProducer.cs b/src/Producers/BaseProducer.cs index 99a671a..8bf7016 100644 --- a/src/Producers/BaseProducer.cs +++ b/src/Producers/BaseProducer.cs @@ -169,6 +169,9 @@ protected LDAPData CreateConfigNCData() { query = allObjectTypesQuery; props.AddRange(CommonProperties.CertAbuseProps); + props.AddRange(CommonProperties.ObjectPropsProps); + props.AddRange(CommonProperties.ContainerProps); + props.AddRange(CommonProperties.ACLProps); } if ((methods & ResolvedCollectionMethod.Container) != 0) From 17f6f6d4a93678aba4761eb1ac79609f87a21856 Mon Sep 17 00:00:00 2001 From: rvazarkar Date: Thu, 18 Jan 2024 14:54:12 -0500 Subject: [PATCH 09/13] chore: add missing msa/gmsa props --- src/Runtime/ObjectProcessors.cs | 54 ++++++++++++++++++--------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/src/Runtime/ObjectProcessors.cs b/src/Runtime/ObjectProcessors.cs index 19e1453..128ba3b 100644 --- a/src/Runtime/ObjectProcessors.cs +++ b/src/Runtime/ObjectProcessors.cs @@ -109,6 +109,10 @@ private async Task ProcessUserObject(ISearchResultEntry entry, ret.Properties.Add("distinguishedname", entry.DistinguishedName.ToUpper()); ret.Properties.Add("domainsid", resolvedSearchResult.DomainSid); ret.Properties.Add("samaccountname", entry.GetProperty(LDAPProperties.SAMAccountName)); + + if (entry.IsMSA()) ret.Properties.Add("msa", true); + + if (entry.IsGMSA()) ret.Properties.Add("gmsa", true); if ((_methods & ResolvedCollectionMethod.ACL) != 0) { @@ -622,34 +626,34 @@ private async Task ProcessEnterpriseCA(ISearchResultEntry entry, R if ((_methods & ResolvedCollectionMethod.CARegistry) != 0) { // Collect properties from CA server registry - var cASecurityCollected = false; - var enrollmentAgentRestrictionsCollected = false; - var isUserSpecifiesSanEnabledCollected = false; - var caName = entry.GetProperty(LDAPProperties.Name); - var dnsHostName = entry.GetProperty(LDAPProperties.DNSHostName); - if ((_methods & ResolvedCollectionMethod.CARegistry) != 0 && caName != null && dnsHostName != null) - { - ret.HostingComputer = await _context.LDAPUtils.ResolveHostToSid(dnsHostName, resolvedSearchResult.Domain); - - CARegistryData cARegistryData = new() + var cASecurityCollected = false; + var enrollmentAgentRestrictionsCollected = false; + var isUserSpecifiesSanEnabledCollected = false; + var caName = entry.GetProperty(LDAPProperties.Name); + var dnsHostName = entry.GetProperty(LDAPProperties.DNSHostName); + if ((_methods & ResolvedCollectionMethod.CARegistry) != 0 && caName != null && dnsHostName != null) { - IsUserSpecifiesSanEnabled = _certAbuseProcessor.IsUserSpecifiesSanEnabled(dnsHostName, caName), - EnrollmentAgentRestrictions = await _certAbuseProcessor.ProcessEAPermissions(caName, resolvedSearchResult.Domain, dnsHostName, ret.HostingComputer), + ret.HostingComputer = await _context.LDAPUtils.ResolveHostToSid(dnsHostName, resolvedSearchResult.Domain); - // The CASecurity exist in the AD object DACL and in registry of the CA server. We prefer to use the values from registry as they are the ground truth. - // If changes are made on the CA server, registry and the AD object is updated. If changes are made directly on the AD object, the CA server registry is not updated. - CASecurity = await _certAbuseProcessor.ProcessRegistryEnrollmentPermissions(caName, resolvedSearchResult.Domain, dnsHostName, ret.HostingComputer) - }; - - cASecurityCollected = cARegistryData.CASecurity.Collected; - enrollmentAgentRestrictionsCollected = cARegistryData.EnrollmentAgentRestrictions.Collected; - isUserSpecifiesSanEnabledCollected = cARegistryData.IsUserSpecifiesSanEnabled.Collected; - ret.CARegistryData = cARegistryData; - } + CARegistryData cARegistryData = new() + { + IsUserSpecifiesSanEnabled = _certAbuseProcessor.IsUserSpecifiesSanEnabled(dnsHostName, caName), + EnrollmentAgentRestrictions = await _certAbuseProcessor.ProcessEAPermissions(caName, resolvedSearchResult.Domain, dnsHostName, ret.HostingComputer), + + // The CASecurity exist in the AD object DACL and in registry of the CA server. We prefer to use the values from registry as they are the ground truth. + // If changes are made on the CA server, registry and the AD object is updated. If changes are made directly on the AD object, the CA server registry is not updated. + CASecurity = await _certAbuseProcessor.ProcessRegistryEnrollmentPermissions(caName, resolvedSearchResult.Domain, dnsHostName, ret.HostingComputer) + }; + + cASecurityCollected = cARegistryData.CASecurity.Collected; + enrollmentAgentRestrictionsCollected = cARegistryData.EnrollmentAgentRestrictions.Collected; + isUserSpecifiesSanEnabledCollected = cARegistryData.IsUserSpecifiesSanEnabled.Collected; + ret.CARegistryData = cARegistryData; + } - ret.Properties.Add("casecuritycollected", cASecurityCollected); - ret.Properties.Add("enrollmentagentrestrictionscollected", enrollmentAgentRestrictionsCollected); - ret.Properties.Add("isuserspecifiessanenabledcollected", isUserSpecifiesSanEnabledCollected); + ret.Properties.Add("casecuritycollected", cASecurityCollected); + ret.Properties.Add("enrollmentagentrestrictionscollected", enrollmentAgentRestrictionsCollected); + ret.Properties.Add("isuserspecifiessanenabledcollected", isUserSpecifiesSanEnabledCollected); } return ret; From cf35034907ffc17160582f896e40ffe52579aa1d Mon Sep 17 00:00:00 2001 From: rvazarkar Date: Thu, 18 Jan 2024 14:54:39 -0500 Subject: [PATCH 10/13] fix: set collected correctly for domains --- src/BaseContext.cs | 3 +++ src/Client/Context.cs | 1 + src/Producers/LdapProducer.cs | 11 ++--------- src/Runtime/LDAPConsumer.cs | 5 +++++ 4 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/BaseContext.cs b/src/BaseContext.cs index 8c6cf51..3ab2934 100644 --- a/src/BaseContext.cs +++ b/src/BaseContext.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.IO; using System.Threading; using System.Threading.Tasks; @@ -64,6 +65,8 @@ public void UpdateLoopTime() CurrentLoopTime = $"{DateTime.Now:yyyyMMddHHmmss}"; } + public HashSet CollectedDomainSids { get; } = new(); + public async Task DoDelay() { if (Throttle == 0) diff --git a/src/Client/Context.cs b/src/Client/Context.cs index 4e52f96..7e24734 100644 --- a/src/Client/Context.cs +++ b/src/Client/Context.cs @@ -75,5 +75,6 @@ public interface IContext string ResolveFileName(string filename, string extension, bool addTimestamp); EnumerationDomain[] Domains { get; set; } void UpdateLoopTime(); + public HashSet CollectedDomainSids { get; } } } \ No newline at end of file diff --git a/src/Producers/LdapProducer.cs b/src/Producers/LdapProducer.cs index 82a456c..fb6027e 100644 --- a/src/Producers/LdapProducer.cs +++ b/src/Producers/LdapProducer.cs @@ -44,7 +44,7 @@ public override async Task Produce() foreach (var domain in Context.Domains) { - Context.Logger.LogInformation("Beginning LDAP search for {Domain}", domain); + Context.Logger.LogInformation("Beginning LDAP search for {Domain}", domain.Name); //Do a basic LDAP search and grab results var successfulConnect = false; try @@ -64,14 +64,7 @@ public override async Task Produce() continue; } - await OutputChannel.Writer.WriteAsync(new Domain - { - ObjectIdentifier = domain.DomainSid, - Properties = new Dictionary - { - { "collected", true }, - } - }); + Context.CollectedDomainSids.Add(domain.DomainSid); foreach (var searchResult in Context.LDAPUtils.QueryLDAP(ldapData.Filter.GetFilter(), SearchScope.Subtree, ldapData.Props.Distinct().ToArray(), cancellationToken, domain.Name, diff --git a/src/Runtime/LDAPConsumer.cs b/src/Runtime/LDAPConsumer.cs index c298d5b..6960bd5 100644 --- a/src/Runtime/LDAPConsumer.cs +++ b/src/Runtime/LDAPConsumer.cs @@ -37,6 +37,11 @@ internal static async Task ConsumeSearchResults(Channel inpu watch.Elapsed.TotalMilliseconds, res.DisplayName); if (processed == null) continue; + + if (processed is Domain d && context.CollectedDomainSids.Contains(d.ObjectIdentifier)) + { + d.Properties.Add("collected", true); + } await outputChannel.Writer.WriteAsync(processed); } catch (Exception e) From e973b35a89eedb1bc485e06ea749dbe1d523c191 Mon Sep 17 00:00:00 2001 From: rvazarkar Date: Thu, 18 Jan 2024 15:24:37 -0500 Subject: [PATCH 11/13] fix: properly populate status --- src/Runtime/ObjectProcessors.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Runtime/ObjectProcessors.cs b/src/Runtime/ObjectProcessors.cs index 128ba3b..8c3a5df 100644 --- a/src/Runtime/ObjectProcessors.cs +++ b/src/Runtime/ObjectProcessors.cs @@ -226,6 +226,7 @@ private async Task ProcessComputerObject(ISearchResultEntry entry, { await compStatusChannel.Writer.WriteAsync(availability.GetCSVStatus(resolvedSearchResult.DisplayName), _cancellationToken); + ret.Status = availability; return ret; } From 367fdb04a3b95aef27c449fef117a90fefece8cf Mon Sep 17 00:00:00 2001 From: rvazarkar Date: Thu, 18 Jan 2024 16:51:18 -0500 Subject: [PATCH 12/13] fix: collect container acl correctly --- src/Runtime/ObjectProcessors.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Runtime/ObjectProcessors.cs b/src/Runtime/ObjectProcessors.cs index 8c3a5df..e4ca533 100644 --- a/src/Runtime/ObjectProcessors.cs +++ b/src/Runtime/ObjectProcessors.cs @@ -498,10 +498,10 @@ private Container ProcessContainerObject(ISearchResultEntry entry, ret.Properties.Add("distinguishedname", entry.DistinguishedName.ToUpper()); ret.Properties.Add("domainsid", resolvedSearchResult.DomainSid); - if ((_methods & ResolvedCollectionMethod.Container) != 0) + if ((_methods & ResolvedCollectionMethod.Container) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) ret.ContainedBy = _containerProcessor.GetContainingObject(entry.DistinguishedName); - if ((_methods & ResolvedCollectionMethod.ACL) != 0) + if ((_methods & ResolvedCollectionMethod.ACL) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) { ret.Aces = _aclProcessor.ProcessACL(resolvedSearchResult, entry) .ToArray(); @@ -509,7 +509,7 @@ private Container ProcessContainerObject(ISearchResultEntry entry, ret.Properties.Add("isaclprotected", ret.IsACLProtected); } - if ((_methods & ResolvedCollectionMethod.ObjectProps) != 0) + if ((_methods & ResolvedCollectionMethod.ObjectProps) != 0 || (_methods & ResolvedCollectionMethod.CertServices) != 0) { if (_context.Flags.CollectAllProperties) { From a1c335b4c0a586ae145e56cf777c107437b85804 Mon Sep 17 00:00:00 2001 From: rvazarkar Date: Fri, 19 Jan 2024 11:42:58 -0500 Subject: [PATCH 13/13] chore: refactor nested if --- src/Runtime/CollectionTask.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/Runtime/CollectionTask.cs b/src/Runtime/CollectionTask.cs index e4403ee..5bb57ff 100644 --- a/src/Runtime/CollectionTask.cs +++ b/src/Runtime/CollectionTask.cs @@ -84,17 +84,15 @@ internal async Task StartCollection() foreach (var wkp in _context.LDAPUtils.GetWellKnownPrincipalOutput(_context.DomainName)) { - if (wkp.ObjectIdentifier.EndsWith(EnterpriseDCSuffix)) + if (!wkp.ObjectIdentifier.EndsWith(EnterpriseDCSuffix)) { - if (wkp is Group g && g.Members.Length == 0) - { - continue; - } + wkp.Properties["reconcile"] = false; } - else + else if (wkp is Group g && g.Members.Length == 0) { - wkp.Properties["reconcile"] = false; + continue; } + await _outputChannel.Writer.WriteAsync(wkp); }