Vanara/Windows.Forms/Dialogs/AccessControlEditor/SecuredObject.cs

377 lines
13 KiB
C#
Raw Blame History

This file contains invisible Unicode characters!

This file contains invisible Unicode characters that may be processed differently from what appears below. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to reveal hidden characters.

using System.IO;
using System.Reflection;
using System.Security.AccessControl;
using Vanara.Extensions.Reflection;
namespace Vanara.Security.AccessControl;
/* Derivatives of NativeObjectSecurity: (default is Group|Owner|Access)
Security Object.GetSecurityControl() V Sec Nm
=========== =========================== == === ===
System.IO.Pipes.PipeSecurity System.IO.Pipes.PipeStream 3.5 N
EventWaitHandleSecurity System.Threading.EventWaitHandle 2 N
FileSystemSecurity System.IO.DirectoryInfo, FileInfo 2 Y Name
FileSystemSecurity System.IO.FileStream 2 N Name
MutexSecurity System.Threading.Mutex 2 N
ObjectSecurity<T> 4 N
RegistrySecurity System.Win32.RegistryKey 2 Y Name
SemaphoreSecurity System.Threading.Semaphore 2 N
System.IO.MemoryMappedFiles.MemoryMappedFileSecurity System.IO.MemoryMappedFiles.MemoryMappedFile 4 N
*/
internal class SecuredObject
{
private static readonly string[] nonContainerTypes = new string[] { "FileSecurity", "PipeSecurity", "CryptoKeySecurity", "MemoryMappedFileSecurity", "TaskSecurity" };
public SecuredObject(CommonObjectSecurity security, string objName, string displayName)
{
ObjectSecurity = security;
ObjectName = objName;
DisplayName = displayName;
IsContainer = IsContainerObject(ObjectSecurity);
MandatoryLabel = new SystemMandatoryLabel(ObjectSecurity);
}
/// <summary>Initializes with the specified known object.</summary>
/// <param name="knownObject">The known object. See Remarks section for acceptable object types.</param>
/// <remarks>
/// Known objects can include:
/// <list type="bullet">
/// <item><description>System.IO.Pipes.PipeStream</description></item>
/// <item><description><see cref="System.Threading.EventWaitHandle"/></description></item>
/// <item><description><see cref="System.IO.DirectoryInfo"/></description></item>
/// <item><description><see cref="System.IO.FileInfo"/></description></item>
/// <item><description><see cref="System.IO.FileStream"/></description></item>
/// <item><description><see cref="System.Threading.Mutex"/></description></item>
/// <item><description>System.Win32.RegistryKey</description></item>
/// <item><description><see cref="System.Threading.Semaphore"/></description></item>
/// <item><description>System.IO.MemoryMappedFiles.MemoryMappedFile</description></item>
/// <item><description><see cref="CommonObjectSecurity"/> or derived class. <c>Note:</c> When using this option, be sure to
/// set the <see cref="IsContainer"/>, <see cref="ResourceType"/>, <see cref="ObjectName"/>, and <see cref="TargetServer"/> properties.</description></item>
/// <item><description>Any object that supports the following methods and properties:
/// <list type="bullet"><item><description><code>GetAccessControl()</code> or <code>GetAccessControl(AccessControlSections)</code> method</description></item>
/// <item><description><code>SetAccessControl(CommonObjectSecurity)</code> method</description></item>
/// <item><description><code>Name</code> or <code>FullName</code> property</description></item>
/// </list>
/// </description></item>
/// </list>
/// </remarks>
public SecuredObject(object knownObject)
{
const AccessControlSections minACS = AccessControlSections.Access | AccessControlSections.Group | AccessControlSections.Owner;
// Special handling for files and directories
if (knownObject is FileSystemInfo fsi)
{
IsContainer = knownObject is DirectoryInfo;
TargetServer = null;
try
{
ObjectSecurity = IsContainer ? new DirectorySecurity(fsi.FullName, AccessControlSections.All) : new FileSecurity(fsi.FullName, AccessControlSections.All);
}
catch (PrivilegeNotHeldException)
{
ObjectSecurity = IsContainer ? new DirectorySecurity(fsi.FullName, minACS) : new FileSecurity(fsi.FullName, minACS);
}
DisplayName = fsi.Name;
ObjectName = fsi.FullName;
BaseObject = ObjectSecurity;
goto FinalInit;
}
// Special handling for Tasks
else if (knownObject.GetType().FullName is "Microsoft.Win32.TaskScheduler.Task" or "Microsoft.Win32.TaskScheduler.TaskFolder")
{
IsContainer = knownObject.GetType().Name == "TaskFolder";
TargetServer = knownObject.GetPropertyValue<object>("TaskService")?.GetPropertyValue<string>("TargetServer");
}
// Get the security object using the standard "GetAccessControl" method
CommonObjectSecurity? objectSecurity = null;
if (objectSecurity == null)
{
try
{
objectSecurity = knownObject.InvokeMethod<CommonObjectSecurity>("GetAccessControl", AccessControlSections.All);
}
catch (TargetInvocationException)
{
try
{
objectSecurity = knownObject.InvokeMethod<CommonObjectSecurity>("GetAccessControl", minACS);
}
catch { }
}
catch { }
}
if (objectSecurity == null)
{
try
{
objectSecurity = knownObject.InvokeMethod<CommonObjectSecurity>("GetAccessControl");
}
catch { }
}
ObjectSecurity = objectSecurity ?? throw new ArgumentException("Object must be valid and have a GetAccessControl member."); ;
// Get the object names
switch (knownObject.GetType().Name)
{
case "RegistryKey":
ObjectName = knownObject.GetPropertyValue<string>("Name") ?? throw new ArgumentException("Unable to retrieve an object name.", nameof(knownObject));
DisplayName = System.IO.Path.GetFileNameWithoutExtension(ObjectName);
break;
case "Task":
DisplayName = knownObject.GetPropertyValue<string>("Name") ?? throw new ArgumentException("Unable to retrieve an object name.", nameof(knownObject));
ObjectName = knownObject.GetPropertyValue<string>("Path") ?? throw new ArgumentException("Unable to retrieve an object path.", nameof(knownObject));
break;
default:
DisplayName = knownObject.GetPropertyValue<string>("Name") ?? throw new ArgumentException("Unable to retrieve an object name.", nameof(knownObject));
ObjectName = knownObject.GetPropertyValue<string>("FullName") ?? DisplayName ?? throw new ArgumentException("Unable to retrieve an object name or full name.", nameof(knownObject));
break;
}
// Set the base object
BaseObject = knownObject;
IsContainer = IsContainerObject(ObjectSecurity);
FinalInit:
MandatoryLabel = new SystemMandatoryLabel(ObjectSecurity);
}
public enum SystemMandatoryLabelLevel
{
None = 0,
Low = 0x1000,
Medium = 0x2000,
High = 0x3000
}
[Flags]
public enum SystemMandatoryLabelPolicy
{
None = 0,
NoWriteUp = 1,
NoReadUp = 2,
NoExecuteUp = 4
}
public object? BaseObject { get; }
public string DisplayName { get; set; }
public bool IsContainer { get; set; }
public SystemMandatoryLabel MandatoryLabel { get; }
public string ObjectName { get; set; }
public CommonObjectSecurity ObjectSecurity { get; }
public ResourceType ResourceType => GetResourceType(ObjectSecurity);
public string? TargetServer { get; set; }
public static object GetAccessMask(CommonObjectSecurity acl, AuthorizationRule rule)
{
if (rule.GetType() == acl.AccessRuleType || rule.GetType() == acl.AuditRuleType)
{
var accRightType = acl.AccessRightType;
foreach (var pi in rule.GetType().GetProperties())
if (pi.PropertyType == accRightType)
return Enum.ToObject(accRightType, pi.GetValue(rule, null) ?? 0);
}
throw new ArgumentException();
}
public static object GetKnownObject(ResourceType resType, string objName, string? serverName)
{
object? obj = null;
switch (resType)
{
case ResourceType.FileObject:
if (!string.IsNullOrEmpty(serverName))
objName = System.IO.Path.Combine(serverName, objName);
if (System.IO.File.Exists(objName))
obj = new System.IO.FileInfo(objName);
else if (System.IO.Directory.Exists(objName))
obj = new System.IO.DirectoryInfo(objName);
break;
case ResourceType.RegistryKey:
obj = GetKeyFromKeyName(objName, serverName);
break;
case Windows.Forms.AccessControlEditorDialog.taskResourceType:
obj = GetTaskObj(objName, serverName);
break;
}
if (obj == null)
throw new ArgumentException("Unable to create an object from supplied arguments.");
return obj;
}
public static ResourceType GetResourceType(CommonObjectSecurity sec) => sec.GetType().Name switch
{
"FileSecurity" or "DirectorySecurity" or "CryptoKeySecurity" => ResourceType.FileObject,
"PipeSecurity" or "EventWaitHandleSecurity" or "MutexSecurity" or "MemoryMappedFileSecurity" or "SemaphoreSecurity" => ResourceType.KernelObject,
"RegistrySecurity" => ResourceType.RegistryKey,
"TaskSecurity" => Windows.Forms.AccessControlEditorDialog.taskResourceType,
_ => ResourceType.Unknown,
};
public static bool IsContainerObject(CommonObjectSecurity sec)
{
var secTypeName = sec.GetType().Name;
return !Array.Exists(nonContainerTypes, s => secTypeName == s);
}
public object GetAccessMask(AuthorizationRule rule) => GetAccessMask(ObjectSecurity, rule);
public void Persist(object? newBase = null)
{
var obj = (newBase ?? BaseObject) ?? throw new ArgumentNullException(nameof(newBase), @"Either newBase or BaseObject must not be null.");
var mi = obj.GetType().GetMethod("SetAccessControl", BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null) ?? throw new InvalidOperationException("Either newBase or BaseObject must represent a securable object.");
mi.Invoke(obj, new object[] { ObjectSecurity });
}
private static Microsoft.Win32.RegistryKey? GetKeyFromKeyName(string? keyName, string? serverName)
{
if (keyName == null)
return null;
var index = keyName.IndexOf('\\');
var str = index != -1 ? keyName.Substring(0, index).ToUpper(System.Globalization.CultureInfo.InvariantCulture) : keyName.ToUpper(System.Globalization.CultureInfo.InvariantCulture);
Microsoft.Win32.RegistryHive hive;
switch (str)
{
case "HKEY_CURRENT_USER":
hive = Microsoft.Win32.RegistryHive.CurrentUser;
break;
case "HKEY_LOCAL_MACHINE":
hive = Microsoft.Win32.RegistryHive.LocalMachine;
break;
case "HKEY_CLASSES_ROOT":
hive = Microsoft.Win32.RegistryHive.ClassesRoot;
break;
case "HKEY_USERS":
hive = Microsoft.Win32.RegistryHive.Users;
break;
case "HKEY_PERFORMANCE_DATA":
hive = Microsoft.Win32.RegistryHive.PerformanceData;
break;
case "HKEY_CURRENT_CONFIG":
hive = Microsoft.Win32.RegistryHive.CurrentConfig;
break;
default:
return null;
}
serverName ??= string.Empty;
var retVal = Microsoft.Win32.RegistryKey.OpenRemoteBaseKey(hive, serverName);
if (index == -1 || index == keyName.Length)
return retVal;
var subKeyName = keyName.Substring(index + 1, keyName.Length - index - 1);
return retVal.OpenSubKey(subKeyName);
}
private static object? GetTaskObj(string objName, string? serverName)
{
try
{
var tsType = Extensions.ReflectionExtensions.LoadType("Microsoft.Win32.TaskScheduler.TaskService", "Microsoft.Win32.TaskScheduler.dll") ??
Extensions.ReflectionExtensions.LoadType("Microsoft.Win32.TaskScheduler.TaskService", "Microsoft.Win32.TaskScheduler-Merged.dll");
if (tsType != null)
{
var ts = Activator.CreateInstance(tsType, serverName, null, null, null, false);
if (ts != null)
{
try
{
var r = ts.InvokeMethod<object>("GetFolder", objName);
if (r != null)
return r;
}
catch { }
try
{
var r = ts.InvokeMethod<object>("GetTask", objName);
if (r != null)
return r;
}
catch { }
try
{
var r = ts.InvokeMethod<object>("FindTask", objName, true);
if (r != null)
return r;
}
catch { }
}
}
}
catch { }
return null;
}
public class SystemMandatoryLabel
{
public SystemMandatoryLabel(CommonObjectSecurity sec)
{
Policy = SystemMandatoryLabelPolicy.None;
Level = SystemMandatoryLabelLevel.None;
try
{
var sd = new RawSecurityDescriptor(sec.GetSecurityDescriptorBinaryForm(), 0);
if (sd.SystemAcl != null)
{
foreach (var ace in sd.SystemAcl)
{
if ((int)ace.AceType == 0x11)
{
var aceBytes = new byte[ace.BinaryLength];
ace.GetBinaryForm(aceBytes, 0);
//_policy = new IntegrityPolicy(aceBytes, 4);
//_level = new IntegrityLevel(aceBytes, 8);
}
}
}
}
catch { }
/*byte[] saclBinaryForm = new byte[sd.SystemAcl.BinaryLength];
sd.SystemAcl.GetBinaryForm(saclBinaryForm, 0);
GenericAce ace = null;
if (null != saclBinaryForm)
{
RawAcl aclRaw = new RawAcl(saclBinaryForm, 0);
if (0 >= aclRaw.Count) throw new ArgumentException("No ACEs in ACL", "saclBinaryForm");
ace = aclRaw[0];
if (Win32.SYSTEM_MANDATORY_LABEL_ACE_TYPE != (int)ace.AceType)
throw new ArgumentException("No Mandatory Integrity Label in ACL", "saclBinaryForm");
byte[] aceBytes = new byte[ace.BinaryLength];
ace.GetBinaryForm(aceBytes, 0);
_policy = new IntegrityPolicy(aceBytes, 4);
_level = new IntegrityLevel(aceBytes, 8);
return;
}
throw new ArgumentNullException("saclBinaryForm");*/
}
public bool IsSet => Policy != SystemMandatoryLabelPolicy.None && Level != SystemMandatoryLabelLevel.None;
public SystemMandatoryLabelLevel Level { get; }
public SystemMandatoryLabelPolicy Policy { get; }
}
}