< Summary

Class:SharpHoundCommonLib.Processors.GroupProcessor
Assembly:SharpHoundCommonLib
File(s):D:\a\SharpHoundCommon\SharpHoundCommon\src\CommonLib\Processors\GroupProcessor.cs
Covered lines:43
Uncovered lines:9
Coverable lines:52
Total lines:95
Line coverage:82.6% (43 of 52)
Covered branches:47
Total branches:68
Branch coverage:69.1% (47 of 68)

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%20100%
ReadGroupMembers(...)0%400%
ReadGroupMembers()72.41%58093.33%
GetPrimaryGroupInfo(...)75%4091.66%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Security.Principal;
 5using Microsoft.Extensions.Logging;
 6using SharpHoundCommonLib.Enums;
 7using SharpHoundCommonLib.OutputTypes;
 8
 9namespace SharpHoundCommonLib.Processors {
 10    public class GroupProcessor {
 11        private readonly ILogger _log;
 12        private readonly ILdapUtils _utils;
 13
 1414        public GroupProcessor(ILdapUtils utils, ILogger log = null) {
 715            _utils = utils;
 716            _log = log ?? Logging.LogProvider.CreateLogger("GroupProc");
 717        }
 18
 019        public IAsyncEnumerable<TypedPrincipal> ReadGroupMembers(ResolvedSearchResult result, IDirectoryObject entry) {
 020            if (entry.TryGetArrayProperty(LDAPProperties.Members, out var members) &&
 021                entry.TryGetDistinguishedName(out var dn)) {
 022                return ReadGroupMembers(dn, members, result.DisplayName);
 23            }
 24
 025            return AsyncEnumerable.Empty<TypedPrincipal>();
 026        }
 27
 28        /// <summary>
 29        ///     Processes the "member" property of groups and converts the resulting list of distinguishednames to Typed
 30        /// </summary>
 31        /// <param name="distinguishedName"></param>
 32        /// <param name="members"></param>
 33        /// <param name="objectName"></param>
 34        /// <returns></returns>
 35        public async IAsyncEnumerable<TypedPrincipal> ReadGroupMembers(string distinguishedName, string[] members,
 236            string objectName = "") {
 237            _log.LogDebug("Running Group Membership Enumeration for {ObjectName}", objectName);
 38            // If our returned array has a length of 0, one of two things is happening
 39            // The first possibility we'll look at is we need to use ranged retrieval, because AD will not return
 40            // more than a certain number of items. If we get nothing back from this, then the group is empty
 341            if (members.Length == 0) {
 142                _log.LogDebug("Member property for {ObjectName} is empty, trying range retrieval",
 143                    objectName);
 1544                await foreach (var result in _utils.RangedRetrieval(distinguishedName, "member")) {
 445                    if (!result.IsSuccess) {
 046                        _log.LogDebug("Failure during ranged retrieval for {ObjectName}: {Message}", objectName, result.
 047                        yield break;
 48                    }
 49
 450                    var member = result.Value;
 451                    _log.LogTrace("Got member {DN} for {ObjectName} from ranged retrieval", member, objectName);
 452                    if (await _utils.ResolveDistinguishedName(member) is (true, var res) &&
 753                        !Helpers.IsSidFiltered(res.ObjectIdentifier)) {
 354                        yield return res;
 455                    } else {
 156                        yield return new TypedPrincipal(member.ToUpper(), Label.Base);
 157                    }
 458                }
 259            } else {
 60                //If we're here, we just read the data directly and life is good
 1561                foreach (var member in members) {
 462                    _log.LogTrace("Got member {DN} for {ObjectName}", member, objectName);
 463                    if (await _utils.ResolveDistinguishedName(member) is (true, var res) &&
 764                        !Helpers.IsSidFiltered(res.ObjectIdentifier)) {
 365                        yield return res;
 466                    } else {
 167                        yield return new TypedPrincipal(member.ToUpper(), Label.Base);
 168                    }
 469                }
 170            }
 271        }
 72
 73        /// <summary>
 74        ///     Reads the primary group info from a user or computer object and massages it into the proper format.
 75        /// </summary>
 76        /// <param name="primaryGroupId"></param>
 77        /// <param name="objectId"></param>
 78        /// <returns></returns>
 379        public static string GetPrimaryGroupInfo(string primaryGroupId, string objectId) {
 380            if (primaryGroupId == null)
 181                return null;
 82
 283            if (objectId == null)
 084                return null;
 85
 286            try {
 287                var domainSid = new SecurityIdentifier(objectId).AccountDomainSid.Value;
 188                var primaryGroupSid = $"{domainSid}-{primaryGroupId}";
 189                return primaryGroupSid;
 290            } catch {
 191                return null;
 92            }
 393        }
 94    }
 95}