Skip to content

Commit f0ae627

Browse files
author
James Forshaw
committed
Made AccessCheckResult more generic and added object list.
1 parent c4e286e commit f0ae627

6 files changed

Lines changed: 184 additions & 42 deletions

File tree

NtApiDotNet/AccessCheckResult.cs

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,33 +12,113 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15+
using System;
1516
using System.Collections.Generic;
1617

1718
namespace NtApiDotNet
1819
{
1920
/// <summary>
20-
/// Result of an access check.
21+
/// Result of an access check with specific access types. This is an extension with
22+
/// generic granted access masks.
2123
/// </summary>
22-
public class AccessCheckResult
24+
/// <typeparam name="T">The access rights type.</typeparam>
25+
public class AccessCheckResult<T> where T : Enum
2326
{
2427
/// <summary>
2528
/// The NT status code from the access check.
2629
/// </summary>
2730
public NtStatus Status { get; }
2831
/// <summary>
29-
/// The granted access from the check.
32+
/// The granted access mask from the check.
3033
/// </summary>
3134
public AccessMask GrantedAccess { get; }
3235
/// <summary>
36+
/// The granted access mapped to generic access mask.
37+
/// </summary>
38+
public AccessMask GenericGrantedAccess { get; }
39+
/// <summary>
3340
/// The required privileges for this access.
3441
/// </summary>
3542
public IEnumerable<TokenPrivilege> PrivilegesRequired { get; }
43+
/// <summary>
44+
/// The specific granted access mask from the check.
45+
/// </summary>
46+
public T SpecificGrantedAccess { get; }
47+
/// <summary>
48+
/// The specific granted access mapped to generic access mask.
49+
/// </summary>
50+
public T SpecificGenericGrantedAccess { get; }
51+
/// <summary>
52+
/// Get access check result as a specific access.
53+
/// </summary>
54+
/// <returns>The specific access results.</returns>
55+
public AccessCheckResult<U> ToSpecificAccess<U>() where U : Enum
56+
{
57+
return new AccessCheckResult<U>(Status, GrantedAccess, GenericGrantedAccess, PrivilegesRequired,
58+
GrantedAccess.ToSpecificAccess<U>(), GenericGrantedAccess.ToSpecificAccess<U>());
59+
}
60+
/// <summary>
61+
/// Get access check result as a specific access.
62+
/// </summary>
63+
/// <returns>The specific access.</returns>
64+
public AccessCheckResult<Enum> ToSpecificAccess(Type specific_access_type)
65+
{
66+
return new AccessCheckResult<Enum>(Status, GrantedAccess, GenericGrantedAccess, PrivilegesRequired,
67+
GrantedAccess.ToSpecificAccess(specific_access_type),
68+
GenericGrantedAccess.ToSpecificAccess(specific_access_type));
69+
}
3670

37-
internal AccessCheckResult(NtStatus status, AccessMask granted_access, SafePrivilegeSetBuffer privilege_set)
71+
internal AccessCheckResult(NtStatus status,
72+
AccessMask granted_access,
73+
AccessMask generic_granted_access,
74+
IEnumerable<TokenPrivilege> privilege_required)
75+
: this(status, granted_access,
76+
generic_granted_access, privilege_required,
77+
granted_access.ToSpecificAccess<T>(),
78+
generic_granted_access.ToSpecificAccess<T>())
79+
{
80+
}
81+
82+
internal AccessCheckResult(NtStatus status,
83+
AccessMask granted_access,
84+
AccessMask generic_granted_access,
85+
IEnumerable<TokenPrivilege> privilege_required,
86+
T specific_granted_access,
87+
T specific_generic_granted_access)
3888
{
3989
Status = status;
4090
GrantedAccess = granted_access;
41-
PrivilegesRequired = privilege_set?.GetPrivileges() ?? new TokenPrivilege[0];
91+
GenericGrantedAccess = generic_granted_access;
92+
PrivilegesRequired = privilege_required;
93+
SpecificGrantedAccess = specific_granted_access;
94+
SpecificGenericGrantedAccess = specific_generic_granted_access;
4295
}
4396
}
97+
98+
/// <summary>
99+
/// Result of an access check.
100+
/// </summary>
101+
public class AccessCheckResult : AccessCheckResult<GenericAccessRights>
102+
{
103+
internal AccessCheckResult(NtStatus status,
104+
AccessMask granted_access,
105+
SafePrivilegeSetBuffer privilege_set,
106+
GenericMapping generic_mapping)
107+
: this(status, granted_access,
108+
generic_mapping.UnmapMask(granted_access),
109+
privilege_set?.GetPrivileges() ?? new TokenPrivilege[0])
110+
{
111+
}
112+
113+
internal AccessCheckResult(
114+
NtStatus status,
115+
AccessMask granted_access,
116+
AccessMask generic_granted_access,
117+
IEnumerable<TokenPrivilege> privilege_required)
118+
: base(status, granted_access, generic_granted_access, privilege_required,
119+
granted_access.ToGenericAccess(), generic_granted_access.ToGenericAccess())
120+
{
121+
}
122+
}
123+
44124
}

