using C4IT.Logging; using System; using System.Collections.Generic; using System.ComponentModel; using System.DirectoryServices.AccountManagement; using System.IO; using System.Linq; using System.Net; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Security.AccessControl; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace LiamNtfs { public class cNtfsBase { private cNtfsLogonInfo privLogonInfo = null; private int scanningDepth; public PrincipalContext adContext = null; public Exception LastException { get; private set; } = null; public string LastErrorMessage { get; private set; } = null; [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ResetError() { LastException = null; LastErrorMessage = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetErrorException(string Action, Exception E, LogLevels lev = LogLevels.Error) { LastException = E; LastErrorMessage = Action + ": " + E.Message; cLogManager.LogEntry(Action, lev); } private async Task privLogonAsync(cNtfsLogonInfo LogonInfo) { try { //TODO: remove dummy delay? await Task.Delay(0); ResetError(); var netResource = new NetResource() { Scope = ResourceScope.GlobalNetwork, ResourceType = ResourceType.Disk, DisplayType = ResourceDisplaytype.Share, RemoteName = LogonInfo.TargetNetworkName }; var result = WNetAddConnection2( netResource, LogonInfo.UserSecret, LogonInfo.User, 0); if(result == 1219) { result = WNetCancelConnection2(LogonInfo.TargetNetworkName,0,true); if (result == 0) return await privLogonAsync(LogonInfo); } if (result != 0) { throw new Win32Exception(result); } var FSLogon = true; adContext = new PrincipalContext(ContextType.Domain, LogonInfo.Domain, LogonInfo.User, new NetworkCredential("", LogonInfo.UserSecret).Password); return FSLogon && adContext != null; } catch (Exception E) { SetErrorException("exception error while Ntfs login", E, LogLevels.Debug); cLogManager.LogException(E, LogLevels.Debug); } return false; } private async Task privRelogon() { if (privLogonInfo == null) return false; var RetVal = await privLogonAsync(privLogonInfo); return RetVal; } public async Task LogonAsync(cNtfsLogonInfo LogonInfo) { var RetVal = await privLogonAsync(LogonInfo); if (RetVal == true) privLogonInfo = LogonInfo; return RetVal; } [DllImport("mpr.dll")] private static extern int WNetAddConnection2(NetResource netResource, string password, string username, int flags); [DllImport("mpr.dll")] private static extern int WNetCancelConnection2(string name, int flags, bool force); public async Task RequestFoldersListAsync(string rootPath, int depth) { ResetError(); try { await Task.Delay(0); if (depth == 0) return null; var Result = privRequestFoldersListAsync(rootPath, depth); if (Result != null) { var RetVal = new cNtfsCollectionBase(Result.Count); foreach (var Entry in Result) { var res = new cNtfsResultFolder((cNtfsResultFolder)Entry); RetVal.Add(res); } return RetVal; } } catch (Exception E) { cLogManager.LogException(E); } return null; } private List privRequestFoldersListAsync(string rootPath, int depth) { if (String.IsNullOrEmpty(rootPath)) return new List(); scanningDepth = depth; return privRequestFoldersListAsync(new DirectoryInfo(rootPath), depth); } private List privRequestFoldersListAsync(DirectoryInfo rootPath, int depth, cNtfsResultFolder parent = null) { ResetError(); var folders = new List(); try { if (depth == 0) return folders; DirectoryInfo[] directories; try { directories = rootPath.GetDirectories(); } catch (Exception E) { cLogManager.LogEntry($"Could not enumerate directories under '{rootPath.FullName}': {E.Message}", LogLevels.Warning); cLogManager.LogException(E, LogLevels.Debug); return folders; } foreach (var directory in directories) { cNtfsResultFolder folder; try { folder = new cNtfsResultFolder() { ID = generateUniquId(directory.FullName), Path = directory.FullName, CreatedDate = directory.CreationTimeUtc.ToString("s"), DisplayName = directory.Name, Level = scanningDepth - depth + 1, Parent = parent }; } catch (Exception E) { cLogManager.LogEntry($"Could not read directory metadata for '{directory.FullName}': {E.Message}", LogLevels.Warning); cLogManager.LogException(E, LogLevels.Debug); continue; } folders.Add(folder); if (depth <= 0) continue; try { var result = privRequestFoldersListAsync(directory, depth - 1, folder); if (result != null && result.Count > 0) folders.AddRange(result); } catch (Exception E) { cLogManager.LogEntry($"Could not scan subtree '{directory.FullName}': {E.Message}", LogLevels.Warning); cLogManager.LogException(E, LogLevels.Debug); } } return folders; } catch (Exception E) { cLogManager.LogException(E); } return null; } public async Task RequestFoldersListAsync(cNtfsResultBase folder, cNtfsResultBase parent) { ResetError(); try { await Task.Delay(0); var Result = new List(); if (Result != null) { var RetVal = new cNtfsCollectionBase(Result.Count); foreach (var Entry in Result) { var res = new cNtfsResultFolder((cNtfsResultFolder)Entry); res.Parent = parent; RetVal.Add(res); } return RetVal; } } catch (Exception E) { cLogManager.LogException(E); } return null; } internal static int getDepth(DirectoryInfo root, DirectoryInfo folder) { var rootDepth = root.FullName.TrimEnd(Path.DirectorySeparatorChar).Split(System.IO.Path.DirectorySeparatorChar).Length; var folderDepth = folder.FullName.TrimEnd(Path.DirectorySeparatorChar).Split(System.IO.Path.DirectorySeparatorChar).Length; return folderDepth - rootDepth; } internal static int getDepth(string root, string folder) { return getDepth(new DirectoryInfo(root), new DirectoryInfo(folder)); } internal static string generateUniquId(string name) { try { var md5 = new System.Security.Cryptography.MD5CryptoServiceProvider(); var utf8 = new System.Text.UTF8Encoding(); var hash = BitConverter.ToString(md5.ComputeHash(utf8.GetBytes(name))); hash = hash.ToLower().Replace("-", ""); return hash; } catch (Exception E) { cLogManager.DefaultLogger.LogException(E); throw; } } } public class cNtfsLogonInfo { public string Domain; public string User; public string UserSecret; public string TargetNetworkName; public string TargetGroupPath; } public class cNtfsResultBase { public string ID { get; set; } = null; public string DisplayName { get; set; } = null; public string Path { get; set; } = null; public int Level { get; set; } = -1; public cNtfsResultBase() { } public cNtfsResultBase(cNtfsResultBase Result) { if (Result == null) return; ID = Result.ID; DisplayName = Result.DisplayName; Path = Result.Path; } } public class cNtfsResultFolder : cNtfsResultBase { public string Description { get; set; } = null; public string Visibility { get; set; } = null; public string OwnerADGroup { get; set; } = null; public string WriteADGroup { get; set; } = null; public string ReadADGroup { get; set; } = null; public cNtfsResultBase Parent { get;set;} = null; public string CreatedDate { get; set; } = DateTime.MinValue.ToString("s"); public cNtfsResultFolder Folders { get; private set; } = null; public cNtfsResultFolder(cNtfsResultFolder Result) : base(Result) { Description = Result.Description; Visibility = Result.Visibility; CreatedDate = Result.CreatedDate; Folders = Result.Folders; Level = Result.Level; Parent = Result.Parent; OwnerADGroup = Result.OwnerADGroup; WriteADGroup = Result.WriteADGroup; ReadADGroup = Result.ReadADGroup; } public cNtfsResultFolder() { } } public class cNtfsResultShare : cNtfsResultBase { public cNtfsCollectionBase Folders { get; private set; } = null; public cNtfsResultShare(cNtfsResultBase Result) : base(Result) { } public cNtfsResultShare() { } public async Task ResolveFolders(cNtfsBase ntfs) { Folders = await ntfs.RequestFoldersListAsync(this.Path, 0); return Folders != null; } } public class cNtfsCollectionBase : SortedList { public cNtfsCollectionBase() { } public cNtfsCollectionBase(int n) : base(n) { } public void Add(cNtfsResultBase Folder) { if (!this.ContainsKey(Folder.ID)) this.Add(Folder.ID, Folder); } } public class cFileshareLogonInfo { public string Domain; public string User; public string UserSecret; public string TargetNetworkName; } [StructLayout(LayoutKind.Sequential)] public class NetResource { public ResourceScope Scope; public ResourceType ResourceType; public ResourceDisplaytype DisplayType; public int Usage; public string LocalName; public string RemoteName; public string Comment; public string Provider; } public enum ResourceScope : int { Connected = 1, GlobalNetwork, Remembered, Recent, Context }; public enum ResourceType : int { Any = 0, Disk = 1, Print = 2, Reserved = 8, } public enum ResourceDisplaytype : int { Generic = 0x0, Domain = 0x01, Server = 0x02, Share = 0x03, File = 0x04, Group = 0x05, Network = 0x06, Root = 0x07, Shareadmin = 0x08, Directory = 0x09, Tree = 0x0a, Ndscontainer = 0x0b } public enum NetError : uint { NERR_Success = 0, NERR_BASE = 2100, NERR_UnknownDevDir = (NERR_BASE + 16), NERR_DuplicateShare = (NERR_BASE + 18), NERR_BufTooSmall = (NERR_BASE + 23), } public enum SHARE_TYPE : uint { STYPE_DISKTREE = 0, STYPE_PRINTQ = 1, STYPE_DEVICE = 2, STYPE_IPC = 3, STYPE_SPECIAL = 0x80000000, } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct SHARE_INFO_1 { public string shi1_netname; public uint shi1_type; public string shi1_remark; public SHARE_INFO_1(string sharename, uint sharetype, string remark) { this.shi1_netname = sharename; this.shi1_type = sharetype; this.shi1_remark = remark; } public override string ToString() { return shi1_netname; } } }