< Summary

Class:SharpHoundCommonLib.Processors.ACLProcessor
Assembly:SharpHoundCommonLib
File(s):D:\a\SharpHoundCommon\SharpHoundCommon\src\CommonLib\Processors\ACLProcessor.cs
Covered lines:282
Uncovered lines:152
Coverable lines:434
Total lines:595
Line coverage:64.9% (282 of 434)
Covered branches:154
Total branches:197
Branch coverage:78.1% (154 of 197)

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.cctor()100%10100%
.ctor(...)100%40100%
BuildGUIDCache(...)30%10033.33%
IsACLProtected(...)100%100%
IsACLProtected(...)100%20100%
ProcessACL(...)100%100%
ProcessACL()78.44%167063.89%
ProcessGMSAReaders(...)100%100%
ProcessGMSAReaders(...)100%10100%
ProcessGMSAReaders()100%14088.09%

File(s)

D:\a\SharpHoundCommon\SharpHoundCommon\src\CommonLib\Processors\ACLProcessor.cs

#LineLine coverage
 1using System;
 2using System.Collections.Concurrent;
 3using System.Collections.Generic;
 4using System.DirectoryServices;
 5using System.Security.AccessControl;
 6using System.Security.Principal;
 7using Microsoft.Extensions.Logging;
 8using SharpHoundCommonLib.Enums;
 9using SharpHoundCommonLib.OutputTypes;
 10using SearchScope = System.DirectoryServices.Protocols.SearchScope;
 11
 12namespace SharpHoundCommonLib.Processors
 13{
 14    public class ACLProcessor
 15    {
 16        private static readonly Dictionary<Label, string> BaseGuids;
 117        private static readonly ConcurrentDictionary<string, string> GuidMap = new();
 18        private static bool _isCacheBuilt;
 19        private readonly ILogger _log;
 20        private readonly ILDAPUtils _utils;
 21
 22        static ACLProcessor()
 123        {
 24            //Create a dictionary with the base GUIDs of each object type
 125            BaseGuids = new Dictionary<Label, string>
 126            {
 127                {Label.User, "bf967aba-0de6-11d0-a285-00aa003049e2"},
 128                {Label.Computer, "bf967a86-0de6-11d0-a285-00aa003049e2"},
 129                {Label.Group, "bf967a9c-0de6-11d0-a285-00aa003049e2"},
 130                {Label.Domain, "19195a5a-6da0-11d0-afd3-00c04fd930c9"},
 131                {Label.GPO, "f30e3bc2-9ff0-11d1-b603-0000f80367c1"},
 132                {Label.OU, "bf967aa5-0de6-11d0-a285-00aa003049e2"},
 133                {Label.Container, "bf967a8b-0de6-11d0-a285-00aa003049e2"},
 134                {Label.Configuration, "bf967a87-0de6-11d0-a285-00aa003049e2"},
 135                {Label.RootCA, "3fdfee50-47f4-11d1-a9c3-0000f80367c1"},
 136                {Label.AIACA, "3fdfee50-47f4-11d1-a9c3-0000f80367c1"},
 137                {Label.EnterpriseCA, "ee4aa692-3bba-11d2-90cc-00c04fd91ab1"},
 138                {Label.NTAuthStore, "3fdfee50-47f4-11d1-a9c3-0000f80367c1"},
 139                {Label.CertTemplate, "e5209ca2-3bba-11d2-90cc-00c04fd91ab1"},
 140                {Label.IssuancePolicy, "37cfd85c-6719-4ad8-8f9e-8678ba627563"}
 141            };
 142        }
 43
 6844        public ACLProcessor(ILDAPUtils utils, bool noGuidCache = false, ILogger log = null, string domain = null)
 6845        {
 6846            _utils = utils;
 6847            _log = log ?? Logging.LogProvider.CreateLogger("ACLProc");
 6848            if (!noGuidCache)
 3549                BuildGUIDCache(domain);
 6850        }
 51
 52        /// <summary>
 53        ///     Builds a mapping of GUID -> Name for LDAP rights. Used for rights that are created using an extended sch
 54        ///     LAPS
 55        /// </summary>
 56        private void BuildGUIDCache(string domain)
 3557        {
 3558            if (_isCacheBuilt)
 059                return;
 60
 3561            var forest = _utils.GetForest(domain);
 3562            if (forest == null)
 3563            {
 3564                _log.LogError("BuildGUIDCache - Unable to resolve forest");
 3565                return;
 66            }
 67
 068            var schema = forest.Schema.Name;
 069            if (string.IsNullOrEmpty(schema))
 070            {
 071                _log.LogError("BuildGUIDCache - Schema string is null or empty");
 072                return;
 73            }
 74
 075            _log.LogTrace("Requesting schema from {Schema}", schema);
 076            foreach (var entry in _utils.QueryLDAP("(schemaIDGUID=*)", SearchScope.Subtree,
 077                         new[] {LDAPProperties.SchemaIDGUID, LDAPProperties.Name}, adsPath: schema))
 078            {
 079                var name = entry.GetProperty(LDAPProperties.Name)?.ToLower();
 080                var guid = new Guid(entry.GetByteProperty(LDAPProperties.SchemaIDGUID)).ToString();
 081                GuidMap.TryAdd(guid, name);
 082            }
 83
 084            _log.LogTrace("BuildGUIDCache - Successfully grabbed schema");
 85
 086            _isCacheBuilt = true;
 3587        }
 88
 89        /// <summary>
 90        ///     Helper function to use commonlib types in IsACLProtected
 91        /// </summary>
 92        /// <param name="entry"></param>
 93        /// <returns></returns>
 94        public bool IsACLProtected(ISearchResultEntry entry)
 095        {
 096            var ntsd = entry.GetByteProperty(LDAPProperties.SecurityDescriptor);
 097            return IsACLProtected(ntsd);
 098        }
 99
 100        /// <summary>
 101        ///     Gets the protection state of the access control list
 102        /// </summary>
 103        /// <param name="ntSecurityDescriptor"></param>
 104        /// <returns></returns>
 105        public bool IsACLProtected(byte[] ntSecurityDescriptor)
 3106        {
 3107            if (ntSecurityDescriptor == null)
 1108                return false;
 109
 2110            var descriptor = _utils.MakeSecurityDescriptor();
 2111            descriptor.SetSecurityDescriptorBinaryForm(ntSecurityDescriptor);
 112
 2113            return descriptor.AreAccessRulesProtected();
 3114        }
 115
 116        /// <summary>
 117        ///     Helper function to use common lib types and pass appropriate vars to ProcessACL
 118        /// </summary>
 119        /// <param name="result"></param>
 120        /// <param name="searchResult"></param>
 121        /// <returns></returns>
 122        public IEnumerable<ACE> ProcessACL(ResolvedSearchResult result, ISearchResultEntry searchResult)
 0123        {
 0124            var descriptor = searchResult.GetByteProperty(LDAPProperties.SecurityDescriptor);
 0125            var domain = result.Domain;
 0126            var type = result.ObjectType;
 0127            var hasLaps = searchResult.HasLAPS();
 0128            var name = result.DisplayName;
 129
 0130            return ProcessACL(descriptor, domain, type, hasLaps, name);
 0131        }
 132
 133        /// <summary>
 134        ///     Read's the ntSecurityDescriptor from a SearchResultEntry and processes the ACEs in the ACL, filtering ou
 135        ///     BloodHound is not interested in
 136        /// </summary>
 137        /// <param name="ntSecurityDescriptor"></param>
 138        /// <param name="objectDomain"></param>
 139        /// <param name="objectName"></param>
 140        /// <param name="objectType"></param>
 141        /// <param name="hasLaps"></param>
 142        /// <returns></returns>
 143        public IEnumerable<ACE> ProcessACL(byte[] ntSecurityDescriptor, string objectDomain,
 144            Label objectType,
 145            bool hasLaps, string objectName = "")
 28146        {
 28147            if (ntSecurityDescriptor == null)
 1148            {
 1149                _log.LogDebug("Security Descriptor is null for {Name}", objectName);
 1150                yield break;
 151            }
 152
 27153            var descriptor = _utils.MakeSecurityDescriptor();
 154            try
 27155            {
 27156                descriptor.SetSecurityDescriptorBinaryForm(ntSecurityDescriptor);
 27157            }
 0158            catch (OverflowException)
 0159            {
 0160                _log.LogWarning(
 0161                    "Security descriptor on object {Name} exceeds maximum allowable length. Unable to process",
 0162                    objectName);
 0163                yield break;
 164            }
 165
 27166            var ownerSid = Helpers.PreProcessSID(descriptor.GetOwner(typeof(SecurityIdentifier)));
 167
 27168            if (ownerSid != null)
 4169            {
 4170                var resolvedOwner = _utils.ResolveIDAndType(ownerSid, objectDomain);
 4171                if (resolvedOwner != null)
 4172                    yield return new ACE
 4173                    {
 4174                        PrincipalType = resolvedOwner.ObjectType,
 4175                        PrincipalSID = resolvedOwner.ObjectIdentifier,
 4176                        RightName = EdgeNames.Owns,
 4177                        IsInherited = false
 4178                    };
 4179            }
 180            else
 23181            {
 23182                _log.LogDebug("Owner is null for {Name}", objectName);
 23183            }
 184
 199185            foreach (var ace in descriptor.GetAccessRules(true, true, typeof(SecurityIdentifier)))
 60186            {
 60187                if (ace == null)
 1188                {
 1189                    _log.LogTrace("Skipping null ACE for {Name}", objectName);
 1190                    continue;
 191                }
 192
 59193                if (ace.AccessControlType() == AccessControlType.Deny)
 1194                {
 1195                    _log.LogTrace("Skipping deny ACE for {Name}", objectName);
 1196                    continue;
 197                }
 198
 58199                if (!ace.IsAceInheritedFrom(BaseGuids[objectType]))
 6200                {
 6201                    _log.LogTrace("Skipping ACE with unmatched GUID/inheritance for {Name}", objectName);
 6202                    continue;
 203                }
 204
 52205                var ir = ace.IdentityReference();
 52206                var principalSid = Helpers.PreProcessSID(ir);
 207
 52208                if (principalSid == null)
 9209                {
 9210                    _log.LogTrace("Pre-Process excluded SID {SID} on {Name}", ir ?? "null", objectName);
 9211                    continue;
 212                }
 213
 43214                var resolvedPrincipal = _utils.ResolveIDAndType(principalSid, objectDomain);
 215
 43216                var aceRights = ace.ActiveDirectoryRights();
 217                //Lowercase this just in case. As far as I know it should always come back that way anyways, but better 
 43218                var aceType = ace.ObjectType().ToString().ToLower();
 43219                var inherited = ace.IsInherited();
 220
 43221                GuidMap.TryGetValue(aceType, out var mappedGuid);
 222
 43223                _log.LogTrace("Processing ACE with rights {Rights} and guid {GUID} on object {Name}", aceRights,
 43224                    aceType, objectName);
 225
 226                //GenericAll applies to every object
 43227                if (aceRights.HasFlag(ActiveDirectoryRights.GenericAll))
 9228                {
 9229                    if (aceType is ACEGuids.AllGuid or "")
 8230                        yield return new ACE
 8231                        {
 8232                            PrincipalType = resolvedPrincipal.ObjectType,
 8233                            PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 8234                            IsInherited = inherited,
 8235                            RightName = EdgeNames.GenericAll
 8236                        };
 237                    //This is a special case. If we don't continue here, every other ACE will match because GenericAll i
 9238                    continue;
 239                }
 240
 241                //WriteDACL and WriteOwner are always useful no matter what the object type is as well because they enab
 34242                if (aceRights.HasFlag(ActiveDirectoryRights.WriteDacl))
 2243                    yield return new ACE
 2244                    {
 2245                        PrincipalType = resolvedPrincipal.ObjectType,
 2246                        PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 2247                        IsInherited = inherited,
 2248                        RightName = EdgeNames.WriteDacl
 2249                    };
 250
 34251                if (aceRights.HasFlag(ActiveDirectoryRights.WriteOwner))
 2252                    yield return new ACE
 2253                    {
 2254                        PrincipalType = resolvedPrincipal.ObjectType,
 2255                        PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 2256                        IsInherited = inherited,
 2257                        RightName = EdgeNames.WriteOwner
 2258                    };
 259
 260                //Cool ACE courtesy of @rookuu. Allows a principal to add itself to a group and no one else
 34261                if (aceRights.HasFlag(ActiveDirectoryRights.Self) &&
 34262                    !aceRights.HasFlag(ActiveDirectoryRights.WriteProperty) &&
 34263                    !aceRights.HasFlag(ActiveDirectoryRights.GenericWrite) && objectType == Label.Group &&
 34264                    aceType == ACEGuids.WriteMember)
 3265                    yield return new ACE
 3266                    {
 3267                        PrincipalType = resolvedPrincipal.ObjectType,
 3268                        PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 3269                        IsInherited = inherited,
 3270                        RightName = EdgeNames.AddSelf
 3271                    };
 272
 273                //Process object type specific ACEs. Extended rights apply to users, domains, computers, and cert templa
 33274                if (aceRights.HasFlag(ActiveDirectoryRights.ExtendedRight))
 13275                {
 13276                    if (objectType == Label.Domain)
 4277                    {
 4278                        if (aceType == ACEGuids.DSReplicationGetChanges)
 1279                            yield return new ACE
 1280                            {
 1281                                PrincipalType = resolvedPrincipal.ObjectType,
 1282                                PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 1283                                IsInherited = inherited,
 1284                                RightName = EdgeNames.GetChanges
 1285                            };
 3286                        else if (aceType == ACEGuids.DSReplicationGetChangesAll)
 1287                            yield return new ACE
 1288                            {
 1289                                PrincipalType = resolvedPrincipal.ObjectType,
 1290                                PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 1291                                IsInherited = inherited,
 1292                                RightName = EdgeNames.GetChangesAll
 1293                            };
 2294                        else if (aceType == ACEGuids.DSReplicationGetChangesInFilteredSet)
 0295                            yield return new ACE
 0296                            {
 0297                                PrincipalType = resolvedPrincipal.ObjectType,
 0298                                PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 0299                                IsInherited = inherited,
 0300                                RightName = EdgeNames.GetChangesInFilteredSet
 0301                            };
 2302                        else if (aceType is ACEGuids.AllGuid or "")
 1303                            yield return new ACE
 1304                            {
 1305                                PrincipalType = resolvedPrincipal.ObjectType,
 1306                                PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 1307                                IsInherited = inherited,
 1308                                RightName = EdgeNames.AllExtendedRights
 1309                            };
 4310                    }
 9311                    else if (objectType == Label.User)
 3312                    {
 3313                        if (aceType == ACEGuids.UserForceChangePassword)
 1314                            yield return new ACE
 1315                            {
 1316                                PrincipalType = resolvedPrincipal.ObjectType,
 1317                                PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 1318                                IsInherited = inherited,
 1319                                RightName = EdgeNames.ForceChangePassword
 1320                            };
 2321                        else if (aceType is ACEGuids.AllGuid or "")
 1322                            yield return new ACE
 1323                            {
 1324                                PrincipalType = resolvedPrincipal.ObjectType,
 1325                                PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 1326                                IsInherited = inherited,
 1327                                RightName = EdgeNames.AllExtendedRights
 1328                            };
 3329                    }
 6330                    else if (objectType == Label.Computer)
 2331                    {
 332                        //ReadLAPSPassword is only applicable if the computer actually has LAPS. Check the world readabl
 2333                        if (hasLaps)
 1334                        {
 1335                            if (aceType is ACEGuids.AllGuid or "")
 1336                                yield return new ACE
 1337                                {
 1338                                    PrincipalType = resolvedPrincipal.ObjectType,
 1339                                    PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 1340                                    IsInherited = inherited,
 1341                                    RightName = EdgeNames.AllExtendedRights
 1342                                };
 0343                            else if (mappedGuid is "ms-mcs-admpwd")
 0344                                yield return new ACE
 0345                                {
 0346                                    PrincipalType = resolvedPrincipal.ObjectType,
 0347                                    PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 0348                                    IsInherited = inherited,
 0349                                    RightName = EdgeNames.ReadLAPSPassword
 0350                                };
 1351                        }
 2352                    }
 4353                    else if (objectType == Label.CertTemplate)
 0354                    {
 0355                        if (aceType is ACEGuids.AllGuid or "")
 0356                            yield return new ACE
 0357                            {
 0358                                PrincipalType = resolvedPrincipal.ObjectType,
 0359                                PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 0360                                IsInherited = inherited,
 0361                                RightName = EdgeNames.AllExtendedRights
 0362                            };
 0363                        else if (aceType is ACEGuids.Enroll)
 0364                            yield return new ACE
 0365                            {
 0366                                PrincipalType = resolvedPrincipal.ObjectType,
 0367                                PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 0368                                IsInherited = inherited,
 0369                                RightName = EdgeNames.Enroll
 0370                            };
 0371                    }
 13372                }
 373
 374                //GenericWrite encapsulates WriteProperty, so process them in tandem to avoid duplicate edges
 33375                if (aceRights.HasFlag(ActiveDirectoryRights.GenericWrite) ||
 33376                    aceRights.HasFlag(ActiveDirectoryRights.WriteProperty))
 8377                {
 8378                    if (objectType is Label.User
 8379                        or Label.Group
 8380                        or Label.Computer
 8381                        or Label.GPO
 8382                        or Label.CertTemplate
 8383                        or Label.RootCA
 8384                        or Label.EnterpriseCA
 8385                        or Label.AIACA
 8386                        or Label.NTAuthStore
 8387                        or Label.IssuancePolicy)
 7388                        if (aceType is ACEGuids.AllGuid or "")
 2389                            yield return new ACE
 2390                            {
 2391                                PrincipalType = resolvedPrincipal.ObjectType,
 2392                                PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 2393                                IsInherited = inherited,
 2394                                RightName = EdgeNames.GenericWrite
 2395                            };
 396
 8397                    if (objectType == Label.User && aceType == ACEGuids.WriteSPN)
 0398                        yield return new ACE
 0399                        {
 0400                            PrincipalType = resolvedPrincipal.ObjectType,
 0401                            PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 0402                            IsInherited = inherited,
 0403                            RightName = EdgeNames.WriteSPN
 0404                        };
 8405                    else if (objectType == Label.Computer && aceType == ACEGuids.WriteAllowedToAct)
 1406                        yield return new ACE
 1407                        {
 1408                            PrincipalType = resolvedPrincipal.ObjectType,
 1409                            PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 1410                            IsInherited = inherited,
 1411                            RightName = EdgeNames.AddAllowedToAct
 1412                        };
 7413                    else if (objectType == Label.Computer && aceType == ACEGuids.UserAccountRestrictions)
 0414                        yield return new ACE
 0415                        {
 0416                            PrincipalType = resolvedPrincipal.ObjectType,
 0417                            PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 0418                            IsInherited = inherited,
 0419                            RightName = EdgeNames.WriteAccountRestrictions
 0420                        };
 7421                    else if (objectType == Label.Group && aceType == ACEGuids.WriteMember)
 4422                        yield return new ACE
 4423                        {
 4424                            PrincipalType = resolvedPrincipal.ObjectType,
 4425                            PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 4426                            IsInherited = inherited,
 4427                            RightName = EdgeNames.AddMember
 4428                        };
 3429                    else if (objectType is Label.User or Label.Computer && aceType == ACEGuids.AddKeyPrincipal)
 0430                        yield return new ACE
 0431                        {
 0432                            PrincipalType = resolvedPrincipal.ObjectType,
 0433                            PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 0434                            IsInherited = inherited,
 0435                            RightName = EdgeNames.AddKeyCredentialLink
 0436                        };
 3437                    else if (objectType is Label.CertTemplate)
 0438                    {
 0439                        if (aceType == ACEGuids.PKIEnrollmentFlag)
 0440                            yield return new ACE
 0441                            {
 0442                                PrincipalType = resolvedPrincipal.ObjectType,
 0443                                PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 0444                                IsInherited = inherited,
 0445                                RightName = EdgeNames.WritePKIEnrollmentFlag
 0446                            };
 0447                        else if (aceType == ACEGuids.PKINameFlag)
 0448                            yield return new ACE
 0449                            {
 0450                                PrincipalType = resolvedPrincipal.ObjectType,
 0451                                PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 0452                                IsInherited = inherited,
 0453                                RightName = EdgeNames.WritePKINameFlag
 0454                            };
 0455                    }
 7456                }
 457
 458                // EnterpriseCA rights
 32459                if (objectType == Label.EnterpriseCA)
 0460                {
 0461                    if (aceType is ACEGuids.Enroll)
 0462                        yield return new ACE
 0463                        {
 0464                            PrincipalType = resolvedPrincipal.ObjectType,
 0465                            PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 0466                            IsInherited = inherited,
 0467                            RightName = EdgeNames.Enroll
 0468                        };
 469
 0470                    var cARights = (CertificationAuthorityRights)aceRights;
 471
 472                    // TODO: These if statements are also present in ProcessRegistryEnrollmentPermissions. Move to share
 0473                    if ((cARights & CertificationAuthorityRights.ManageCA) != 0)
 0474                        yield return new ACE
 0475                        {
 0476                            PrincipalType = resolvedPrincipal.ObjectType,
 0477                            PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 0478                            IsInherited = inherited,
 0479                            RightName = EdgeNames.ManageCA
 0480                        };
 0481                    if ((cARights & CertificationAuthorityRights.ManageCertificates) != 0)
 0482                        yield return new ACE
 0483                        {
 0484                            PrincipalType = resolvedPrincipal.ObjectType,
 0485                            PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 0486                            IsInherited = inherited,
 0487                            RightName = EdgeNames.ManageCertificates
 0488                        };
 489
 0490                    if ((cARights & CertificationAuthorityRights.Enroll) != 0)
 0491                        yield return new ACE
 0492                        {
 0493                            PrincipalType = resolvedPrincipal.ObjectType,
 0494                            PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 0495                            IsInherited = inherited,
 0496                            RightName = EdgeNames.Enroll
 0497                        };
 0498                }
 32499            }
 25500        }
 501
 502        /// <summary>
 503        ///     Helper function to use commonlib types and pass to ProcessGMSAReaders
 504        /// </summary>
 505        /// <param name="resolvedSearchResult"></param>
 506        /// <param name="searchResultEntry"></param>
 507        /// <returns></returns>
 508        public IEnumerable<ACE> ProcessGMSAReaders(ResolvedSearchResult resolvedSearchResult,
 509            ISearchResultEntry searchResultEntry)
 0510        {
 0511            var descriptor = searchResultEntry.GetByteProperty(LDAPProperties.GroupMSAMembership);
 0512            var domain = resolvedSearchResult.Domain;
 0513            var name = resolvedSearchResult.DisplayName;
 514
 0515            return ProcessGMSAReaders(descriptor, name, domain);
 0516        }
 517
 518        /// <summary>
 519        ///     ProcessGMSAMembership with no account name
 520        /// </summary>
 521        /// <param name="groupMSAMembership"></param>
 522        /// <param name="objectDomain"></param>
 523        /// <returns></returns>
 524        public IEnumerable<ACE> ProcessGMSAReaders(byte[] groupMSAMembership, string objectDomain)
 5525        {
 5526            return ProcessGMSAReaders(groupMSAMembership, "", objectDomain);
 5527        }
 528
 529        /// <summary>
 530        ///     Processes the msds-groupmsamembership property and returns ACEs representing principals that can read th
 531        ///     password from an object
 532        /// </summary>
 533        /// <param name="groupMSAMembership"></param>
 534        /// <param name="objectName"></param>
 535        /// <param name="objectDomain"></param>
 536        /// <returns></returns>
 537        public IEnumerable<ACE> ProcessGMSAReaders(byte[] groupMSAMembership, string objectName, string objectDomain)
 5538        {
 5539            if (groupMSAMembership == null)
 1540            {
 1541                _log.LogDebug("GMSA bytes are null for {Name}", objectName);
 1542                yield break;
 543            }
 544
 4545            var descriptor = _utils.MakeSecurityDescriptor();
 546            try
 4547            {
 4548                descriptor.SetSecurityDescriptorBinaryForm(groupMSAMembership);
 4549            }
 0550            catch (OverflowException)
 0551            {
 0552                _log.LogWarning("GMSA ACL length on object {Name} exceeds allowable length. Unable to process",
 0553                    objectName);
 0554            }
 555
 556
 20557            foreach (var ace in descriptor.GetAccessRules(true, true, typeof(SecurityIdentifier)))
 4558            {
 4559                if (ace == null)
 1560                {
 1561                    _log.LogTrace("Skipping null GMSA ACE for {Name}", objectName);
 1562                    continue;
 563                }
 564
 3565                if (ace.AccessControlType() == AccessControlType.Deny)
 1566                {
 1567                    _log.LogTrace("Skipping deny GMSA ACE for {Name}", objectName);
 1568                    continue;
 569                }
 570
 2571                var ir = ace.IdentityReference();
 2572                var principalSid = Helpers.PreProcessSID(ir);
 573
 2574                if (principalSid == null)
 1575                {
 1576                    _log.LogTrace("Pre-Process excluded SID {SID} on {Name}", ir ?? "null", objectName);
 1577                    continue;
 578                }
 579
 1580                _log.LogTrace("Processing GMSA ACE with principal {Principal}", principalSid);
 581
 1582                var resolvedPrincipal = _utils.ResolveIDAndType(principalSid, objectDomain);
 583
 1584                if (resolvedPrincipal != null)
 1585                    yield return new ACE
 1586                    {
 1587                        RightName = EdgeNames.ReadGMSAPassword,
 1588                        PrincipalType = resolvedPrincipal.ObjectType,
 1589                        PrincipalSID = resolvedPrincipal.ObjectIdentifier,
 1590                        IsInherited = ace.IsInherited()
 1591                    };
 1592            }
 4593        }
 594    }
 595}