NtApiDotNet/AccessMask.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,11 @@ public A ToSpecificAccess<A>() where A : Enum
9797
/// </summary>
9898
/// <param name="enum_type">The type of enumeration to convert to.</param>
9999
/// <returns>The converted value.</returns>
100-
public object ToSpecificAccess(Type enum_type)
100+
public Enum ToSpecificAccess(Type enum_type)
101101
{
102102
if (!enum_type.IsEnum)
103103
throw new ArgumentException("Type must be an Enum", "enum_type");
104-
return Enum.ToObject(enum_type, Access);
104+
return (Enum)Enum.ToObject(enum_type, Access);
105105
}
106106

107107
/// <summary>

NtApiDotNet/NtSecurity.cs

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,7 @@ public static NtResult<AccessCheckResult> AccessCheck(SecurityDescriptor sd, NtT
391391

392392
if (desired_access.IsEmpty)
393393
{
394-
return new AccessCheckResult(NtStatus.STATUS_ACCESS_DENIED, 0, null).CreateResult();
394+
return new AccessCheckResult(NtStatus.STATUS_ACCESS_DENIED, 0, null, generic_mapping).CreateResult();
395395
}
396396

397397
using (var list = new DisposableList())
@@ -404,19 +404,19 @@ public static NtResult<AccessCheckResult> AccessCheck(SecurityDescriptor sd, NtT
404404
}
405405
var self_sid = list.AddResource(principal?.ToSafeBuffer() ?? SafeSidBufferHandle.Null);
406406
var privs = list.AddResource(new SafePrivilegeSetBuffer());
407-
var object_type_list = list.AddResource(ConvertObjectTypes(object_types));
407+
var object_type_list = ConvertObjectTypes(object_types, list);
408408
int repeat_count = 1;
409409

410410
while (true)
411411
{
412412
int buffer_length = privs.Length;
413413
NtStatus status = NtSystemCalls.NtAccessCheckByType(sd_buffer, self_sid, imp_token.Result.Handle, desired_access,
414-
object_type_list, 0, ref generic_mapping, privs,
414+
object_type_list, object_type_list?.Length ?? 0, ref generic_mapping, privs,
415415
ref buffer_length, out AccessMask granted_access, out NtStatus result_status);
416416
if (repeat_count == 0 || status != NtStatus.STATUS_BUFFER_TOO_SMALL)
417417
{
418418
return status.CreateResult(throw_on_error, ()
419-
=> new AccessCheckResult(result_status, granted_access, privs));
419+
=> new AccessCheckResult(result_status, granted_access, privs, generic_mapping));
420420
}
421421

422422
repeat_count--;
@@ -425,6 +425,26 @@ public static NtResult<AccessCheckResult> AccessCheck(SecurityDescriptor sd, NtT
425425
}
426426
}
427427

