mirror of https://github.com/dahall/Vanara.git
297 lines
13 KiB
C#
297 lines
13 KiB
C#
using System.Diagnostics.CodeAnalysis;
|
|
using System.Security.AccessControl;
|
|
using System.Security.Principal;
|
|
using Vanara.PInvoke;
|
|
using static Vanara.PInvoke.AclUI;
|
|
using static Vanara.PInvoke.AdvApi32;
|
|
using static Vanara.PInvoke.Authz;
|
|
using static Vanara.PInvoke.Macros;
|
|
|
|
namespace Vanara.Security.AccessControl;
|
|
|
|
internal class SecurityEventArg : EventArgs
|
|
{
|
|
public SecurityEventArg(SafePSECURITY_DESCRIPTOR sd, SECURITY_INFORMATION parts)
|
|
{
|
|
Parts = parts;
|
|
SecurityDesciptor = sd;
|
|
}
|
|
|
|
public SECURITY_INFORMATION Parts { get; }
|
|
|
|
public SafePSECURITY_DESCRIPTOR SecurityDesciptor { get; }
|
|
}
|
|
|
|
/// <summary>Internal implementation of a number of COM interfaces needed for interaction with the Windows ACL Editor.</summary>
|
|
internal class SecurityInfoImpl : ISecurityInformation, ISecurityInformation3, ISecurityInformation4, ISecurityObjectTypeInfo, IEffectivePermission, IEffectivePermission2
|
|
{
|
|
internal SI_OBJECT_INFO objectInfo;
|
|
private SI_OBJECT_INFO_Flags currentElevation;
|
|
private readonly string fullObjectName;
|
|
[NotNull] private IAccessControlEditorDialogProvider? prov;
|
|
private SafeByteArray pSD = new(0);
|
|
|
|
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
|
public SecurityInfoImpl(SI_OBJECT_INFO_Flags flags, string objectName, string fullName, string? serverName = null, string? pageTitle = null)
|
|
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
|
|
{
|
|
objectInfo = new(flags, objectName, serverName ?? Environment.MachineName, pageTitle);
|
|
currentElevation = 0; // flags & (SI_OBJECT_INFO_Flags.OwnerElevationRequired | SI_OBJECT_INFO_Flags.AuditElevationRequired | SI_OBJECT_INFO_Flags.PermsElevationRequired);
|
|
fullObjectName = fullName;
|
|
}
|
|
|
|
public event EventHandler<SecurityEventArg>? OnSetSecurity;
|
|
|
|
public byte[] SecurityDescriptor
|
|
{
|
|
get => pSD.ToArray();
|
|
set => pSD = new SafeByteArray(value);
|
|
}
|
|
|
|
HRESULT IEffectivePermission.GetEffectivePermission(in Guid pguidObjectType, PSID pUserSid, string? pszServerName, PSECURITY_DESCRIPTOR pSecDesc,
|
|
out OBJECT_TYPE_LIST[]? ppObjectTypeList, out uint pcObjectTypeListLength, out ACCESS_MASK[]? ppGrantedAccessList, out uint pcGrantedAccessListLength)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"GetEffectivePermission: {pguidObjectType}, {pszServerName}");
|
|
if (pguidObjectType == Guid.Empty)
|
|
{
|
|
ppGrantedAccessList = prov.GetEffectivePermission(pUserSid, pszServerName, pSecDesc);
|
|
pcGrantedAccessListLength = (uint)ppGrantedAccessList.Length;
|
|
ppObjectTypeList = new[] { OBJECT_TYPE_LIST.Self };
|
|
pcObjectTypeListLength = (uint)ppObjectTypeList.Length;
|
|
}
|
|
else
|
|
{
|
|
var hr = prov.GetEffectivePermission(pguidObjectType, pUserSid, pszServerName, pSecDesc, out ppObjectTypeList, out ppGrantedAccessList);
|
|
pcGrantedAccessListLength = (uint)(ppGrantedAccessList?.Length ?? 0);
|
|
pcObjectTypeListLength = (uint)(ppObjectTypeList?.Length ?? 0);
|
|
if (hr.Failed) return hr;
|
|
}
|
|
return HRESULT.S_OK;
|
|
}
|
|
|
|
HRESULT ISecurityInformation.GetAccessRights(IntPtr guidObject, SI_OBJECT_INFO_Flags dwFlags, out SI_ACCESS[] access, ref uint accessCount, out uint defaultAccess)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"GetAccessRight: {guidObject}, {dwFlags}");
|
|
prov.GetAccessListInfo(dwFlags, out access, out defaultAccess);
|
|
accessCount = (uint)access.Length;
|
|
return HRESULT.S_OK;
|
|
}
|
|
|
|
HRESULT ISecurityInformation.GetInheritTypes(out SI_INHERIT_TYPE[] inheritTypes, out uint inheritTypesCount)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine("GetInheritTypes");
|
|
inheritTypes = prov.GetInheritTypes();
|
|
inheritTypesCount = (uint)inheritTypes.Length;
|
|
return HRESULT.S_OK;
|
|
}
|
|
|
|
HRESULT ISecurityInformation.GetObjectInformation(ref SI_OBJECT_INFO objInfo)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"GetObjectInformation: {objInfo.dwFlags} {currentElevation}");
|
|
objInfo = objectInfo;
|
|
objInfo.dwFlags &= ~currentElevation;
|
|
return HRESULT.S_OK;
|
|
}
|
|
|
|
HRESULT ISecurityInformation.GetSecurity(SECURITY_INFORMATION requestInformation, out PSECURITY_DESCRIPTOR ppSecurityDescriptor, bool fDefault)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"GetSecurity: {requestInformation}{(fDefault ? " (Def)" : "")}");
|
|
var sd = new PSECURITY_DESCRIPTOR(fDefault ? prov.GetDefaultSecurity() : (IntPtr)pSD);
|
|
var ret = sd.GetPrivateObjectSecurity(requestInformation);
|
|
System.Diagnostics.Debug.WriteLine(
|
|
$"GetSecurity={ret.ToSddl(requestInformation) ?? "null"} <- {sd.ToSddl(requestInformation) ?? "null"}");
|
|
ppSecurityDescriptor = ret.DangerousGetHandle();
|
|
ret.SetHandleAsInvalid();
|
|
return HRESULT.S_OK;
|
|
}
|
|
|
|
HRESULT ISecurityInformation.MapGeneric(IntPtr guidObjectType, ref AceFlags AceFlags, ref ACCESS_MASK Mask)
|
|
{
|
|
var stMask = Mask;
|
|
var gm = prov.GetGenericMapping(AceFlags);
|
|
MapGenericMask(ref Mask, gm);
|
|
//if (Mask != gm.GenericAll)
|
|
// Mask &= ~(uint)FileSystemRights.Synchronize;
|
|
System.Diagnostics.Debug.WriteLine($"MapGeneric: {guidObjectType}, {(AceFlags)AceFlags}, 0x{stMask:X}->0x{Mask:X}");
|
|
return HRESULT.S_OK;
|
|
}
|
|
|
|
HRESULT ISecurityInformation.PropertySheetPageCallback(HWND hwnd, PropertySheetCallbackMessage uMsg, SI_PAGE_TYPE uPage)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"PropertySheetPageCallback: {hwnd}, {uMsg}, {uPage}");
|
|
prov.PropertySheetPageCallback(hwnd, uMsg, uPage);
|
|
return HRESULT.S_OK;
|
|
}
|
|
|
|
HRESULT ISecurityInformation.SetSecurity(SECURITY_INFORMATION requestInformation, PSECURITY_DESCRIPTOR sd)
|
|
{
|
|
OnSetSecurity?.Invoke(this, new SecurityEventArg(new SafePSECURITY_DESCRIPTOR((IntPtr)sd, false), requestInformation));
|
|
return HRESULT.S_OK;
|
|
}
|
|
|
|
HRESULT ISecurityInformation3.GetFullResourceName(out string name)
|
|
{
|
|
name = fullObjectName;
|
|
return HRESULT.S_OK;
|
|
}
|
|
|
|
HRESULT ISecurityInformation3.OpenElevatedEditor(HWND hWnd, SI_PAGE_TYPE uPage)
|
|
{
|
|
var pgType = (SI_PAGE_TYPE)LOWORD((uint)uPage);
|
|
var pgActv = (SI_PAGE_ACTIVATED)HIWORD((uint)uPage);
|
|
System.Diagnostics.Debug.WriteLine($"OpenElevatedEditor: {pgType} - {pgActv}");
|
|
var lastElev = currentElevation;
|
|
switch (pgActv)
|
|
{
|
|
case SI_PAGE_ACTIVATED.SI_SHOW_DEFAULT:
|
|
currentElevation |= SI_OBJECT_INFO_Flags.SI_PERMS_ELEVATION_REQUIRED | SI_OBJECT_INFO_Flags.SI_VIEW_ONLY;
|
|
break;
|
|
|
|
case SI_PAGE_ACTIVATED.SI_SHOW_PERM_ACTIVATED:
|
|
currentElevation |= SI_OBJECT_INFO_Flags.SI_PERMS_ELEVATION_REQUIRED | SI_OBJECT_INFO_Flags.SI_VIEW_ONLY;
|
|
pgType = SI_PAGE_TYPE.SI_PAGE_ADVPERM;
|
|
break;
|
|
|
|
case SI_PAGE_ACTIVATED.SI_SHOW_AUDIT_ACTIVATED:
|
|
currentElevation |= SI_OBJECT_INFO_Flags.SI_AUDITS_ELEVATION_REQUIRED;
|
|
pgType = SI_PAGE_TYPE.SI_PAGE_AUDIT;
|
|
break;
|
|
|
|
case SI_PAGE_ACTIVATED.SI_SHOW_OWNER_ACTIVATED:
|
|
currentElevation |= SI_OBJECT_INFO_Flags.SI_OWNER_ELEVATION_REQUIRED;
|
|
pgType = SI_PAGE_TYPE.SI_PAGE_OWNER;
|
|
break;
|
|
|
|
case SI_PAGE_ACTIVATED.SI_SHOW_EFFECTIVE_ACTIVATED:
|
|
break;
|
|
|
|
case SI_PAGE_ACTIVATED.SI_SHOW_SHARE_ACTIVATED:
|
|
break;
|
|
|
|
case SI_PAGE_ACTIVATED.SI_SHOW_CENTRAL_POLICY_ACTIVATED:
|
|
break;
|
|
}
|
|
ShowDialog(hWnd, pgType, pgActv);
|
|
currentElevation = lastElev;
|
|
return HRESULT.S_OK;
|
|
}
|
|
|
|
public HRESULT GetSecondarySecurity(out SECURITY_OBJECT[] securityObjects, out uint securityObjectCount)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine("GetSecondarySecurity:");
|
|
securityObjects = new SECURITY_OBJECT[0];
|
|
securityObjectCount = 0;
|
|
return HRESULT.S_OK;
|
|
}
|
|
|
|
HRESULT ISecurityObjectTypeInfo.GetInheritSource(int si, PACL pAcl, out INHERITED_FROM[] ppInheritArray)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"GetInheritSource: {(SECURITY_INFORMATION)si}");
|
|
ppInheritArray = prov.GetInheritSource(fullObjectName, objectInfo.pszServerName, objectInfo.IsContainer, (uint)si, pAcl);
|
|
return HRESULT.S_OK;
|
|
}
|
|
|
|
public void SetProvider(IAccessControlEditorDialogProvider provider) => prov = provider;
|
|
|
|
public RawSecurityDescriptor? ShowDialog(HWND hWnd, SI_PAGE_TYPE pageType = SI_PAGE_TYPE.SI_PAGE_PERM, SI_PAGE_ACTIVATED pageAct = SI_PAGE_ACTIVATED.SI_SHOW_DEFAULT)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"ShowDialog: {pageType} {pageAct}");
|
|
SecurityEventArg? sd = null;
|
|
void fn(object? o, SecurityEventArg e) => sd = e;
|
|
try
|
|
{
|
|
OnSetSecurity += fn;
|
|
if (Environment.OSVersion.Version.Major == 5 || pageType == SI_PAGE_TYPE.SI_PAGE_PERM && pageAct == SI_PAGE_ACTIVATED.SI_SHOW_DEFAULT)
|
|
{
|
|
#pragma warning disable IL2050 // Correctness of COM interop cannot be guaranteed after trimming. Interfaces and interface members might be removed.
|
|
Win32Error.ThrowLastErrorIfFalse(EditSecurity(hWnd, this));
|
|
#pragma warning restore IL2050 // Correctness of COM interop cannot be guaranteed after trimming. Interfaces and interface members might be removed.
|
|
}
|
|
else
|
|
{
|
|
EditSecurityAdvanced(hWnd, this, pageType, pageAct).ThrowIfFailed();
|
|
}
|
|
if (sd != null)
|
|
{
|
|
var sddl = sd.SecurityDesciptor.ToSddl(sd.Parts);
|
|
if (!string.IsNullOrEmpty(sddl))
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"ShowDialog: Return: {sddl}");
|
|
return new RawSecurityDescriptor(sddl);
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
OnSetSecurity -= fn;
|
|
}
|
|
System.Diagnostics.Debug.WriteLine("ShowDialog: Return: null");
|
|
return null;
|
|
}
|
|
|
|
public HRESULT ComputeEffectivePermissionWithSecondarySecurity(PSID pSid, PSID pDeviceSid, string? pszServerName, SECURITY_OBJECT[] pSecurityObjects,
|
|
uint dwSecurityObjectCount, in TOKEN_GROUPS pUserGroups, AUTHZ_SID_OPERATION[]? pAuthzUserGroupsOperations, in TOKEN_GROUPS pDeviceGroups,
|
|
AUTHZ_SID_OPERATION[]? pAuthzDeviceGroupsOperations, in AUTHZ_SECURITY_ATTRIBUTES_INFORMATION pAuthzUserClaims,
|
|
AUTHZ_SECURITY_ATTRIBUTE_OPERATION[]? pAuthzUserClaimsOperations, in AUTHZ_SECURITY_ATTRIBUTES_INFORMATION pAuthzDeviceClaims,
|
|
AUTHZ_SECURITY_ATTRIBUTE_OPERATION[]? pAuthzDeviceClaimsOperations, EFFPERM_RESULT_LIST[] pEffpermResultLists)
|
|
{
|
|
System.Diagnostics.Debug.WriteLine($"ComputeEffectivePermissionWithSecondarySecurity({dwSecurityObjectCount}):{new SecurityIdentifier((IntPtr)pSid).Value};{new SecurityIdentifier((IntPtr)pDeviceSid).Value}");
|
|
if (dwSecurityObjectCount != 1)
|
|
return HRESULT.E_FAIL;
|
|
if (pSecurityObjects[0].Id != (uint)SECURITY_OBJECT_ID.SECURITY_OBJECT_ID_OBJECT_SD)
|
|
return HRESULT.E_FAIL;
|
|
if (pSid.IsNull)
|
|
return HRESULT.E_INVALIDARG;
|
|
|
|
if (!AuthzInitializeResourceManager(AuthzResourceManagerFlags.AUTHZ_RM_FLAG_NO_AUDIT, null, null, null, prov.ToString(), out var hAuthzResourceManager))
|
|
return HRESULT.S_OK;
|
|
|
|
var identifier = new LUID();
|
|
SafeAUTHZ_CLIENT_CONTEXT_HANDLE? hAuthzCompoundContext = null;
|
|
if (!AuthzInitializeContextFromSid(AuthzContextFlags.DEFAULT, pSid, hAuthzResourceManager, IntPtr.Zero, identifier, IntPtr.Zero, out var hAuthzUserContext))
|
|
return HRESULT.S_OK;
|
|
|
|
pAuthzDeviceGroupsOperations ??= new AUTHZ_SID_OPERATION[0];
|
|
pAuthzUserClaimsOperations ??= new AUTHZ_SECURITY_ATTRIBUTE_OPERATION[0];
|
|
pAuthzDeviceClaimsOperations ??= new AUTHZ_SECURITY_ATTRIBUTE_OPERATION[0];
|
|
if (!pDeviceSid.IsNull)
|
|
{
|
|
if (AuthzInitializeContextFromSid(AuthzContextFlags.DEFAULT, pDeviceSid, hAuthzResourceManager, IntPtr.Zero, identifier, IntPtr.Zero, out var hAuthzDeviceContext))
|
|
if (AuthzInitializeCompoundContext(hAuthzUserContext, hAuthzDeviceContext, out hAuthzCompoundContext))
|
|
if (pAuthzDeviceClaims.Version != 0)
|
|
AuthzModifyClaims(hAuthzCompoundContext, AUTHZ_CONTEXT_INFORMATION_CLASS.AuthzContextInfoDeviceClaims, pAuthzDeviceClaimsOperations, pAuthzDeviceClaims);
|
|
}
|
|
else
|
|
{
|
|
hAuthzCompoundContext = hAuthzUserContext;
|
|
}
|
|
|
|
if (hAuthzCompoundContext is null)
|
|
return HRESULT.S_OK;
|
|
|
|
if (pAuthzUserClaims.Version != 0)
|
|
if (!AuthzModifyClaims(hAuthzCompoundContext, AUTHZ_CONTEXT_INFORMATION_CLASS.AuthzContextInfoUserClaims, pAuthzUserClaimsOperations, pAuthzUserClaims))
|
|
return HRESULT.S_OK;
|
|
|
|
if (pDeviceGroups.GroupCount != 0)
|
|
if (!AuthzModifySids(hAuthzCompoundContext, AUTHZ_CONTEXT_INFORMATION_CLASS.AuthzContextInfoDeviceSids, pAuthzDeviceGroupsOperations, pDeviceGroups))
|
|
return HRESULT.S_OK;
|
|
|
|
if (pUserGroups.GroupCount != 0 && pAuthzUserGroupsOperations != null)
|
|
if (!AuthzModifySids(hAuthzCompoundContext, AUTHZ_CONTEXT_INFORMATION_CLASS.AuthzContextInfoGroupsSids, pAuthzUserGroupsOperations, pUserGroups))
|
|
return HRESULT.S_OK;
|
|
|
|
var request = new AUTHZ_ACCESS_REQUEST((uint)ACCESS_MASK.MAXIMUM_ALLOWED);
|
|
var sd = new SafePSECURITY_DESCRIPTOR(pSecurityObjects[0].pData, false);
|
|
var reply = new AUTHZ_ACCESS_REPLY(1);
|
|
if (!AuthzAccessCheck(AuthzAccessCheckFlags.NONE, hAuthzCompoundContext, request, default, sd, null, 0, reply, out _))
|
|
return HRESULT.S_OK;
|
|
|
|
pEffpermResultLists[0].fEvaluated = true;
|
|
pEffpermResultLists[0].pGrantedAccessList = reply.GrantedAccessMaskValues;
|
|
pEffpermResultLists[0].pObjectTypeList = new[] { OBJECT_TYPE_LIST.Self };
|
|
pEffpermResultLists[0].cObjectTypeListLength = 1;
|
|
|
|
return HRESULT.S_OK;
|
|
}
|
|
} |