using System; using System.Collections.Generic; using System.Data.Common; using System.DirectoryServices.AccountManagement; using System.DirectoryServices.Protocols; using System.Globalization; using System.Linq; using System.Net; using System.Reflection; using System.Security.Principal; using System.Text; using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using ActiveDs; using C4IT.FASD.Base; using C4IT.HTTP; using C4IT.Logging; using C4IT.MsGraph; using C4IT_DataHistoryProvider_Base.DataSources; using Newtonsoft.Json.Linq; using static C4IT.Logging.cLogManager; namespace C4IT.DataHistoryProvider { public enum enumLdapPropertyType { ldapString, ldapTimeInterval, ldapSid, ldapPhone }; public class cDataHistoryCollectorActiveDirectory : cDataHistoryCollectorModule, ISearchResultRelationProvider { public const string constConnectorName = "MS Active Directory, Entra ID & Intune connector"; public const string constLicenseId = "B2D48023-56E8-44D5-AB77-BE1DCF4EA4EB"; public const string constMsGraphUserList = "users?$top={0}&$count=true&$select=id,displayName,onPremisesSyncEnabled,onPremisesSecurityIdentifier&$filter=onPremisesSyncEnabled eq true"; public const string constMsGraphUserEntry = "users/{0}"; public const string constMsGraphUserDeviceEntry = "users/{0}"; public const int constMsGraphUserPaging = 100; public const string constMsGraphDeviceList = "devices/?$top={0}&$select=id,deviceId,displayName,onPremisesSyncEnabled,onPremisesSecurityIdentifier&$filter=onPremisesSyncEnabled eq true"; public const string constMsGraphDeviceEntry = "devices/{0}"; public const int constMsGraphDevicePaging = 100; public const string constMsGraphManagedDeviceList = "deviceManagement/managedDevices?$top={0}&$select=id,azureActiveDirectoryDeviceId,deviceName,userPrincipalName,lastSyncDateTime&$filter=operatingSystem eq 'Windows'"; public const string constMsGraphManagedMobileDeviceList = "users/{0}/managedDevices?$select=id,userId,deviceName,lastSyncDateTime&$filter=operatingSystem eq 'Android' or operatingSystem eq 'iOS' or operatingSystem eq 'iPadOS'"; public const string constMsGraphManagedDeviceEntry = "deviceManagement/managedDevices/{0}?$select=id,complianceState,userPrincipalName,userDisplayName,managementState,lastSyncDateTime"; public const string constMsGraphManagedDeviceAllEntry = "deviceManagement/managedDevices/{0}"; public const string constMsGraphManagedDeviceSyncDevice = "deviceManagement/managedDevices/{0}/syncDevice"; public const string constMsGraphManagedDeviceDetectedApps = "deviceManagement/managedDevices/{0}/detectedApps"; public const string constMsGraphManagedDeviceCount = "users/{0}/managedDevices?$select=operatingSystem"; public const string constMsGraphManagedUserDeviceEntry = "users/{0}/managedDevices?$select=id,deviceName,model,operatingSystem,manufacturer,serialNumber"; public const string constMsGraphManagedDeviceChangeManagedStatus = "deviceManagement/managedDevices/{0}/{1}"; public const string constMsGraphTest = "deviceManagement/managedDevices"; public const string constMsGraphDeviceBitlockerRecoveryKey = "informationProtection/bitlocker/recoveryKeys?$filter=deviceId eq '{0}'"; public const string constMsGraphDeviceBitlockerRecoveryKeyDetails = "informationProtection/bitlocker/recoveryKeys/{0}?$select=key"; public const string constMsGraphDeviceLAPS = "directory/deviceLocalCredentials/{0}?$select=credentials,deviceName"; public const int constMsGraphManagedDevicePaging = 100; public const string constTableNameIntuneDeviceDetails = "intune-deviceInfo"; public const string constTableNameIntuneUserDeviceCount = "intune-userDeviceCount"; public const string constTableNameIntuneUserDeviceCountDetail = "intune-userDeviceCount-details"; public const string constTableNameIntuneMobileDeviceDetails = "intune-mobile-deviceInfo"; public readonly List UserMainProperties = new List() { new cLdapPropertyInfo() { Name="objectSid", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapSid, DbName=null}, new cLdapPropertyInfo() { Name="cn", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null}, new cLdapPropertyInfo() { Name="DistinguishedName", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null}, new cLdapPropertyInfo() { Name="uSNChanged", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapTimeInterval, DbName=null}, new cLdapPropertyInfo() { Name="sAMAccountName", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null}, new cLdapPropertyInfo() { Name="displayName", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null}, new cLdapPropertyInfo() { Name="userPrincipalName", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName="upn_external"}, new cLdapPropertyInfo() { Name="userAccountControl", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null}, new cLdapPropertyInfo() { Name="mail", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName="mail"} }; public readonly List UserPhoneProperties = new List() { new cLdapPropertyInfo() { Name="telephoneNumber", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapPhone, DbName="phone", Cumulative = true, Reverse = true}, new cLdapPropertyInfo() { Name="mobile", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapPhone, DbName="phone", Cumulative = true, Reverse = true}, new cLdapPropertyInfo() { Name="ipPhone", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapPhone, DbName="phone", Cumulative = true, Reverse = true}, new cLdapPropertyInfo() { Name="otherTelephone", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapPhone, DbName="phone", Cumulative = true, Reverse = true}, new cLdapPropertyInfo() { Name="otherMobile", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapPhone, DbName="phone", Cumulative = true, Reverse = true}, new cLdapPropertyInfo() { Name="otherIpPhone", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapPhone, DbName="phone", Cumulative = true, Reverse = true} }; public readonly List ComputerMainProperties = new List() { new cLdapPropertyInfo() { Name="objectSid", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapSid, DbName=null}, new cLdapPropertyInfo() { Name="cn", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null}, new cLdapPropertyInfo() { Name="DistinguishedName", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null}, new cLdapPropertyInfo() { Name="uSNChanged", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapTimeInterval, DbName=null}, new cLdapPropertyInfo() { Name="sAMAccountName", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null}, new cLdapPropertyInfo() { Name="userAccountControl", LdapType= DataHistoryProvider.enumLdapPropertyType.ldapString, DbName=null} }; private readonly cDataHistoryCollector _collector; private static readonly DateTime BaseDateTime; private static readonly long MinSqlDateTimeTicks = 0; private static readonly long MaxSqlDateTimeTicks = 0; private readonly Dictionary Domains = new Dictionary(); private readonly Dictionary DomainsRev = new Dictionary(); private DateTime LastDomainsUpdate = DateTime.MinValue; private bool DomainsUpdateRunning = false; private Dictionary tenantGraphCache = new Dictionary(); static cDataHistoryCollectorActiveDirectory() { BaseDateTime = new DateTime(1601, 1, 1, 0, 0, 0, DateTimeKind.Utc); MinSqlDateTimeTicks = (long)(new DateTime(1753, 1, 1, 0, 0, 0, DateTimeKind.Utc) - BaseDateTime).TotalMilliseconds * 10000 + 1; var dt = DateTime.SpecifyKind(DateTime.MaxValue, DateTimeKind.Utc) - TimeSpan.FromDays(1); MaxSqlDateTimeTicks = (long)(dt - BaseDateTime).TotalMilliseconds * 10000 - 1; } public cDataHistoryCollectorActiveDirectory(cDataHistoryCollector Collector) : base(Collector, new List() { enumDataHistoryOrigin.ActiveDirectory, enumDataHistoryOrigin.Intune }, constConnectorName, constLicenseId) { _collector = Collector; } public HashSet GetSupportedInformationClasses() => new HashSet() { enumFasdInformationClass.MobileDevice }; private async Task> GetLastAdScansAsync(cDbConnection Conn, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; if (!DataHistorySqlHelper.GetWellKnownSqlStatement("GetAdScanInfo", out var Query)) return null; var Retval = new Dictionary(); int apiError = 0; try { if (Token.IsCancellationRequested) return null; DbDataReader Reader = await DataHistorySqlHelper.GetTableResultAsync(Conn, Query, null, Token); if (Token.IsCancellationRequested) return null; if (Reader == null) return null; try { if (Reader.HasRows) { while (await Reader.ReadAsync()) { if (Token.IsCancellationRequested) return null; try { var Entry = new cDbAdScanInfo { ID = Reader.GetInt32(0), Name = Reader.GetString(1), LastRun = DataHistorySqlHelper.ConvertDBDateTimeToUTC(Reader.GetDateTime(2)) }; if (Retval.ContainsKey(Entry.Name)) Retval[Entry.Name] = Entry; else Retval.Add(Entry.Name, Entry); } catch (Exception E) { apiError = E.HResult; cLogManager.DefaultLogger.LogException(E); } } } } catch (Exception E) { apiError = E.HResult; cLogManager.DefaultLogger.LogException(E); } finally { Reader.Close(); Reader.Dispose(); } return Retval; } catch (Exception E) { apiError = E.HResult; cLogManager.DefaultLogger.LogException(E); } finally { if (DataHistorySqlHelper.LogSql) DataHistorySqlHelper.SaveSqlTimingEntry(Query.Name, _startTime, "", apiError, requestInfo?.requestName); if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task RemoveAdScanEntryAsync(cDbConnection Conn, int ID, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!DataHistorySqlHelper.GetWellKnownSqlStatement("RemoveAdScanInfo", out var Query)) return; if (Token.IsCancellationRequested) return; var Params = new Dictionary { { "ID", ID } }; var Cnt = await DataHistorySqlHelper.ExecuteAsync(Conn, Query, Params, requestInfo, Token); } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return; } private async Task UpdateAdScanEntryAsync(cDbConnection Conn, cDbAdScanInfo Entry, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateAdScanInfo", out var Query)) return -1; if (Token.IsCancellationRequested) return -1; var Params = new Dictionary { { "ID", Entry.ID }, { "Name", Entry.Name }, { "LastRun", Entry.LastRun } }; var ID = await DataHistorySqlHelper.GetScalarResultAsync(Conn, Query, Params, requestInfo, Token); return ID; } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return -1; } private async Task> GetLastAdScanNode(int ScanID, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; if (!DataHistorySqlHelper.GetWellKnownSqlStatement("GetAdScanNodes", out var Query)) return null; var Params = new Dictionary { { "ScanID", ScanID } }; int apiError = 0; var Retval = new Dictionary(); try { using (var Conn = new cDbConnection(Collector.mainDbConnection)) { if (Token.IsCancellationRequested) return null; DbDataReader Reader = await DataHistorySqlHelper.GetTableResultAsync(Conn, Query, Params, Token); if (Token.IsCancellationRequested) return null; if (Reader == null) return null; try { if (Reader.HasRows) { while (await Reader.ReadAsync()) { if (Token.IsCancellationRequested) return null; try { var Entry = new cDbAdScanNode { ScanID = Reader.GetInt32(0), Name = Reader.GetString(1), Fingerprint = Reader.GetString(2), InvocationID = Reader.GetString(3) }; var o = Reader[4]; if ((o == null) || (o is DBNull)) Entry.LastUSN = 0; else { Decimal d = (Decimal)o; Entry.LastUSN = Convert.ToUInt64(d); } if (Retval.ContainsKey(Entry.Name) && (Entry.ScanID == ScanID)) Retval[Entry.Name] = Entry; else Retval.Add(Entry.Name, Entry); } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); } } } } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); } finally { Reader.Close(); Reader.Dispose(); } } return Retval; } catch (Exception E) { apiError = E.HResult; cLogManager.DefaultLogger.LogException(E); } finally { if (DataHistorySqlHelper.LogSql) DataHistorySqlHelper.SaveSqlTimingEntry(Query.Name, _startTime, Params, apiError, requestInfo?.requestName); if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task UpdateAdScanNodeEntryAsync(cDbAdScanNode Node, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateAdScanNode", out var Query)) return; using (var Conn = new cDbConnection(Collector.mainDbConnection)) { if (Token.IsCancellationRequested) return; var Params = new Dictionary { { "ScanID", Node.ScanID }, { "Name", Node.Name }, { "Fingerprint", Node.Fingerprint }, { "InvocationId", Node.InvocationID }, { "LastUsn", Convert.ToDecimal(Node.LastUSN) } }; await DataHistorySqlHelper.ExecuteAsync(Conn, Query, Params, requestInfo, Token); } } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } } private static string GetStringFromLdapAttribute(SearchResultAttributeCollection Attributes, string Name, string ObjectName, int MaxLength = -1, Dictionary Props = null) { try { if (Attributes.Contains(Name)) { var arrAttr = Attributes[Name]; if ((arrAttr != null) && (arrAttr.Count > 0)) { var objAttr = arrAttr[0]; if (objAttr == null) return null; var strAttr = objAttr.ToString(); if (MaxLength > 0 && strAttr.Length > MaxLength) strAttr = strAttr.Substring(0, MaxLength); if (Props != null && Props.TryGetValue(Name, out var _prop)) { if (_prop.Cardinal > 0 && strAttr.Length > _prop.Cardinal) strAttr = strAttr.Substring(0, _prop.Cardinal); } return strAttr; } } } catch (Exception E) { cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("error getting LDAP property '{0}' for object '{1}'", Name, ObjectName)); cLogManager.DefaultLogger.LogException(E, LogLevels.Debug); } return null; } private static Int32? GetInt32FromLdapAttribute(SearchResultAttributeCollection Attributes, string Name, string ObjectName) { try { if (Attributes.Contains(Name)) { var arrAttr = Attributes[Name]; if ((arrAttr != null) && (arrAttr.Count > 0)) { var objAttr = arrAttr[0]; if (objAttr == null) return null; var strAttr = objAttr.ToString(); if (Int32.TryParse(strAttr, out var RetVal)) return RetVal; } } } catch (Exception E) { cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("error getting LDAP property '{0}' for object '{1}'", Name, ObjectName)); cLogManager.DefaultLogger.LogException(E, LogLevels.Debug); } return null; } private static UInt32? GetUInt32FromLdapAttribute(SearchResultAttributeCollection Attributes, string Name, string ObjectName) { try { if (Attributes.Contains(Name)) { var arrAttr = Attributes[Name]; if ((arrAttr != null) && (arrAttr.Count > 0)) { var objAttr = arrAttr[0]; if (objAttr == null) return null; var strAttr = objAttr.ToString(); if (UInt32.TryParse(strAttr, out var RetVal)) return RetVal; } } } catch (Exception E) { cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("error getting LDAP property '{0}' for object '{1}'", Name, ObjectName)); cLogManager.DefaultLogger.LogException(E, LogLevels.Debug); } return null; } private static UInt64? GetUInt64FromLdapAttribute(SearchResultAttributeCollection Attributes, string Name, string ObjectName) { try { if (Attributes.Contains(Name)) { var arrAttr = Attributes[Name]; if ((arrAttr != null) && (arrAttr.Count > 0)) { var objAttr = arrAttr[0]; if (objAttr == null) return null; var strAttr = objAttr.ToString(); if (UInt64.TryParse(strAttr, out var RetVal)) return RetVal; } } } catch (Exception E) { cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("error getting LDAP property '{0}' for object '{1}'", Name, ObjectName)); cLogManager.DefaultLogger.LogException(E, LogLevels.Debug); } return null; } private static DateTime? GetDateTimeFromLdapAttribute(SearchResultAttributeCollection Attributes, string Name, string ObjectName) { try { if (Attributes.Contains(Name)) { var arrAttr = Attributes[Name]; if ((arrAttr != null) && (arrAttr.Count > 0)) { var objAttr = arrAttr[0]; if (objAttr == null) return null; var strAttr = objAttr.ToString(); if (strAttr.EndsWith("Z")) { strAttr = strAttr.Remove(strAttr.Length - 1, 1); if (DateTime.TryParseExact(strAttr, "yyyyMMddHHmmss.f", CultureInfo.InvariantCulture, System.Globalization.DateTimeStyles.AdjustToUniversal, out var dt)) { dt = DateTime.SpecifyKind(dt, DateTimeKind.Utc); return dt; } } else { if (long.TryParse(strAttr, out var ticks)) { if (ticks <= MinSqlDateTimeTicks || ticks >= MaxSqlDateTimeTicks) return null; long msecs = ticks / 10000; var ts = TimeSpan.FromMilliseconds(msecs); var RetVal = BaseDateTime + ts; return RetVal; } } } } } catch (Exception E) { cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("error getting LDAP property '{0}' for object '{1}'", Name, ObjectName)); cLogManager.DefaultLogger.LogException(E, LogLevels.Debug); } return null; } private static object GetValueFromLdapAttribute(SearchResultAttributeCollection Attributes, string Name, string ObjectName) { try { if (Attributes.Contains(Name)) { var arrAttr = Attributes[Name]; if ((arrAttr != null) && (arrAttr.Count > 0)) { var objAttr = arrAttr[0]; if (objAttr != null) return objAttr; } } } catch (Exception E) { cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("error getting LDAP property '{0}' for object '{1}'", Name, ObjectName)); cLogManager.DefaultLogger.LogException(E, LogLevels.Debug); } return null; } private static byte[] GetByteArrayFromLdapAttribute(SearchResultAttributeCollection Attributes, string Name, string ObjectName) { try { if (Attributes.Contains(Name)) { var arrAttr = Attributes[Name]; if ((arrAttr != null) && (arrAttr.Count > 0)) { var objAttr = arrAttr[0]; if (objAttr is byte[] RetVal) return RetVal; } } } catch (Exception E) { cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("error getting LDAP property '{0}' for object '{1}'", Name, ObjectName)); cLogManager.DefaultLogger.LogException(E, LogLevels.Debug); } return null; } private static string GetSidFromByteArray(byte[] B, string Name, string ObjectName) { try { var objSid = new SecurityIdentifier(B, 0); var strSid = objSid.ToString(); return strSid; } catch (Exception E) { cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("error converting ldap property '{0}' for object '{1}' to SID", Name, ObjectName)); cLogManager.DefaultLogger.LogException(E, LogLevels.Debug); } return null; } public static byte[] GetByteArrayFromSid(string Sid) { try { var objSid = new SecurityIdentifier(Sid); byte[] RetVal = new byte[objSid.BinaryLength]; objSid.GetBinaryForm(RetVal, 0); return RetVal; } catch (Exception E) { cLogManager.DefaultLogger.LogEntry(LogLevels.Warning, string.Format($"error converting string to SID: {Sid}")); cLogManager.DefaultLogger.LogException(E, LogLevels.Debug); } return null; } private static string FormatPlainPhone(string s) { if (s == null) return null; var RetVal = ""; foreach (var C in s) if ((C >= '0') && (C <= '9')) RetVal += C; return RetVal; } private void GetSubOUs(LdapConnection LC, List nodeList, string DSN, List AlreadyScannedNodes) { var UL = new List(); try { foreach (var AlreadyNode in AlreadyScannedNodes) { if (AlreadyNode == DSN.ToUpperInvariant()) return; } PageResultRequestControl pageRequestControl = new PageResultRequestControl((int)1000); PageResultResponseControl pageResponseControl; SearchOptionsControl soc = new SearchOptionsControl(System.DirectoryServices.Protocols.SearchOption.DomainScope); string[] Attr = { "ou", "DistinguishedName" }; var SR = new SearchRequest(DSN, "(objectclass=organizationalUnit)", System.DirectoryServices.Protocols.SearchScope.OneLevel, Attr); SR.Controls.Add(pageRequestControl); SR.Controls.Add(soc); while (true) { var searchResponse = (SearchResponse)LC.SendRequest(SR); pageResponseControl = (PageResultResponseControl)searchResponse.Controls[0]; foreach (SearchResultEntry Entry in searchResponse.Entries) { try { var dn = Entry.Attributes["DistinguishedName"][0].ToString(); UL.Add(dn); } catch { } } if (pageResponseControl.Cookie.Length == 0) break; pageRequestControl.Cookie = pageResponseControl.Cookie; } foreach (var DN in UL) { var skip = false; foreach (var AlreadyNode in AlreadyScannedNodes) { if (AlreadyNode == DN.ToUpperInvariant()) { skip = true; break; } } if (!skip) { nodeList.Add(DN); GetSubOUs(LC, nodeList, DN, AlreadyScannedNodes); } } } catch (Exception E) { cLogManager.DefaultLogger.LogEntry(LogLevels.Error, string.Format("Error getting sub OUs from '{0}' at server {1}", DSN, LC.SessionOptions.HostName)); cLogManager.DefaultLogger.LogException(E); return; } } private async Task DoAdScanAsync(cDbConnection Conn, cDataHistoryConfigActiveDirectory AdConfig, Dictionary LdapProps, cDataHistoryAdScan Scan, cDataHistoryAdScanNode Node, List AlreadyScannedNodes, cDbAdScanNode NodeInfo, cDbAdGeneralInfo GeneralInfo, bool Rescan, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { cDataHistoryAdServer Server = null; string InvocationId = null; UInt64 maxUsn = 0; LdapConnection LC = null; var AdDomain = Node.AdDomain; var Creds = new NetworkCredential(AdDomain.Credential.User, AdDomain.Credential.NwCredential.Password, AdDomain.Credential.Domain); foreach (var tmpServer in AdDomain.Servers) { maxUsn = 0; // create the Ldap connection object var LdapInfo = new LdapDirectoryIdentifier(tmpServer.FQDN, tmpServer.Port, false, false); LC = new LdapConnection(LdapInfo); LC.SessionOptions.ProtocolVersion = 3; LC.Credential = Creds; // try get the server invocationId try { string[] Attr0 = { "dsServiceName", "highestCommittedUSN", "defaultNamingContext", "configurationNamingContext" }; var SR1 = new SearchRequest(null, "(objectclass=*)", System.DirectoryServices.Protocols.SearchScope.Base, Attr0); var SRR = (SearchResponse)LC.SendRequest(SR1); var currentDomain = SRR.Entries[0].Attributes["defaultNamingContext"][0].ToString(); var CNC = SRR.Entries[0].Attributes["configurationNamingContext"][0].ToString(); NodeInfo.domainDNS = ""; var arrDoms = currentDomain.Split(','); if (arrDoms != null) for (int i = 0; i < arrDoms.Length; i++) { var arr2 = arrDoms[i].Split('='); if (arr2 == null || arr2.Length != 2 || string.IsNullOrEmpty(arr2[1])) break; if (NodeInfo.domainDNS != "") NodeInfo.domainDNS += "."; NodeInfo.domainDNS += arr2[1].ToLowerInvariant(); } if (NodeInfo.domainDNS != AdDomain.FQDN.ToLowerInvariant()) { LogEntry($"The DC server '{tmpServer.FQDN}' is not member of the domain '{AdDomain.FQDN}'.", LogLevels.Warning); Collector.DoProcessUiMessage(2, $"Warning: The DC server '{tmpServer.FQDN}' is not member of the domain '{AdDomain.FQDN}', it is member of '{NodeInfo.domainDNS}'."); continue; } var SN = SRR.Entries[0].Attributes["dsServiceName"][0].ToString(); var strMaxUsn = SRR.Entries[0].Attributes["highestcommittedusn"][0].ToString(); if (!UInt64.TryParse(strMaxUsn, out maxUsn)) maxUsn = 0; string[] Attr1 = { "invocationId" }; SR1 = new SearchRequest(SN, "(objectclass=*)", System.DirectoryServices.Protocols.SearchScope.Base, Attr1); SRR = (SearchResponse)LC.SendRequest(SR1); var arrId = (byte[])SRR.Entries[0].Attributes["invocationId"][0]; InvocationId = ""; foreach (var b in arrId) InvocationId += b.ToString("X2"); NodeInfo.domainNB = null; try { string[] Attr2 = { "netbiosname" }; var filter = $"(&(objectclass=crossRef)(nCName={currentDomain}))"; //var filter = $"(objectclass=crossRef)"; SR1 = new SearchRequest("CN=Partitions," + CNC, filter, System.DirectoryServices.Protocols.SearchScope.OneLevel, Attr2); SRR = (SearchResponse)LC.SendRequest(SR1); NodeInfo.domainNB = SRR.Entries[0].Attributes["netbiosname"][0].ToString().ToUpperInvariant(); } catch (Exception E) { LogEntry("", LogLevels.Warning); LogException(E, LogLevels.Warning); } if (!string.IsNullOrEmpty(NodeInfo.domainNB)) { if (!GeneralInfo.Domains.ContainsKey(NodeInfo.domainDNS)) GeneralInfo.Domains[NodeInfo.domainDNS] = NodeInfo.domainNB; } } catch (Exception E) { cLogManager.DefaultLogger.LogEntry(LogLevels.Debug, string.Format("Could not get AD invocation id from server {0}", tmpServer)); cLogManager.DefaultLogger.LogException(E, LogLevels.Debug); InvocationId = null; } if (!string.IsNullOrEmpty(InvocationId)) { Server = tmpServer; break; } } // exit, if no login was sucessfull if (Server == null) { Collector.DoProcessUiMessage(2, $"Error: Could not get a valid AD server with invocation id for domain '{AdDomain.FQDN}', aborting scan..."); LogEntry("Could not get a valid AD server with invocation id, aborting AD scan.", LogLevels.Error); return null; } // check, if we have to do a full scan var Fingerprint = string.Format("{0}|{1}|{2}|{3}", Scan.Name, Node.AdDomain, Node.Path, Server.FQDN); Fingerprint = GetHash(Fingerprint); bool bFullScan = true; if ((NodeInfo.Fingerprint == Fingerprint) && (NodeInfo.InvocationID == InvocationId) && (NodeInfo.LastUSN > 0) && !Rescan) bFullScan = false; else { NodeInfo.Fingerprint = Fingerprint; NodeInfo.InvocationID = InvocationId; NodeInfo.LastUSN = 0; } // create the ldap filter & the list of ldap poperties needed string ldapFilter; string[] defAttr; if (Scan.Type == enumFasdInformationClass.User) { ldapFilter = "(objectClass=user)(objectCategory=person)"; } else if (Scan.Type == enumFasdInformationClass.Computer) { ldapFilter = "(objectClass=computer)(objectCategory=computer)"; } else { LogEntry($"Illegal scan tpye '{Scan.Type}' used, aborting AD scan.", LogLevels.Error); return null; } defAttr = LdapProps.Keys.ToArray(); if (!bFullScan) ldapFilter += string.Format("(!(uSNChanged<={0}))", NodeInfo.LastUSN.ToString()); if (!string.IsNullOrEmpty(Node.LdapFilter)) { var strFilter = Node.LdapFilter; if (!(strFilter.StartsWith("(") && strFilter.EndsWith(")"))) strFilter = "(" + strFilter + ")"; ldapFilter += strFilter; } ldapFilter = "(&" + ldapFilter + ")"; // get the list of sub ous var nodeList = new List(); var DN = GetBaseDN(Node); nodeList.Add(DN); GetSubOUs(LC, nodeList, DN, AlreadyScannedNodes); AlreadyScannedNodes.Add(DN.ToUpperInvariant()); Collector.DoProcessUiMessage(2, string.Format("Scanning {0} OUs on server {1}...", nodeList.Count, Server.FQDN)); // scan the ous int numChanges = 0; foreach (var dnOU in nodeList) { try { Collector.DoProcessUiMessage(4, $"scan node '{dnOU}'"); PageResultRequestControl pageRequestControl = new PageResultRequestControl((int)1000); PageResultResponseControl pageResponseControl; SearchOptionsControl soc = new SearchOptionsControl(System.DirectoryServices.Protocols.SearchOption.DomainScope); var SR = new SearchRequest(dnOU, ldapFilter, System.DirectoryServices.Protocols.SearchScope.OneLevel, defAttr); var _al = defAttr.ToList(); _al.Insert(0, "Search request with attribute list:"); cLogManager.DefaultLogger.LogList(LogLevels.Debug, _al); SR.Controls.Add(pageRequestControl); SR.Controls.Add(soc); while (true) { var searchResponse = (SearchResponse)LC.SendRequest(SR); pageResponseControl = (PageResultResponseControl)searchResponse.Controls[0]; if (searchResponse?.Entries?.Count > 0) { LogEntry($" processing {searchResponse.Entries.Count} entries for scan node '{dnOU}'", LogLevels.Debug); foreach (SearchResultEntry Entry in searchResponse.Entries) { try { bool res = await ProcessSearchEntry(Conn, Entry, LdapProps, Scan, Node, NodeInfo, requestInfo, LogDeep + 1, Token); if (res) numChanges++; } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); } } } if (pageResponseControl.Cookie.Length == 0) break; pageRequestControl.Cookie = pageResponseControl.Cookie; } LogEntry($" finished scan node '{dnOU}'", LogLevels.Debug); } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); } } Collector.DoProcessUiMessage(2, string.Format("... finished, {0} updates processed", numChanges)); NodeInfo.LastUSN = maxUsn; return Server.FQDN; } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task ProcessSearchEntry(cDbConnection Conn, SearchResultEntry Entry, Dictionary LdapProps, cDataHistoryAdScan Scan, cDataHistoryAdScanNode Node, cDbAdScanNode NodeInfo, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { var SqlParams = new Dictionary(); // get the USN var cName = GetStringFromLdapAttribute(Entry.Attributes, "cn", Entry.DistinguishedName, Props: LdapProps); var account = GetStringFromLdapAttribute(Entry.Attributes, "sAMAccountName", Entry.DistinguishedName, Props: LdapProps); if (Node.WildcardFilter != null || Node.RegExFilter != null) { string strFilter; switch (Node.FilterProperty) { case cDataHistoryAdScanNode.enumFilterPropery.distinguishedName: strFilter = Entry.DistinguishedName; break; case cDataHistoryAdScanNode.enumFilterPropery.samAccountName: strFilter = account; break; default: strFilter = cName; break; } if (Node.WildcardFilter != null) if (Node.WildcardFilter.IsMatch(strFilter) ^ !Node.WildcardFilterNot) return false; if (Node.RegExFilter != null) if (!Node.RegExFilter.IsMatch(strFilter)) return false; } var strSNC = GetStringFromLdapAttribute(Entry.Attributes, "usnchanged", Entry.DistinguishedName, Props: LdapProps); if (!UInt64.TryParse(strSNC, out ulong uSNChanged)) uSNChanged = 0; if (cLogManager.DefaultLogger.IsDebug) { var _al = new List { $"SearchResultEntry attributes for '{Entry.DistinguishedName}':" }; foreach (var _E in Entry.Attributes.AttributeNames) _al.Add(_E?.ToString()); cLogManager.DefaultLogger.LogList(LogLevels.Debug, _al); } if (uSNChanged >= NodeInfo.LastUSN) { // get the SID var Sid = GetByteArrayFromLdapAttribute(Entry.Attributes, "objectSid", Entry.DistinguishedName); if (Sid == null) return false; var strSid = GetSidFromByteArray(Sid, "objectSid", Entry.DistinguishedName); if (string.IsNullOrEmpty(strSid)) return false; SqlParams.Add("sid", strSid); SqlParams.Add("sid_bin", Sid); // get the account name & internal UPN if (string.IsNullOrEmpty(account)) return false; var internalUPN = account.ToLowerInvariant() + "@" + Node.AdDomain.FQDN; if (Scan.Type == enumFasdInformationClass.User) SqlParams.Add("account", account); SqlParams.Add("upn_internal", internalUPN); // get the userAccountControl & active flag int? isActive = null; var Uac = GetUInt32FromLdapAttribute(Entry.Attributes, "userAccountControl", Entry.DistinguishedName); if (Uac is UInt32 Uac2) { if ((Uac2 & 2) == 0) isActive = 1; else isActive = 0; } if (isActive == null) { if (Entry.Attributes.Contains("userAccountControl")) { var arrAttr = Entry.Attributes["userAccountControl"]; if ((arrAttr != null) && (arrAttr.Count > 0)) { var objAttr = arrAttr[0]; if (objAttr == null) { LogEntry($"The ldap result for '{Entry.DistinguishedName}' contains property 'userAccountControl' with value NULL", LogLevels.Debug); } else { var strAttr = objAttr.ToString(); LogEntry($"The ldap result for '{Entry.DistinguishedName}' contains property 'userAccountControl' with value string {strAttr}", LogLevels.Debug); if (!UInt32.TryParse(strAttr, out var RetVal)) LogEntry($"The ldap result for '{Entry.DistinguishedName}' has a property which is not UINT32", LogLevels.Debug); } } else LogEntry($"The ldap result for '{Entry.DistinguishedName}' has an empty property 'userAccountControl'", LogLevels.Debug); } else { LogEntry($"The ldap result for '{Entry.DistinguishedName}' do not contain the property 'userAccountControl'", LogLevels.Debug); } } SqlParams.Add("is_active", isActive); if (Scan.Type == enumFasdInformationClass.User) { SqlParams.Add("domainDns", NodeInfo.domainDNS); SqlParams.Add("domainNB", NodeInfo.domainNB); } GetSqlParameters(Entry, LdapProps, ref SqlParams); bool res = false; if (Scan.Type == enumFasdInformationClass.User) res = await ProcessSearchEntryUser(Conn, Entry, SqlParams, LdapProps, requestInfo, LogDeep + 1, Token); else if (Scan.Type == enumFasdInformationClass.Computer) res = await ProcessSearchEntryComputer(Conn, SqlParams, requestInfo, LogDeep + 1, Token); if (res) await ProcessAdditionalTables(Conn, Entry, Scan.Type, Node, NodeInfo, requestInfo, LogDeep + 1, Token); return res; } } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return false; } private void GetSqlParameters(SearchResultEntry Entry, Dictionary LdapProps, ref Dictionary SqlParams) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { foreach (var Prop in LdapProps.Values) { if (string.IsNullOrEmpty(Prop.DbName)) continue; var objValue = GetValueFromLdapAttribute(Entry.Attributes, Prop.Name, Entry.DistinguishedName); if (Prop.Cumulative) { var strVal = GetStringFromLdapAttribute(Entry.Attributes, Prop.Name, Entry.DistinguishedName, Prop.Cardinal); switch (Prop.LdapType) { case DataHistoryProvider.enumLdapPropertyType.ldapString: break; case DataHistoryProvider.enumLdapPropertyType.ldapPhone: strVal = FormatPlainPhone(strVal); break; default: continue; } if (!string.IsNullOrWhiteSpace(strVal)) { if (SqlParams.ContainsKey(Prop.DbName)) SqlParams[Prop.DbName] += " " + strVal; else SqlParams.Add(Prop.DbName, strVal); if (Prop.Reverse) { char[] arrRev = (strVal + 'x').ToArray(); Array.Reverse(arrRev); var strRev = new string(arrRev); var pName = Prop.DbName + "_rev"; if (SqlParams.ContainsKey(pName)) SqlParams[pName] += " " + strRev; else SqlParams.Add(pName, strRev); } } } else { switch (Prop.LdapType) { case DataHistoryProvider.enumLdapPropertyType.ldapString: var strVal = GetStringFromLdapAttribute(Entry.Attributes, Prop.Name, Entry.DistinguishedName, Prop.Cardinal); SqlParams[Prop.DbName] = strVal; break; } } } foreach (var Prop in LdapProps.Values) { if (Prop.DbName == null) continue; if (!SqlParams.ContainsKey(Prop.DbName)) SqlParams.Add(Prop.DbName, DBNull.Value); if (Prop.Cumulative && Prop.Reverse) { var strName = Prop.DbName + "_rev"; if (!SqlParams.ContainsKey(strName)) SqlParams.Add(strName, DBNull.Value); } } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } } private async Task ProcessSearchEntryUser(cDbConnection Conn, SearchResultEntry Entry, Dictionary SqlParams, Dictionary LdapProps, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { // get the full name var fullName = GetStringFromLdapAttribute(Entry.Attributes, "displayName", Entry.DistinguishedName, Props: LdapProps); if (string.IsNullOrEmpty(fullName)) fullName = SqlParams["account"] as string; SqlParams.Add("full_name", fullName); var lastSeen = DateTime.UtcNow; SqlParams.Add("last_seen", lastSeen); var res = await DataHistorySqlHelper.DoUpdateOrInsertAsync(Conn, "main-user", SqlParams, new List() { "sid_bin" }, requestInfo, LogDeep + 1, Token); if (res) return true; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return false; } private async Task ProcessSearchEntryComputer(cDbConnection Conn, Dictionary SqlParams, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateMainComputerLdap", out var QueryInsert)) return false; var ret = await DataHistorySqlHelper.ExecuteAsync(Conn, QueryInsert, SqlParams, requestInfo, Token); return ret > 0; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return false; } private async Task ProcessAdditionalTables(cDbConnection Conn, SearchResultEntry Entry, enumFasdInformationClass InformationClass, cDataHistoryAdScanNode Node, cDbAdScanNode NodeInfo, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { foreach (var DataCluster in Collector.ClusterConfig.Clusters.Values) { if (DataCluster.Origin != enumDataHistoryOrigin.ActiveDirectory || DataCluster.InformationClass != InformationClass) continue; foreach (var Table in DataCluster.Tables.Values) { if (Table.Type != eDataHistoryTableType.Static) continue; var SqlParams = new Dictionary(); foreach (var col in Table.Columns.Values) { if (!(col is cDataHistoryConfigColumn tableCol)) continue; if (SqlParams.ContainsKey(col.Name)) continue; try { object Value = null; object Value_bin = null; bool IsBin = false; switch (tableCol.SourceType) { case eDataHistoryQueryType.Static: switch (tableCol.SourceName) { case "scan": Value = Node.parentScan?.Name; break; case "domainDns": Value = NodeInfo.domainDNS; break; case "domainNB": Value = NodeInfo.domainNB; break; } break; case eDataHistoryQueryType.Query: switch (tableCol.ValueType) { case enumFasdValueType.SID: IsBin = true; var arrSid = GetByteArrayFromLdapAttribute(Entry.Attributes, tableCol.SourceName, Entry.DistinguishedName); Value_bin = arrSid; if (Value_bin != null) Value = GetSidFromByteArray(arrSid, tableCol.SourceName, Entry.DistinguishedName); break; case enumFasdValueType.STRING: Value = GetStringFromLdapAttribute(Entry.Attributes, tableCol.SourceName, Entry.DistinguishedName, tableCol.Cardinal); break; case enumFasdValueType.INT: Value = GetInt32FromLdapAttribute(Entry.Attributes, tableCol.SourceName, Entry.DistinguishedName); break; case enumFasdValueType.BIGINT: Value = (Int64?)GetUInt64FromLdapAttribute(Entry.Attributes, tableCol.SourceName, Entry.DistinguishedName); break; case enumFasdValueType.DATETIME: Value = GetDateTimeFromLdapAttribute(Entry.Attributes, tableCol.SourceName, Entry.DistinguishedName); break; } break; } SqlParams.Add(tableCol.Name, Value); if (IsBin) SqlParams.Add(tableCol.Name + "_bin", Value_bin); } catch (Exception E) { LogEntry($"error getting ldap property '{tableCol.SourceName}' from object {Entry.DistinguishedName}", LogLevels.Warning); LogException(E, LogLevels.Warning); } } var lstIndex = new List(); foreach (var Key in Table.KeyColumns) { if (Key.SqlTypeBin != null) lstIndex.Add(Key.Name + "_bin"); else lstIndex.Add(Key.Name); } var res = await DataHistorySqlHelper.DoUpdateOrInsertAsync(Conn, Table.SourceName, SqlParams, lstIndex, requestInfo, LogDeep + 1, Token); } } } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } } private async Task ProcessScanNode(cDbConnection Conn, Dictionary LdapProps, cDataHistoryAdScan Scan, cDataHistoryAdScanNode Node, List AlreadyScannedNodes, cDbAdScanNode NodeInfo, cDbAdGeneralInfo GeneralInfo, bool Rescan, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { try { var Server = await Task.Run(async () => { return await DoAdScanAsync(Conn, Collector.InfrastructureConfig.ActiveDirectory, LdapProps, Scan, Node, AlreadyScannedNodes, NodeInfo, GeneralInfo, Rescan, requestInfo, LogDeep + 1, Token); }); } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); } // save the new run info await UpdateAdScanNodeEntryAsync(NodeInfo, requestInfo, LogDeep + 1, Token); } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } } public async Task LogonAzureTenantAsync(cDataHistoryAzureTenant Tenant, bool force = false) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { var MsGraph = new cMsGraphBase(); if (Tenant == null) { return null; } var LI = new cMsGraphLogonInfo() { Tenant = Tenant?.TenantID.ToString(), ClientID = Tenant.Credential?.User, ClientSecret = Tenant.Credential?.NwCredential.Password }; var RetVal = await MsGraph.LogonAsync(LI); if (RetVal) return MsGraph; } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return null; } public async Task GetGraphForTenantAsync(Guid tenant, bool force = false) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { if (tenant == null) return null; cDataHistoryAzureTenant _AzureTenant = null; var tenants = Collector.InfrastructureConfig.AzureTenants?.Values; if (tenants is null) { return null; } foreach (var Tenant in tenants) { if (Tenant.TenantID == tenant) { _AzureTenant = Tenant; break; } } if (_AzureTenant != null) { if (_AzureTenant != null && tenantGraphCache.TryGetValue(_AzureTenant.TenantID, out var cachedGraph)) return cachedGraph; var graph = await LogonAzureTenantAsync(_AzureTenant, force: force); if (graph != null) tenantGraphCache[_AzureTenant.TenantID] = graph; return graph; } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return null; } public async Task DoAzureScanAsync(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { var tenants = Collector.InfrastructureConfig.AzureTenants?.Values; if (tenants is null) { Collector.DoProcessUiMessage(2, $" could not logon to tenant - Tenant is null ", LogLevels.Error); return true; } if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateMainUserAzureAd", out var QueryInsert)) return false; using (var Conn = new cDbConnection(Collector.mainDbConnection)) { if (!Conn.IsOpen) { LogEntry($"Could not open main sql database '{Collector.mainDbConnection.Database}', aborting scan...'", LogLevels.Error); return false; } Collector.DoProcessUiMessage(0, string.Format("doing the Azure cloud scans:")); foreach (var Tenant in tenants) { try { Collector.DoProcessUiMessage(1, $"scanning tenat {Tenant.Domain} for users ..."); var MsGraph = await GetGraphForTenantAsync(Tenant.TenantID, true); if (MsGraph == null) { Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {Tenant.Domain}", LogLevels.Error); continue; } var strUrl = string.Format(constMsGraphUserList, constMsGraphUserPaging); if (!string.IsNullOrEmpty(Tenant.ScanFilter)) strUrl += " and (" + Tenant.ScanFilter + ")"; var Result = await MsGraph.RequestListAsync(strUrl, UseBeta: false, retryForbidden: false, loadPaged: true, ConsistencyLevelEventual: Tenant.UseConsistencyLevelEventual); var Count = 0; var Updated = 0; while (Result != null && Result.Count > 0) { foreach (var Entry in Result) { if (Entry.Result.onPremisesSecurityIdentifier == null) continue; Count++; try { var _strId = Entry.ID; if (!Guid.TryParse(_strId, out var Id)) continue; var _sid = Entry.Result.onPremisesSecurityIdentifier.ToString(); var sid = new SecurityIdentifier(_sid); var arrSid = new byte[sid.BinaryLength]; sid.GetBinaryForm(arrSid, 0); var SqlParams = new Dictionary(2) { { "sid_bin", arrSid }, { "id_azure", Id }, { "tenant", Tenant.TenantID } }; var _res = await DataHistorySqlHelper.ExecuteAsync(Conn, QueryInsert, SqlParams, requestInfo, Token); if (_res >= 0) Updated++; } catch (Exception E) { LogException(E); } } Collector.DoProcessUiMessage(2, $"{Count} accounts processed, {Updated} updated."); if (Result.Count < constMsGraphUserPaging) break; Result.Clear(); if (!await MsGraph.RequestNextAsync(Result)) break; } } catch (Exception E) { LogException(E); } Collector.DoProcessUiMessage(1, $"...scanning tenat {Tenant.Domain} finished"); } Collector.DoProcessUiMessage(0, "... Azure cloud scans finished."); return true; } } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return false; } private async Task TestAzureAccessItem(cMsGraphBase MsGraph, bool UseBeta, string strUrlList, string strUrlEntry, string MessageList, string MessageEntry, CancellationToken Token, string nameProperty = null) { var strUrl = string.Format(strUrlList, 999); var ResultAdList = await MsGraph.RequestListAsync(strUrl, UseBeta: UseBeta, retryForbidden: false, loadPaged: false); if (ResultAdList != null && ResultAdList.Count > 0) { Collector.DoProcessUiMessage(1, $" {ResultAdList.Count} {MessageList} found."); var _rnd = new Random(); int p = _rnd.Next(ResultAdList.Count - 1); var _id = ResultAdList[p].ID; if (!string.IsNullOrEmpty(_id)) { strUrl = string.Format(strUrlEntry, _id); var ResultAdEntry = await MsGraph.RequestAsync(strUrl, UseBeta: UseBeta, retryForbidden: false, nameProperty: nameProperty); if (ResultAdEntry != null) { var _ty = ResultAdEntry.Result.GetType(); var _props = ResultAdEntry.Result as JObject; if (_props != null) { Collector.DoProcessUiMessage(3, $" properties of random selected {MessageEntry} '{ResultAdEntry.DisplayName}' with {_props.Count} properties could be read."); } } } } else { Collector.DoProcessUiMessage(1, $" warning: no {MessageList} could be found.", LogLevels.Warning); } } public async Task TestAzureAccess(CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { var tenants = Collector.InfrastructureConfig.AzureTenants?.Values; if (tenants is null) { Collector.DoProcessUiMessage(2, $" could not logon to tenant - Tenant is null ", LogLevels.Error); return; } foreach (var Tenant in tenants) { // logon to tenant Collector.DoProcessUiMessage(0, $"test scanning tenat {Tenant.Domain} for users, devices and managed devices ..."); var MsGraph = await GetGraphForTenantAsync(Tenant.TenantID, true); if (MsGraph == null) { Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {Tenant.Domain}", LogLevels.Error); continue; } await TestAzureAccessItem(MsGraph, false, constMsGraphUserList, constMsGraphUserEntry, "synchronized user accounts", "user", Token); await TestAzureAccessItem(MsGraph, false, constMsGraphDeviceList, constMsGraphDeviceEntry, "synchronized device accounts", "device", Token); await TestAzureAccessItem(MsGraph, true, constMsGraphManagedDeviceList, constMsGraphManagedDeviceEntry, "Intune managed Windows device accounts", "Windows device", Token, nameProperty: "deviceName"); } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } } public async Task DoDeviceScanAzureAsync(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { var tenants = Collector.InfrastructureConfig.AzureTenants?.Values; if (tenants is null) { return true; } if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateComputerAzure", out var QueryInsert)) return false; using (var Conn = new cDbConnection(Collector.mainDbConnection)) { if (!Conn.IsOpen) { LogEntry($"Could not open main sql database '{Collector.mainDbConnection.Database}', aborting scan...'", LogLevels.Error); return false; } foreach (var Tenant in tenants) { try { if (Tenant.ScanIntuneDevices) { // logon to tenant Collector.DoProcessUiMessage(1, $"scanning tenat {Tenant.Domain} for devices ..."); var MsGraph = await GetGraphForTenantAsync(Tenant.TenantID, true); if (MsGraph == null) { Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {Tenant.Domain}", LogLevels.Error); continue; } var strUrl = string.Format(constMsGraphDeviceList, constMsGraphDevicePaging); var Result = await MsGraph.RequestListAsync(strUrl, UseBeta: false, retryForbidden: false, loadPaged: true); var Count = 0; var Updated = 0; while (Result != null == Result.Count > 0) { foreach (var Entry in Result) { if (Entry.Result.onPremisesSecurityIdentifier == null) continue; Count++; try { var _strId = Entry.ID; if (!Guid.TryParse(_strId, out var Id)) continue; string _dvcId = Entry.Result.deviceId; if (!Guid.TryParse(_dvcId, out var deviceId)) continue; var _sid = Entry.Result.onPremisesSecurityIdentifier.ToString(); var sid = new SecurityIdentifier(_sid); var arrSid = new byte[sid.BinaryLength]; sid.GetBinaryForm(arrSid, 0); var SqlParams = new Dictionary(2) { { "sid_bin", arrSid }, { "id", Id }, { "deviceid", deviceId }, { "tenant", Tenant.TenantID } }; var _res = await DataHistorySqlHelper.ExecuteAsync(Conn, QueryInsert, SqlParams, requestInfo, Token); if (_res > 0) Updated++; } catch (Exception E) { LogException(E); } } Collector.DoProcessUiMessage(2, $"{Count} devices processed, {Updated} updated."); if (Result.Count < constMsGraphManagedDevicePaging) break; Result.Clear(); if (!await MsGraph.RequestNextAsync(Result)) break; } } } catch (Exception E) { LogException(E); } } Collector.DoProcessUiMessage(0, "... Device cloud scans finished."); } } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return true; } private async Task SetVitalSignInfo(cDbConnection Conn, Guid AzureDeviceID, DateTime LastIntuneSync, Guid Tenant, Guid IntuneID, string Name, string UserName, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; if (!DataHistorySqlHelper.GetWellKnownSqlStatement("GetComputerVitalSignInfoForIntune", out var QueryGetVitalSignInfo)) return; if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateVitalSignMonitoringAll", out var QueryUpdateVitalSignInfoAll)) return; if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateVitalSignMonitoringIntune", out var QueryUpdateVitalSignInfoIntune)) return; try { // try getting the F4SD device id and the lastSeenByAgent info for the given AzureDeviceID var SqlParams = new Dictionary(1) { { "AZURE_ID", AzureDeviceID } }; Guid _id = Guid.Empty; DateTime? lastSeen = null; using (var _reader = await DataHistorySqlHelper.GetDataReaderAsync(Conn, QueryGetVitalSignInfo, SqlParams, Token)) { if (_reader != null) try { if (_reader.HasRows) { if (await _reader.ReadAsync()) { try { _id = _reader.GetGuid(0); lastSeen = _reader.IsDBNull(1) ? (DateTime?)null : _reader.GetDateTime(1); } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); } } } } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); } } if (_id == Guid.Empty) { SqlParams = new Dictionary(3) { { "@TenantId", Tenant }, { "@IntuneId", IntuneID }, { "@name", Name }, { "@AzureID", AzureDeviceID }, { "@UserName", UserName }, { "@TimeStamp", LastIntuneSync } }; await DataHistorySqlHelper.ExecuteAsync(Conn, QueryUpdateVitalSignInfoIntune, SqlParams, requestInfo, Token); return; } // update the vital sign info of the device found int diffToAgent = int.MinValue; if (lastSeen != null) diffToAgent = (int)(LastIntuneSync - lastSeen.Value).TotalHours; if (diffToAgent == 0) { } SqlParams = new Dictionary(3) { { "@Id", _id }, { "@TimeStamp", LastIntuneSync }, { "@DiffToAgent", diffToAgent }, { "@UserName", UserName }, }; await DataHistorySqlHelper.ExecuteAsync(Conn, QueryUpdateVitalSignInfoAll, SqlParams, requestInfo, Token); } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } } public async Task DoScanIntuneAsync(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { var tenants = Collector.InfrastructureConfig.AzureTenants?.Values; if (tenants is null) { return true; } if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateComputerIntune", out var QueryInsert)) return false; using (var Conn = new cDbConnection(Collector.mainDbConnection)) { if (!Conn.IsOpen) { LogEntry($"Could not open main sql database '{Collector.mainDbConnection.Database}', aborting scan...'", LogLevels.Error); return false; } bool HasTenantToScan = false; foreach (var Tenant in tenants) { try { if (Tenant.ScanIntuneDevices) { HasTenantToScan = true; // logon to tenant Collector.DoProcessUiMessage(1, $"scanning tenat {Tenant.Domain} for devices in Intune ..."); var MsGraph = await GetGraphForTenantAsync(Tenant.TenantID, true); if (MsGraph == null) { Collector.DoProcessUiMessage(2, $"error: could not logon to tenat {Tenant.Domain}", LogLevels.Error); continue; } var strUrl = string.Format(constMsGraphManagedDeviceList, constMsGraphManagedDevicePaging); var Result = await MsGraph.RequestListAsync(strUrl, UseBeta: true, retryForbidden: false, loadPaged: true); var Count = 0; var Updated = 0; while (Result != null == Result.Count > 0) { foreach (var Entry in Result) { Count++; try { var _strId = Entry.ID; if (!Guid.TryParse(_strId, out var Id)) continue; string _dvcId = Entry.Result.azureActiveDirectoryDeviceId; if (!Guid.TryParse(_dvcId, out var deviceId)) continue; var SqlParams = new Dictionary(2) { { "id", Id }, { "deviceid", deviceId } }; var _res = await DataHistorySqlHelper.ExecuteAsync(Conn, QueryInsert, SqlParams, requestInfo, Token); if (_res > 0) Updated++; else { if (DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateComputerIntune2", out var QueryInsert2)) { string _dvcName = Entry.Result.deviceName; var SqlParams2 = new Dictionary(2) { { "id", Id }, { "deviceid", deviceId }, { "tenant", Tenant.TenantID }, { "upn_internal", (_dvcName+"$@"+Tenant.TenantID).ToLowerInvariant() } }; var _res2 = await DataHistorySqlHelper.ExecuteAsync(Conn, QueryInsert2, SqlParams2, requestInfo, Token); if (_res2 > 0) Updated++; } } #if VITALSIGN_MONITORING String Name = Entry.Result.deviceName; String UserName = Entry.Result.userPrincipalName; if (Entry.Result.lastSyncDateTime is JValue proplastSyncTime) { var lastSyncTime = proplastSyncTime.ToObject(); await SetVitalSignInfo(Conn, deviceId, lastSyncTime, Tenant.TenantID, Id, Name, UserName, requestInfo, LogDeep + 1, Token); } #endif } catch (Exception E) { LogException(E); } } Collector.DoProcessUiMessage(2, $"{Count} devices processed, {Updated} updated."); if (Result.Count < constMsGraphManagedDevicePaging) break; Result.Clear(); if (!await MsGraph.RequestNextAsync(Result)) break; } } } catch (Exception E) { LogException(E); } } if (HasTenantToScan) Collector.DoProcessUiMessage(0, "... Intune cloud scans finished."); } } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return true; } public async Task GetRelationsAsync(IEnumerable ids, enumFasdInformationClass informationClass, int age, CancellationToken token = default) { try { List Ids = await _collector.getConnectorIdList(ids.ToList(), token, null, 0); List relations = await GetMobileDeviceAsync(Ids, token, null); return new cF4sdStagedSearchResultRelations() { Relations = relations }; } catch (Exception ex) { LogException(ex); } return null; } public override async Task> GetTableResultsVirtualAsync(List Tables, Dictionary Identities, DateTime RefTime, int MaxAge, bool instantly, Guid? CacheId, CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { var bDeviceExists = Tables.Exists(v => v.Name == constTableNameIntuneDeviceDetails && v.ParentCluster?.Origin == enumDataHistoryOrigin.Intune); var bUserDeviceExists = Tables.Exists(v => v.Name == constTableNameIntuneUserDeviceCount && v.ParentCluster?.Origin == enumDataHistoryOrigin.Intune); var bMobileDeviceExists = Tables.Exists(v => v.Name == constTableNameIntuneMobileDeviceDetails && v.ParentCluster?.Origin == enumDataHistoryOrigin.Intune); if (!Identities.TryGetValue(enumFasdInformationClass.Computer, out var computer) && computer?.intuneId != Guid.Empty) bDeviceExists = false; if(!Identities.TryGetValue(enumFasdInformationClass.MobileDevice, out var mobile) && mobile?.Id != Guid.Empty) bMobileDeviceExists = false; if(!Identities.TryGetValue(enumFasdInformationClass.User, out var user) && user?.intuneId != Guid.Empty) bUserDeviceExists = false; if (!(bDeviceExists || bMobileDeviceExists || bUserDeviceExists)) return null; var retVal = new List(); var listTasks = new List>>(2); if (bDeviceExists) if (CacheId == null && !instantly) { retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() { Name = constTableNameIntuneDeviceDetails, InformationClass = enumFasdInformationClass.Computer, Origin = enumDataHistoryOrigin.Intune, IsIncomplete = true }); } else listTasks.Add(Task.Run>(async () => { return await GetIntuneDeviceTableAsync(computer.intuneId, computer.tenantId, requestInfo, LogDeep + 1, Token); })); if (bMobileDeviceExists) if (CacheId == null && !instantly) { retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() { Name = constTableNameIntuneMobileDeviceDetails, InformationClass = enumFasdInformationClass.MobileDevice, Origin = enumDataHistoryOrigin.Intune, IsIncomplete = true }); } else listTasks.Add(Task.Run>(async () => { return await GetIntuneMobileDeviceTableAsync(mobile.Id, user.tenantId, requestInfo, LogDeep + 1, Token); })); if (bUserDeviceExists) if (CacheId == null && !instantly) { retVal.Add(new cF4SDHealthCardRawData.cHealthCardTable() { Name = constTableNameIntuneUserDeviceCount, InformationClass = enumFasdInformationClass.User, Origin = enumDataHistoryOrigin.Intune, IsIncomplete = true }); } else listTasks.Add(Task.Run>(async () => { return await GetIntuneUserDeviceCountTableAsync(user.intuneId, user.tenantId, requestInfo, LogDeep + 1, Token); })); var arrRes = await Task.WhenAll(listTasks.ToArray()); foreach (var Entry in arrRes) if (Entry != null) retVal.AddRange(Entry); if (retVal.Count == 0) return null; return retVal; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } public override async Task> GetDetailsTableResultsVirtualAsync(List Tables, Dictionary Identities, DateTime RefTime, int MaxAge, CancellationToken Token, cF4sdWebRequestInfo requestInfo, int LogDeep) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { var bUserDeviceDetailExists = Tables.Exists(v => v.Name == constTableNameIntuneUserDeviceCountDetail && v.ParentCluster?.Origin == enumDataHistoryOrigin.Intune); if (!Identities.TryGetValue(enumFasdInformationClass.User, out var userIds)) { bUserDeviceDetailExists = false; } if (!bUserDeviceDetailExists) return null; var listTasks = new List>>(2); if (bUserDeviceDetailExists) listTasks.Add(Task.Run>(async () => { return await GetIntuneUserDeviceCountDetailTableAsync(userIds.intuneId, userIds.tenantId, requestInfo, LogDeep + 1, Token); })); var arrRes = await Task.WhenAll(listTasks.ToArray()); var retVal = new List(); foreach (var Entry in arrRes) if (Entry != null) retVal.AddRange(Entry); if (retVal.Count == 0) return null; return retVal; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetIntuneDeviceTableAsync(Guid IntuneId, Guid TenantId, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameIntuneDeviceDetails, out var IntuneTableDetails)) return null; var strUrl = string.Format(constMsGraphManagedDeviceEntry, IntuneId); var MsGraph = await GetGraphForTenantAsync(TenantId, true); if (MsGraph == null) return null; var res = await MsGraph.RequestAsync(strUrl, UseBeta: true, retryForbidden: false); if (res != null) { JObject deviceInfos = res.Result; var retVal = new cF4SDHealthCardRawData.cHealthCardTable() { Name = constTableNameIntuneDeviceDetails, InformationClass = enumFasdInformationClass.Computer, Origin = enumDataHistoryOrigin.Intune, IsStatic = true, TableType = eDataHistoryTableType.Static }; var dicVals = new Dictionary(); var vals = deviceInfos.Children(); if (cLogManager.DefaultLogger.IsDebug) LogEntry("SpecialDebug GetIntuneMainTableAsync #3"); foreach (var token in vals) { try { if (token is JProperty prop) if (prop.Value is JValue val) dicVals[prop.Name] = val.Value; } catch (Exception E) { LogEntry($"Error getting intune detail property '{token.Path}'", LogLevels.Error); LogException(E); } } foreach (var colInfo in IntuneTableDetails.Columns.Values) { try { if (!dicVals.TryGetValue(colInfo.SourceName, out var objVal)) objVal = null; objVal = ConvertToF4sdType(objVal, colInfo.ValueType); var _col = new cF4SDHealthCardRawData.cHealthCardTableColumn(retVal) { ColumnName = colInfo.Name, Values = new List(1) { objVal } }; retVal.Columns[colInfo.Name] = _col; } catch { } } return new List(1) { retVal }; } else { LogEntry($"Could not get detailed device information from intune with id {IntuneId}. Device content is empty.", LogLevels.Warning); } } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetIntuneUserDeviceCountTableAsync(Guid Userid, Guid TenantId, cF4sdWebRequestInfo RequestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameIntuneUserDeviceCount, out var IntuneTableDetails)) return null; var strUrl = string.Format(constMsGraphManagedDeviceCount, Userid); if (cLogManager.DefaultLogger.IsDebug) LogEntry("SpecialDebug GetIntuneUserDeviceCountTableAsync #1"); var MsGraph = await GetGraphForTenantAsync(TenantId, true); if (cLogManager.DefaultLogger.IsDebug) LogEntry("SpecialDebug GetIntuneUserDeviceCountTableAsync #2"); if (MsGraph == null) return null; if (cLogManager.DefaultLogger.IsDebug) LogEntry("SpecialDebug GetIntuneUserDeviceCountTableAsync #3"); var res = await MsGraph.RequestAsync(strUrl, UseBeta: false, retryForbidden: false); if (cLogManager.DefaultLogger.IsDebug) LogEntry("SpecialDebug GetIntuneUserDeviceCountTableAsync #4"); if (res != null) { JObject deviceInfos = res.Result; var retVal = new cF4SDHealthCardRawData.cHealthCardTable() { Name = constTableNameIntuneUserDeviceCount, InformationClass = enumFasdInformationClass.User, Origin = enumDataHistoryOrigin.Intune, IsStatic = true, TableType = eDataHistoryTableType.Static }; var vals = deviceInfos.Children(); var devices = deviceInfos["value"]? .Select(d => (JObject)d) .GroupBy(d => (string)d["operatingSystem"]) .ToDictionary(g => g.Key, g => (object)g.Count()).ToList(); if (cLogManager.DefaultLogger.IsDebug) LogEntry("SpecialDebug GetIntuneUserDeviceCountTableAsync #5"); var dicVals = new Dictionary { { "id", Userid }, { "windows", 0 }, { "iOS", 0 }, { "android", 0 }, { "linux", 0 }, { "macOs", 0 }, { "total", 0 } }; if (devices != null) { dicVals["total"] = devices.Sum(kvp => (int)kvp.Value); foreach (var value in devices) { switch (value.Key.ToLower()) { case "windows": dicVals["windows"] = value.Value; break; case "ios": dicVals["iOS"] = value.Value; break; case "android": dicVals["android"] = value.Value; break; case "linux": dicVals["linux"] = value.Value; break; case "macos": dicVals["macOs"] = value.Value; break; } } foreach (var colInfo in IntuneTableDetails.Columns.Values) { try { if (!dicVals.TryGetValue(colInfo.SourceName, out var objVal)) objVal = null; objVal = ConvertToF4sdType(objVal, colInfo.ValueType); var _col = new cF4SDHealthCardRawData.cHealthCardTableColumn(retVal) { ColumnName = colInfo.Name, Values = new List(1) { objVal } }; retVal.Columns[colInfo.Name] = _col; } catch { } } } return new List(1) { retVal }; } else { LogEntry($"Could not get detailed DeviceCount information from intune with id {Userid}. DeviceCount content is empty.", LogLevels.Warning); } } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetIntuneUserDeviceCountDetailTableAsync(Guid Userid, Guid TenantId, cF4sdWebRequestInfo RequestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && RequestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, RequestInfo.id, RequestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameIntuneUserDeviceCountDetail, out var IntuneTableDetails)) return null; var strUrl = string.Format(constMsGraphManagedUserDeviceEntry, Userid); // logon to tenant var MsGraph = await GetGraphForTenantAsync(TenantId, true); if (MsGraph == null) return null; var res = await MsGraph.RequestAsync(strUrl, UseBeta: false, retryForbidden: false); if (res != null) { JObject deviceInfos = res.Result; var retValList = new List(); var vals = deviceInfos["value"] as JArray; if (vals != null) { var retVal = new cF4SDHealthCardRawData.cHealthCardDetailsTable() { Name = constTableNameIntuneUserDeviceCountDetail, Columns = IntuneTableDetails.Columns.Values.Select(v => v.Name).ToList(), Values = new Dictionary>() }; int index = 0; foreach (var device in vals) { try { var dicVals = device.Children().ToDictionary(prop => prop.Name, prop => prop.Value?.ToObject()); var row = retVal.Columns.Select(colName => dicVals.TryGetValue(colName, out var value) ? value : null).ToArray(); if (!retVal.Values.ContainsKey(0)) { retVal.Values[0] = new List(); } retVal.Values[0].Add(row); } catch (Exception e) { LogEntry($"Error processing device entry '{device.Path}'", LogLevels.Error); LogException(e); } index++; } retValList.Add(retVal); return retValList; } else { LogEntry($"Could not get detailed DeviceCount information from intune with id {Userid}. DeviceCount content is empty.", LogLevels.Warning); } } return null; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && RequestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, RequestInfo.id, RequestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } public async Task> GetMobileDeviceAsync(List UserId, CancellationToken Token, cF4sdWebRequestInfo requestInfo) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { string userIds = string.Join(",", UserId.Where(u => !string.IsNullOrWhiteSpace(u?.intuneId.ToString())).Select(u => u.intuneId)); var tenantId = UserId?.FirstOrDefault(u => u?.tenantId != Guid.Empty)?.tenantId ?? Guid.Empty; var userId_str = "ef333f80-0ae1-40b3-826f-2dc69c80c1f0"; var intuneId = new Guid(userId_str); var strUrl = string.Format(constMsGraphManagedMobileDeviceList, intuneId); var MsGraph = await GetGraphForTenantAsync(tenantId, true); if (MsGraph == null) return null; // Schritt 1: Abrufen der Recovery-Key IDs var Result = await MsGraph.RequestListAsync(strUrl, UseBeta: false, retryForbidden: false); if (Result != null == Result.Count > 0) { var searchResult = new List(Result.Count); //double _sumDuration = 0; foreach (var Entry in Result) { var endDate = DateTime.MinValue; if (!DateTime.TryParse(Entry.Result.lastSyncDateTime?.ToString(), out endDate)) endDate = DateTime.UtcNow; //var startDate = DateTime.MinValue; //if (!DateTime.TryParse(Entry.Result.StartDate?.ToString(), out startDate)) // startDate = DateTime.UtcNow; //TimeSpan _duration = endDate - startDate; //_sumDuration += _duration.TotalSeconds; var searchInfo = new cF4sdApiSearchResultRelation() { Type = enumF4sdSearchResultClass.MobileDevice, Name = Entry.Result.deviceName?.ToString(), DisplayName = Entry.Result.deviceName?.ToString(), LastUsed = endDate, UsingLevel = 0, id = Entry.Result.id, Status = enumF4sdSearchResultStatus.Active, Identities = new cF4sdIdentityList() { new cF4sdIdentityEntry(){ Class = enumFasdInformationClass.MobileDevice, Id = Entry.Result?.id}, new cF4sdIdentityEntry(){ Class = enumFasdInformationClass.User, Id = (Entry.Result.userId == null || Entry.Result?.userId == DBNull.Value) ? UserId[0].Id : Entry.Result.userId } } }; searchResult.Add(searchInfo); } //if (searchResult != null && searchResult.Count > 0) //{ // foreach (var Entry in searchResult) // { // Entry.UsingLevel /= _sumDuration; // } //} return searchResult; } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return null; } private async Task> GetIntuneMobileDeviceTableAsync(Guid IntuneId, Guid TenantId, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!Collector.ClusterConfig.Tables.TryGetValue(constTableNameIntuneMobileDeviceDetails, out var IntuneTableDetails)) return null; var strUrl = string.Format(constMsGraphManagedDeviceAllEntry, IntuneId); var MsGraph = await GetGraphForTenantAsync(TenantId, true); if (MsGraph == null) return null; var res = await MsGraph.RequestAsync(strUrl, UseBeta: true, retryForbidden: false); if (res != null) { JObject deviceInfos = res.Result; var retVal = new cF4SDHealthCardRawData.cHealthCardTable() { Name = constTableNameIntuneMobileDeviceDetails, InformationClass = enumFasdInformationClass.MobileDevice, Origin = enumDataHistoryOrigin.Intune, IsStatic = true, TableType = eDataHistoryTableType.Static }; var dicVals = new Dictionary(); var vals = deviceInfos.Children(); if (cLogManager.DefaultLogger.IsDebug) LogEntry("SpecialDebug GetIntuneMainTableAsync #3"); foreach (var token in vals) { try { if (token is JProperty prop) if (prop.Value is JValue val) dicVals[prop.Name] = val.Value; } catch (Exception E) { LogEntry($"Error getting intune detail property '{token.Path}'", LogLevels.Error); LogException(E); } } foreach (var colInfo in IntuneTableDetails.Columns.Values) { try { if (!dicVals.TryGetValue(colInfo.SourceName, out var objVal)) objVal = null; objVal = ConvertToF4sdType(objVal, colInfo.ValueType); var _col = new cF4SDHealthCardRawData.cHealthCardTableColumn(retVal) { ColumnName = colInfo.Name, Values = new List(1) { objVal } }; retVal.Columns[colInfo.Name] = _col; } catch { } } return new List(1) { retVal }; } else { LogEntry($"Could not get detailed device information from intune with id {IntuneId}. Device content is empty.", LogLevels.Warning); } } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } public async Task QuickActionRun(cF4SDServerQuickActionParameters jsonRequest, cF4sdWebRequestInfo requestInfo, CancellationToken Token) { dynamic result = null; switch (jsonRequest.Action) { case "UpdateComplianceState": result = await UpdateComplianceState(jsonRequest.Identities, requestInfo, CancellationToken.None); break; case "ChangeManagedStatus": result = await ChangeManagedStatus(jsonRequest.Identities, requestInfo, jsonRequest.AdjustableParameter?.Values?.FirstOrDefault()?.ToString() ?? string.Empty, CancellationToken.None); break; case "GetManagedApps": result = await GetManagedApps(jsonRequest.Identities, requestInfo, CancellationToken.None); break; case "GetBitlockerKey": result = await GetBitlockerKey(jsonRequest.Identities, requestInfo, CancellationToken.None); break; case "GetLAPS": result = await GetLAPS(jsonRequest.Identities, requestInfo, CancellationToken.None); break; case "GetBitlockerKeyTest": result = await GetBitlockerKeyTest(jsonRequest.Identities, requestInfo, CancellationToken.None); break; case "GetLapsKeyTest": result = await GetLapsKeyTest(jsonRequest.Identities, requestInfo, CancellationToken.None); break; } return result; } public override async Task> GetQuickActionList() { var HasIntuneConfig = (Collector.InfrastructureConfig.AzureTenants?.Values.Any(t => t.ScanIntuneDevices) == true); if (HasIntuneConfig) { var quickactions = new List{ "Update ComplianceState", "Change ManagedStatus", "Get Managed Apps", "Get BitlockerKey", "Get LAPS", "GetLapsKeyTest", }; return await Task.FromResult(quickactions); } var result = new List(); return result; } public async Task UpdateComplianceState(List Identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token) { var Ids = await Collector.getConntectorIds(Identities, Token, requestInfo, 1); if (!Ids.TryGetValue(enumFasdInformationClass.Computer, out var _ids)) return null; var strUrl = string.Format(constMsGraphManagedDeviceSyncDevice, _ids.intuneId); var MsGraph = await GetGraphForTenantAsync(_ids.tenantId, true); if (MsGraph == null) { return null; } var Result = await MsGraph.RequestAsync(strUrl, UseBeta: false, httpMethod: eHttpMethod.post, retryForbidden: false); if (Result != null) { var ResultJson = Result.Result as JObject; if (ResultJson != null) { Collector.DoProcessUiMessage(3, $" properties of selected '{ResultJson}' with {ResultJson.Count} properties could be read."); } return ResultJson; } return null; } public async Task GetBitlockerKey(List identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token) { var Ids = await Collector.getConntectorIds(identities, Token, requestInfo, 1); if (!Ids.TryGetValue(enumFasdInformationClass.Computer, out var _ids)) return null; // URL zum Abrufen der Recovery-Keys var strUrl = string.Format(constMsGraphDeviceBitlockerRecoveryKey, _ids.azureDeviceId); var MsGraph = await GetGraphForTenantAsync(_ids.tenantId, true); if (MsGraph == null) return null; // Schritt 1: Abrufen der Recovery-Key IDs var resultRecoveryKeys = await MsGraph.RequestAsync(strUrl, UseBeta: false, retryForbidden: false); if (resultRecoveryKeys?.Result["value"] is JArray recoveryKeyArray && recoveryKeyArray.Count > 0) { var results = new JArray(); foreach (var item in recoveryKeyArray) { var recoveryKeyId = item["id"].ToString(); // Schritt 2: Abrufen des Recovery-Key-Wertes var strUrl2 = string.Format(constMsGraphDeviceBitlockerRecoveryKeyDetails, recoveryKeyId); var resultKey = await MsGraph.RequestAsync(strUrl2, UseBeta: false, retryForbidden: false); if (resultKey?.Result != null) { results.Add(resultKey.Result as JObject); } } Collector.DoProcessUiMessage(3, $"{results.Count} Bitlocker-Key(s) erfolgreich abgerufen."); return results; } Collector.DoProcessUiMessage(3, "Kein Bitlocker-Key gefunden oder Abruf fehlgeschlagen."); return null; } public async Task GetLAPS(List identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token) { var Ids = await Collector.getConntectorIds(identities, Token, requestInfo, 1); if (!Ids.TryGetValue(enumFasdInformationClass.Computer, out var _ids)) return null; var strUrl = string.Format(constMsGraphDeviceLAPS, _ids.azureDeviceId); var MsGraph = await GetGraphForTenantAsync(_ids.tenantId, true); if (MsGraph == null) return null; var Result = await MsGraph.RequestAsync(strUrl, UseBeta: false, retryForbidden: false); if (Result != null) { var _resultJson = Result.Result["credentials"] as JArray; if (_resultJson != null) { try { var ResultJson = _resultJson.ToObject>().GroupBy(obj => (string)obj["accountName"]) .Select(group => group.OrderByDescending(obj => DateTimeOffset.Parse((string)obj["backupDateTime"], CultureInfo.InvariantCulture)).First()).ToList(); foreach (JObject item in ResultJson) { string _base64Password = (string)item["passwordBase64"]; byte[] passwordBytes = Convert.FromBase64String(_base64Password); string decodedPassword = Encoding.UTF8.GetString(passwordBytes); item["passwordBase64"] = decodedPassword; } Collector.DoProcessUiMessage(3, $" properties of selected '{ResultJson}' with {ResultJson.Count} properties could be read."); return ResultJson; } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); } } } return null; } public async Task GetManagedApps(List identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token) { var Ids = await Collector.getConntectorIds(identities, Token, requestInfo, 1); if (!Ids.TryGetValue(enumFasdInformationClass.Computer, out var _ids)) return null; var strUrl = string.Format(constMsGraphManagedDeviceDetectedApps, _ids.intuneId); var MsGraph = await GetGraphForTenantAsync(_ids.tenantId, true); if (MsGraph == null) return null; var Result = await MsGraph.RequestAsync(strUrl, UseBeta: true, retryForbidden: false); if (Result != null) { var ResultJson = Result.Result["value"] as JArray; if (ResultJson != null) { Collector.DoProcessUiMessage(3, $" properties of selected '{ResultJson}' with {ResultJson.Count} properties could be read."); } return ResultJson; } return null; } public async Task ChangeManagedStatus(List identities, cF4sdWebRequestInfo requestInfo, string status, CancellationToken Token) { if (!string.IsNullOrWhiteSpace(status)) { var Ids = await Collector.getConntectorIds(identities, Token, requestInfo, 1); if (!Ids.TryGetValue(enumFasdInformationClass.Computer, out var _ids)) return null; var strUrl = string.Format(constMsGraphManagedDeviceChangeManagedStatus, _ids.intuneId, status); var MsGraph = await GetGraphForTenantAsync(_ids.tenantId, true); if (MsGraph == null) return null; var Result = await MsGraph.RequestAsync(strUrl, UseBeta: false, retryForbidden: false); if (Result != null) { var ResultJson = Result.Result as JObject; if (ResultJson != null) { Collector.DoProcessUiMessage(3, $" properties of selected '{ResultJson}' with {ResultJson.Count} properties could be read."); } return ResultJson; } } return null; } public async Task GetBitlockerKeyTest(List identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token) { var Ids = await Collector.getConntectorIds(identities, Token, requestInfo, 1); if (!Ids.TryGetValue(enumFasdInformationClass.Computer, out var _ids)) return null; // URL zum Abrufen der Recovery-Keys var deviceId_str = "02f2fd68-a1c6-4e73-96a5-48f72792ee08"; _ids.intuneId = new Guid(deviceId_str); var strUrl = string.Format(constMsGraphDeviceBitlockerRecoveryKey, _ids.intuneId); var MsGraph = await GetGraphForTenantAsync(_ids.tenantId, true); if (MsGraph == null) return null; // Schritt 1: Abrufen der Recovery-Key IDs var resultRecoveryKeys = await MsGraph.RequestAsync(strUrl, UseBeta: false, retryForbidden: false); if (resultRecoveryKeys?.Result["value"] is JArray recoveryKeyArray && recoveryKeyArray.Count > 0) { var results = new JArray(); foreach (var item in recoveryKeyArray) { var recoveryKeyId = item["id"].ToString(); // Schritt 2: Abrufen des Recovery-Key-Wertes var strUrl2 = string.Format(constMsGraphDeviceBitlockerRecoveryKeyDetails, recoveryKeyId); var resultKey = await MsGraph.RequestAsync(strUrl2, UseBeta: false, retryForbidden: false); if (resultKey?.Result != null) { results.Add(resultKey.Result as JObject); } } Collector.DoProcessUiMessage(3, $"{results.Count} Bitlocker-Key(s) erfolgreich abgerufen."); return results; } Collector.DoProcessUiMessage(3, "Kein Bitlocker-Key gefunden oder Abruf fehlgeschlagen."); return null; } public async Task GetLapsKeyTest(List identities, cF4sdWebRequestInfo requestInfo, CancellationToken Token) { var Ids = await Collector.getConntectorIds(identities, Token, requestInfo, 1); if (!Ids.TryGetValue(enumFasdInformationClass.Computer, out var _ids)) return null; var azureId_str = "02f2fd68-a1c6-4e73-96a5-48f72792ee08"; _ids.azureDeviceId = new Guid(azureId_str); var strUrl = string.Format(constMsGraphDeviceLAPS, _ids.azureDeviceId); var MsGraph = await GetGraphForTenantAsync(_ids.tenantId, true); if (MsGraph == null) return null; var Result = await MsGraph.RequestAsync(strUrl, UseBeta: false, retryForbidden: false); if (Result != null) { var ResultJson = Result.Result["credentials"] as JArray; if (ResultJson != null) { foreach (JObject item in ResultJson) { string _base64Password = (string)item["passwordBase64"]; byte[] passwordBytes = Convert.FromBase64String(_base64Password); string decodedPassword = Encoding.UTF8.GetString(passwordBytes); item["passwordBase64"] = decodedPassword; } Collector.DoProcessUiMessage(3, $" properties of selected '{ResultJson}' with {ResultJson.Count} properties could be read."); } JObject singleCredential = ResultJson[0] as JObject; return singleCredential; } return null; } public override async Task DoScanAsync(bool Always, bool Rescan, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token, bool EnhancedDebug = false) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { if (Collector?.InfrastructureConfig?.ActiveDirectory?.Scans?.Count != null && Collector.InfrastructureConfig.ActiveDirectory.Scans.Count == 0) { Collector.DoProcessUiMessage(0, "no AD scans defined, aborting AD scan."); Collector.DoProcessUiMessage(0, ""); return false; } if (!Always) { var scanInfo = await Collector.ActiveDirectory.GetScanTimeInfoAsync(requestInfo, LogDeep + 1, Token); if (scanInfo == null) { Collector.DoProcessUiMessage(0, "could not get valid scan times info for AD connector"); return false; } if (scanInfo.NextScan == null) { Collector.DoProcessUiMessage(0, "not valid next scan time info for AD connector"); return false; } var nextScan = (DateTime)scanInfo.NextScan; if (!(scanInfo.LastScan is DateTime lastScan) || (lastScan >= nextScan)) { Collector.DoProcessUiMessage(0, $"currently no scan needed for AD connector, lastScan={scanInfo.LastScan} (UTC), nextScan={scanInfo.NextScan} (UTC)"); return false; } } Collector.DoProcessUiMessage(0, ""); var Scans = new List(); var LdapProps = new Dictionary>() { {enumFasdInformationClass.User, GetPropertyList( enumFasdInformationClass.User) }, {enumFasdInformationClass.Computer, GetPropertyList( enumFasdInformationClass.Computer) } }; using (var Conn = new cDbConnection(Collector.mainDbConnection)) { if (!Conn.IsOpen) { LogEntry($"Could not open main sql database '{Collector.mainDbConnection.Database}', aborting scan...'", LogLevels.Error); return false; } // get the last as scan infos var ScanHistory = await GetLastAdScansAsync(Conn, requestInfo, LogDeep + 1, Token); var AlreadyScannedNodes = new Dictionary>() { { enumFasdInformationClass.Computer, new List() }, { enumFasdInformationClass.User, new List() } }; // do the configured ad scans var ScanCount = 0; var GeneralInfo = new cDbAdGeneralInfo(); foreach (var Scan in Collector.InfrastructureConfig.ActiveDirectory.Scans.Values) { if (Scan.Type != enumFasdInformationClass.User && Scan.Type != enumFasdInformationClass.Computer) continue; // get the infos of the last scan if (!ScanHistory.TryGetValue(Scan.Name, out var LastScanInfo)) LastScanInfo = null; else { ScanHistory.Remove(Scan.Name); } // now we have to do the ad scan !!! if (ScanCount == 0) Collector.DoProcessUiMessage(0, string.Format("doing the AD scans:")); ScanCount++; Collector.DoProcessUiMessage(1, string.Format("starting AD scan '{0}'...", Scan.Name)); // create a new scan info, if not exist if (LastScanInfo == null) { LastScanInfo = new cDbAdScanInfo() { ID = -1, Name = Scan.Name, LastRun = new DateTime(1970, 1, 1) }; LastScanInfo.ID = await UpdateAdScanEntryAsync(Conn, LastScanInfo, requestInfo, LogDeep + 1, Token); } LastScanInfo.LastRun = DateTime.UtcNow; // get the infos about the last node scans var NodeHistory = await GetLastAdScanNode(LastScanInfo.ID, requestInfo, LogDeep + 1, Token); if (NodeHistory == null) NodeHistory = new Dictionary(); // do the defined scan nodes foreach (var ScanNode in Scan.ScanNodes) { // if we could not find a history for the scan node, we create a new one var SName = $"{Scan.Name}|{ScanNode.AdDomain.Name}|{ScanNode.Path}"; if (!NodeHistory.TryGetValue(SName, out var NodeInfo)) NodeInfo = new cDbAdScanNode() { ScanID = LastScanInfo.ID, Name = SName, Fingerprint = "", InvocationID = "0", LastUSN = 0 }; if (LdapProps != null) await ProcessScanNode(Conn, LdapProps[Scan.Type], Scan, ScanNode, AlreadyScannedNodes[Scan.Type], NodeInfo, GeneralInfo, Rescan, requestInfo, LogDeep + 1, Token); } // and save the new scan info LastScanInfo.ID = await UpdateAdScanEntryAsync(Conn, LastScanInfo, requestInfo, LogDeep + 1, Token); if (LastScanInfo.ID < 0) { cLogManager.DefaultLogger.LogEntry(LogLevels.Error, string.Format("The ad scan db entry with ID='{0}' and Name='{1}' could not be created or updated!", LastScanInfo.ID, LastScanInfo.Name)); continue; } } if (ScanCount == 0) Collector.DoProcessUiMessage(0, "currently no AD scans needed."); else Collector.DoProcessUiMessage(0, "... AD scan finished."); // update the domain list await UpdateAdDomains(Conn, GeneralInfo.Domains, requestInfo, LogDeep + 1, Token); // delete the remaining scan history entries, they are not needed any more foreach (var Scan in ScanHistory.Values) { try { await RemoveAdScanEntryAsync(Conn, Scan.ID, requestInfo, LogDeep + 1, Token); } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); } } Collector.DoProcessUiMessage(0, ""); } if (Collector.InfrastructureConfig.AzureTenants != null) await DoAzureScanAsync(requestInfo, LogDeep + 1, Token); await Collector.SetLastScanTime("AdScan-all", DateTime.UtcNow, requestInfo, LogDeep + 1, Token); return true; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return false; } private async Task UpdateAdDomains(cDbConnection Conn, Dictionary Domains, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!DataHistorySqlHelper.GetWellKnownSqlStatement("UpdateAdScanDomains", out var DbQuery)) return; foreach (var Entry in Domains) { var Params = new Dictionary(2) { { "domainDns", Entry.Key }, { "domainNB", Entry.Value } }; await DataHistorySqlHelper.ExecuteAsync(Conn, DbQuery, Params, requestInfo, Token); } } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } } public async Task GetDomainDns(string domain, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { await UpdateDomainList(requestInfo, LogDeep + 1, Token); if (DomainsRev.TryGetValue(domain, out var retVal)) return retVal; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } public async Task GetDomainNB(string domain, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { await UpdateDomainList(requestInfo, LogDeep + 1, Token); if (Domains.TryGetValue(domain, out var retVal)) return retVal; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return null; } public async Task UpdateDomainList(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { // check, if we have to update the cached domain list var DoUpdate = false; Dictionary _domains = null; try { var TS = DateTime.UtcNow; lock (Domains) { if (TS > LastDomainsUpdate.AddMinutes(10) && !DomainsUpdateRunning) { DoUpdate = true; DomainsUpdateRunning = true; } } try { if (DoUpdate) { // read the new domain list from SQL table if (DataHistorySqlHelper.GetWellKnownSqlStatement("GetAdScanDomains", out var DbQuery)) { int apiError = 0; try { using (var Conn = new cDbConnection(Collector.mainDbConnection)) { using (var _reader = await DataHistorySqlHelper.GetDataReaderAsync(Conn, DbQuery, null, Token)) { if (_reader != null) try { _domains = new Dictionary(); if (_reader.HasRows) { while (await _reader.ReadAsync()) { try { var domainDns = _reader.GetString(0); var domainNB = _reader.GetString(1); if (!string.IsNullOrEmpty(domainDns) && !string.IsNullOrEmpty(domainNB)) _domains[domainDns] = domainNB; } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); } } } } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); } } } } catch (Exception E) { apiError = E.HResult; LogException(E); } finally { if (DataHistorySqlHelper.LogSql) DataHistorySqlHelper.SaveSqlTimingEntry(DbQuery.Name, _startTime, "", apiError, requestInfo?.requestName); } } } } catch (Exception E) { LogException(E); } } catch (Exception E) { LogException(E); } if (DoUpdate) // yes, we have an newer one lock (Domains) { DomainsUpdateRunning = true; if (_domains != null && _domains.Count > 0) { Domains.Clear(); DomainsRev.Clear(); foreach (var Entry in _domains) { Domains[Entry.Key] = Entry.Value; DomainsRev[Entry.Value] = Entry.Key; } } LastDomainsUpdate = DateTime.UtcNow; } } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } } private static void AddToLdapPropertyList(List list, ref Dictionary dicLdap) { foreach (var Entry in list) { if (!dicLdap.ContainsKey(Entry.Name)) dicLdap.Add(Entry.Name.ToLowerInvariant(), Entry); } } private static void AddToLdapPropertyList(cDataHistoryConfigColumn column, ref Dictionary dicLdap) { if (column.SourceType == eDataHistoryQueryType.Static) return; if (dicLdap.TryGetValue(column.SourceName.ToLowerInvariant(), out var _entry)) _entry.Cardinal = Math.Max(_entry.Cardinal, column.Cardinal); else { var ldapType = enumLdapPropertyType.ldapString; var prop = new cLdapPropertyInfo { Name = column.SourceName, LdapType = ldapType, Cardinal = column.Cardinal }; dicLdap.Add(prop.Name.ToLowerInvariant(), prop); } } private static void AddToLdapPropertyList(cDataHistoryConfigTable table, ref Dictionary dicLdap) { foreach (var col in table.Columns.Values) if (col is cDataHistoryConfigColumn col2) AddToLdapPropertyList(col2, ref dicLdap); } private Dictionary GetPropertyList(enumFasdInformationClass InformationClass) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } var dicLdap = new Dictionary(); try { switch (InformationClass) { case enumFasdInformationClass.User: AddToLdapPropertyList(UserMainProperties, ref dicLdap); if (Collector.InfrastructureConfig.ActiveDirectory.ScanPhoneNumbers) AddToLdapPropertyList(UserPhoneProperties, ref dicLdap); break; case enumFasdInformationClass.Computer: AddToLdapPropertyList(ComputerMainProperties, ref dicLdap); break; } foreach (var DataCluster in Collector.ClusterConfig.Clusters.Values) { if (DataCluster.Origin != enumDataHistoryOrigin.ActiveDirectory || DataCluster.InformationClass != InformationClass) continue; foreach (var Table in DataCluster.Tables.Values) if (Table.Type == eDataHistoryTableType.Static || Table.Type == eDataHistoryTableType.History) AddToLdapPropertyList(Table, ref dicLdap); } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return dicLdap; } public override bool CheckIfLateDelivery(cDataHistoryConfigTable Table) { if (Table?.ParentCluster?.Origin == enumDataHistoryOrigin.Intune) return true; return false; } private object ConvertToF4sdType(object objVal, enumFasdValueType ValueType) { try { if (objVal != null) { switch (ValueType) { case enumFasdValueType.INT: return cF4SDHealthCardRawData.GetInteger(objVal); case enumFasdValueType.GUID: return XML.cXmlParser.GetGuidFromString(objVal.ToString()); case enumFasdValueType.BOOLEAN: if (objVal == null) return false; if (objVal is bool) return (bool)objVal; if (objVal is int) return (int)objVal != 0; if (objVal is string) return bool.TryParse((string)objVal, out bool result) ? result : ((string)objVal == "1"); return false; // Standardrückgabewert, falls keine Bedingung zutrifft case enumFasdValueType.DATETIME: DateTime? dt = null; if (objVal is DateTime _dt) dt = _dt; else { if (DateTime.TryParse(objVal.ToString(), out _dt)) dt = _dt; } if (dt is DateTime _dt2) { if (_dt2 == new DateTime(1, 1, 1, 0, 0, 0)) dt = null; } return dt; default: return objVal.ToString(); } } } catch (Exception E) { LogException(E); } finally { } return null; } private static string GetHash(string s) { var arr = Encoding.UTF8.GetBytes(s); byte[] hashBytes; using (System.Security.Cryptography.MD5 md5 = System.Security.Cryptography.MD5.Create()) { hashBytes = md5.ComputeHash(arr); } return Convert.ToBase64String(hashBytes); } private static string GetBaseDN(cDataHistoryAdScanNode Node) { try { var strDN = Node.Path; var arrDom = Node.AdDomain.FQDN.Split('.'); foreach (var Entry in arrDom) { strDN += $",DC={Entry}"; } if (strDN.StartsWith(",")) strDN = strDN.Remove(0, 1); return strDN; } catch (Exception E) { LogException(E); } return null; } public Dictionary GetNT4DomainNames() { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } var RetVal = new Dictionary(); try { foreach (var Domain in Collector?.InfrastructureConfig?.ActiveDirectory?.Domains?.Values) { var NT4 = GetNT4DomainName(Domain); if (!string.IsNullOrEmpty(NT4)) RetVal.Add(NT4, Domain.FQDN); } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return RetVal; } private static string GetNT4DomainName(cDataHistoryAdDomain Domain) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { IADsNameTranslate oNameTranslate = new ActiveDs.NameTranslate(); oNameTranslate.InitEx((int)ADS_NAME_INITTYPE_ENUM.ADS_NAME_INITTYPE_DOMAIN, Domain.FQDN, Domain.Credential.User, Domain.Credential.Domain, Domain.Credential.NwCredential.Password); string[] aSplitDN = Domain.FQDN.Split(new Char[] { '.' }); string sDistinguishedName = ""; //Convert Domain Name to Distinguished Name foreach (string sDomainPart in aSplitDN) { if (sDistinguishedName != "") sDistinguishedName += ","; sDistinguishedName = sDistinguishedName + "DC=" + sDomainPart; } oNameTranslate.Set((int)ADS_NAME_TYPE_ENUM.ADS_NAME_TYPE_UNKNOWN, sDistinguishedName); string sFriendlyName = oNameTranslate.Get((int)ADS_NAME_TYPE_ENUM.ADS_NAME_TYPE_NT4); sFriendlyName = sFriendlyName.Replace(@"\", ""); return sFriendlyName; } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } return null; } public override async Task> GetIds(cF4sdConnectorIds IdEntry, enumFasdInformationClass InfoClass, cF4sdWebRequestInfo requestInfo, int LogDeep) { await Task.CompletedTask; if (IdEntry.sid == null) return null; return new List() { IdEntry.sid }; } public override async Task GetScanTimeInfoAsync(cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { return await GetScanTimeInfoAsync(Collector.InfrastructureConfig.ActiveDirectory.ScanTiming, "AdScan-all", requestInfo, LogDeep, Token); } public override async Task ColumnUpatePostProcessingAsync(cDbConnection DbConn, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { if (!DataHistorySqlHelper.GetWellKnownSqlStatement("PostColumnCreateActiveDirectory", out var Query2)) return false; await DataHistorySqlHelper.ExecuteAsync(DbConn, Query2, null, requestInfo, Token); return true; } catch (Exception E) { LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } return false; } public override async Task ValidateMainTablesAsync(cDbConnection DbConn, cF4sdWebRequestInfo requestInfo, int LogDeep, CancellationToken Token) { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } if (cPerformanceLogger.IsActive && requestInfo != null) { if (CM == null) CM = MethodBase.GetCurrentMethod(); cPerformanceLogger.LogPerformanceStart(LogDeep, CM, requestInfo.id, requestInfo.created); } var _startTime = DateTime.UtcNow; try { // validate the ad scan tables Collector.DoProcessUiMessage(1, $"start validating the ad scan tables..."); if (!DataHistorySqlHelper.GetWellKnownSqlStatement("Validate_AdScanTables", out var Query)) return false; var n = await DataHistorySqlHelper.GetScalarResultAsync(DbConn, Query, null, requestInfo, Token); if (n > 0) { Collector.DoProcessUiMessage(1, $"the validation of the ad scan tables finished successful."); return true; } // create the ad scan tables Collector.DoProcessUiMessage(2, $"the ad scan tables are not complete, creating them..."); if (!DataHistorySqlHelper.GetWellKnownSqlStatement("Create_AdScanTables", out Query)) return false; var n2 = await DataHistorySqlHelper.ExecuteAsync(DbConn, Query, null, requestInfo, Token); // validate the created ad scan tables if (!DataHistorySqlHelper.GetWellKnownSqlStatement("Validate_AdScanTables", out Query)) return false; n = await DataHistorySqlHelper.GetScalarResultAsync(DbConn, Query, null, requestInfo, Token); if (n > 0) { Collector.DoProcessUiMessage(2, $"the ad scan tables were created sucessfull."); return true; } Collector.DoProcessUiMessage(2, "An error occurred when creating the ad main tables."); } catch (Exception E) { Collector.DoProcessUiMessage(1, $"the validation of the ad scan tables aborted with an exceptional error."); LogException(E); } finally { if (cPerformanceLogger.IsActive && requestInfo != null) { cPerformanceLogger.LogPerformanceEnd(LogDeep, CM, requestInfo.id, requestInfo.created, _startTime); } if (CM != null) LogMethodEnd(CM); } LogEntry($"the main tables could not be validated sucessfull.", LogLevels.Fatal); return false; } public void ResolveWinAuthenticationGroups() { MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); } try { if (Collector?.InfrastructureConfig?.Authorisation?.MembershipGroups?.GroupsAd == null) return; LogEntry("Resolving AD groups SIDs..."); var _lstDomains = Collector.InfrastructureConfig.Authorisation.MembershipGroups.GroupsAd; LogEntry("Resolving AD groups SIDs finished"); foreach (var _domain in _lstDomains.Values) { try { LogEntry($"Start processing domain"); LogEntry($"Processing domain {_domain?.Domain?.FQDN}"); PrincipalContext PC; if (_domain.Domain.Credential == null) PC = new PrincipalContext(ContextType.Domain, _domain.Domain.FQDN); else PC = new PrincipalContext(ContextType.Domain, _domain.Domain.FQDN, _domain.Domain.Credential.User, _domain.Domain.Credential.NwCredential.Password); LogEntry($"Principal context successful created"); foreach (var _group in _domain.Values) { try { LogEntry($"Start processing group."); LogEntry($"Group: {_group.Name}."); var GI = GroupPrincipal.FindByIdentity(PC, IdentityType.SamAccountName, _group.Account); LogEntry($"Group principal found"); LogEntry($"GI.SID: {GI.Sid.ToString()}"); if (GI != null) if (GI.Sid != null) { _group.SID = GI.Sid.ToString(); LogEntry($"AD group '{_group.Account}' in domain '{_domain.Domain.FQDN}' resolved. SID = {_group.SID}"); } if (_group.SID == null) LogEntry($"Could not resolve AD group '{_group.Account}' in domain '{_domain.Domain.FQDN}'", LogLevels.Warning); } catch (Exception E) { LogException(E); } finally { LogEntry($"End processing group."); } } } catch (Exception E) { LogException(E); } finally { LogEntry($"End processing domain"); } } } catch (Exception E) { LogException(E); } finally { if (CM != null) LogMethodEnd(CM); } } static public Guid ConvertAzureSidToGuid(string sid) { if (!sid.StartsWith("S-1-12-1-")) return Guid.Empty; try { var objSid = GetByteArrayFromSid(sid); if (objSid == null || objSid.Length != 28) return Guid.Empty; var arrB = new byte[16]; Array.Copy(objSid, 12, arrB, 0, 16); var azureId = new Guid(arrB); return azureId; } catch (Exception) { return Guid.Empty; } } } public class cDbAdScanInfo { public int ID; public string Name; public DateTime LastRun; } public class cDbAdScanNode { public int ScanID; public string Name; public string Fingerprint; public string InvocationID; public UInt64 LastUSN; public string domainDNS; public string domainNB; } public class cDbAdGeneralInfo { public Dictionary Domains = new Dictionary(); } public class cLdapPropertyInfo { public string Name { get; set; } public enumLdapPropertyType LdapType { get; set; } public string DbName { get; set; } public bool Cumulative { get; set; } = false; public bool Reverse { get; set; } = false; public int Cardinal = -1; public cLdapPropertyInfo() { } } }