428+
/// <summary>
429+
/// Do an access check between a security descriptor and a token to determine the allowed access.
430+
/// </summary>
431+
/// <param name="sd">The security descriptor</param>
432+
/// <param name="token">The access token.</param>
433+
/// <param name="desired_access">The set of access rights to check against</param>
434+
/// <param name="principal">An optional principal SID used to replace the SELF SID in a security descriptor.</param>
435+
/// <param name="generic_mapping">The type specific generic mapping (get from corresponding NtType entry).</param>
436+
/// <param name="object_types">List of object types to check against.</param>
437+
/// <param name="throw_on_error">True to throw on error.</param>
438+
/// <returns>The result of the access check.</returns>
439+
/// <exception cref="NtException">Thrown if an error occurred in the access check.</exception>
440+
public static NtResult<AccessCheckResult<T>> AccessCheck<T>(SecurityDescriptor sd, NtToken token,
441+
T desired_access, Sid principal, GenericMapping generic_mapping, IEnumerable<ObjectTypeEntry> object_types,
442+
bool throw_on_error) where T : Enum
443+
{
444+
return AccessCheck(sd, token, (AccessMask)desired_access, principal,
445+
generic_mapping, object_types, throw_on_error).Map(r => r.ToSpecificAccess<T>());
446+
}
447+
428448
/// <summary>
429449
/// Do an access check between a security descriptor and a token to determine the allowed access.
430450
/// </summary>
@@ -443,6 +463,24 @@ public static NtResult<AccessCheckResult> AccessCheck(SecurityDescriptor sd, NtT
443463
return AccessCheck(sd, token, desired_access, principal, generic_mapping, null, throw_on_error);
444464
}
445465

466+
/// <summary>
467+
/// Do an access check between a security descriptor and a token to determine the allowed access.
468+
/// </summary>
469+
/// <param name="sd">The security descriptor</param>
470+
/// <param name="token">The access token.</param>
471+
/// <param name="desired_access">The set of access rights to check against</param>
472+
/// <param name="principal">An optional principal SID used to replace the SELF SID in a security descriptor.</param>
473+
/// <param name="generic_mapping">The type specific generic mapping (get from corresponding NtType entry).</param>
474+
/// <param name="throw_on_error">True to throw on error.</param>
475+
/// <returns>The result of the access check.</returns>
476+
/// <exception cref="NtException">Thrown if an error occurred in the access check.</exception>
477+
public static NtResult<AccessCheckResult<T>> AccessCheck<T>(SecurityDescriptor sd, NtToken token,
478+
T desired_access, Sid principal, GenericMapping generic_mapping,
479+
bool throw_on_error) where T : Enum
480+
{
481+
return AccessCheck(sd, token, desired_access, principal, generic_mapping, null, throw_on_error);
482+
}
483+
446484
/// <summary>
447485
/// Do an access check between a security descriptor and a token to determine the allowed access.
448486
/// </summary>
@@ -459,6 +497,22 @@ public static AccessCheckResult AccessCheck(SecurityDescriptor sd, NtToken token
459497
return AccessCheck(sd, token, desired_access, principal, generic_mapping, true).Result;
460498
}
461499

500+
/// <summary>
501+
/// Do an access check between a security descriptor and a token to determine the allowed access.
502+
/// </summary>
503+
/// <param name="sd">The security descriptor</param>
504+
/// <param name="token">The access token.</param>
505+
/// <param name="desired_access">The set of access rights to check against</param>
506+
/// <param name="principal">An optional principal SID used to replace the SELF SID in a security descriptor.</param>
507+
/// <param name="generic_mapping">The type specific generic mapping (get from corresponding NtType entry).</param>
508+
/// <returns>The result of the access check.</returns>
509+
/// <exception cref="NtException">Thrown if an error occurred in the access check.</exception>
510+
public static AccessCheckResult<T> AccessCheck<T>(SecurityDescriptor sd, NtToken token,
511+
T desired_access, Sid principal, GenericMapping generic_mapping) where T : Enum
512+
{
513+
return AccessCheck(sd, token, desired_access, principal, generic_mapping, true).Result;
514+
}
515+
462516
/// <summary>
463517
/// Do an access check between a security descriptor and a token to determine the allowed access.
464518
/// </summary>
@@ -1232,26 +1286,12 @@ private static NtResult<NtToken> DuplicateForAccessCheck(NtToken token, bool thr
12321286
}
12331287
}
12341288

