mirror of https://github.com/raandree/NTFSSecurity
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
322 lines
11 KiB
322 lines
11 KiB
using Alphaleonis.Win32.Filesystem;
|
|
using Microsoft.Win32.SafeHandles;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Runtime.InteropServices;
|
|
using System.Security.AccessControl;
|
|
|
|
namespace Security2
|
|
{
|
|
internal partial class Win32
|
|
{
|
|
internal const string AUTHZ_OBJECTUUID_WITHCAP = "9a81c2bd-a525-471d-a4ed-49907c0b23da";
|
|
internal const string RCP_OVER_TCP_PROTOCOL = "ncacn_ip_tcp";
|
|
|
|
IntPtr userClientCtxt = IntPtr.Zero;
|
|
SafeAuthzRMHandle authzRM;
|
|
IntPtr pGrantedAccess = IntPtr.Zero;
|
|
IntPtr pErrorSecObj = IntPtr.Zero;
|
|
|
|
#region GetInheritedFrom
|
|
public static List<string> GetInheritedFrom(FileSystemInfo item, ObjectSecurity sd)
|
|
{
|
|
var inheritedFrom = new List<string>();
|
|
|
|
var sdBytes = sd.GetSecurityDescriptorBinaryForm();
|
|
byte[] aclBytes = null;
|
|
var rawSd = new RawSecurityDescriptor(sdBytes, 0);
|
|
|
|
var aceCount = 0;
|
|
|
|
if (rawSd.SystemAcl != null)
|
|
{
|
|
aceCount = rawSd.SystemAcl.Count;
|
|
aclBytes = new byte[rawSd.SystemAcl.BinaryLength];
|
|
rawSd.SystemAcl.GetBinaryForm(aclBytes, 0);
|
|
|
|
try
|
|
{
|
|
inheritedFrom = GetInheritedFrom(item.FullName,
|
|
aclBytes,
|
|
aceCount,
|
|
item is DirectoryInfo ? true : false,
|
|
SECURITY_INFORMATION.SACL_SECURITY_INFORMATION);
|
|
}
|
|
catch
|
|
{
|
|
inheritedFrom = new List<string>();
|
|
for (int i = 0; i < aceCount; i++)
|
|
{
|
|
inheritedFrom.Add("unknown parent");
|
|
}
|
|
}
|
|
}
|
|
else if (rawSd.DiscretionaryAcl != null)
|
|
{
|
|
aceCount = rawSd.DiscretionaryAcl.Count;
|
|
aclBytes = new byte[rawSd.DiscretionaryAcl.BinaryLength];
|
|
rawSd.DiscretionaryAcl.GetBinaryForm(aclBytes, 0);
|
|
|
|
try
|
|
{
|
|
inheritedFrom = GetInheritedFrom(item.FullName,
|
|
aclBytes,
|
|
aceCount,
|
|
item is DirectoryInfo ? true : false,
|
|
SECURITY_INFORMATION.DACL_SECURITY_INFORMATION);
|
|
}
|
|
catch
|
|
{
|
|
inheritedFrom = new List<string>();
|
|
for (int i = 0; i < aceCount; i++)
|
|
{
|
|
inheritedFrom.Add("unknown parent");
|
|
}
|
|
}
|
|
}
|
|
|
|
return inheritedFrom;
|
|
}
|
|
public static List<string> GetInheritedFrom(string path, byte[] aclBytes, int aceCount, bool isContainer, SECURITY_INFORMATION aclType)
|
|
{
|
|
var inheritedFrom = new List<string>();
|
|
path = Path.GetLongPath(path);
|
|
|
|
uint returnValue = 0;
|
|
GENERIC_MAPPING genericMap = new GENERIC_MAPPING();
|
|
genericMap.GenericRead = (uint)MappedGenericRights.FILE_GENERIC_READ;
|
|
genericMap.GenericWrite = (uint)MappedGenericRights.FILE_GENERIC_WRITE;
|
|
genericMap.GenericExecute = (uint)MappedGenericRights.FILE_GENERIC_EXECUTE;
|
|
genericMap.GenericAll = (uint)MappedGenericRights.FILE_GENERIC_ALL;
|
|
|
|
var pInheritInfo = Marshal.AllocHGlobal(aceCount * Marshal.SizeOf(typeof(PINHERITED_FROM)));
|
|
|
|
returnValue = GetInheritanceSource(
|
|
path,
|
|
ResourceType.FileObject,
|
|
aclType,
|
|
isContainer,
|
|
IntPtr.Zero,
|
|
0,
|
|
aclBytes,
|
|
IntPtr.Zero,
|
|
ref genericMap,
|
|
pInheritInfo
|
|
);
|
|
|
|
if (returnValue != 0)
|
|
{
|
|
throw new System.ComponentModel.Win32Exception((int)returnValue);
|
|
}
|
|
|
|
for (int i = 0; i < aceCount; i++)
|
|
{
|
|
var inheritInfo = pInheritInfo.ElementAt<PINHERITED_FROM>(i);
|
|
|
|
inheritedFrom.Add(
|
|
!string.IsNullOrEmpty(inheritInfo.AncestorName) && inheritInfo.AncestorName.StartsWith(@"\\?\") ? inheritInfo.AncestorName.Substring(4) : inheritInfo.AncestorName
|
|
);
|
|
}
|
|
|
|
FreeInheritedFromArray(pInheritInfo, (ushort)aceCount, IntPtr.Zero);
|
|
Marshal.FreeHGlobal(pInheritInfo);
|
|
|
|
return inheritedFrom;
|
|
}
|
|
#endregion GetInheritedFrom
|
|
|
|
public int GetEffectiveAccess(ObjectSecurity sd, IdentityReference2 identity, string serverName, out bool remoteServerAvailable, out Exception authzException)
|
|
{
|
|
int effectiveAccess = 0;
|
|
remoteServerAvailable = false;
|
|
authzException = null;
|
|
|
|
try
|
|
{
|
|
GetEffectivePermissions_AuthzInitializeResourceManager(serverName, out remoteServerAvailable);
|
|
|
|
try
|
|
{
|
|
GetEffectivePermissions_AuthzInitializeContextFromSid(identity);
|
|
effectiveAccess = GetEffectivePermissions_AuthzAccessCheck(sd);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
authzException = ex;
|
|
}
|
|
}
|
|
catch
|
|
{ }
|
|
finally
|
|
{
|
|
GetEffectivePermissions_FreeResouces();
|
|
}
|
|
|
|
return effectiveAccess;
|
|
}
|
|
|
|
#region Win32 Wrapper
|
|
private void GetEffectivePermissions_AuthzInitializeResourceManager(string serverName, out bool remoteServerAvailable)
|
|
{
|
|
remoteServerAvailable = false;
|
|
|
|
var rpcInitInfo = new AUTHZ_RPC_INIT_INFO_CLIENT();
|
|
|
|
rpcInitInfo.version = AuthzRpcClientVersion.V1;
|
|
rpcInitInfo.objectUuid = AUTHZ_OBJECTUUID_WITHCAP;
|
|
rpcInitInfo.protocol = RCP_OVER_TCP_PROTOCOL;
|
|
rpcInitInfo.server = serverName;
|
|
|
|
SafeHGlobalHandle pRpcInitInfo = SafeHGlobalHandle.AllocHGlobalStruct(rpcInitInfo);
|
|
if (!AuthzInitializeRemoteResourceManager(pRpcInitInfo.ToIntPtr(), out authzRM))
|
|
{
|
|
int error = Marshal.GetLastWin32Error();
|
|
|
|
if (error != Win32Error.EPT_S_NOT_REGISTERED) //if not RPC server unavailable
|
|
{
|
|
throw new Win32Exception(error);
|
|
}
|
|
|
|
if (serverName == "localhost")
|
|
{
|
|
remoteServerAvailable = true;
|
|
}
|
|
|
|
//
|
|
// As a fallback we do AuthzInitializeResourceManager. But the results can be inaccurate.
|
|
//
|
|
if (!AuthzInitializeResourceManager(
|
|
AuthzResourceManagerFlags.NO_AUDIT,
|
|
IntPtr.Zero,
|
|
IntPtr.Zero,
|
|
IntPtr.Zero,
|
|
"EffectiveAccessCheck",
|
|
out authzRM))
|
|
{
|
|
throw new Win32Exception(Marshal.GetLastWin32Error());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
remoteServerAvailable = true;
|
|
}
|
|
}
|
|
|
|
private void GetEffectivePermissions_AuthzInitializeContextFromSid(IdentityReference2 id)
|
|
{
|
|
var rawSid = id.GetBinaryForm();
|
|
|
|
//
|
|
// Create an AuthZ context based on the user account
|
|
//
|
|
if (!AuthzInitializeContextFromSid(
|
|
AuthzInitFlags.Default,
|
|
rawSid,
|
|
authzRM,
|
|
IntPtr.Zero,
|
|
Win32.LUID.NullLuid,
|
|
IntPtr.Zero,
|
|
out userClientCtxt))
|
|
{
|
|
Win32Exception win32Expn = new Win32Exception(Marshal.GetLastWin32Error());
|
|
|
|
if (win32Expn.NativeErrorCode != Win32Error.RPC_S_SERVER_UNAVAILABLE)
|
|
{
|
|
throw win32Expn;
|
|
}
|
|
}
|
|
}
|
|
|
|
private int GetEffectivePermissions_AuthzAccessCheck(ObjectSecurity sd)
|
|
{
|
|
var request = new AUTHZ_ACCESS_REQUEST();
|
|
request.DesiredAccess = StdAccess.MAXIMUM_ALLOWED;
|
|
request.PrincipalSelfSid = null;
|
|
request.ObjectTypeList = IntPtr.Zero;
|
|
request.ObjectTypeListLength = 0;
|
|
request.OptionalArguments = IntPtr.Zero;
|
|
|
|
var reply = new AUTHZ_ACCESS_REPLY();
|
|
reply.ResultListLength = 1;
|
|
reply.SaclEvaluationResults = IntPtr.Zero;
|
|
reply.GrantedAccessMask = pGrantedAccess = Marshal.AllocHGlobal(sizeof(uint));
|
|
reply.Error = pErrorSecObj = Marshal.AllocHGlobal(sizeof(uint));
|
|
|
|
byte[] rawSD = sd.GetSecurityDescriptorBinaryForm();
|
|
|
|
if (!AuthzAccessCheck(
|
|
AuthzACFlags.None,
|
|
userClientCtxt,
|
|
ref request,
|
|
IntPtr.Zero,
|
|
rawSD,
|
|
null,
|
|
0,
|
|
ref reply,
|
|
IntPtr.Zero))
|
|
{
|
|
var error = Marshal.GetLastWin32Error();
|
|
if (error != 0)
|
|
{
|
|
throw new Win32Exception();
|
|
}
|
|
}
|
|
|
|
var grantedAccess = Marshal.ReadInt32(pGrantedAccess);
|
|
|
|
return grantedAccess;
|
|
}
|
|
|
|
private void GetEffectivePermissions_FreeResouces()
|
|
{
|
|
Marshal.FreeHGlobal(pGrantedAccess);
|
|
Marshal.FreeHGlobal(pErrorSecObj);
|
|
|
|
if (userClientCtxt != IntPtr.Zero)
|
|
{
|
|
AuthzFreeContext(userClientCtxt);
|
|
userClientCtxt = IntPtr.Zero;
|
|
}
|
|
}
|
|
|
|
static RawSecurityDescriptor GetRawSecurityDescriptor(SafeFileHandle handle, SecurityInformationClass infoClass)
|
|
{
|
|
return new RawSecurityDescriptor(GetByteSecurityDescriptor(handle, infoClass), 0);
|
|
}
|
|
|
|
public static byte[] GetByteSecurityDescriptor(SafeFileHandle handle, SecurityInformationClass infoClass)
|
|
{
|
|
var tempSD = IntPtr.Zero;
|
|
var buffer = new byte[0];
|
|
try
|
|
{
|
|
uint error = GetSecurityInfo(handle,
|
|
ObjectType.File,
|
|
infoClass,
|
|
IntPtr.Zero,
|
|
IntPtr.Zero,
|
|
IntPtr.Zero,
|
|
IntPtr.Zero,
|
|
out tempSD);
|
|
if (error != Win32Error.ERROR_SUCCESS)
|
|
{
|
|
throw new Win32Exception(Marshal.GetLastWin32Error());
|
|
}
|
|
|
|
UInt32 sdLength = GetSecurityDescriptorLength(tempSD);
|
|
|
|
buffer = new byte[sdLength];
|
|
Marshal.Copy(tempSD, buffer, 0, (int)sdLength);
|
|
}
|
|
finally
|
|
{
|
|
Marshal.FreeHGlobal(tempSD);
|
|
tempSD = IntPtr.Zero;
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
#endregion
|
|
}
|
|
}
|