< Summary

Class:SharpHoundCommonLib.Processors.ComputerAvailability
Assembly:SharpHoundCommonLib
File(s):D:\a\SharpHoundCommon\SharpHoundCommon\src\CommonLib\Processors\ComputerAvailability.cs
Covered lines:89
Uncovered lines:11
Coverable lines:100
Total lines:158
Line coverage:89% (89 of 100)
Covered branches:20
Total branches:30
Branch coverage:66.6% (20 of 30)

Metrics

MethodBranch coverage Cyclomatic complexity NPath complexity Sequence coverage
.ctor(...)100%20100%
.ctor(...)100%20100%
IsComputerAvailable(...)100%100%
IsComputerAvailable()68.18%22092.85%
SendComputerStatus()25%40100%

File(s)

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

#LineLine coverage
 1using System;
 2using System.Threading.Tasks;
 3using Microsoft.Extensions.Logging;
 4using SharpHoundCommonLib.OutputTypes;
 5
 6namespace SharpHoundCommonLib.Processors
 7{
 8    public class ComputerAvailability
 9    {
 10        public delegate Task ComputerStatusDelegate(CSVComputerStatus status);
 11
 12        private readonly int _computerExpiryDays;
 13        private readonly ILogger _log;
 14        private readonly PortScanner _scanner;
 15        private readonly int _scanTimeout;
 16        private readonly bool _skipPasswordCheck;
 17        private readonly bool _skipPortScan;
 18
 219        public ComputerAvailability(int timeout = 500, int computerExpiryDays = 60, bool skipPortScan = false,
 220            bool skipPasswordCheck = false, ILogger log = null)
 221        {
 222            _scanner = new PortScanner();
 223            _scanTimeout = timeout;
 224            _skipPortScan = skipPortScan;
 225            _log = log ?? Logging.LogProvider.CreateLogger("CompAvail");
 226            _computerExpiryDays = computerExpiryDays;
 227            _skipPasswordCheck = skipPasswordCheck;
 228        }
 29
 230        public ComputerAvailability(PortScanner scanner, int timeout = 500, int computerExpiryDays = 60,
 231            bool skipPortScan = false, bool skipPasswordCheck = false,
 232            ILogger log = null)
 233        {
 234            _scanner = scanner;
 235            _scanTimeout = timeout;
 236            _skipPortScan = skipPortScan;
 237            _log = log ?? Logging.LogProvider.CreateLogger("CompAvail");
 238            _computerExpiryDays = computerExpiryDays;
 239            _skipPasswordCheck = skipPasswordCheck;
 240        }
 41
 42        public event ComputerStatusDelegate ComputerStatusEvent;
 43
 44        /// <summary>
 45        ///     Helper function to use commonlib types for IsComputerAvailable
 46        /// </summary>
 47        /// <param name="result"></param>
 48        /// <param name="entry"></param>
 49        /// <returns></returns>
 50        public Task<ComputerStatus> IsComputerAvailable(ResolvedSearchResult result, ISearchResultEntry entry)
 051        {
 052            var name = result.DisplayName;
 053            var os = entry.GetProperty(LDAPProperties.OperatingSystem);
 054            var pwdlastset = entry.GetProperty(LDAPProperties.PasswordLastSet);
 55
 056            return IsComputerAvailable(name, os, pwdlastset);
 057        }
 58
 59        /// <summary>
 60        ///     Checks if a computer is available for SharpHound enumeration using the following criteria:
 61        ///     The "operatingsystem" LDAP attribute must contain the string "Windows"
 62        ///     The "pwdlastset" LDAP attribute must be within 60 days of the current date by default.
 63        ///     Port 445 must be open to allow API calls to succeed
 64        /// </summary>
 65        /// <param name="computerName">The computer to check availability for</param>
 66        /// <param name="operatingSystem">The LDAP operatingsystem attribute value</param>
 67        /// <param name="pwdLastSet">The LDAP pwdlastset attribute value</param>
 68        /// <returns>A <cref>ComputerStatus</cref> object that represents the availability of the computer</returns>
 69        public async Task<ComputerStatus> IsComputerAvailable(string computerName, string operatingSystem,
 70            string pwdLastSet)
 471        {
 472            if (operatingSystem != null && !operatingSystem.StartsWith("Windows", StringComparison.OrdinalIgnoreCase))
 173            {
 174                _log.LogDebug("{ComputerName} is not available because operating system {OperatingSystem} is not valid",
 175                    computerName, operatingSystem);
 176                await SendComputerStatus(new CSVComputerStatus
 177                {
 178                    Status = ComputerStatus.NonWindowsOS,
 179                    Task = "ComputerAvailability",
 180                    ComputerName = computerName
 181                });
 182                return new ComputerStatus
 183                {
 184                    Connectable = false,
 185                    Error = ComputerStatus.NonWindowsOS
 186                };
 87            }
 88
 389            if (!_skipPasswordCheck)
 390            {
 391                var passwordLastSet = Helpers.ConvertLdapTimeToLong(pwdLastSet);
 392                var threshold = DateTime.Now.AddDays(_computerExpiryDays * -1).ToFileTimeUtc();
 93
 394                if (passwordLastSet < threshold)
 195                {
 196                    _log.LogDebug(
 197                        "{ComputerName} is not available because password last set {PwdLastSet} is out of range",
 198                        computerName, passwordLastSet);
 199                    await SendComputerStatus(new CSVComputerStatus
 1100                    {
 1101                        Status = ComputerStatus.OldPwd,
 1102                        Task = "ComputerAvailability",
 1103                        ComputerName = computerName
 1104                    });
 1105                    return new ComputerStatus
 1106                    {
 1107                        Connectable = false,
 1108                        Error = ComputerStatus.OldPwd
 1109                    };
 110                }
 2111            }
 112
 2113            if (_skipPortScan)
 0114                return new ComputerStatus
 0115                {
 0116                    Connectable = true,
 0117                    Error = null
 0118                };
 119
 120
 2121            if (!await _scanner.CheckPort(computerName, timeout: _scanTimeout))
 1122            {
 1123                _log.LogDebug("{ComputerName} is not available because port 445 is unavailable", computerName);
 1124                await SendComputerStatus(new CSVComputerStatus
 1125                {
 1126                    Status = ComputerStatus.PortNotOpen,
 1127                    Task = "ComputerAvailability",
 1128                    ComputerName = computerName
 1129                });
 1130                return new ComputerStatus
 1131                {
 1132                    Connectable = false,
 1133                    Error = ComputerStatus.PortNotOpen
 1134                };
 135            }
 136
 1137            _log.LogDebug("{ComputerName} is available for enumeration", computerName);
 138
 1139            await SendComputerStatus(new CSVComputerStatus
 1140            {
 1141                Status = CSVComputerStatus.StatusSuccess,
 1142                Task = "ComputerAvailability",
 1143                ComputerName = computerName
 1144            });
 145
 1146            return new ComputerStatus
 1147            {
 1148                Connectable = true,
 1149                Error = null
 1150            };
 4151        }
 152
 153        private async Task SendComputerStatus(CSVComputerStatus status)
 4154        {
 4155            if (ComputerStatusEvent is not null) await ComputerStatusEvent.Invoke(status);
 4156        }
 157    }
 158}