1235-
private static SafeArrayBuffer<ObjectTypeList> ConvertObjectTypes(IEnumerable<ObjectTypeEntry> object_types)
1289+
private static ObjectTypeList[] ConvertObjectTypes(IEnumerable<ObjectTypeEntry> object_types, DisposableList list)
12361290
{
12371291
if (object_types == null || !object_types.Any())
1238-
return SafeArrayBuffer<ObjectTypeList>.Null;
1292+
return null;
12391293

1240-
var guids = object_types.Select(o => o.ObjectType).ToArray();
1241-
var ret = new SafeArrayBuffer<ObjectTypeList>(new ObjectTypeList[guids.Length], guids.Length * 16);
1242-
try
1243-
{
1244-
IntPtr ptr = ret.Data.DangerousGetHandle();
1245-
var arr = object_types.Select((t, i) => new ObjectTypeList() { Level = (short)t.Level, ObjectType = ptr + (i * 16) }).ToArray();
1246-
ret.WriteArray(0, arr, 0, arr.Length);
1247-
ret.Data.WriteArray(0, guids, 0, guids.Length);
1248-
return ret;
1249-
}
1250-
catch
1251-
{
1252-
ret?.Dispose();
1253-
throw;
1254-
}
1294+
return object_types.Select(o => o.ToStruct(list)).ToArray();
12551295
}
12561296

12571297
private static CachedSigningLevelEaBuffer ReadCachedSigningLevelVersion1(BinaryReader reader)

NtApiDotNet/NtSecurityNative.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -554,6 +554,15 @@ public class ObjectTypeEntry
554554
{
555555
public int Level { get; set; }
556556
public Guid ObjectType { get; set; }
557+
558+
internal ObjectTypeList ToStruct(DisposableList resources)
559+
{
560+
return new ObjectTypeList()
561+
{
562+
Level = (short)Level,
563+
ObjectType = resources.AddResource(new SafeStructureInOutBuffer<Guid>(ObjectType)).DangerousGetHandle()
564+
};
565+
}
557566
}
558567

559568
public static partial class NtRtl
@@ -752,7 +761,7 @@ public static extern NtStatus NtAccessCheckByType(
752761
SafeHandle PrincipalSelfSid,
753762
SafeKernelObjectHandle ClientToken,
754763
AccessMask DesiredAccess,
755-
SafeBuffer ObjectTypeList,
764+
[In] ObjectTypeList[] ObjectTypeList,
756765
int ObjectTypeListLength,
757766
ref GenericMapping GenericMapping,
758767
SafePrivilegeSetBuffer RequiredPrivilegesBuffer,

NtObjectManager/NtDirectoryEntry.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
// limitations under the License.
1414

1515
using NtApiDotNet;
16+
using System;
1617

1718
namespace NtObjectManager
1819
{
@@ -24,7 +25,7 @@ public class NtDirectoryEntry
2425
private NtDirectory _base_directory;
2526
private SecurityDescriptor _sd;
2627
private string _symlink_target;
27-
private object _maximum_granted_access;
28+
private Enum _maximum_granted_access;
2829
private bool _data_populated;
2930

3031
private void PopulateData()
@@ -49,8 +50,7 @@ private void PopulateData()
4950
_sd = obj.SecurityDescriptor;
5051
}
5152

52-
NtSymbolicLink link = obj as NtSymbolicLink;
53-
if (link != null && link.IsAccessGranted(SymbolicLinkAccessRights.Query))
53+
if (obj is NtSymbolicLink link && link.IsAccessGranted(SymbolicLinkAccessRights.Query))
5454
{
5555
_symlink_target = link.Target;
5656
}
@@ -117,7 +117,7 @@ public string SymbolicLinkTarget
117117
/// <summary>
118118
/// The maximum granted access to the entry. Can be set to 0 if the caller doesn't have permission to open the actual object.
119119
/// </summary>
120-
public object MaximumGrantedAccess
120+
public Enum MaximumGrantedAccess
121121
{
122122
get
123123
{

0 commit comments

Comments
 (0)