using Microsoft.WindowsServerSolutions.Web.Security; using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Management; using Microsoft.WindowsServerSolutions.Web.Storage; using System.Threading; using System.Globalization; using System.DirectoryServices.AccountManagement; using System.Security.Principal; using Microsoft.WindowsServerSolutions.Storage.Common; using System.Linq; using Microsoft.WindowsServerSolutions.Web; namespace johndandison.SBS { public class SBSAndConfigBasedStorageInformationProvider : IStorageInformationProvider { private const uint ShareTypeDiskDrive = 0u; private string[] sharesFromConfig; private Dictionary> perUserShareCache = new Dictionary>(); private ReaderWriterLockSlim perUserShareCacheLock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion); private LockTable perUserLock = new LockTable(); private List CachedShares { get { CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture; List shareList; try { Thread.CurrentThread.CurrentUICulture = CultureInfo.InstalledUICulture; if (RemoteAccessUser.Current == null) { return null; } string logonName = RemoteAccessUser.Current.LogonName; RemoteAccessLog.WriteCurrentUser("johndandison.SBSAndConfigBasedStorageInformationProvider.GetShares called. Caller identity:"); this.perUserShareCacheLock.EnterReadLock(); bool flag = this.perUserShareCache.TryGetValue(logonName, out shareList); this.perUserShareCacheLock.ExitReadLock(); if (!flag) { lock (this.perUserLock.GetLockObject(logonName)) { this.perUserShareCacheLock.EnterReadLock(); flag = this.perUserShareCache.TryGetValue(logonName, out shareList); this.perUserShareCacheLock.ExitReadLock(); if (!flag) { RemoteAccessLog.Write("Storage", "johndandison.SBSAndConfigBasedStorageInformationProvider.GetShares: Getting shares from storage service"); shareList = GetSharesFromStorageAPI(); RemoteAccessLog.Write("Storage", "johndandison.SBSAndConfigBasedStorageInformationProvider.GetShares: Getting shares from storage service: " + shareList.Count() + " items added."); RemoteAccessLog.Write("Storage", "johndandison.SBSAndConfigBasedStorageInformationProvider.GetShares: Getting shares from config file"); shareList.AddRange(GetSharesFromConfig()); RemoteAccessLog.Write("Storage", "johndandison.SBSAndConfigBasedStorageInformationProvider.GetShares: Getting shares from config file: " + shareList.Count() + " items added."); this.perUserShareCacheLock.EnterWriteLock(); this.perUserShareCache[logonName] = shareList; this.perUserShareCacheLock.ExitWriteLock(); } } } } finally { Thread.CurrentThread.CurrentUICulture = currentCulture; } return shareList; } } private bool UseSharesFromConfig { get { //return null != this.sharesFromConfig; return true; } } public string LocalShareRootPath { get { return null; } } public bool RequiresImpersonation { get { return false; } } public void Initialize(NameValueCollection config) { string text = config["shares"]; if (!string.IsNullOrEmpty(text)) { this.sharesFromConfig = text.Split(new char[] { ';' }); } } public IList GetShares() { return new List(this.CachedShares); } private List GetSharesFromConfig() { List list = new List(); string[] array = this.sharesFromConfig; for (int i = 0; i < array.Length; i++) { string path = array[i]; string server; string name; ParseUNCSharePath(path, out server, out name); list.Add(new SBSCShareInfo(server, name, ShareAccessPermission.Unknown)); } return list; } private static void ParseUNCSharePath(string path, out string server, out string share) { if (!path.StartsWith("\\\\", StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException("Path should represent a UNC path in the form of \\\\server\\share", "path"); } int num = path.IndexOf('\\', 2); if (num == -1 || num != path.LastIndexOf('\\')) { throw new ArgumentException("Path should represent a UNC path in the form of \\\\server\\share", "path"); } server = path.Substring(2, num - 2); share = path.Substring(num + 1); } private static List GetSharesFromFileSystem() { List list = new List(); RemoteAccessLog.Write("Storage", "Creating ManagementObjectSearcher"); using (ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher("SELECT Name FROM Win32_Share")) { using (ManagementObjectCollection managementObjectCollection = managementObjectSearcher.Get()) { using (ManagementObjectCollection.ManagementObjectEnumerator enumerator = managementObjectCollection.GetEnumerator()) { while (enumerator.MoveNext()) { ManagementObject managementObject = (ManagementObject)enumerator.Current; string text = (string)managementObject["Name"]; RemoteAccessLog.Write("Storage", "Validating share: " + text); if (!text.EndsWith("$", StringComparison.OrdinalIgnoreCase)) { list.Add(new SBSCShareInfo(Environment.MachineName, text, ShareAccessPermission.Unknown)); RemoteAccessLog.Write("Storage", "Added share: " + text); } } } } } return list; } public IShareInfo GetShare(string path) { return this.CachedShares.Find((IShareInfo share) => string.Equals(share.UNCPath, path, StringComparison.OrdinalIgnoreCase)); } public void Refresh() { if (RemoteAccessUser.Current != null) { string logonName = RemoteAccessUser.Current.LogonName; this.perUserShareCacheLock.EnterWriteLock(); this.perUserShareCache.Remove(logonName); this.perUserShareCacheLock.ExitWriteLock(); } } public void Dispose() { this.perUserShareCacheLock.Dispose(); this.perUserLock.Dispose(); } //sbs provider private static List GetSharesFromStorageAPI() { return ( from folder in SBSSharedFoldersInfo.GetSharedFolders let share = new SBSCAPIShareInfo(Environment.MachineName, folder.SharedFolderName, folder.SharedFolderPath) where share.GetAccessPermission() != ShareAccessPermission.None select share).ToList(); } } //have to re-implement the ShareInfos, since the ctors are all private public class SBSCShareInfo : IShareInfo { [Flags] private enum NativeSharePermissions { FILE_LIST_DIRECTORY = 1, ReadMask = 1, FILE_ADD_FILE = 2, FILE_ADD_SUBDIRECTORY = 4, FILE_DELETE_CHILD = 64, DELETE = 65536, WriteMask = 65606 } private ShareAccessPermission shareAccess = ShareAccessPermission.Undetermined; public string Server { get; private set; } public string Name { get; private set; } public string UNCPath { get { return string.Format(CultureInfo.InvariantCulture, "\\\\{0}\\{1}", new object[] { this.Server, this.Name }); } } public string LocalPath { get { return null; } } public ShareType Type { get { return ShareType.Generic; } } public FolderView DefaultView { get { return FolderView.Details; } } private SBSCShareInfo(string server, string name) { this.Server = server; this.Name = name; } internal SBSCShareInfo(string server, string name, ShareAccessPermission shareAccess) : this(server, name) { this.shareAccess = shareAccess; } public long GetFreeSpace() { return -1L; } public ShareAccessPermission GetAccessPermission() { if (this.shareAccess == ShareAccessPermission.Undetermined) { using (ManagementObject managementObject = new ManagementObject("root\\CIMV2", string.Format(CultureInfo.InvariantCulture, "Win32_Share.Name='{0}'", new object[] { this.Name }), null)) { uint accessMask = (uint)managementObject.InvokeMethod("GetAccessMask", null); this.shareAccess = GetPermissionFromAccessMask(accessMask); } } return this.shareAccess; } public void Refresh() { this.shareAccess = ShareAccessPermission.Undetermined; } private static ShareAccessPermission GetPermissionFromAccessMask(uint accessMask) { if ((accessMask & 1u) == 0u) { return ShareAccessPermission.None; } if ((accessMask & 65606u) == 0u) { return ShareAccessPermission.ReadOnly; } return ShareAccessPermission.ReadWrite; } } //this one is def the biggest PITA public class SBSCAPIShareInfo : IShareInfo { private enum NativeSharePermissions { FILE_LIST_DIRECTORY = 1, ReadMask = 1, FILE_ADD_FILE = 2, FILE_ADD_SUBDIRECTORY = 4, FILE_DELETE_CHILD = 64, DELETE = 65536, WriteMask = 65606 } private ShareAccessPermission shareAccess = ShareAccessPermission.Undetermined; public string Server { get; private set; } public string Name { get; private set; } public string UNCPath { get { return string.Format(CultureInfo.InvariantCulture, "\\\\{0}\\{1}", new object[] { this.Server, this.Name }); } } public string LocalPath { get; private set; } public ShareType Type { get { return ShareType.Generic; } } public FolderView DefaultView { get { return FolderView.Details; } } internal SBSCAPIShareInfo(string server, string name, string localPath) { this.Server = server; this.Name = name; this.LocalPath = localPath; } public long GetFreeSpace() { return -1L; } public ShareAccessPermission GetAccessPermission() { if (this.shareAccess == ShareAccessPermission.Undetermined) { this.shareAccess = this.GetAccessPermissionInternal(); } return this.shareAccess; } public void Refresh() { this.shareAccess = ShareAccessPermission.Undetermined; } private ShareAccessPermission GetAccessPermissionInternal() { if (RemoteAccessUser.Current == null) { RemoteAccessLog.Warn("Storage", string.Format(CultureInfo.InvariantCulture, "User is not logged in - unable to determine share '{0}' access permission", new object[] { this.Name })); return ShareAccessPermission.Unknown; } RemoteAccessLog.Info("Storage", string.Format(CultureInfo.InvariantCulture, "Determining access to share '{0}'", new object[] { this.Name })); ClrUtils.ClearErrorInfo(); List list = new List(); List list2 = new List(); using (PrincipalContext principalContext = new PrincipalContext(ContextType.Domain)) { using (PrincipalContext principalContext2 = new PrincipalContext(ContextType.Domain, principalContext.ConnectedServer)) { using (UserPrincipal userPrincipal = UserPrincipal.FindByIdentity(principalContext2, IdentityType.SamAccountName, RemoteAccessUser.Current.IdentityName)) { string value = new SecurityIdentifier(WellKnownSidType.WorldSid, null).Translate(typeof(NTAccount)).Value; SharePermissions sharePermissions = new SharePermissions(this.LocalPath, this.Name); foreach (IdentityInfo current in sharePermissions.ListUsersAndGroupsHavingShareAccess()) { bool flag = false; if (current.IsUser) { flag = (string.Compare(current.CommonName, userPrincipal.Name, StringComparison.Ordinal) == 0); } else { flag = (string.Compare(current.CommonName, value, StringComparison.Ordinal) == 0); if (!flag) { using (GroupPrincipal groupPrincipal = GroupPrincipal.FindByIdentity(principalContext2, IdentityType.Name, current.CommonName)) { flag = groupPrincipal.GetMembers(true).Contains(userPrincipal); } } } if (flag) { PermissionInfo permissionInfo = current.IsUser ? sharePermissions.GetUserShareFullPermissions(current.CommonName) : sharePermissions.GetGroupShareFullPermissions(current.CommonName); RemoteAccessLog.Write("Storage", string.Format(CultureInfo.InvariantCulture, " DACL Rule for {0}: Type={1}, Access Mask={2}", new object[] { current.CommonName, permissionInfo.PermissionType, permissionInfo.Permission })); if (permissionInfo.PermissionType == AceType.AccessDenied) { list.Add(permissionInfo); } else { list2.Add(permissionInfo); } } } } } } ShareAccessPermission shareAccessPermission = ShareAccessPermission.ReadWrite; ShareAccessPermission val = ShareAccessPermission.None; foreach (PermissionInfo current2 in list) { if (shareAccessPermission == ShareAccessPermission.ReadWrite && (current2.Permission & SharePermissions.CHANGE_SHARE_ACCESS) != 0) { shareAccessPermission = ShareAccessPermission.ReadOnly; } if ((current2.Permission & SharePermissions.READ_SHARE_ACCESS) != 0) { shareAccessPermission = ShareAccessPermission.None; } } foreach (PermissionInfo current3 in list2) { if ((current3.Permission & SharePermissions.CHANGE_SHARE_ACCESS) != 0) { val = ShareAccessPermission.ReadWrite; break; } if ((current3.Permission & SharePermissions.READ_SHARE_ACCESS) != 0) { val = ShareAccessPermission.ReadOnly; } } ShareAccessPermission shareAccessPermission2 = (ShareAccessPermission)Math.Min((int)shareAccessPermission, (int)val); RemoteAccessLog.Info("Storage", string.Format(CultureInfo.InvariantCulture, "Access to share '{0}': {1}", new object[] { this.Name, shareAccessPermission2 })); return shareAccessPermission2; } } }