648 lines
24 KiB
C#
648 lines
24 KiB
C#
using C4IT.FASD.Base;
|
|
using C4IT.Logging;
|
|
using C4IT.Security;
|
|
using C4IT.XML;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using System.Text.RegularExpressions;
|
|
using System.Xml;
|
|
using static C4IT.Logging.cLogManager;
|
|
|
|
namespace C4IT.DataHistoryProvider
|
|
{
|
|
public class cDataHistoryConfigActiveDirectory : IConfigNodeValidation
|
|
{
|
|
public bool IsValid { get; private set; } = false;
|
|
|
|
public bool ScanPhoneNumbers { get; private set; } = false;
|
|
|
|
public cDataHistoryScanTiming ScanTiming { get; private set; } = null;
|
|
|
|
|
|
public Dictionary<string, cDataHistoryAdDomain> Domains { get; private set; } = new Dictionary<string, cDataHistoryAdDomain>();
|
|
public Dictionary<string, cDataHistoryAdScan> Scans { get; private set; } = new Dictionary<string, cDataHistoryAdScan>();
|
|
|
|
public cDataHistoryConfigActiveDirectory(XmlElement XNode, Dictionary<string, cCredential> Credentials, cXmlParser Parser)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
var XNode2 = XNode.SelectSingleNode("Active-Directory");
|
|
if (!(XNode2 is XmlElement XRoot))
|
|
{
|
|
Parser.AddMessage(XNode, "Could not find a valid <Active-Directory> element.");
|
|
return;
|
|
}
|
|
|
|
Parser.EnterElement("Active-Directory");
|
|
try
|
|
{
|
|
ScanPhoneNumbers = cXmlParser.GetBoolFromXmlAttribute(XNode2, "ScanPhoneNumbers");
|
|
|
|
ScanTiming = new cDataHistoryScanTiming(XRoot, TimeSpan.FromHours(24), TimeSpan.FromMinutes(10), Parser);
|
|
|
|
Domains = cDataHistoryAdDomain.LoadFromXml(XRoot, Credentials, Parser);
|
|
if (Domains == null || Domains.Count == 0)
|
|
{
|
|
Parser.AddMessage(XNode, "The element <Active-Directory> has no valid <AD-Domain> entries.");
|
|
return;
|
|
}
|
|
|
|
Scans = cDataHistoryAdScan.LoadFromXml(XRoot, this, Parser);
|
|
if (Scans == null || Scans.Count == 0)
|
|
{
|
|
Parser.AddMessage(XNode, "The element <Active-Directory> has no valid <AD-Scan> entries.");
|
|
return;
|
|
}
|
|
|
|
IsValid = true;
|
|
}
|
|
finally
|
|
{
|
|
Parser.LeaveElement("Active-Directory");
|
|
}
|
|
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public class cDataHistoryAdScan : cConfigNodeNamed
|
|
{
|
|
public enumFasdInformationClass Type { get; private set; }
|
|
|
|
public List<cDataHistoryAdScanNode> ScanNodes { get; private set; } = new List<cDataHistoryAdScanNode>();
|
|
|
|
internal cDataHistoryAdScan(XmlElement XNode, cDataHistoryConfigActiveDirectory AdConfig, cXmlParser Parser) :
|
|
base(XNode, Parser)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
if (!IsValid)
|
|
return;
|
|
|
|
IsValid = false;
|
|
|
|
Type = (enumFasdInformationClass)cXmlParser.GetEnumFromAttribute<enumFasdInformationClass>(XNode, "Type", enumFasdInformationClass.Unknown);
|
|
if (Type != enumFasdInformationClass.User && Type != enumFasdInformationClass.Computer)
|
|
{
|
|
Parser.AddInvalidAttribute(XNode, Name, "AD-Type");
|
|
return;
|
|
}
|
|
|
|
ScanNodes = cDataHistoryAdScanNode.LoadFromXml(XNode, AdConfig, this, Parser);
|
|
|
|
if (ScanNodes == null || ScanNodes.Count == 0)
|
|
{
|
|
Parser.AddMessage(XNode, "The <AD-Scan> element has no valid <AD-Scan-Node> entries.");
|
|
return;
|
|
}
|
|
|
|
IsValid = true;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
}
|
|
|
|
internal static Dictionary<string, cDataHistoryAdScan> LoadFromXml(XmlElement XNode, cDataHistoryConfigActiveDirectory AdConfig, cXmlParser Parser)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
|
|
var RetVal = new Dictionary<string, cDataHistoryAdScan>();
|
|
|
|
try
|
|
{
|
|
var XRoot = XNode.SelectSingleNode("AD-Scans");
|
|
if (XRoot == null)
|
|
{
|
|
Parser.AddMessage(XNode, $"No <AD-Scans> element found within element <{XNode.Name}>.");
|
|
return null;
|
|
}
|
|
|
|
Parser.EnterElement("AD-Scans");
|
|
try
|
|
{
|
|
var XList = XRoot.SelectNodes("AD-Scan");
|
|
if (XList != null && XList.Count > 0)
|
|
{
|
|
Parser.EnterElement("AD-Scan");
|
|
try
|
|
{
|
|
foreach (var Entry in XList)
|
|
{
|
|
if (!(Entry is XmlElement XEntry))
|
|
continue;
|
|
|
|
var Node = new cDataHistoryAdScan(XEntry, AdConfig, Parser);
|
|
if (Node.IsValid)
|
|
{
|
|
if (RetVal.ContainsKey(Node.Name))
|
|
{
|
|
Parser.AddDuplicateName(XEntry, Node.Name, LogLevels.Warning);
|
|
}
|
|
else
|
|
RetVal.Add(Node.Name, Node);
|
|
}
|
|
Parser.SelectElementNext();
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Parser.LeaveElement("AD-Scan");
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Parser.LeaveElement("AD-Scans");
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return RetVal;
|
|
}
|
|
}
|
|
|
|
|
|
public class cDataHistoryAdScanNode : IConfigNodeValidation
|
|
{
|
|
public enum enumFilterPropery { unknown = 0, commonName, distinguishedName, samAccountName }
|
|
|
|
public bool IsValid { get; private set; } = false;
|
|
public cDataHistoryAdDomain AdDomain { get; private set; }
|
|
public string Path { get; private set; }
|
|
public string LdapFilter { get; private set; }
|
|
public enumFilterPropery FilterProperty { get; private set; } = enumFilterPropery.unknown;
|
|
public bool WildcardFilterNot { get; private set; } = false;
|
|
public Regex WildcardFilter { get; private set; }
|
|
public Regex RegExFilter { get; private set; }
|
|
|
|
public cDataHistoryAdScan parentScan { get; private set; }
|
|
|
|
internal cDataHistoryAdScanNode(XmlElement XNode, cDataHistoryConfigActiveDirectory AdConfig, cDataHistoryAdScan Scan, cXmlParser Parser)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
parentScan = Scan;
|
|
var strAdDomain = cXmlParser.GetStringFromXmlAttribute(XNode, "AD-Domain");
|
|
if (!AdConfig.Domains.TryGetValue(strAdDomain, out var AdD))
|
|
{
|
|
Parser.AddInvalidAttribute(XNode, null, "AD-Domain");
|
|
return;
|
|
}
|
|
AdDomain = AdD;
|
|
|
|
Path = cXmlParser.GetStringFromXmlAttribute(XNode, "Path", "");
|
|
|
|
LdapFilter = cXmlParser.GetStringFromXmlAttribute(XNode, "LDAP-Filter");
|
|
|
|
FilterProperty = cXmlParser.GetEnumFromAttribute<enumFilterPropery>(XNode, "Filter-Property", enumFilterPropery.unknown);
|
|
|
|
var strWildcardFilter = cXmlParser.GetStringFromXmlAttribute(XNode, "Wildcard-Filter");
|
|
if (!string.IsNullOrEmpty(strWildcardFilter))
|
|
{
|
|
if (strWildcardFilter.StartsWith("!"))
|
|
{
|
|
WildcardFilterNot = true;
|
|
strWildcardFilter.Remove(0, 1);
|
|
}
|
|
var _expression = "^" + Regex.Escape(strWildcardFilter)
|
|
.Replace("\\\\\\?", "??").Replace("\\?", ".").Replace("??", "\\?")
|
|
.Replace("\\\\\\*", "**").Replace("\\*", ".*").Replace("**", "\\*") + "$";
|
|
WildcardFilter = new Regex(_expression, RegexOptions.Compiled);
|
|
}
|
|
|
|
var strRexExFilter = cXmlParser.GetStringFromXmlAttribute(XNode, "RegEx-Filter");
|
|
if (!string.IsNullOrEmpty(strRexExFilter))
|
|
RegExFilter = new Regex(strRexExFilter, RegexOptions.Compiled);
|
|
|
|
IsValid = true;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
}
|
|
|
|
internal static List<cDataHistoryAdScanNode> LoadFromXml(XmlElement XNode, cDataHistoryConfigActiveDirectory AdConfig, cDataHistoryAdScan Scan, cXmlParser Parser)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
|
|
var RetVal = new List<cDataHistoryAdScanNode>();
|
|
try
|
|
{
|
|
var XList = XNode.SelectNodes("AD-Scan-Node");
|
|
if (XList != null && XList.Count > 0)
|
|
{
|
|
Parser.EnterElement("AD-Scan-Node");
|
|
try
|
|
{
|
|
foreach (var Entry in XList)
|
|
{
|
|
if (!(Entry is XmlElement XEntry))
|
|
continue;
|
|
|
|
var Node = new cDataHistoryAdScanNode(XEntry, AdConfig, Scan, Parser);
|
|
if (Node.IsValid)
|
|
RetVal.Add(Node);
|
|
|
|
Parser.SelectElementNext();
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Parser.LeaveElement("AD-Scan-Node");
|
|
}
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return RetVal;
|
|
}
|
|
}
|
|
|
|
public class cDataHistoryAdDomain : cConfigNodeNamed
|
|
{
|
|
public string FQDN { get; private set; } = null;
|
|
public cCredential Credential { get; private set; } = null;
|
|
|
|
public List<cDataHistoryAdServer> Servers { get; private set; } = new List<cDataHistoryAdServer>();
|
|
|
|
internal cDataHistoryAdDomain(XmlElement XNode, Dictionary<string, cCredential> Credentials, cXmlParser Parser) :
|
|
base(XNode, Parser)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
if (!IsValid)
|
|
return;
|
|
|
|
IsValid = false;
|
|
|
|
FQDN = cXmlParser.GetStringFromXmlAttribute(XNode, "FQDN");
|
|
if (string.IsNullOrWhiteSpace(FQDN))
|
|
{
|
|
Parser.AddInvalidAttribute(XNode, null, "FQDN");
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
var strCreds = cXmlParser.GetStringFromXmlAttribute(XNode, "Credential");
|
|
if (!string.IsNullOrWhiteSpace(strCreds))
|
|
{
|
|
if (Credentials.TryGetValue(strCreds, out var Cred))
|
|
Credential = Cred;
|
|
}
|
|
|
|
if (Credential == null)
|
|
{
|
|
Parser.AddInvalidAttribute(XNode, Name, "Credential");
|
|
return;
|
|
}
|
|
|
|
Servers = cDataHistoryAdServer.LoadFromXml(XNode, Parser);
|
|
if (Servers?.Count == 0)
|
|
{
|
|
Parser.AddInvalidAttribute(XNode, Name, "No valid <AD-Server> element could be found.");
|
|
return;
|
|
}
|
|
|
|
IsValid = true;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
}
|
|
|
|
internal static Dictionary<string, cDataHistoryAdDomain> LoadFromXml(XmlElement XNode, Dictionary<string, cCredential> Credentials, cXmlParser Parser)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
|
|
var RetVal = new Dictionary<string, cDataHistoryAdDomain>();
|
|
|
|
try
|
|
{
|
|
var XRoot = XNode.SelectSingleNode("AD-Domains");
|
|
if (XRoot == null)
|
|
{
|
|
Parser.AddMessage(XNode, $"No <AD-Domains> element found within element <{XNode.Name}>.");
|
|
return null;
|
|
}
|
|
|
|
Parser.EnterElement("AD-Domains");
|
|
try
|
|
{
|
|
var XList = XRoot.SelectNodes("AD-Domain");
|
|
if (XList != null && XList.Count > 0)
|
|
{
|
|
Parser.EnterElement("AD-Domain");
|
|
try
|
|
{
|
|
foreach (var Entry in XList)
|
|
{
|
|
if (!(Entry is XmlElement XEntry))
|
|
continue;
|
|
|
|
var Node = new cDataHistoryAdDomain(XEntry, Credentials, Parser);
|
|
if (Node.IsValid)
|
|
{
|
|
if (RetVal.ContainsKey(Node.Name))
|
|
{
|
|
Parser.AddDuplicateName(XEntry, Node.Name, LogLevels.Warning);
|
|
}
|
|
else
|
|
RetVal.Add(Node.Name, Node);
|
|
}
|
|
Parser.SelectElementNext();
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Parser.LeaveElement("AD-Domain");
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Parser.LeaveElement("AD-Domains");
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return RetVal;
|
|
}
|
|
}
|
|
|
|
public class cDataHistoryAdServer : IConfigNodeValidation
|
|
{
|
|
public bool IsValid { get; private set; } = false;
|
|
public string FQDN { get; private set; }
|
|
public int Port { get; private set; }
|
|
public bool useSSL { get; private set; }
|
|
|
|
internal cDataHistoryAdServer(XmlElement XNode, cXmlParser Parser)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
FQDN = cXmlParser.GetStringFromXmlAttribute(XNode, "FQDN");
|
|
if (string.IsNullOrWhiteSpace(FQDN))
|
|
{
|
|
Parser.AddInvalidAttribute(XNode, null, "FQDN");
|
|
return;
|
|
}
|
|
|
|
Port = cXmlParser.GetIntegerFromXmlAttribute(XNode, "Port");
|
|
useSSL = cXmlParser.GetBoolFromXmlAttribute(XNode, "UseSSL");
|
|
|
|
IsValid = true;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
}
|
|
|
|
internal static List<cDataHistoryAdServer> LoadFromXml(XmlElement XNode, cXmlParser Parser)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
|
|
var RetVal = new List<cDataHistoryAdServer>();
|
|
try
|
|
{
|
|
var XList = XNode.SelectNodes("AD-Server");
|
|
if (XList != null && XList.Count > 0)
|
|
{
|
|
Parser.EnterElement("AD-Server");
|
|
try
|
|
{
|
|
foreach (var Entry in XList)
|
|
{
|
|
if (!(Entry is XmlElement XEntry))
|
|
continue;
|
|
|
|
var Node = new cDataHistoryAdServer(XEntry, Parser);
|
|
if (Node.IsValid)
|
|
RetVal.Add(Node);
|
|
|
|
Parser.SelectElementNext();
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Parser.LeaveElement("AD-Server");
|
|
}
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return RetVal;
|
|
}
|
|
}
|
|
|
|
public class cDataHistoryAzureTenant : IConfigNodeValidation
|
|
{
|
|
public bool IsValid { get; private set; } = false;
|
|
|
|
public string Domain { get; private set; } = "";
|
|
|
|
public Guid TenantID { get; private set; } = Guid.Empty;
|
|
|
|
public cCredential Credential { get; private set; } = null;
|
|
public bool ScanIntuneDevices { get; private set; } = false;
|
|
public bool WithMobileDevices { get; private set; } = false;
|
|
|
|
internal cDataHistoryAzureTenant(XmlElement XNode, Dictionary<string, cCredential> Credentials, cXmlParser Parser)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
try
|
|
{
|
|
Domain = cXmlParser.GetStringFromXmlAttribute(XNode, "Domain");
|
|
if (string.IsNullOrWhiteSpace(Domain))
|
|
{
|
|
Parser.AddInvalidAttribute(XNode, null, "Domain");
|
|
return;
|
|
}
|
|
TenantID = cXmlParser.GetGuidFromXmlAttribute(XNode, "TenantID");
|
|
if (TenantID == Guid.Empty)
|
|
{
|
|
Parser.AddInvalidAttribute(XNode, Domain, "TenantID");
|
|
return;
|
|
}
|
|
|
|
var strCreds = cXmlParser.GetStringFromXmlAttribute(XNode, "Credential");
|
|
if (!string.IsNullOrWhiteSpace(strCreds))
|
|
{
|
|
if (Credentials.TryGetValue(strCreds, out var Cred))
|
|
Credential = Cred;
|
|
}
|
|
|
|
if (Credential == null)
|
|
{
|
|
Parser.AddInvalidAttribute(XNode, Domain, "Credential");
|
|
return;
|
|
}
|
|
ScanIntuneDevices = cXmlParser.GetBoolFromXmlAttribute(XNode, "ScanIntuneDevices");
|
|
WithMobileDevices = cXmlParser.GetBoolFromXmlAttribute(XNode, "WithMobileDevices");
|
|
|
|
IsValid = true;
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
}
|
|
|
|
internal static Dictionary<string, cDataHistoryAzureTenant> LoadFromXml(XmlElement XNode, Dictionary<string, cCredential> Credentials, cXmlParser Parser)
|
|
{
|
|
MethodBase CM = null; if (cLogManager.DefaultLogger.IsDebug) { CM = MethodBase.GetCurrentMethod(); LogMethodBegin(CM); }
|
|
|
|
var RetVal = new Dictionary<string, cDataHistoryAzureTenant>();
|
|
|
|
try
|
|
{
|
|
var XRoot = XNode.SelectSingleNode("Azure-AD");
|
|
if (XRoot == null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
Parser.EnterElement("Azure-AD");
|
|
try
|
|
{
|
|
var XList = XRoot.SelectNodes("Azure-Tenant");
|
|
if (XList != null && XList.Count > 0)
|
|
{
|
|
Parser.EnterElement("Azure-Tenant");
|
|
try
|
|
{
|
|
foreach (var Entry in XList)
|
|
{
|
|
if (!(Entry is XmlElement XEntry))
|
|
continue;
|
|
|
|
var Node = new cDataHistoryAzureTenant(XEntry, Credentials, Parser);
|
|
if (Node.IsValid)
|
|
{
|
|
if (RetVal.ContainsKey(Node.Domain))
|
|
{
|
|
Parser.AddDuplicateName(XEntry, Node.Domain, LogLevels.Warning);
|
|
}
|
|
else
|
|
RetVal.Add(Node.Domain, Node);
|
|
}
|
|
Parser.SelectElementNext();
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Parser.LeaveElement("Azure-Tenant");
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
Parser.LeaveElement("Azure-AD");
|
|
}
|
|
}
|
|
catch (Exception E)
|
|
{
|
|
LogException(E);
|
|
}
|
|
finally
|
|
{
|
|
if (CM != null) LogMethodEnd(CM);
|
|
}
|
|
|
|
return RetVal;
|
|
}
|
|
|
|
}
|
|
|
|
public class cWildcardPattern
|
|
{
|
|
private readonly string _expression;
|
|
private readonly Regex _regex;
|
|
|
|
public cWildcardPattern(string pattern)
|
|
{
|
|
if (string.IsNullOrEmpty(pattern)) throw new ArgumentNullException(nameof(pattern));
|
|
|
|
_expression = "^" + Regex.Escape(pattern)
|
|
.Replace("\\\\\\?", "??").Replace("\\?", ".").Replace("??", "\\?")
|
|
.Replace("\\\\\\*", "**").Replace("\\*", ".*").Replace("**", "\\*") + "$";
|
|
_regex = new Regex(_expression, RegexOptions.Compiled);
|
|
}
|
|
|
|
public bool IsMatch(string value)
|
|
{
|
|
return _regex.IsMatch(value);
|
|
}
|
|
}
|
|
|
|
|
|
}
|