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.
9118 lines
275 KiB
9118 lines
275 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
authzp.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the internal worker routines for access check and
|
|
audit APIs.
|
|
|
|
Author:
|
|
|
|
Kedar Dubhashi - March 2000
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
|
|
Revision History:
|
|
|
|
Created - March 2000
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
#pragma hdrstop
|
|
|
|
#include <authzp.h>
|
|
#include <ntrmlsa.h>
|
|
|
|
DWORD AuthzpPrincipalSelfSidBuffer[] = {0x101, 0x5000000, 0xA};
|
|
PSID pAuthzPrincipalSelfSid = (PSID) AuthzpPrincipalSelfSidBuffer;
|
|
USHORT AuthzpPrincipalSelfSidLen = sizeof(AuthzpPrincipalSelfSidBuffer);
|
|
|
|
DWORD AuthzpCreatorOwnerSidBuffer[] = {0x101, 0x3000000, 0x0};
|
|
PSID pAuthzCreatorOwnerSid = (PSID) AuthzpCreatorOwnerSidBuffer;
|
|
|
|
DWORD AuthzpEveryoneSidBuffer[] = {0x101, 0x1000000, 0x0};
|
|
PSID pAuthzEveryoneSid = (PSID) AuthzpEveryoneSidBuffer;
|
|
USHORT AuthzpEveryoneSidLen = sizeof(AuthzpEveryoneSidBuffer);
|
|
|
|
DWORD AuthzpAnonymousSidBuffer[] = {0x101, 0x5000000, 0x7};
|
|
PSID pAuthzAnonymousSid = (PSID) AuthzpAnonymousSidBuffer;
|
|
USHORT AuthzpAnonymousSidLen = sizeof(AuthzpAnonymousSidBuffer);
|
|
|
|
//
|
|
// This is used to get the index of the first '1' bit in a given byte.
|
|
// a[0] = 9 // This is a dummy.
|
|
// a[1] = 0
|
|
// a[2] = 1
|
|
// a[3] = 0
|
|
// ...
|
|
// a[i] = first '1' bit from LSB
|
|
//
|
|
|
|
UCHAR AuthzpByteToIndexLookupTable[] = {
|
|
9,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
|
|
4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
|
|
5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
|
|
4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
|
|
6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
|
|
4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
|
|
5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
|
|
4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
|
|
7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
|
|
4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
|
|
5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
|
|
4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
|
|
6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
|
|
4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
|
|
5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,
|
|
4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0
|
|
};
|
|
|
|
#ifndef AUTHZ_DEBUG_MEMLEAK
|
|
|
|
#define AuthzpAlloc(s) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, (s))
|
|
#define AuthzpFree(p) LocalFree((p))
|
|
|
|
#else
|
|
|
|
//
|
|
// This is to be used for debugging memory leaks. Primitive method but works in
|
|
// a small project like this.
|
|
//
|
|
|
|
|
|
PVOID
|
|
AuthzpAlloc(IN DWORD Size)
|
|
{
|
|
|
|
PVOID l = LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, Size);
|
|
|
|
wprintf(L"Allocated %d at %x\n", Size, l);
|
|
|
|
return l;
|
|
}
|
|
|
|
|
|
VOID
|
|
AuthzpFree(PVOID l)
|
|
{
|
|
LocalFree(l);
|
|
wprintf(L"Freeing %x\n", l);
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
BOOL
|
|
AuthzpVerifyAccessCheckArguments(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
IN PSECURITY_DESCRIPTOR *OptionalSecurityDescriptorArray OPTIONAL,
|
|
IN DWORD OptionalSecurityDescriptorCount,
|
|
IN OUT PAUTHZ_ACCESS_REPLY pReply,
|
|
IN OUT PAUTHZ_ACCESS_CHECK_RESULTS_HANDLE phAccessCheckResults OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine validates inputs for the access check call. It also initializes
|
|
input parameters to default.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pSecurityDescriptor - Primary security descriptor to be used for access
|
|
checks. The owner sid for the object is picked from this one. A NULL
|
|
DACL in this security descriptor represents a NULL DACL for the entire
|
|
object. A NULL SACL in this security descriptor is treated the same way
|
|
as an EMPTY SACL.
|
|
|
|
OptionalSecurityDescriptorArray - The caller may optionally specify a list
|
|
of security descriptors. NULL ACLs in these security descriptors are
|
|
treated as EMPTY ACLS and the ACL for the entire object is the logical
|
|
concatenation of all the ACLs.
|
|
|
|
OptionalSecurityDescriptorCount - Number of optional security descriptors
|
|
This does not include the Primary security descriptor.
|
|
|
|
pReply - Supplies a pointer to a reply structure used to return the results.
|
|
|
|
phAccessCheckResults - Supplies a pointer to return a handle to the cached results
|
|
of access check.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
if (ARGUMENT_PRESENT(phAccessCheckResults))
|
|
{
|
|
*phAccessCheckResults = NULL;
|
|
}
|
|
|
|
if (!ARGUMENT_PRESENT(pCC))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!ARGUMENT_PRESENT(pSecurityDescriptor))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef DBG
|
|
if (!RtlValidSecurityDescriptor(pSecurityDescriptor))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
if (!ARGUMENT_PRESENT(RtlpOwnerAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) pSecurityDescriptor)))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if ((0 != OptionalSecurityDescriptorCount) && (!ARGUMENT_PRESENT(OptionalSecurityDescriptorArray)))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// The caller can specify one of the two values for Reply->ResultListLength
|
|
// a. 1 - representing the whole object.
|
|
// b. pRequest->ObjectTypeListLength - for every node in the type list.
|
|
//
|
|
|
|
if ((1 != pReply->ResultListLength) &&
|
|
(pReply->ResultListLength != pRequest->ObjectTypeListLength))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpVerifyOpenObjectArguments(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
IN PSECURITY_DESCRIPTOR *OptionalSecurityDescriptorArray OPTIONAL,
|
|
IN DWORD OptionalSecurityDescriptorCount,
|
|
IN PAUTHZI_AUDIT_EVENT pAuditEvent
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine validates inputs for AuthzOpenObjectAuditAlarm.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pSecurityDescriptor - Primary security descriptor to be used for access
|
|
checks. The owner sid for the object is picked from this one. A NULL
|
|
DACL in this security descriptor represents a NULL DACL for the entire
|
|
object. A NULL SACL in this security descriptor is treated the same way
|
|
as an EMPTY SACL.
|
|
|
|
OptionalSecurityDescriptorArray - The caller may optionally specify a list
|
|
of security descriptors. NULL ACLs in these security descriptors are
|
|
treated as EMPTY ACLS and the ACL for the entire object is the logical
|
|
concatenation of all the ACLs.
|
|
|
|
OptionalSecurityDescriptorCount - Number of optional security descriptors
|
|
This does not include the Primary security descriptor.
|
|
|
|
pReply - Supplies a pointer to a reply structure containing the results of
|
|
an AuthzAccessCheck.
|
|
|
|
pAuditEvent - pointer to AUTHZ_AUDIT_EVENT.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
if (!ARGUMENT_PRESENT(pCC))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!ARGUMENT_PRESENT(pAuditEvent))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!ARGUMENT_PRESENT(pSecurityDescriptor))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
#if DBG
|
|
if (!RtlValidSecurityDescriptor(pSecurityDescriptor))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
if (!ARGUMENT_PRESENT(RtlpOwnerAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) pSecurityDescriptor)))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if ((0 != OptionalSecurityDescriptorCount) && (!ARGUMENT_PRESENT(OptionalSecurityDescriptorArray)))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpCaptureObjectTypeList(
|
|
IN POBJECT_TYPE_LIST ObjectTypeList,
|
|
IN DWORD ObjectTypeListLength,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalCachingTypeList OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine captures an object type list into internal structres.
|
|
|
|
Arguments:
|
|
|
|
ObjectTypeList - Object type list to capture.
|
|
|
|
ObjectTypeListLength - Length of the object type list.
|
|
|
|
LocalTypeList - To return the internal representation of the
|
|
input list. Memory is allocated by the caller.
|
|
|
|
LocalCachingTypeList - To return internal representation of the
|
|
input list This list will be used to hold static always-granted access bits.
|
|
Memory is allocated by the caller.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i = 0;
|
|
BOOL b = TRUE;
|
|
|
|
DWORD Levels[ACCESS_MAX_LEVEL + 1];
|
|
|
|
if ((0 == ObjectTypeListLength) || (!ARGUMENT_PRESENT(ObjectTypeList)))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < ObjectTypeListLength; i++)
|
|
{
|
|
USHORT CurrentLevel;
|
|
|
|
CurrentLevel = ObjectTypeList[i].Level;
|
|
|
|
if (CurrentLevel > ACCESS_MAX_LEVEL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Copy data the caller passed in.
|
|
//
|
|
|
|
LocalTypeList[i].Level = CurrentLevel;
|
|
LocalTypeList[i].Flags = 0;
|
|
LocalTypeList[i].ObjectType = *ObjectTypeList[i].ObjectType;
|
|
LocalTypeList[i].Remaining = 0;
|
|
LocalTypeList[i].CurrentGranted = 0;
|
|
LocalTypeList[i].CurrentDenied = 0;
|
|
|
|
//
|
|
// Ensure that the level number is consistent with the
|
|
// level number of the previous entry.
|
|
//
|
|
|
|
if (0 == i)
|
|
{
|
|
if (0 != CurrentLevel)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The previous entry is either:
|
|
// my immediate parent,
|
|
// my sibling, or
|
|
// the child (or grandchild, etc.) of my sibling.
|
|
//
|
|
|
|
if ( CurrentLevel > LocalTypeList[i-1].Level + 1 )
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Don't support two roots.
|
|
//
|
|
|
|
if (0 == CurrentLevel)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the above rules are maintained,
|
|
// then my parent object is the last object seen that
|
|
// has a level one less than mine.
|
|
//
|
|
|
|
if (0 == CurrentLevel)
|
|
{
|
|
LocalTypeList[i].ParentIndex = -1;
|
|
}
|
|
else
|
|
{
|
|
LocalTypeList[i].ParentIndex = Levels[CurrentLevel-1];
|
|
}
|
|
|
|
//
|
|
// Save this obect as the last object seen at this level.
|
|
//
|
|
|
|
Levels[CurrentLevel] = i;
|
|
}
|
|
|
|
//
|
|
// If the caller has requested caching then create a copy of the object type
|
|
// list that we just captured.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(LocalCachingTypeList))
|
|
{
|
|
memcpy(
|
|
LocalCachingTypeList,
|
|
LocalTypeList,
|
|
(ObjectTypeListLength * sizeof(IOBJECT_TYPE_LIST))
|
|
);
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
Cleanup:
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID
|
|
AuthzpFillReplyStructure(
|
|
IN OUT PAUTHZ_ACCESS_REPLY pReply,
|
|
IN DWORD Error,
|
|
IN ACCESS_MASK GrantedAccess
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine fills the reply structure with supplied parameters.
|
|
|
|
Arguments:
|
|
|
|
pReply - The reply structure to fill.
|
|
|
|
Error - The same error value is filled in all the elements of the array.
|
|
|
|
GrantedAccess - Access granted to the entire object.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i = 0;
|
|
|
|
for (i = 0; i < pReply->ResultListLength; i++)
|
|
{
|
|
pReply->GrantedAccessMask[i] = GrantedAccess;
|
|
pReply->Error[i] = Error;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpMaximumAllowedAccessCheck(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
IN PSECURITY_DESCRIPTOR *OptionalSecurityDescriptorArray OPTIONAL,
|
|
IN DWORD OptionalSecurityDescriptorCount,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalCachingTypeList OPTIONAL,
|
|
IN DWORD LocalTypeListLength,
|
|
IN BOOL ObjectTypeListPresent,
|
|
OUT PDWORD pCachingFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine does a maximum allowed access check on multiple security
|
|
descriptors. In case of restricted tokens, access granted is a bitwise
|
|
AND of granted access by the normal as well as the restricted parts of
|
|
the client context.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pSecurityDescriptor - Primary security descriptor to be used for access
|
|
checks. The owner sid for the object is picked from this one. A NULL
|
|
DACL in this security descriptor represents a NULL DACL for the entire
|
|
object. A NULL SACL in this security descriptor is treated the same way
|
|
as an EMPTY SACL.
|
|
|
|
OptionalSecurityDescriptorArray - The caller may optionally specify a list
|
|
of security descriptors. NULL ACLs in these security descriptors are
|
|
treated as EMPTY ACLS and the ACL for the entire object is the logical
|
|
concatenation of all the ACLs.
|
|
|
|
OptionalSecurityDescriptorCount - Number of optional security descriptors
|
|
This does not include the Primary security descriptor.
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results.
|
|
|
|
LocalCachingTypeList - Internal object type list structure used to hold
|
|
intermediate results for static granted bits.
|
|
|
|
LocalTypeListLength - Length of the array represnting the object.
|
|
|
|
ObjectTypeListPresent - Whether the called supplied an object type list.
|
|
|
|
pCachingFlags - To return the flags that will be stored in the caching
|
|
handle if the caller requested caching.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b = TRUE;
|
|
|
|
//
|
|
// Do the access check with the non-restricted part of the client context.
|
|
//
|
|
|
|
b = AuthzpMaximumAllowedMultipleSDAccessCheck(
|
|
pCC,
|
|
pRequest,
|
|
pSecurityDescriptor,
|
|
OptionalSecurityDescriptorArray,
|
|
OptionalSecurityDescriptorCount,
|
|
LocalTypeList,
|
|
LocalCachingTypeList,
|
|
LocalTypeListLength,
|
|
ObjectTypeListPresent,
|
|
FALSE, // Non-restricted access check
|
|
pCachingFlags
|
|
);
|
|
|
|
#ifdef AUTHZ_DEBUG
|
|
wprintf(L"AuthzpMaximumAllowedAccessCheck: GrantedAccess = %x\n",
|
|
LocalTypeList->CurrentGranted);
|
|
#endif
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Do the access check with the restricted part of the client context if
|
|
// it exists.
|
|
//
|
|
|
|
if (AUTHZ_TOKEN_RESTRICTED(pCC))
|
|
{
|
|
b = AuthzpMaximumAllowedMultipleSDAccessCheck(
|
|
pCC,
|
|
pRequest,
|
|
pSecurityDescriptor,
|
|
OptionalSecurityDescriptorArray,
|
|
OptionalSecurityDescriptorCount,
|
|
LocalTypeList,
|
|
LocalCachingTypeList,
|
|
LocalTypeListLength,
|
|
ObjectTypeListPresent,
|
|
TRUE, // Restricted access check
|
|
pCachingFlags
|
|
);
|
|
|
|
#ifdef AUTHZ_DEBUG
|
|
wprintf(L"AuthzpMaximumAllowedAccessCheck: RestrictedGrantedAccess = %x\n",
|
|
LocalTypeList->CurrentGranted);
|
|
#endif
|
|
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpMaximumAllowedMultipleSDAccessCheck(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
IN PSECURITY_DESCRIPTOR *OptionalSecurityDescriptorArray OPTIONAL,
|
|
IN DWORD OptionalSecurityDescriptorCount,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalCachingTypeList OPTIONAL,
|
|
IN DWORD LocalTypeListLength,
|
|
IN BOOL ObjectTypeListPresent,
|
|
IN BOOL Restricted,
|
|
OUT PDWORD pCachingFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine performs access check for all security descriptors provided.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pSecurityDescriptor - Primary security descriptor to be used for access
|
|
checks. The owner sid for the object is picked from this one. A NULL
|
|
DACL in this security descriptor represents a NULL DACL for the entire
|
|
object. A NULL SACL in this security descriptor is treated the same way
|
|
as an EMPTY SACL.
|
|
|
|
OptionalSecurityDescriptorArray - The caller may optionally specify a list
|
|
of security descriptors. NULL ACLs in these security descriptors are
|
|
treated as EMPTY ACLS and the ACL for the entire object is the logical
|
|
concatenation of all the ACLs.
|
|
|
|
OptionalSecurityDescriptorCount - Number of optional security descriptors
|
|
This does not include the Primary security descriptor.
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results.
|
|
|
|
LocalCachingTypeList - Internal object type list structure used to hold
|
|
intermediate results for static granted bits.
|
|
|
|
LocalTypeListLength - Length of the array represnting the object.
|
|
|
|
ObjectTypeListPresent - Whether the called supplied an object type list.
|
|
|
|
Restricted - Whether the restricted part of the client context should be
|
|
used for access check.
|
|
|
|
pCachingFlags - To return the flags that will be stored in the caching
|
|
handle if the caller requested caching.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i = 0;
|
|
DWORD j = 0;
|
|
DWORD SidCount = 0;
|
|
BOOL b = TRUE;
|
|
PSID_AND_ATTRIBUTES pSidAttr = NULL;
|
|
PACL pAcl = NULL;
|
|
PSID pOwnerSid = NULL;
|
|
PAUTHZI_SID_HASH_ENTRY pSidHash = NULL;
|
|
|
|
pOwnerSid = RtlpOwnerAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) pSecurityDescriptor);
|
|
|
|
if (Restricted)
|
|
{
|
|
//
|
|
// For restricted client context, granted access is a bitwise AND of
|
|
// granted access from both parts of the context.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(LocalCachingTypeList))
|
|
{
|
|
for (j = 0; j < LocalTypeListLength; j++)
|
|
{
|
|
LocalTypeList[j].Remaining = LocalTypeList[j].CurrentGranted;
|
|
LocalTypeList[j].CurrentGranted = 0;
|
|
|
|
LocalCachingTypeList[j].Remaining = LocalCachingTypeList[j].CurrentGranted;
|
|
LocalCachingTypeList[j].CurrentGranted = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (j = 0; j < LocalTypeListLength; j++)
|
|
{
|
|
LocalTypeList[j].Remaining = LocalTypeList[j].CurrentGranted;
|
|
LocalTypeList[j].CurrentGranted = 0;
|
|
}
|
|
}
|
|
|
|
pSidAttr = pCC->RestrictedSids;
|
|
SidCount = pCC->RestrictedSidCount;
|
|
pSidHash = pCC->RestrictedSidHash;
|
|
}
|
|
else
|
|
{
|
|
pSidAttr = pCC->Sids;
|
|
SidCount = pCC->SidCount;
|
|
pSidHash = pCC->SidHash;
|
|
}
|
|
|
|
pAcl = RtlpDaclAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) pSecurityDescriptor);
|
|
|
|
b = AuthzpMaximumAllowedSingleAclAccessCheck(
|
|
pCC,
|
|
pSidAttr,
|
|
SidCount,
|
|
pSidHash,
|
|
pRequest,
|
|
pAcl,
|
|
pOwnerSid,
|
|
LocalTypeList,
|
|
LocalCachingTypeList,
|
|
LocalTypeListLength,
|
|
ObjectTypeListPresent,
|
|
pCachingFlags
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < OptionalSecurityDescriptorCount; i++)
|
|
{
|
|
pAcl = RtlpDaclAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) OptionalSecurityDescriptorArray[i]);
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(pAcl))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
b = AuthzpMaximumAllowedSingleAclAccessCheck(
|
|
pCC,
|
|
pSidAttr,
|
|
SidCount,
|
|
pSidHash,
|
|
pRequest,
|
|
pAcl,
|
|
pOwnerSid,
|
|
LocalTypeList,
|
|
LocalCachingTypeList,
|
|
LocalTypeListLength,
|
|
ObjectTypeListPresent,
|
|
pCachingFlags
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpIsAccessMaskApplicable(
|
|
IN PIOBJECT_TYPE_LIST LocalTypeList,
|
|
IN DWORD LocalTypeListLength,
|
|
IN DWORD StartIndex,
|
|
IN ACCESS_MASK AceMask
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if a given access mask might have any effect on the
|
|
object type list if the ace were applicable.
|
|
|
|
Arguments:
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results.
|
|
|
|
LocalTypeListLength - Length of the array represnting the object.
|
|
|
|
StartIndex - Index to the root element where the ace might be applicable.
|
|
|
|
AceMask - Access mask in the ace.
|
|
|
|
Return Value:
|
|
|
|
Boolean: TRUE if the ace will change the Allowed/Denied structure of the
|
|
tree if it were applicable. False otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Index = 0;
|
|
|
|
ACCESS_MASK GrantedOrDeniedMask = LocalTypeList[StartIndex].CurrentDenied |
|
|
LocalTypeList[StartIndex].CurrentGranted;
|
|
|
|
//
|
|
// If the ace mask has bits that have not already been allowed/denied at the
|
|
// root of the tree then return TRUE. This quick check will save us the
|
|
// tree traversal in most cases.
|
|
//
|
|
|
|
if (0 != (AceMask & ~GrantedOrDeniedMask))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Loop handling all children of the target.
|
|
//
|
|
|
|
for (Index = StartIndex + 1; Index < LocalTypeListLength; Index++)
|
|
{
|
|
|
|
//
|
|
// By definition, the children of an object are all those entries
|
|
// immediately following the target. The list of children (or
|
|
// grandchildren) stops as soon as we reach an entry the has the
|
|
// same level as the target (a sibling) or lower than the target
|
|
// (an uncle).
|
|
//
|
|
|
|
if (LocalTypeList[Index].Level <= LocalTypeList[StartIndex].Level)
|
|
{
|
|
break;
|
|
}
|
|
|
|
GrantedOrDeniedMask &= (LocalTypeList[Index].CurrentDenied |
|
|
LocalTypeList[Index].CurrentGranted);
|
|
}
|
|
|
|
//
|
|
// If the ace mask has bits that have not already been allowed/denied at the
|
|
// some node in the subtree stating at the specified index then return TRUE.
|
|
//
|
|
|
|
if (0 != (AceMask & ~GrantedOrDeniedMask))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpMaximumAllowedSingleAclAccessCheck(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PSID_AND_ATTRIBUTES pSidAttr,
|
|
IN DWORD SidCount,
|
|
IN PAUTHZI_SID_HASH_ENTRY pSidHash,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN PACL pAcl,
|
|
IN PSID pOwnerSid,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalCachingTypeList OPTIONAL,
|
|
IN DWORD LocalTypeListLength,
|
|
IN BOOL ObjectTypeListPresent,
|
|
OUT PDWORD pCachingFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine
|
|
|
|
Arguments:
|
|
pCC - Pointer to the client context.
|
|
|
|
pSidAttr - Sids and attributes to be used for matching the one in the aces.
|
|
|
|
SidCount - Number of sids in the pSidAttr array
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pAcl - Discretionary acl against which access check will be performed.
|
|
It is assumed that the acl is non-null.
|
|
|
|
pOwnerSid - To support the future DS requirement of not mapping Creator
|
|
Owner at the time of object creation.
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results.
|
|
|
|
LocalCachingTypeList - Internal object type list structure used to hold
|
|
intermediate results for static granted bits.
|
|
|
|
LocalTypeListLength - Length of the array represnting the object.
|
|
|
|
ObjectTypeListPresent - Whether the called supplied an object type list.
|
|
|
|
Restricted - Whether the restricted part of the client context should be
|
|
used for access check.
|
|
|
|
pCachingFlags - To return the flags that will be stored in the caching
|
|
handle if the caller requested caching.
|
|
|
|
Algorithm:
|
|
|
|
BEGIN_MAX_ALGO
|
|
|
|
for all aces
|
|
skip INHERIT_ONLY aces
|
|
|
|
ACCESS_ALLOWED_COMPOUND_ACE_TYPE:
|
|
|
|
If (the sid is applicable) AND (Server Sid is also applicable)
|
|
Update the entire tree starting at the root.
|
|
Grant those bits that have not already been denied.
|
|
|
|
Note: No one uses these!!
|
|
|
|
ACCESS_ALLOWED_ACE_TYPE
|
|
|
|
If the sid is applicable
|
|
Update the entire tree starting at the root.
|
|
Grant those bits that have not already been denied.
|
|
|
|
ACCESS_ALLOWED_CALLBACK_ACE_TYPE
|
|
|
|
If (the sid is applicable) AND (callback call evaluates ace applicable)
|
|
Update the entire tree starting at the root.
|
|
Grant those bits that have not already been denied.
|
|
|
|
ACCESS_ALLOWED_OBJECT_ACE_TYPE
|
|
|
|
If the sid is applicable
|
|
get the ObjectType guid from the ace
|
|
|
|
if the guid is NULL
|
|
Update the entire tree starting at the root.
|
|
Grant those bits that have not already been denied.
|
|
else
|
|
If (object type list exists) AND (contains a matching guid)
|
|
Update the tree starting at the MATCH.
|
|
Grant those bits that have not already been denied.
|
|
|
|
ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE
|
|
|
|
If (the sid is applicable) AND (callback call evaluates ace applicable)
|
|
get the ObjectType guid from the ace
|
|
|
|
if the guid is NULL
|
|
Update the entire tree starting at the root.
|
|
Grant those bits that have not already been denied.
|
|
else
|
|
If (object type list exists) AND (contains a matching guid)
|
|
Update the tree starting at the MATCH.
|
|
Grant those bits that have not already been denied.
|
|
|
|
ACCESS_DENIED_ACE_TYPE
|
|
|
|
If the sid is applicable
|
|
Update the entire tree starting at the root.
|
|
Deny those bits that have not already been granted.
|
|
Propagate the deny all the way up to the root.
|
|
|
|
ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE
|
|
|
|
If (the sid is applicable) AND (callback call evaluates ace applicable)
|
|
Update the entire tree starting at the root.
|
|
Deny those bits that have not already been granted.
|
|
Propagate the deny all the way up to the root.
|
|
|
|
ACCESS_DENIED_OBJECT_ACE_TYPE
|
|
|
|
If the sid is applicable
|
|
get the ObjectType guid from the ace
|
|
|
|
if the guid is NULL
|
|
Update the entire tree starting at the root.
|
|
Deny those bits that have not already been granted.
|
|
Propagate the deny all the way up to the root.
|
|
else
|
|
If (object type list exists) AND (contains a matching guid)
|
|
Update the tree starting at the MATCH.
|
|
Deny those bits that have not already been granted.
|
|
Propagate the deny all the way up to the root.
|
|
|
|
ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE
|
|
|
|
If (the sid is applicable) AND (callback call evaluates ace applicable)
|
|
get the ObjectType guid from the ace
|
|
|
|
if the guid is NULL
|
|
Update the entire tree starting at the root.
|
|
Deny those bits that have not already been granted.
|
|
Propagate the deny all the way up to the root.
|
|
else
|
|
If (object type list exists) AND (contains a matching guid)
|
|
Update the tree starting at the MATCH.
|
|
Deny those bits that have not already been granted.
|
|
Propagate the deny all the way up to the root.
|
|
|
|
Note:
|
|
|
|
LocalCachingFlags is used to return whether the ace is contains principal
|
|
self sid.
|
|
|
|
We take a pessimistic view for dynamic aces. There are two types of
|
|
dynamic aces:
|
|
1. Call back aces
|
|
2. Normal aces with principal self sid in it.
|
|
|
|
For any dynamic ace,
|
|
Grant aces are never applicable to the caching list.
|
|
Deny aces are always applicable to the caching list.
|
|
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD AceCount = 0;
|
|
DWORD i = 0;
|
|
DWORD Index = 0;
|
|
PVOID Ace = NULL;
|
|
GUID * ObjectTypeInAce = NULL;
|
|
BOOL bAceApplicable = FALSE;
|
|
BOOL bCallbackMayApply = FALSE;
|
|
DWORD LocalCachingFlags = 0;
|
|
|
|
AceCount = pAcl->AceCount;
|
|
|
|
for (i = 0, Ace = FirstAce(pAcl); i < AceCount; i++, Ace = NextAce(Ace))
|
|
{
|
|
|
|
//
|
|
// Skip INHERIT_ONLY aces.
|
|
//
|
|
|
|
if (FLAG_ON(((PACE_HEADER) Ace)->AceFlags, INHERIT_ONLY_ACE))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
LocalCachingFlags = 0;
|
|
|
|
switch (((PACE_HEADER) Ace)->AceType)
|
|
{
|
|
case ACCESS_ALLOWED_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled. S-1-5-A is replaced by the principal sid
|
|
// supplied by the caller. In future, Creator Owner will be replaced
|
|
// by the owner sid in the primary security descriptor.
|
|
//
|
|
|
|
bAceApplicable = AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
AuthzAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
FALSE,
|
|
&LocalCachingFlags
|
|
);
|
|
|
|
//
|
|
// Record the caching flags for this ace into the global flags.
|
|
//
|
|
|
|
*pCachingFlags |= LocalCachingFlags;
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Grant access bits that have not already been denied.
|
|
//
|
|
|
|
LocalTypeList->CurrentGranted |= (((PKNOWN_ACE) Ace)->Mask & ~LocalTypeList->CurrentDenied);
|
|
|
|
//
|
|
// A grant ace is considered static only if PrincipalSelf sid
|
|
// is not found in the ace.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(LocalCachingTypeList) && (0 == LocalCachingFlags))
|
|
{
|
|
LocalCachingTypeList->CurrentGranted |= (((PKNOWN_ACE) Ace)->Mask & ~LocalCachingTypeList->CurrentDenied);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate grant bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
|
|
//
|
|
// A grant ace is considered static only if PrincipalSelf sid
|
|
// is not found in the ace.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(LocalCachingTypeList) && (0 == LocalCachingFlags))
|
|
{
|
|
AuthzpAddAccessTypeList(
|
|
LocalCachingTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_CALLBACK_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled. S-1-5-A is replaced by the principal sid
|
|
// supplied by the caller. In future, Creator Owner will be replaced
|
|
// by the owner sid in the primary security descriptor.
|
|
//
|
|
|
|
bAceApplicable = AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
AuthzCallbackAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
FALSE,
|
|
&LocalCachingFlags
|
|
);
|
|
|
|
|
|
//
|
|
// Record the presence of a dynamic grant ace as well as the caching
|
|
// flags for this ace into the global flags.
|
|
//
|
|
|
|
*pCachingFlags |= (LocalCachingFlags | AUTHZ_DYNAMIC_ALLOW_ACE_PRESENT);
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// If the all the bits from this ace have already been granted
|
|
// or denied, then there is no need to evaluate this callback ace.
|
|
//
|
|
|
|
if (0 == ((((PKNOWN_ACE) Ace)->Mask) &
|
|
~(LocalTypeList->CurrentGranted | LocalTypeList->CurrentDenied)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We have an object type list. Compute whether we need to break
|
|
// out early.
|
|
//
|
|
|
|
bCallbackMayApply = AuthzpIsAccessMaskApplicable(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask
|
|
);
|
|
|
|
if (!bCallbackMayApply)
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
bAceApplicable = FALSE;
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Grant access bits that have not already been denied.
|
|
//
|
|
|
|
LocalTypeList->CurrentGranted |= (((PKNOWN_ACE) Ace)->Mask & ~LocalTypeList->CurrentDenied);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate grant bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
|
|
|
|
ObjectTypeInAce = RtlObjectAceObjectType(Ace);
|
|
|
|
if ( ObjectTypeInAce && ( pRequest->ObjectTypeListLength == 0 ))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled. S-1-5-A is replaced by the principal sid
|
|
// supplied by the caller. In future, Creator Owner will be replaced
|
|
// by the owner sid in the primary security descriptor.
|
|
//
|
|
|
|
bAceApplicable = AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
AuthzObjectAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
FALSE,
|
|
&LocalCachingFlags
|
|
);
|
|
|
|
//
|
|
// Record the the caching flags for this ace into the global flags.
|
|
//
|
|
|
|
*pCachingFlags |= LocalCachingFlags;
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// An object type ace with a NULL Object Type guid is the same as a
|
|
// normal ace.
|
|
//
|
|
|
|
if (NULL == ObjectTypeInAce)
|
|
{
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Grant access bits that have not already been denied.
|
|
//
|
|
|
|
LocalTypeList->CurrentGranted |= (((PKNOWN_OBJECT_ACE) Ace)->Mask & ~LocalTypeList->CurrentDenied);
|
|
|
|
//
|
|
// A grant ace is considered static only if PrincipalSelf sid
|
|
// is not found in the ace.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(LocalCachingTypeList) && (0 == LocalCachingFlags))
|
|
{
|
|
LocalCachingTypeList->CurrentGranted |= (((PKNOWN_OBJECT_ACE) Ace)->Mask & ~LocalCachingTypeList->CurrentDenied);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate grant bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
|
|
//
|
|
// A grant ace is considered static only if PrincipalSelf sid
|
|
// is not found in the ace.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(LocalCachingTypeList) && (0 == LocalCachingFlags))
|
|
{
|
|
AuthzpAddAccessTypeList(
|
|
LocalCachingTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// So, it is a true object type ace. Proceed only if we are doing
|
|
// access check on a object type list.
|
|
//
|
|
|
|
else if (ObjectTypeListPresent)
|
|
{
|
|
//
|
|
// Look for a matching object type guid that matches the one in
|
|
// the ace.
|
|
//
|
|
|
|
if (AuthzpObjectInTypeList(
|
|
ObjectTypeInAce,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
&Index
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// Propagate grant bits down the tree starting at the
|
|
// index at which the guids matched.
|
|
// In the case when this is the last of the siblings to be
|
|
// granted access, the parent also is granted access.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
Index,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
|
|
//
|
|
// A grant ace is considered static only if PrincipalSelf sid
|
|
// is not found in the ace.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(LocalCachingTypeList) && (0 == LocalCachingFlags))
|
|
{
|
|
AuthzpAddAccessTypeList(
|
|
LocalCachingTypeList,
|
|
LocalTypeListLength,
|
|
Index,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled. S-1-5-A is replaced by the principal sid
|
|
// supplied by the caller. In future, Creator Owner will be replaced
|
|
// by the owner sid in the primary security descriptor.
|
|
//
|
|
|
|
bAceApplicable = AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
AuthzCallbackObjectAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
FALSE,
|
|
&LocalCachingFlags
|
|
);
|
|
|
|
//
|
|
// Record the presence of a dynamic grant ace as well as the caching
|
|
// flags for this ace into the global flags.
|
|
//
|
|
|
|
*pCachingFlags |= (LocalCachingFlags | AUTHZ_DYNAMIC_ALLOW_ACE_PRESENT);
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
bAceApplicable = FALSE;
|
|
|
|
ObjectTypeInAce = RtlObjectAceObjectType(Ace);
|
|
|
|
//
|
|
// An object type ace with a NULL Object Type guid is the same as a
|
|
// normal ace.
|
|
//
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(ObjectTypeInAce))
|
|
{
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// If the all the bits from this ace have already been granted
|
|
// or denied, then there is no need to evaluate this callback ace.
|
|
//
|
|
|
|
if (0 == ((((PKNOWN_ACE) Ace)->Mask) &
|
|
~(LocalTypeList->CurrentGranted | LocalTypeList->CurrentDenied)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We have an object type list. Compute whether we need to break
|
|
// out early.
|
|
//
|
|
|
|
bCallbackMayApply = AuthzpIsAccessMaskApplicable(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask
|
|
);
|
|
|
|
if (!bCallbackMayApply)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
bAceApplicable = FALSE;
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Grant access bits that have not already been denied.
|
|
//
|
|
|
|
LocalTypeList->CurrentGranted |= (((PKNOWN_OBJECT_ACE) Ace)->Mask & ~LocalTypeList->CurrentDenied);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate grant bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// So, it is a true object type ace. Proceed only if we are doing
|
|
// access check on a object type list.
|
|
//
|
|
|
|
else if (ObjectTypeListPresent)
|
|
{
|
|
//
|
|
// Look for a matching object type guid that matches the one in
|
|
// the ace.
|
|
//
|
|
|
|
if (AuthzpObjectInTypeList(
|
|
ObjectTypeInAce,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
&Index
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// We have an object type list. Compute whether we need to break
|
|
// out early.
|
|
//
|
|
|
|
bCallbackMayApply = AuthzpIsAccessMaskApplicable(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
Index,
|
|
((PKNOWN_ACE) Ace)->Mask
|
|
);
|
|
|
|
if (!bCallbackMayApply)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Propagate grant bits down the tree starting at the
|
|
// index at which the guids matched.
|
|
// In the case when this is the last of the siblings to be
|
|
// granted access, the parent also is granted access.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
Index,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_COMPOUND_ACE_TYPE:
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(pCC->Server))
|
|
{
|
|
break;
|
|
}
|
|
|
|
LocalCachingFlags = 0;
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled. S-1-5-A is replaced by the principal sid
|
|
// supplied by the caller. In future, Creator Owner will be replaced
|
|
// by the owner sid in the primary security descriptor.
|
|
//
|
|
|
|
bAceApplicable = AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
RtlCompoundAceClientSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
FALSE,
|
|
&LocalCachingFlags
|
|
);
|
|
|
|
//
|
|
// Record the caching flags for this ace into the global flags.
|
|
//
|
|
|
|
*pCachingFlags |= LocalCachingFlags;
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
bAceApplicable = AuthzpSidApplicable(
|
|
pCC->Server->SidCount,
|
|
pCC->Server->Sids,
|
|
pCC->Server->SidHash,
|
|
RtlCompoundAceServerSid(Ace),
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Grant access bits that have not already been denied.
|
|
//
|
|
|
|
LocalTypeList->CurrentGranted |= (((PCOMPOUND_ACCESS_ALLOWED_ACE) Ace)->Mask & ~LocalTypeList->CurrentDenied);
|
|
|
|
//
|
|
// A grant ace is considered static only if PrincipalSelf sid
|
|
// is not found in the ace.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(LocalCachingTypeList) && (0 == LocalCachingFlags))
|
|
{
|
|
LocalCachingTypeList->CurrentGranted |= (((PCOMPOUND_ACCESS_ALLOWED_ACE) Ace)->Mask & ~LocalCachingTypeList->CurrentDenied);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate grant bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PCOMPOUND_ACCESS_ALLOWED_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
|
|
//
|
|
// A grant ace is considered static only if PrincipalSelf sid
|
|
// is not found in the ace.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(LocalCachingTypeList) && (0 == LocalCachingFlags))
|
|
{
|
|
AuthzpAddAccessTypeList(
|
|
LocalCachingTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PCOMPOUND_ACCESS_ALLOWED_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_DENIED_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled or enabled for deny only. S-1-5-A is replaced by
|
|
// the principal sid supplied by the caller. In future, Creator
|
|
// Owner will be replaced by the owner sid in the primary security
|
|
// descriptor.
|
|
//
|
|
|
|
bAceApplicable = AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
AuthzAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
TRUE,
|
|
&LocalCachingFlags
|
|
);
|
|
|
|
//
|
|
// Record the presence of a Deny ace as well as the caching
|
|
// flags for this ace into the global flags.
|
|
//
|
|
|
|
*pCachingFlags |= (LocalCachingFlags | AUTHZ_DENY_ACE_PRESENT);
|
|
|
|
//
|
|
// If the ace is applicable or principal self sid is present then
|
|
// deny the access bits to the cached access check results. We
|
|
// take a pessimistic view and assume that the principal self sid
|
|
// will be applicable in the next access check.
|
|
//
|
|
|
|
if ((bAceApplicable || (0 != LocalCachingFlags)) &&
|
|
(ARGUMENT_PRESENT(LocalCachingTypeList)))
|
|
{
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Deny access bits that have not already been granted.
|
|
//
|
|
|
|
LocalCachingTypeList->CurrentDenied |= (((PKNOWN_ACE) Ace)->Mask & ~LocalCachingTypeList->CurrentGranted);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate deny bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalCachingTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentDenied
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Deny access bits that have not already been granted.
|
|
//
|
|
|
|
LocalTypeList->CurrentDenied |= (((PKNOWN_ACE) Ace)->Mask & ~LocalTypeList->CurrentGranted);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate deny bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentDenied
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_DENIED_CALLBACK_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled or enabled for deny only. S-1-5-A is replaced by
|
|
// the principal sid supplied by the caller. In future, Creator
|
|
// Owner will be replaced by the owner sid in the primary security
|
|
// descriptor.
|
|
//
|
|
|
|
bAceApplicable = AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
AuthzCallbackAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
TRUE,
|
|
&LocalCachingFlags
|
|
);
|
|
|
|
//
|
|
// Record the presence of a dynamic Deny ace as well as the caching
|
|
// flags for this ace into the global flags.
|
|
//
|
|
|
|
*pCachingFlags |= (LocalCachingFlags | AUTHZ_DYNAMIC_DENY_ACE_PRESENT);
|
|
|
|
//
|
|
// If the ace is applicable or principal self sid is present then
|
|
// deny the access bits to the cached access check results. We
|
|
// take a pessimistic view and assume that the principal self sid
|
|
// will be applicable in the next access check.
|
|
//
|
|
|
|
if ((bAceApplicable || (0 != LocalCachingFlags)) &&
|
|
(ARGUMENT_PRESENT(LocalCachingTypeList)))
|
|
{
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Deny access bits that have not already been granted.
|
|
//
|
|
|
|
LocalCachingTypeList->CurrentDenied |= (((PKNOWN_ACE) Ace)->Mask & ~LocalCachingTypeList->CurrentGranted);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate deny bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalCachingTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentDenied
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// If the all the bits from this ace have already been granted
|
|
// or denied, then there is no need to evaluate this callback ace.
|
|
//
|
|
|
|
if (0 == ((((PKNOWN_ACE) Ace)->Mask) &
|
|
~(LocalTypeList->CurrentGranted | LocalTypeList->CurrentDenied)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We have an object type list. Compute whether we need to break
|
|
// out early.
|
|
//
|
|
|
|
bCallbackMayApply = AuthzpIsAccessMaskApplicable(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask
|
|
);
|
|
|
|
if (!bCallbackMayApply)
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
bAceApplicable = FALSE;
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Deny access bits that have not already been granted.
|
|
//
|
|
|
|
LocalTypeList->CurrentDenied |= (((PKNOWN_ACE) Ace)->Mask & ~LocalTypeList->CurrentGranted);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate deny bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentDenied
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_DENIED_OBJECT_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled or enabled for deny only. S-1-5-A is replaced by
|
|
// the principal sid supplied by the caller. In future, Creator
|
|
// Owner will be replaced by the owner sid in the primary security
|
|
// descriptor.
|
|
//
|
|
|
|
bAceApplicable = AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
AuthzObjectAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
TRUE,
|
|
&LocalCachingFlags
|
|
);
|
|
|
|
//
|
|
// Record the presence of a Deny ace as well as the caching
|
|
// flags for this ace into the global flags.
|
|
//
|
|
|
|
*pCachingFlags |= (LocalCachingFlags | AUTHZ_DENY_ACE_PRESENT);
|
|
|
|
ObjectTypeInAce = RtlObjectAceObjectType(Ace);
|
|
|
|
//
|
|
// If the ace is applicable or principal self sid is present then
|
|
// deny the access bits to the cached access check results. We
|
|
// take a pessimistic view and assume that the principal self sid
|
|
// will be applicable in the next access check.
|
|
//
|
|
|
|
if ((bAceApplicable || (0 != LocalCachingFlags)) &&
|
|
(ARGUMENT_PRESENT(LocalCachingTypeList)))
|
|
{
|
|
//
|
|
// An object type ace with a NULL Object Type guid is the same
|
|
// as a normal ace.
|
|
//
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(ObjectTypeInAce))
|
|
{
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Deny access bits that have not already been granted.
|
|
//
|
|
|
|
LocalCachingTypeList->CurrentDenied |= (((PKNOWN_OBJECT_ACE) Ace)->Mask & ~LocalCachingTypeList->CurrentGranted);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate deny bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalCachingTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentDenied
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// So, it is a true object type ace. Proceed only if we are doing
|
|
// access check on a object type list.
|
|
//
|
|
|
|
else if (ObjectTypeListPresent)
|
|
{
|
|
//
|
|
// Look for a matching object type guid that matches the one in
|
|
// the ace.
|
|
//
|
|
|
|
if (AuthzpObjectInTypeList(
|
|
ObjectTypeInAce,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
&Index
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// Propagate deny bits down the tree starting at the
|
|
// index at which the guids matched. Deny bits are
|
|
// propagated to all the ancestors as well.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalCachingTypeList,
|
|
LocalTypeListLength,
|
|
Index,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentDenied
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// An object type ace with a NULL Object Type guid is the same as a
|
|
// normal ace.
|
|
//
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(ObjectTypeInAce))
|
|
{
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Deny access bits that have not already been granted.
|
|
//
|
|
|
|
LocalTypeList->CurrentDenied |= (((PKNOWN_OBJECT_ACE) Ace)->Mask & ~LocalTypeList->CurrentGranted);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate deny bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentDenied
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// So, it is a true object type ace. Proceed only if we are doing
|
|
// access check on a object type list.
|
|
//
|
|
|
|
else if (ObjectTypeListPresent)
|
|
{
|
|
//
|
|
// Look for a matching object type guid that matches the one in
|
|
// the ace.
|
|
//
|
|
|
|
if (AuthzpObjectInTypeList(
|
|
ObjectTypeInAce,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
&Index
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// Propagate deny bits down the tree starting at the
|
|
// index at which the guids matched. Deny bits are
|
|
// propagated to all the ancestors as well.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
Index,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentDenied
|
|
);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled or enabled for deny only. S-1-5-A is replaced by
|
|
// the principal sid supplied by the caller. In future, Creator
|
|
// Owner will be replaced by the owner sid in the primary security
|
|
// descriptor.
|
|
//
|
|
|
|
bAceApplicable = AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
AuthzObjectAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
TRUE,
|
|
&LocalCachingFlags
|
|
);
|
|
|
|
//
|
|
// Record the presence of a dynamic Deny ace as well as the caching
|
|
// flags for this ace into the global flags.
|
|
//
|
|
|
|
*pCachingFlags |= (LocalCachingFlags | AUTHZ_DYNAMIC_DENY_ACE_PRESENT);
|
|
|
|
ObjectTypeInAce = RtlObjectAceObjectType(Ace);
|
|
|
|
//
|
|
// If the ace is applicable or principal self sid is present then
|
|
// deny the access bits to the cached access check results. We
|
|
// take a pessimistic view and assume that the principal self sid
|
|
// will be applicable in the next access check.
|
|
//
|
|
|
|
if ((bAceApplicable || (0 != LocalCachingFlags)) &&
|
|
(ARGUMENT_PRESENT(LocalCachingTypeList)))
|
|
{
|
|
//
|
|
// An object type ace with a NULL Object Type guid is the same
|
|
// as a normal ace.
|
|
//
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(ObjectTypeInAce))
|
|
{
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Deny access bits that have not already been granted.
|
|
//
|
|
|
|
LocalCachingTypeList->CurrentDenied |= (((PKNOWN_OBJECT_ACE) Ace)->Mask & ~LocalCachingTypeList->CurrentGranted);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate deny bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalCachingTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentDenied
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// So, it is a true object type ace. Proceed only if we are doing
|
|
// access check on a object type list.
|
|
//
|
|
|
|
else if (ObjectTypeListPresent)
|
|
{
|
|
//
|
|
// Look for a matching object type guid that matches the one in
|
|
// the ace.
|
|
//
|
|
|
|
if (AuthzpObjectInTypeList(
|
|
ObjectTypeInAce,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
&Index
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// Propagate deny bits down the tree starting at the
|
|
// index at which the guids matched. Deny bits are
|
|
// propagated to all the ancestors as well.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalCachingTypeList,
|
|
LocalTypeListLength,
|
|
Index,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentDenied
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
bAceApplicable = FALSE;
|
|
|
|
//
|
|
// An object type ace with a NULL Object Type guid is the same as a
|
|
// normal ace.
|
|
//
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(ObjectTypeInAce))
|
|
{
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// If the all the bits from this ace have already been granted
|
|
// or denied, then there is no need to evaluate this callback ace.
|
|
//
|
|
|
|
if (0 == ((((PKNOWN_ACE) Ace)->Mask) &
|
|
~(LocalTypeList->CurrentGranted | LocalTypeList->CurrentDenied)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We have an object type list. Compute whether we need to break
|
|
// out early.
|
|
//
|
|
|
|
bCallbackMayApply = AuthzpIsAccessMaskApplicable(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask
|
|
);
|
|
|
|
if (!bCallbackMayApply)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Deny access bits that have not already been granted.
|
|
//
|
|
|
|
LocalTypeList->CurrentDenied |= (((PKNOWN_OBJECT_ACE) Ace)->Mask & ~LocalTypeList->CurrentGranted);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate deny bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentDenied
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// So, it is a true object type ace. Proceed only if we are doing
|
|
// access check on a object type list.
|
|
//
|
|
|
|
else if (ObjectTypeListPresent)
|
|
{
|
|
//
|
|
// Look for a matching object type guid that matches the one in
|
|
// the ace.
|
|
//
|
|
|
|
if (AuthzpObjectInTypeList(
|
|
ObjectTypeInAce,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
&Index
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// We have an object type list. Compute whether we need to break
|
|
// out early.
|
|
//
|
|
|
|
bCallbackMayApply = AuthzpIsAccessMaskApplicable(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
Index,
|
|
((PKNOWN_ACE) Ace)->Mask
|
|
);
|
|
|
|
if (!bCallbackMayApply)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Propagate deny bits down the tree starting at the
|
|
// index at which the guids matched. Deny bits are
|
|
// propagated to all the ancestors as well.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
Index,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentDenied
|
|
);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#if 0
|
|
|
|
BOOL
|
|
AuthzpSidApplicableOrig(
|
|
IN DWORD SidCount,
|
|
IN PSID_AND_ATTRIBUTES pSidAttr,
|
|
IN PSID pAceSid,
|
|
IN PSID PrincipalSelfSid,
|
|
IN PSID CreatorOwnerSid,
|
|
IN BOOL DenyAce,
|
|
OUT PDWORD pCachingFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine decides whether the ace is applicable to the client context.
|
|
|
|
Arguments:
|
|
|
|
SidCount - Number of sids in the pSidAttrArray
|
|
|
|
pSidAttr - Sid and attributes against which the ace sid should be compared.
|
|
|
|
pAceSid - Sid in the ace.
|
|
|
|
PrincipalSelfSid - To replace the ace sid if the ace sid is
|
|
Principal Self Sid (S-1-5-A).
|
|
|
|
CreatorOwnerSid - To replace the ace sid if the ace sid is Creator Owner
|
|
sid (S-1-3-0). This will not be used in the current implementation but
|
|
will come in effect once we do single instancing.
|
|
|
|
DenyAce - Boolean specifying whether the ace is a deny ace.
|
|
|
|
pCachingFlags - To return caching information in the form of:
|
|
AUTHZ_PRINCIPAL_SELF_ACE_PRESENT
|
|
AUTHZ_DYNAMIC_ALLOW_ACE_PRESENT
|
|
AUTHZ_DYNAMIC_DENY_ACE_PRESENT
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the effective sid is found in the client
|
|
context and (is enabled OR enabled for deny only for deny aces).
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i = 0;
|
|
PISID MatchSid = NULL;
|
|
PSID pSid = pAceSid;
|
|
DWORD SidLen = RtlLengthSid(pAceSid);
|
|
|
|
UNREFERENCED_PARAMETER(CreatorOwnerSid);
|
|
|
|
//
|
|
// If the principal ace sid is principal self sid and Principal self sid
|
|
// has been provided then use it.
|
|
//
|
|
|
|
if (AUTHZ_EQUAL_SID(pSid, pAuthzPrincipalSelfSid, SidLen))
|
|
{
|
|
//
|
|
// Record the presence of a principal self sid in this ace.
|
|
//
|
|
|
|
*pCachingFlags |= AUTHZ_PRINCIPAL_SELF_ACE_PRESENT;
|
|
|
|
if (ARGUMENT_PRESENT(PrincipalSelfSid))
|
|
{
|
|
pSid = PrincipalSelfSid;
|
|
SidLen = RtlLengthSid(pSid);
|
|
}
|
|
else
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
#ifdef AUTHZ_SINGLE_INSTANCE
|
|
|
|
//
|
|
// Single instance security descriptor code
|
|
//
|
|
|
|
else if (ARGUMENT_PRESENT(CreatorOwnerSid) && AUTHZ_EQUAL_SID(pSid, pAuthzCreatorOwnerSid, SidLen))
|
|
{
|
|
pSid = CreatorOwnerSid;
|
|
SidLen = RtlLengthSid(pSid);
|
|
}
|
|
|
|
#endif
|
|
|
|
//
|
|
// Loop thru the sids to find a match.
|
|
//
|
|
|
|
for (i = 0; i < SidCount; i++, pSidAttr++)
|
|
{
|
|
MatchSid = (PISID) pSidAttr->Sid;
|
|
|
|
if (AUTHZ_EQUAL_SID(pSid, MatchSid, SidLen))
|
|
{
|
|
//
|
|
// Return TRUE if
|
|
// a. the sid is enabled OR
|
|
// b the sid is enabled for deny only and the ace is a Deny ace.
|
|
// Else
|
|
// return FALSE.
|
|
//
|
|
|
|
if (FLAG_ON(pSidAttr->Attributes, SE_GROUP_ENABLED) ||
|
|
(DenyAce && FLAG_ON(pSidAttr->Attributes, SE_GROUP_USE_FOR_DENY_ONLY))
|
|
)
|
|
{
|
|
|
|
#ifdef AUTHZ_DEBUG
|
|
wprintf(L"Applicable sid = %x, %x, %x, %x, %x, %x, %x\n",
|
|
((DWORD *) MatchSid)[0], ((DWORD *) MatchSid)[1],
|
|
((DWORD *) MatchSid)[2], ((DWORD *) MatchSid)[3],
|
|
((DWORD *) MatchSid)[4], ((DWORD *) MatchSid)[5],
|
|
((DWORD *) MatchSid)[1]);
|
|
#endif
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
#define AUTHZ_SID_HASH_BYTE(s) ((UCHAR)(((PISID)s)->SubAuthority[((PISID)s)->SubAuthorityCount - 1]))
|
|
|
|
|
|
VOID
|
|
AuthzpInitSidHash(
|
|
IN PSID_AND_ATTRIBUTES pSidAttr,
|
|
IN ULONG SidCount,
|
|
OUT PAUTHZI_SID_HASH_ENTRY pHash
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Initializes the SID hash table.
|
|
|
|
Arguments
|
|
|
|
pSidAttr - array of sids to store in hash.
|
|
|
|
SidCount - number of sids in array.
|
|
|
|
pHash - pointer to the sid hash table.
|
|
|
|
Return Value
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i = 0;
|
|
ULONG PositionBit = 0;
|
|
BYTE HashByte = 0;
|
|
|
|
//
|
|
// Zero the table.
|
|
//
|
|
|
|
RtlZeroMemory(
|
|
pHash,
|
|
sizeof(AUTHZI_SID_HASH_ENTRY) * AUTHZI_SID_HASH_SIZE
|
|
);
|
|
|
|
if (pSidAttr == NULL)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Can only hash the number of sids that each table entry can hold
|
|
//
|
|
|
|
if (SidCount > AUTHZI_SID_HASH_ENTRY_NUM_BITS)
|
|
{
|
|
SidCount = AUTHZI_SID_HASH_ENTRY_NUM_BITS;
|
|
}
|
|
|
|
for (i = 0; i < SidCount; i++)
|
|
{
|
|
//
|
|
// HashByte is last byte in SID.
|
|
//
|
|
|
|
HashByte = AUTHZ_SID_HASH_BYTE(pSidAttr[i].Sid);
|
|
|
|
//
|
|
// Position indicates the location of this SID in pSidAttr.
|
|
//
|
|
|
|
PositionBit = 1 << i;
|
|
|
|
//
|
|
// Store the position bit in the low hash indexed by the low order bits of the HashByte
|
|
//
|
|
|
|
pHash[(HashByte & AUTHZ_SID_HASH_LOW_MASK)] |= PositionBit;
|
|
|
|
//
|
|
// Store the position bit in the high hash indexed by the high order bits of the HashByte
|
|
//
|
|
|
|
pHash[AUTHZ_SID_HASH_HIGH + ((HashByte & AUTHZ_SID_HASH_HIGH_MASK) >> 4)] |= PositionBit;
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpSidApplicable(
|
|
IN DWORD SidCount,
|
|
IN PSID_AND_ATTRIBUTES pSidAttr,
|
|
IN PAUTHZI_SID_HASH_ENTRY pHash,
|
|
IN PSID pAceSid,
|
|
IN PSID PrincipalSelfSid,
|
|
IN PSID CreatorOwnerSid,
|
|
IN BOOL DenyAce,
|
|
OUT PDWORD pCachingFlags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine decides whether the ace is applicable to the client context.
|
|
|
|
Arguments:
|
|
|
|
SidCount - Number of sids in the pSidAttrArray
|
|
|
|
pSidAttr - Sid and attributes against which the ace sid should be compared.
|
|
|
|
pHash - Hash table of the pSidAttr array.
|
|
|
|
pAceSid - Sid in the ace.
|
|
|
|
PrincipalSelfSid - To replace the ace sid if the ace sid is
|
|
Principal Self Sid (S-1-5-A).
|
|
|
|
CreatorOwnerSid - To replace the ace sid if the ace sid is Creator Owner
|
|
sid (S-1-3-0). This will not be used in the current implementation but
|
|
will come in effect once we do single instancing.
|
|
|
|
DenyAce - Boolean specifying whether the ace is a deny ace.
|
|
|
|
pCachingFlags - To return caching information in the form of:
|
|
AUTHZ_PRINCIPAL_SELF_ACE_PRESENT
|
|
AUTHZ_DYNAMIC_ALLOW_ACE_PRESENT
|
|
AUTHZ_DYNAMIC_DENY_ACE_PRESENT
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the effective sid is found in the client
|
|
context and (is enabled OR enabled for deny only for deny aces).
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD SidLen;
|
|
DWORD BitIndex;
|
|
UCHAR HashByte;
|
|
DWORD i;
|
|
UCHAR ByteToExamine;
|
|
UCHAR ByteOffset;
|
|
AUTHZI_SID_HASH_ENTRY SidPositionBitMap;
|
|
PSID_AND_ATTRIBUTES pSA;
|
|
|
|
UNREFERENCED_PARAMETER(CreatorOwnerSid);
|
|
|
|
//
|
|
// If the principal ace sid is principal self sid and Principal self sid
|
|
// has been provided then use it.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(PrincipalSelfSid))
|
|
{
|
|
SidLen = RtlLengthSid(pAceSid);
|
|
|
|
if ((SidLen == AuthzpPrincipalSelfSidLen) &&
|
|
(AUTHZ_EQUAL_SID(pAceSid, pAuthzPrincipalSelfSid, SidLen)))
|
|
{
|
|
//
|
|
// Record the presence of a principal self sid in this ace.
|
|
//
|
|
|
|
*pCachingFlags |= AUTHZ_PRINCIPAL_SELF_ACE_PRESENT;
|
|
|
|
pAceSid = PrincipalSelfSid;
|
|
SidLen = RtlLengthSid(pAceSid);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Index into pHash by the last byte of the SID. The resulting value (SidPositionBitMap)
|
|
// indicates the locations of SIDs in a corresponding SID_AND_ATTRIBUTES array that match
|
|
// that last byte.
|
|
//
|
|
|
|
HashByte = AUTHZ_SID_HASH_BYTE(pAceSid);
|
|
SidPositionBitMap = AUTHZ_SID_HASH_LOOKUP(pHash, HashByte);
|
|
SidLen = RtlLengthSid(pAceSid);
|
|
ByteOffset = 0;
|
|
|
|
while (0 != SidPositionBitMap)
|
|
{
|
|
ByteToExamine = (UCHAR)SidPositionBitMap;
|
|
|
|
while (ByteToExamine)
|
|
{
|
|
//
|
|
// Get the first nonzero bit in ByteToExamine
|
|
//
|
|
|
|
BitIndex = AuthzpByteToIndexLookupTable[ByteToExamine];
|
|
|
|
//
|
|
// Find the PSID_AND_ATTRIBUTES to which the bit refers.
|
|
//
|
|
|
|
pSA = &pSidAttr[ByteOffset + BitIndex];
|
|
|
|
if (AUTHZ_EQUAL_SID(pAceSid, pSA->Sid, SidLen))
|
|
{
|
|
if ((FLAG_ON(pSA->Attributes, SE_GROUP_ENABLED)) ||
|
|
(DenyAce && FLAG_ON(pSA->Attributes, SE_GROUP_USE_FOR_DENY_ONLY)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Turn the current bit off and try the next SID.
|
|
//
|
|
|
|
ByteToExamine ^= (1 << BitIndex);
|
|
}
|
|
|
|
ByteOffset = ByteOffset + sizeof(UCHAR);
|
|
SidPositionBitMap = SidPositionBitMap >> sizeof(UCHAR);
|
|
}
|
|
|
|
//
|
|
// If a matching sid was not found in the pHash and there are SIDS in pSidAttr which did not
|
|
// get placed in pHash, then search those SIDS for a match.
|
|
//
|
|
|
|
for (i = AUTHZI_SID_HASH_ENTRY_NUM_BITS; i < SidCount; i++)
|
|
{
|
|
pSA = &pSidAttr[i];
|
|
|
|
if (AUTHZ_EQUAL_SID(pAceSid, pSA->Sid, SidLen))
|
|
{
|
|
if ((FLAG_ON(pSA->Attributes, SE_GROUP_ENABLED)) ||
|
|
(DenyAce && FLAG_ON(pSA->Attributes, SE_GROUP_USE_FOR_DENY_ONLY)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpAccessCheckWithCaching(
|
|
IN DWORD Flags,
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
IN PSECURITY_DESCRIPTOR *OptionalSecurityDescriptorArray OPTIONAL,
|
|
IN DWORD OptionalSecurityDescriptorCount,
|
|
IN OUT PAUTHZ_ACCESS_REPLY pReply,
|
|
OUT PAUTHZ_ACCESS_CHECK_RESULTS_HANDLE phAccessCheckResults OPTIONAL,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalCachingTypeList OPTIONAL,
|
|
IN DWORD LocalTypeListLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine does a MaximumAllowed access check. It is called if any of the
|
|
following is TRUE
|
|
1. RM has requested for caching
|
|
2. DesiredAccessMask has MAXIMUM_ALLOWED turned on.
|
|
3. ObjectTypeList is present and pReply->ResultList has a length > 1
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pSecurityDescriptor - Primary security descriptor to be used for access
|
|
checks. The owner sid for the object is picked from this one. A NULL
|
|
DACL in this security descriptor represents a NULL DACL for the entire
|
|
object. A NULL SACL in this security descriptor is treated the same way
|
|
as an EMPTY SACL.
|
|
|
|
OptionalSecurityDescriptorArray - The caller may optionally specify a list
|
|
of security descriptors. NULL ACLs in these security descriptors are
|
|
treated as EMPTY ACLS and the ACL for the entire object is the logical
|
|
concatenation of all the ACLs.
|
|
|
|
OptionalSecurityDescriptorCount - Number of optional security descriptors
|
|
This does not include the Primary security descriptor.
|
|
|
|
pReply - To return the results of access check call.
|
|
|
|
phAccessCheckResults - To return a handle to cached results of the access check
|
|
call.
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results.
|
|
|
|
LocalCachingTypeList - Internal object type list structure used to hold
|
|
intermediate results for static granted bits.
|
|
|
|
LocalTypeListLength - Length of the array represnting the object.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
ACCESS_MASK PreviouslyGrantedAccess = 0;
|
|
DWORD CachingFlags = 0;
|
|
DWORD i = 0;
|
|
BOOL b = TRUE;
|
|
PACL pAcl = NULL;
|
|
|
|
//
|
|
// Owner is always granted READ_CONTROL and WRITE_DAC.
|
|
//
|
|
|
|
if (AuthzpOwnerSidInClientContext(pCC, pSecurityDescriptor))
|
|
{
|
|
PreviouslyGrantedAccess |= (WRITE_DAC | READ_CONTROL);
|
|
}
|
|
|
|
//
|
|
// Take ownership privilege grants WRITE_OWNER.
|
|
//
|
|
|
|
if (AUTHZ_PRIVILEGE_CHECK(pCC, AUTHZ_TAKE_OWNERSHIP_PRIVILEGE_ENABLED))
|
|
{
|
|
PreviouslyGrantedAccess |= WRITE_OWNER;
|
|
}
|
|
|
|
//
|
|
// SecurityPrivilege grants ACCESS_SYSTEM_SECURITY.
|
|
//
|
|
|
|
if (AUTHZ_PRIVILEGE_CHECK(pCC, AUTHZ_SECURITY_PRIVILEGE_ENABLED))
|
|
{
|
|
PreviouslyGrantedAccess |= ACCESS_SYSTEM_SECURITY;
|
|
}
|
|
else if (FLAG_ON(pRequest->DesiredAccess, ACCESS_SYSTEM_SECURITY))
|
|
{
|
|
AuthzpFillReplyStructure(
|
|
pReply,
|
|
ERROR_PRIVILEGE_NOT_HELD,
|
|
0
|
|
);
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
return TRUE;
|
|
}
|
|
|
|
pAcl = RtlpDaclAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) pSecurityDescriptor);
|
|
|
|
//
|
|
// NULL Dacl is synonymous with Full Control.
|
|
//
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(pAcl))
|
|
{
|
|
PreviouslyGrantedAccess |= (STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL);
|
|
|
|
for (i = 0; i < LocalTypeListLength; i++)
|
|
{
|
|
LocalTypeList[i].CurrentGranted |= PreviouslyGrantedAccess;
|
|
|
|
if (ARGUMENT_PRESENT(LocalCachingTypeList))
|
|
{
|
|
LocalCachingTypeList[i].CurrentGranted |= PreviouslyGrantedAccess;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < LocalTypeListLength; i++)
|
|
{
|
|
LocalTypeList[i].CurrentGranted |= PreviouslyGrantedAccess;
|
|
|
|
if (ARGUMENT_PRESENT(LocalCachingTypeList))
|
|
{
|
|
LocalCachingTypeList[i].CurrentGranted |= PreviouslyGrantedAccess;
|
|
}
|
|
}
|
|
|
|
b = AuthzpMaximumAllowedAccessCheck(
|
|
pCC,
|
|
pRequest,
|
|
pSecurityDescriptor,
|
|
OptionalSecurityDescriptorArray,
|
|
OptionalSecurityDescriptorCount,
|
|
LocalTypeList,
|
|
LocalCachingTypeList,
|
|
LocalTypeListLength,
|
|
0 != pRequest->ObjectTypeListLength,
|
|
&CachingFlags
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If the caller asked for caching then allocate a handle, store the results
|
|
// of the static access check in it and insert it into the list of handles.
|
|
//
|
|
|
|
if (ARGUMENT_PRESENT(phAccessCheckResults))
|
|
{
|
|
b = AuthzpCacheResults(
|
|
Flags,
|
|
pCC,
|
|
LocalCachingTypeList,
|
|
LocalTypeListLength,
|
|
pSecurityDescriptor,
|
|
OptionalSecurityDescriptorArray,
|
|
OptionalSecurityDescriptorCount,
|
|
CachingFlags,
|
|
phAccessCheckResults
|
|
);
|
|
|
|
if (!b) goto Cleanup;
|
|
}
|
|
|
|
AuthzpFillReplyFromParameters(
|
|
pRequest,
|
|
pReply,
|
|
LocalTypeList
|
|
);
|
|
|
|
Cleanup:
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
VOID
|
|
AuthzpFillReplyFromParameters(
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN OUT PAUTHZ_ACCESS_REPLY pReply,
|
|
IN PIOBJECT_TYPE_LIST LocalTypeList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine fills in the reply structure wih the results of access check
|
|
supplied by LocalTypeList.
|
|
|
|
Arguments:
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pReply - The reply structure to fill.
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results. This is used to fill the reply structure.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ACCESS_MASK DesiredAccess = 0;
|
|
ACCESS_MASK RelevantAccess = 0;
|
|
DWORD i = 0;
|
|
|
|
if (FLAG_ON(pRequest->DesiredAccess, ACCESS_SYSTEM_SECURITY))
|
|
{
|
|
RelevantAccess = STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL | ACCESS_SYSTEM_SECURITY;
|
|
}
|
|
else
|
|
{
|
|
RelevantAccess = STANDARD_RIGHTS_ALL | SPECIFIC_RIGHTS_ALL;
|
|
}
|
|
|
|
if (FLAG_ON(pRequest->DesiredAccess, MAXIMUM_ALLOWED))
|
|
{
|
|
DesiredAccess = pRequest->DesiredAccess & ~(MAXIMUM_ALLOWED);
|
|
|
|
for (i = 0; i < pReply->ResultListLength; i++)
|
|
{
|
|
if (FLAG_ON(DesiredAccess, ~(LocalTypeList[i].CurrentGranted)) ||
|
|
(0 == LocalTypeList[i].CurrentGranted))
|
|
{
|
|
pReply->GrantedAccessMask[i] = 0;
|
|
pReply->Error[i] = ERROR_ACCESS_DENIED;
|
|
}
|
|
else
|
|
{
|
|
pReply->GrantedAccessMask[i] = LocalTypeList[i].CurrentGranted & RelevantAccess;
|
|
pReply->Error[i] = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < pReply->ResultListLength; i++)
|
|
{
|
|
if (FLAG_ON(pRequest->DesiredAccess, ~(LocalTypeList[i].CurrentGranted)))
|
|
{
|
|
pReply->GrantedAccessMask[i] = 0;
|
|
pReply->Error[i] = ERROR_ACCESS_DENIED;
|
|
}
|
|
else
|
|
{
|
|
pReply->GrantedAccessMask[i] = pRequest->DesiredAccess & RelevantAccess;
|
|
pReply->Error[i] = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpNormalAccessCheckWithoutCaching(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
IN PSECURITY_DESCRIPTOR *OptionalSecurityDescriptorArray OPTIONAL,
|
|
IN DWORD OptionalSecurityDescriptorCount,
|
|
IN OUT PAUTHZ_ACCESS_REPLY pReply,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList,
|
|
IN DWORD LocalTypeListLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine does a normal access check. If desired access is denied at any
|
|
point then it stops acl evaluation.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pSecurityDescriptor - Primary security descriptor to be used for access
|
|
checks. The owner sid for the object is picked from this one. A NULL
|
|
DACL in this security descriptor represents a NULL DACL for the entire
|
|
object. A NULL SACL in this security descriptor is treated the same way
|
|
as an EMPTY SACL.
|
|
|
|
OptionalSecurityDescriptorArray - The caller may optionally specify a list
|
|
of security descriptors. NULL ACLs in these security descriptors are
|
|
treated as EMPTY ACLS and the ACL for the entire object is the logical
|
|
concatenation of all the ACLs.
|
|
|
|
OptionalSecurityDescriptorCount - Number of optional security descriptors
|
|
This does not include the Primary security descriptor.
|
|
|
|
pReply - The reply structure to return the results.
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results.
|
|
|
|
LocalTypeListLength - Length of the array represnting the object.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
ACCESS_MASK Remaining = pRequest->DesiredAccess;
|
|
PACL pAcl = NULL;
|
|
BOOL b = TRUE;
|
|
|
|
//
|
|
// Check for SeSecurityPrivilege.
|
|
//
|
|
|
|
if (FLAG_ON(Remaining, ACCESS_SYSTEM_SECURITY))
|
|
{
|
|
if (AUTHZ_PRIVILEGE_CHECK(pCC, AUTHZ_SECURITY_PRIVILEGE_ENABLED))
|
|
{
|
|
Remaining &= ~(ACCESS_SYSTEM_SECURITY);
|
|
}
|
|
else
|
|
{
|
|
AuthzpFillReplyStructure(
|
|
pReply,
|
|
ERROR_PRIVILEGE_NOT_HELD,
|
|
0
|
|
);
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Ownership of the object grants READ_CONTROL and WRITE_DAC.
|
|
//
|
|
|
|
if (FLAG_ON(Remaining, (READ_CONTROL | WRITE_DAC)) &&
|
|
AuthzpOwnerSidInClientContext(pCC, pSecurityDescriptor))
|
|
{
|
|
Remaining &= ~(WRITE_DAC | READ_CONTROL);
|
|
}
|
|
|
|
//
|
|
// SeTakeOwnership privilege grants WRITE_OWNER.
|
|
//
|
|
|
|
if (FLAG_ON(Remaining, WRITE_OWNER) &&
|
|
AUTHZ_PRIVILEGE_CHECK(pCC, AUTHZ_TAKE_OWNERSHIP_PRIVILEGE_ENABLED))
|
|
{
|
|
Remaining &= ~(WRITE_OWNER);
|
|
}
|
|
|
|
pAcl = RtlpDaclAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) pSecurityDescriptor);
|
|
|
|
//
|
|
// Null acl represents FULL CONTROL.
|
|
//
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(pAcl))
|
|
{
|
|
Remaining = 0;
|
|
}
|
|
|
|
//
|
|
// If we have been granted access at this point then acl evaluation is not
|
|
// needed.
|
|
//
|
|
|
|
if (0 == Remaining)
|
|
{
|
|
AuthzpFillReplyStructure(
|
|
pReply,
|
|
ERROR_SUCCESS,
|
|
pRequest->DesiredAccess
|
|
);
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Do the access check with the non-restricted part of the client context.
|
|
//
|
|
|
|
b = AuthzpNormalMultipleSDAccessCheck(
|
|
pCC,
|
|
pCC->Sids,
|
|
pCC->SidCount,
|
|
pCC->SidHash,
|
|
Remaining,
|
|
pRequest,
|
|
pSecurityDescriptor,
|
|
OptionalSecurityDescriptorArray,
|
|
OptionalSecurityDescriptorCount,
|
|
LocalTypeList,
|
|
LocalTypeListLength
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
#ifdef AUTHZ_DEBUG
|
|
wprintf(L"Remaining = %x, LocalTypeList = %x\n", Remaining, LocalTypeList->Remaining);
|
|
#endif
|
|
|
|
if (0 != LocalTypeList->Remaining)
|
|
{
|
|
AuthzpFillReplyStructure(
|
|
pReply,
|
|
ERROR_ACCESS_DENIED,
|
|
0
|
|
);
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// If the client context is resticted then even the resticted part has to
|
|
// grant all the access bits that were asked.
|
|
//
|
|
|
|
if (AUTHZ_TOKEN_RESTRICTED(pCC))
|
|
{
|
|
b = AuthzpNormalMultipleSDAccessCheck(
|
|
pCC,
|
|
pCC->RestrictedSids,
|
|
pCC->RestrictedSidCount,
|
|
pCC->RestrictedSidHash,
|
|
Remaining,
|
|
pRequest,
|
|
pSecurityDescriptor,
|
|
OptionalSecurityDescriptorArray,
|
|
OptionalSecurityDescriptorCount,
|
|
LocalTypeList,
|
|
LocalTypeListLength
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (0 != LocalTypeList->Remaining)
|
|
{
|
|
AuthzpFillReplyStructure(
|
|
pReply,
|
|
ERROR_ACCESS_DENIED,
|
|
0
|
|
);
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we made it till here then all access bits have been granted.
|
|
//
|
|
|
|
AuthzpFillReplyStructure(
|
|
pReply,
|
|
ERROR_SUCCESS,
|
|
pRequest->DesiredAccess
|
|
);
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpNormalMultipleSDAccessCheck(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PSID_AND_ATTRIBUTES pSidAttr,
|
|
IN DWORD SidCount,
|
|
IN PAUTHZI_SID_HASH_ENTRY pSidHash,
|
|
IN ACCESS_MASK Remaining,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
IN PSECURITY_DESCRIPTOR *OptionalSecurityDescriptorArray OPTIONAL,
|
|
IN DWORD OptionalSecurityDescriptorCount,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList OPTIONAL,
|
|
IN DWORD LocalTypeListLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine loops thru all the security descriptors and calls
|
|
AuthzpNormalAccessCheck.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pSidAttr - Sids and attributes array to compare ace sids.
|
|
|
|
SidCount - Number of elements in the array.
|
|
|
|
Remaining - Access bits that are yet to be granted.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pSecurityDescriptor - Primary security descriptor to be used for access
|
|
checks. The owner sid for the object is picked from this one. A NULL
|
|
DACL in this security descriptor represents a NULL DACL for the entire
|
|
object. A NULL SACL in this security descriptor is treated the same way
|
|
as an EMPTY SACL.
|
|
|
|
OptionalSecurityDescriptorArray - The caller may optionally specify a list
|
|
of security descriptors. NULL ACLs in these security descriptors are
|
|
treated as EMPTY ACLS and the ACL for the entire object is the logical
|
|
concatenation of all the ACLs.
|
|
|
|
OptionalSecurityDescriptorCount - Number of optional security descriptors
|
|
This does not include the Primary security descriptor.
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results.
|
|
|
|
LocalTypeListLength - Length of the array represnting the object.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PSID pOwnerSid = RtlpOwnerAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) pSecurityDescriptor);
|
|
PACL pAcl = RtlpDaclAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) pSecurityDescriptor);
|
|
BOOL b = TRUE;
|
|
DWORD i = 0;
|
|
|
|
ASSERT(AUTHZ_NON_NULL_PTR(pAcl));
|
|
|
|
b = AuthzpNormalAccessCheck(
|
|
pCC,
|
|
pSidAttr,
|
|
SidCount,
|
|
pSidHash,
|
|
Remaining,
|
|
pRequest,
|
|
pAcl,
|
|
pOwnerSid,
|
|
LocalTypeList,
|
|
LocalTypeListLength
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < OptionalSecurityDescriptorCount; i++)
|
|
{
|
|
pAcl = RtlpDaclAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) OptionalSecurityDescriptorArray[i]);
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(pAcl))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
b = AuthzpNormalAccessCheck(
|
|
pCC,
|
|
pSidAttr,
|
|
SidCount,
|
|
pSidHash,
|
|
LocalTypeList->Remaining,
|
|
pRequest,
|
|
pAcl,
|
|
pOwnerSid,
|
|
LocalTypeList,
|
|
LocalTypeListLength
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpOwnerSidInClientContext(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PISECURITY_DESCRIPTOR pSecurityDescriptor
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine determines if client context is the owner of the given object.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pSecurityDescriptor - To supply the owner sid for the object.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the owner sid in the security descriptor is
|
|
present in the normal part (as well as the restricted part, if it exists).
|
|
|
|
--*/
|
|
|
|
{
|
|
PSID pOwnerSid = RtlpOwnerAddrSecurityDescriptor(pSecurityDescriptor);
|
|
BOOL b = TRUE;
|
|
|
|
//
|
|
// Check if the sid exists in the normal part of the token.
|
|
//
|
|
|
|
b = AuthzpSidApplicable(
|
|
pCC->SidCount,
|
|
pCC->Sids,
|
|
pCC->SidHash,
|
|
pOwnerSid,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If the token is restricted then the sid must exist in the restricted part
|
|
// of the token.
|
|
//
|
|
|
|
if (AUTHZ_TOKEN_RESTRICTED(pCC))
|
|
{
|
|
|
|
b = AuthzpSidApplicable(
|
|
pCC->RestrictedSidCount,
|
|
pCC->RestrictedSids,
|
|
pCC->RestrictedSidHash,
|
|
pOwnerSid,
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
return b;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
AuthzpNormalAccessCheck(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PSID_AND_ATTRIBUTES pSidAttr,
|
|
IN DWORD SidCount,
|
|
IN PAUTHZI_SID_HASH_ENTRY pSidHash,
|
|
IN ACCESS_MASK Remaining,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN PACL pAcl,
|
|
IN PSID pOwnerSid,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList,
|
|
IN DWORD LocalTypeListLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine loops thru the acl till we are denied some access bit that the
|
|
caller asked for or the acl is exhausted.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pSidAttr - Sids and attributes array to compare ace sids.
|
|
|
|
SidCount - Number of elements in the array.
|
|
|
|
Remaining - Access bits that are yet to be granted.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pAcl - Dacl against which the access check will be performed.
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results.
|
|
|
|
LocalCachingTypeList - Internal object type list structure used to hold
|
|
intermediate results for static granted bits.
|
|
|
|
LocalTypeListLength - Length of the array represnting the object.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i = 0;
|
|
DWORD AceCount = 0;
|
|
DWORD Index = 0;
|
|
DWORD Ignore = 0;
|
|
PVOID Ace = NULL;
|
|
GUID * ObjectTypeInAce = NULL;
|
|
BOOL ObjectTypeListPresent = (0 != pRequest->ObjectTypeListLength);
|
|
BOOL bAceApplicable = FALSE;
|
|
|
|
for (i = 0; i < LocalTypeListLength; i++)
|
|
{
|
|
LocalTypeList[i].Remaining = Remaining;
|
|
}
|
|
|
|
AceCount = pAcl->AceCount;
|
|
|
|
for (i = 0, Ace = FirstAce(pAcl);
|
|
(i < AceCount) && (LocalTypeList[0].Remaining != 0);
|
|
i++, Ace = NextAce(Ace))
|
|
{
|
|
//
|
|
// Skip INHERIT_ONLY aces.
|
|
//
|
|
|
|
if (FLAG_ON(((PACE_HEADER) Ace)->AceFlags, INHERIT_ONLY_ACE))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
switch (((PACE_HEADER) Ace)->AceType)
|
|
{
|
|
|
|
case ACCESS_ALLOWED_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled. S-1-5-A is replaced by the principal sid
|
|
// supplied by the caller. In future, Creator Owner will be replaced
|
|
// by the owner sid in the primary security descriptor.
|
|
//
|
|
|
|
if (!AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
AuthzAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
FALSE,
|
|
&Ignore
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
|
|
#ifdef AUTHZ_DEBUG
|
|
wprintf(L"Allowed access Mask = %x\n", ((PKNOWN_ACE) Ace)->Mask);
|
|
#endif
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Clear the granted bits from the remaining.
|
|
//
|
|
|
|
LocalTypeList->Remaining &= ~((PKNOWN_ACE) Ace)->Mask;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Clear off the granted bits from the remaining for the entire
|
|
// tree.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask,
|
|
AuthzUpdateRemaining
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_CALLBACK_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled. S-1-5-A is replaced by the principal sid
|
|
// supplied by the caller. In future, Creator Owner will be replaced
|
|
// by the owner sid in the primary security descriptor.
|
|
//
|
|
|
|
if (!AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
AuthzAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
FALSE,
|
|
&Ignore
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If the all the bits granted by this ace have already been granted
|
|
// or denied, then there is no need to evaluate this callback ace.
|
|
//
|
|
|
|
if (0 == (LocalTypeList->Remaining & ((PKNOWN_ACE) Ace)->Mask))
|
|
{
|
|
break;
|
|
}
|
|
|
|
bAceApplicable = FALSE;
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Clear the granted bits from the remaining.
|
|
//
|
|
|
|
LocalTypeList->Remaining &= ~((PKNOWN_ACE) Ace)->Mask;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Clear off the granted bits from the remaining for the entire
|
|
// tree.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask,
|
|
AuthzUpdateRemaining
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_COMPOUND_ACE_TYPE:
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(pCC->Server))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
RtlCompoundAceClientSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
FALSE,
|
|
&Ignore
|
|
) ||
|
|
!AuthzpSidApplicable(
|
|
pCC->Server->SidCount,
|
|
pCC->Server->Sids,
|
|
pCC->Server->SidHash,
|
|
RtlCompoundAceServerSid(Ace),
|
|
NULL,
|
|
NULL,
|
|
FALSE,
|
|
&Ignore
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Clear the granted bits from the remaining.
|
|
//
|
|
|
|
LocalTypeList->Remaining &= ~((PKNOWN_ACE) Ace)->Mask;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Clear off the granted bits from the remaining for the entire
|
|
// tree.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PCOMPOUND_ACCESS_ALLOWED_ACE) Ace)->Mask,
|
|
AuthzUpdateRemaining
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_DENIED_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled or enabled for deny only. S-1-5-A is replaced by
|
|
// the principal sid supplied by the caller. In future, Creator
|
|
// Owner will be replaced by the owner sid in the primary security
|
|
// descriptor.
|
|
//
|
|
|
|
if (!AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
AuthzAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
TRUE,
|
|
&Ignore
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
#ifdef AUTHZ_DEBUG
|
|
wprintf(L"Allowed access Mask = %x\n", ((PKNOWN_ACE) Ace)->Mask);
|
|
#endif
|
|
|
|
//
|
|
// If any of the remaining bits are denied by this ace, exit early.
|
|
//
|
|
|
|
if (LocalTypeList->Remaining & ((PKNOWN_ACE) Ace)->Mask)
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_DENIED_CALLBACK_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled or enabled for deny only. S-1-5-A is replaced by
|
|
// the principal sid supplied by the caller. In future, Creator
|
|
// Owner will be replaced by the owner sid in the primary security
|
|
// descriptor.
|
|
//
|
|
|
|
if (!AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
AuthzCallbackAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
TRUE,
|
|
&Ignore
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If none of the bits denied by this ace are desired, then
|
|
// there is no need to evaluate this callback ace.
|
|
//
|
|
|
|
if (0 == (LocalTypeList->Remaining & ((PKNOWN_ACE) Ace)->Mask))
|
|
{
|
|
break;
|
|
}
|
|
|
|
bAceApplicable = FALSE;
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Exit early.
|
|
//
|
|
|
|
return TRUE;
|
|
|
|
case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
|
|
|
|
ObjectTypeInAce = RtlObjectAceObjectType(Ace);
|
|
|
|
if ((ObjectTypeInAce) && (0 == pRequest->ObjectTypeListLength))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled. S-1-5-A is replaced by the principal sid
|
|
// supplied by the caller. In future, Creator Owner will be replaced
|
|
// by the owner sid in the primary security descriptor.
|
|
//
|
|
|
|
if (!AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
RtlObjectAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
FALSE,
|
|
&Ignore
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// An object type ace with a NULL Object Type guid is the same as a
|
|
// normal ace.
|
|
//
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(ObjectTypeInAce))
|
|
{
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Clear the granted bits from the remaining.
|
|
//
|
|
|
|
LocalTypeList->Remaining &= ~((PKNOWN_OBJECT_ACE) Ace)->Mask;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Clear off the granted bits from the remaining for the entire
|
|
// tree.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateRemaining
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// So, it is a true object type ace. Proceed only if we are doing
|
|
// access check on a object type list.
|
|
//
|
|
|
|
else if (ObjectTypeListPresent)
|
|
{
|
|
//
|
|
// Look for a matching object type guid that matches the one in
|
|
// the ace.
|
|
//
|
|
|
|
if (AuthzpObjectInTypeList(
|
|
ObjectTypeInAce,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
&Index
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// Clear off the granted bits from the remaining for the
|
|
// subtree starting at the matched Index.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
Index,
|
|
((PKNOWN_OBJECT_ACE)Ace)->Mask,
|
|
AuthzUpdateRemaining
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled. S-1-5-A is replaced by the principal sid
|
|
// supplied by the caller. In future, Creator Owner will be replaced
|
|
// by the owner sid in the primary security descriptor.
|
|
//
|
|
|
|
if (!AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
AuthzObjectAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
FALSE,
|
|
&Ignore
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
bAceApplicable = FALSE;
|
|
|
|
ObjectTypeInAce = RtlObjectAceObjectType(Ace);
|
|
|
|
//
|
|
// An object type ace with a NULL Object Type guid is the same as a
|
|
// normal ace.
|
|
//
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(ObjectTypeInAce))
|
|
{
|
|
//
|
|
// If none of the bits granted by this ace are desired, then
|
|
// there is no need to evaluate this callback ace.
|
|
//
|
|
|
|
if (0 == (LocalTypeList->Remaining & ((PKNOWN_OBJECT_ACE) Ace)->Mask))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Clear the granted bits from the remaining.
|
|
//
|
|
|
|
LocalTypeList->Remaining &= ~((PKNOWN_OBJECT_ACE) Ace)->Mask;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Clear off the granted bits from the remaining for the entire
|
|
// tree.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateRemaining
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// So, it is a true object type ace. Proceed only if we are doing
|
|
// access check on a object type list.
|
|
//
|
|
|
|
else if (ObjectTypeListPresent)
|
|
{
|
|
//
|
|
// Look for a matching object type guid that matches the one in
|
|
// the ace.
|
|
//
|
|
|
|
if (AuthzpObjectInTypeList(
|
|
ObjectTypeInAce,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
&Index
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// If none of the bits granted by this ace are desired, then
|
|
// there is no need to evaluate this callback ace.
|
|
//
|
|
|
|
if (0 == (LocalTypeList[Index].Remaining & ((PKNOWN_OBJECT_ACE) Ace)->Mask))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Clear off the granted bits from the remaining for the
|
|
// subtree starting at the matched Index.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
Index,
|
|
((PKNOWN_OBJECT_ACE)Ace)->Mask,
|
|
AuthzUpdateRemaining
|
|
);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_DENIED_OBJECT_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled or enabled for deny only. S-1-5-A is replaced by
|
|
// the principal sid supplied by the caller. In future, Creator
|
|
// Owner will be replaced by the owner sid in the primary security
|
|
// descriptor.
|
|
//
|
|
|
|
if (!AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
AuthzObjectAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
TRUE,
|
|
&Ignore
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
ObjectTypeInAce = RtlObjectAceObjectType(Ace);
|
|
|
|
//
|
|
// An object type ace with a NULL Object Type guid is the same as a
|
|
// normal ace.
|
|
//
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(ObjectTypeInAce))
|
|
{
|
|
//
|
|
// If any of the remaining bits are denied by this ace, exit
|
|
// early.
|
|
//
|
|
|
|
if (LocalTypeList->Remaining & ((PKNOWN_OBJECT_ACE) Ace)->Mask)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Look for a matching object type guid that matches the one in
|
|
// the ace.
|
|
//
|
|
|
|
if (AuthzpObjectInTypeList(
|
|
ObjectTypeInAce,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
&Index
|
|
))
|
|
{
|
|
//
|
|
// If any of the remaining bits are denied by this ace, exit
|
|
// early.
|
|
//
|
|
|
|
if (LocalTypeList[Index].Remaining & ((PKNOWN_OBJECT_ACE) Ace)->Mask)
|
|
{
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled or enabled for deny only. S-1-5-A is replaced by
|
|
// the principal sid supplied by the caller. In future, Creator
|
|
// Owner will be replaced by the owner sid in the primary security
|
|
// descriptor.
|
|
//
|
|
|
|
if (!AuthzpSidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
AuthzObjectAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
TRUE,
|
|
&Ignore
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
bAceApplicable = FALSE;
|
|
|
|
ObjectTypeInAce = RtlObjectAceObjectType(Ace);
|
|
|
|
//
|
|
// An object type ace with a NULL Object Type guid is the same as a
|
|
// normal ace.
|
|
//
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(ObjectTypeInAce))
|
|
{
|
|
//
|
|
// If none of the bits denied by this ace are desired, then
|
|
// there is no need to evaluate this callback ace.
|
|
//
|
|
|
|
if (0 == (LocalTypeList->Remaining & ((PKNOWN_OBJECT_ACE) Ace)->Mask))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Exit early.
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Look for a matching object type guid that matches the one in
|
|
// the ace.
|
|
//
|
|
|
|
if (AuthzpObjectInTypeList(
|
|
ObjectTypeInAce,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
&Index
|
|
))
|
|
{
|
|
//
|
|
// If none of the bits granted by this ace are desired, then
|
|
// there is no need to evaluate this callback ace.
|
|
//
|
|
|
|
if (0 == (LocalTypeList[Index].Remaining & ((PKNOWN_OBJECT_ACE) Ace)->Mask))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Exit early.
|
|
//
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpQuickNormalAccessCheck(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PAUTHZI_HANDLE pAH,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN OUT PAUTHZ_ACCESS_REPLY pReply,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList,
|
|
IN DWORD LocalTypeListLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine calls AuthzpQuickNormalAccessCheck on regular part as well as
|
|
restricted part (if it exists).
|
|
|
|
Access granted = AccessGranted(Sids) [&& AccessGranted(RestrictedSids)
|
|
for restricted tokens]
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pAH - Pointer to the authz handle structure.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
pReply - The reply structure to return the results.
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results.
|
|
|
|
LocalTypeListLength - Length of the array represnting the object.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b = TRUE;
|
|
ACCESS_MASK Remaining = pRequest->DesiredAccess & ~pAH->GrantedAccessMask[0];
|
|
|
|
//
|
|
// Do the access check with the non-restricted part of the client context.
|
|
//
|
|
|
|
b = AuthzpAllowOnlyNormalMultipleSDAccessCheck(
|
|
pCC,
|
|
pCC->Sids,
|
|
pCC->SidCount,
|
|
pCC->SidHash,
|
|
Remaining,
|
|
pRequest,
|
|
pAH->pSecurityDescriptor,
|
|
pAH->OptionalSecurityDescriptorArray,
|
|
pAH->OptionalSecurityDescriptorCount,
|
|
LocalTypeList,
|
|
LocalTypeListLength
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// If some access bits were not granted then no access bits will be granted.
|
|
//
|
|
|
|
if (0 != LocalTypeList->Remaining)
|
|
{
|
|
AuthzpFillReplyStructure(
|
|
pReply,
|
|
ERROR_ACCESS_DENIED,
|
|
0
|
|
);
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Do the access check with the restricted part of the client context if
|
|
// it exists.
|
|
//
|
|
|
|
if (AUTHZ_TOKEN_RESTRICTED(pCC))
|
|
{
|
|
b = AuthzpAllowOnlyNormalMultipleSDAccessCheck(
|
|
pCC,
|
|
pCC->RestrictedSids,
|
|
pCC->RestrictedSidCount,
|
|
pCC->RestrictedSidHash,
|
|
Remaining,
|
|
pRequest,
|
|
pAH->pSecurityDescriptor,
|
|
pAH->OptionalSecurityDescriptorArray,
|
|
pAH->OptionalSecurityDescriptorCount,
|
|
LocalTypeList,
|
|
LocalTypeListLength
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Make sure that all the bits are granted by the restricted part too.
|
|
//
|
|
|
|
if (0 != LocalTypeList->Remaining)
|
|
{
|
|
AuthzpFillReplyStructure(
|
|
pReply,
|
|
ERROR_ACCESS_DENIED,
|
|
0
|
|
);
|
|
|
|
SetLastError(ERROR_SUCCESS);
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
AuthzpFillReplyStructure(
|
|
pReply,
|
|
ERROR_SUCCESS,
|
|
pRequest->DesiredAccess
|
|
);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpAllowOnlyNormalMultipleSDAccessCheck(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PSID_AND_ATTRIBUTES pSidAttr,
|
|
IN DWORD SidCount,
|
|
IN PAUTHZI_SID_HASH_ENTRY pSidHash,
|
|
IN ACCESS_MASK Remaining,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
IN PSECURITY_DESCRIPTOR *OptionalSecurityDescriptorArray OPTIONAL,
|
|
IN DWORD OptionalSecurityDescriptorCount,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList,
|
|
IN DWORD LocalTypeListLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine loops thru all the security descriptors and calls
|
|
AuthzpAllowOnlyNormalSingleAclAccessCheck.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pSidAttr - Sids and attributes array to compare ace sids.
|
|
|
|
SidCount - Number of elements in the array.
|
|
|
|
Remaining - Access bits that are yet to be granted.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pSecurityDescriptor - Primary security descriptor to be used for access
|
|
checks. The owner sid for the object is picked from this one. A NULL
|
|
DACL in this security descriptor represents a NULL DACL for the entire
|
|
object. A NULL SACL in this security descriptor is treated the same way
|
|
as an EMPTY SACL.
|
|
|
|
OptionalSecurityDescriptorArray - The caller may optionally specify a list
|
|
of security descriptors. NULL ACLs in these security descriptors are
|
|
treated as EMPTY ACLS and the ACL for the entire object is the logical
|
|
concatenation of all the ACLs.
|
|
|
|
OptionalSecurityDescriptorCount - Number of optional security descriptors
|
|
This does not include the Primary security descriptor.
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results.
|
|
|
|
LocalTypeListLength - Length of the array represnting the object.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PACL pAcl = RtlpDaclAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) pSecurityDescriptor);
|
|
BOOL b = TRUE;
|
|
DWORD i = 0;
|
|
|
|
ASSERT(AUTHZ_NON_NULL_PTR(pAcl));
|
|
|
|
b = AuthzpAllowOnlyNormalSingleAclAccessCheck(
|
|
pCC,
|
|
pSidAttr,
|
|
SidCount,
|
|
pSidHash,
|
|
Remaining,
|
|
pRequest,
|
|
pAcl,
|
|
LocalTypeList,
|
|
LocalTypeListLength
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < OptionalSecurityDescriptorCount; i++)
|
|
{
|
|
pAcl = RtlpDaclAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) OptionalSecurityDescriptorArray[i]);
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(pAcl))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
b = AuthzpAllowOnlyNormalSingleAclAccessCheck(
|
|
pCC,
|
|
pSidAttr,
|
|
SidCount,
|
|
pSidHash,
|
|
LocalTypeList->Remaining,
|
|
pRequest,
|
|
pAcl,
|
|
LocalTypeList,
|
|
LocalTypeListLength
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpAllowOnlyNormalSingleAclAccessCheck(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PSID_AND_ATTRIBUTES pSidAttr,
|
|
IN DWORD SidCount,
|
|
IN PAUTHZI_SID_HASH_ENTRY pSidHash,
|
|
IN ACCESS_MASK Remaining,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN PACL pAcl,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList,
|
|
IN DWORD LocalTypeListLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine evaluates only those Grant aces that have principal self sid or
|
|
are callback aces. Deny aces can not exist in the acl unless the resource
|
|
manager messed up with the assumption and changed the security descriptors.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pSidAttr - Sids and attributes array to compare ace sids.
|
|
|
|
SidCount - Number of elements in the array.
|
|
|
|
Remaining - Access bits that are yet to be granted.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pAcl - Dacl against which the accesscheck will be preformed.
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results.
|
|
|
|
LocalTypeListLength - Length of the array represnting the object.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL ObjectTypeListPresent = (0 != pRequest->ObjectTypeListLength);
|
|
DWORD i = 0;
|
|
DWORD Index = 0;
|
|
DWORD AceCount = 0;
|
|
GUID * ObjectTypeInAce = NULL;
|
|
PVOID Ace = NULL;
|
|
PSID pSid = NULL;
|
|
BOOL bAceApplicable = FALSE;
|
|
|
|
for (i = 0; i < LocalTypeListLength; i++)
|
|
{
|
|
LocalTypeList[i].Remaining = Remaining;
|
|
}
|
|
|
|
AceCount = pAcl->AceCount;
|
|
|
|
for (i = 0, Ace = FirstAce(pAcl); i < AceCount; i++, Ace = NextAce(Ace))
|
|
{
|
|
//
|
|
// Skip INHERIT_ONLY aces.
|
|
//
|
|
|
|
if (FLAG_ON(((PACE_HEADER) Ace)->AceFlags, INHERIT_ONLY_ACE))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
switch (((PACE_HEADER) Ace)->AceType)
|
|
{
|
|
case ACCESS_ALLOWED_ACE_TYPE:
|
|
|
|
//
|
|
// We have determined that only dynamic aces should be evaluated.
|
|
// For non-callback aces, the dynamic factor arises from Principal
|
|
// self sid. Skip the ace if the ace sid is not Principal Self sid.
|
|
//
|
|
|
|
if (!AUTHZ_IS_PRINCIPAL_SELF_SID(&((PKNOWN_ACE) Ace)->SidStart))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check if the caller supplied Principal self sid is present in the
|
|
// client context and is enabled.
|
|
//
|
|
|
|
if (!AuthzpAllowOnlySidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
pRequest->PrincipalSelfSid
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Clear the granted bits from the remaining.
|
|
//
|
|
|
|
LocalTypeList->Remaining &= ~((PKNOWN_ACE) Ace)->Mask;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Clear off the granted bits from the remaining for the entire
|
|
// tree.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask,
|
|
AuthzUpdateRemaining
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_COMPOUND_ACE_TYPE:
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(pCC->Server))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We have determined that only dynamic aces should be evaluated.
|
|
// For non-callback aces, the dynamic factor arises from Principal
|
|
// self sid. Skip the ace if the ace sid is not Principal Self sid.
|
|
//
|
|
|
|
if (!AUTHZ_IS_PRINCIPAL_SELF_SID(RtlCompoundAceClientSid(Ace)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!AuthzpAllowOnlySidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
pRequest->PrincipalSelfSid
|
|
) ||
|
|
!AuthzpAllowOnlySidApplicable(
|
|
pCC->Server->SidCount,
|
|
pCC->Server->Sids,
|
|
pCC->Server->SidHash,
|
|
RtlCompoundAceServerSid(Ace)
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Clear the granted bits from the remaining.
|
|
//
|
|
|
|
LocalTypeList->Remaining &= ~((PKNOWN_ACE) Ace)->Mask;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Clear off the granted bits from the remaining for the entire
|
|
// tree.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PCOMPOUND_ACCESS_ALLOWED_ACE) Ace)->Mask,
|
|
AuthzUpdateRemaining
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
|
|
|
|
//
|
|
// We have determined that only dynamic aces should be evaluated.
|
|
// For non-callback aces, the dynamic factor arises from Principal
|
|
// self sid. Skip the ace if the ace sid is not Principal Self sid.
|
|
//
|
|
|
|
if (!AUTHZ_IS_PRINCIPAL_SELF_SID(RtlObjectAceSid(Ace)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check if the caller supplied Principal self sid is present in the
|
|
// client context and is enabled.
|
|
//
|
|
|
|
if (!AuthzpAllowOnlySidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
pRequest->PrincipalSelfSid
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
ObjectTypeInAce = RtlObjectAceObjectType(Ace);
|
|
|
|
//
|
|
// An object type ace with a NULL Object Type guid is the same as a
|
|
// normal ace.
|
|
//
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(ObjectTypeInAce))
|
|
{
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Clear the granted bits from the remaining.
|
|
//
|
|
|
|
LocalTypeList->Remaining &= ~((PKNOWN_OBJECT_ACE) Ace)->Mask;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate grant bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateRemaining
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// So, it is a true object type ace. Proceed only if we are doing
|
|
// access check on a object type list.
|
|
//
|
|
|
|
else if (ObjectTypeListPresent)
|
|
{
|
|
//
|
|
// Look for a matching object type guid that matches the one in
|
|
// the ace.
|
|
//
|
|
|
|
if (AuthzpObjectInTypeList(
|
|
ObjectTypeInAce,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
&Index
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// Clear off the granted bits from the remaining for the
|
|
// subtree starting at the matched Index.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
Index,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateRemaining
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_CALLBACK_ACE_TYPE:
|
|
|
|
//
|
|
// If the ace sid is Principal Self, replace it with the caller
|
|
// supplied Principal sid.
|
|
//
|
|
|
|
if (!AUTHZ_IS_PRINCIPAL_SELF_SID(&(((PKNOWN_ACE) Ace)->SidStart)))
|
|
{
|
|
pSid = (PSID) &(((PKNOWN_ACE) Ace)->SidStart);
|
|
}
|
|
else
|
|
{
|
|
pSid = pRequest->PrincipalSelfSid;
|
|
}
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled.
|
|
//
|
|
|
|
if (!AuthzpAllowOnlySidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
pSid
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
bAceApplicable = FALSE;
|
|
|
|
//
|
|
// If the all the bits granted by this ace have already been granted
|
|
// or denied, then there is no need to evaluate this callback ace.
|
|
//
|
|
|
|
if (0 == (LocalTypeList->Remaining & ((PKNOWN_ACE) Ace)->Mask))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Clear the granted bits from the remaining.
|
|
//
|
|
|
|
LocalTypeList->Remaining &= ~((PKNOWN_ACE) Ace)->Mask;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Clear off the granted bits from the remaining for the entire
|
|
// tree.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask,
|
|
AuthzUpdateRemaining
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE:
|
|
|
|
//
|
|
// If the ace sid is Principal Self, replace it with the caller
|
|
// supplied Principal sid.
|
|
//
|
|
|
|
if (!AUTHZ_IS_PRINCIPAL_SELF_SID(RtlObjectAceSid(Ace)))
|
|
{
|
|
pSid = RtlObjectAceSid(Ace);
|
|
}
|
|
else
|
|
{
|
|
pSid = pRequest->PrincipalSelfSid;
|
|
}
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled.
|
|
//
|
|
|
|
if (!AuthzpAllowOnlySidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
pSid
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
bAceApplicable = FALSE;
|
|
|
|
ObjectTypeInAce = RtlObjectAceObjectType(Ace);
|
|
|
|
//
|
|
// An object type ace with a NULL Object Type guid is the same as a
|
|
// normal ace.
|
|
//
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(ObjectTypeInAce))
|
|
{
|
|
//
|
|
// If the all the bits granted by this ace have already been granted
|
|
// or denied, then there is no need to evaluate this callback ace.
|
|
//
|
|
|
|
if (0 == (LocalTypeList->Remaining & ((PKNOWN_ACE) Ace)->Mask))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Clear the granted bits from the remaining.
|
|
//
|
|
|
|
LocalTypeList->Remaining &= ~((PKNOWN_OBJECT_ACE) Ace)->Mask;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate grant bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateRemaining
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// So, it is a true object type ace. Proceed only if we are doing
|
|
// access check on a object type list.
|
|
//
|
|
|
|
else if (ObjectTypeListPresent)
|
|
{
|
|
//
|
|
// Look for a matching object type guid that matches the one in
|
|
// the ace.
|
|
//
|
|
|
|
if (AuthzpObjectInTypeList(
|
|
ObjectTypeInAce,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
&Index
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// If the all the bits granted by this ace are not desired, then
|
|
// there is no need to evaluate this callback ace.
|
|
//
|
|
|
|
if (0 == (LocalTypeList[Index].Remaining & ((PKNOWN_OBJECT_ACE) Ace)->Mask))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Propagate grant bits down the tree starting at the
|
|
// index at which the guids matched.
|
|
// In the case when this is the last of the siblings to be
|
|
// granted access, the parent also is granted access.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
Index,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateRemaining
|
|
);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_DENIED_OBJECT_ACE_TYPE:
|
|
case ACCESS_DENIED_ACE_TYPE:
|
|
case ACCESS_DENIED_CALLBACK_ACE_TYPE:
|
|
case ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE:
|
|
ASSERT(FALSE);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpAllowOnlySidApplicable(
|
|
IN DWORD SidCount,
|
|
IN PSID_AND_ATTRIBUTES pSidAttr,
|
|
IN PAUTHZI_SID_HASH_ENTRY pSidHash,
|
|
IN PSID pSid
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine determine whether the given ace sid is present in the client
|
|
context and is enabled.
|
|
|
|
Arguments:
|
|
|
|
SidCount - Number of sids in the pSidAttrArray
|
|
|
|
pSidAttr - Sid and attributes against which the ace sid should be compared.
|
|
|
|
pAceSid - Sid in the ace.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the effective sid is found in the client
|
|
context and is enabled.
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i = 0;
|
|
DWORD SidLen = 0;
|
|
PISID MatchSid = NULL;
|
|
|
|
UNREFERENCED_PARAMETER(pSidHash);
|
|
|
|
if (!ARGUMENT_PRESENT(pSid))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
SidLen = RtlLengthSid(pSid);
|
|
|
|
//
|
|
// Loop thru the sids and return TRUE is a match is found and the sid
|
|
// is enabled.
|
|
//
|
|
|
|
for (i = 0; i < SidCount; i++, pSidAttr++)
|
|
{
|
|
MatchSid = (PISID) pSidAttr->Sid;
|
|
|
|
if (AUTHZ_EQUAL_SID(pSid, MatchSid, SidLen))
|
|
{
|
|
if (FLAG_ON(pSidAttr->Attributes, SE_GROUP_ENABLED))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
AuthzpQuickMaximumAllowedAccessCheck(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PAUTHZI_HANDLE pAH,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN OUT PAUTHZ_ACCESS_REPLY pReply,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList,
|
|
IN DWORD LocalTypeListLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine calls AuthzpQuickMaximumAllowedAccessCheck on normal part (as
|
|
well as restricted part if it exists).
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pAH - Pointer to the authz handle structure.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pReply - The reply structure to return the results.
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results.
|
|
|
|
LocalTypeListLength - Length of the array represnting the object.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b = FALSE;
|
|
BOOL ObjectTypeListPresent = (0 != pRequest->ObjectTypeListLength);
|
|
|
|
UNREFERENCED_PARAMETER(pReply);
|
|
|
|
//
|
|
// Do the access check with the non-restricted part of the client context.
|
|
//
|
|
|
|
b = AuthzpAllowOnlyMaximumAllowedMultipleSDAccessCheck(
|
|
pCC,
|
|
pRequest,
|
|
pAH->pSecurityDescriptor,
|
|
pAH->OptionalSecurityDescriptorArray,
|
|
pAH->OptionalSecurityDescriptorCount,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
ObjectTypeListPresent,
|
|
FALSE
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Do the access check with the restricted part of the client context if
|
|
// it exists.
|
|
//
|
|
|
|
if (AUTHZ_TOKEN_RESTRICTED(pCC))
|
|
{
|
|
b = AuthzpAllowOnlyMaximumAllowedMultipleSDAccessCheck(
|
|
pCC,
|
|
pRequest,
|
|
pAH->pSecurityDescriptor,
|
|
pAH->OptionalSecurityDescriptorArray,
|
|
pAH->OptionalSecurityDescriptorCount,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
ObjectTypeListPresent,
|
|
TRUE
|
|
);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpAllowOnlyMaximumAllowedMultipleSDAccessCheck(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
IN PSECURITY_DESCRIPTOR *OptionalSecurityDescriptorArray OPTIONAL,
|
|
IN DWORD OptionalSecurityDescriptorCount OPTIONAL,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList,
|
|
IN DWORD LocalTypeListLength,
|
|
IN BOOL ObjectTypeListPresent,
|
|
IN BOOL Restricted
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine call AuthzpAllowOnlyMaximumAllowedSingleAclAccessCheck for all
|
|
given security descriptors.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pSecurityDescriptor - Primary security descriptor to be used for access
|
|
checks. The owner sid for the object is picked from this one. A NULL
|
|
DACL in this security descriptor represents a NULL DACL for the entire
|
|
object. A NULL SACL in this security descriptor is treated the same way
|
|
as an EMPTY SACL.
|
|
|
|
OptionalSecurityDescriptorArray - The caller may optionally specify a list
|
|
of security descriptors. NULL ACLs in these security descriptors are
|
|
treated as EMPTY ACLS and the ACL for the entire object is the logical
|
|
concatenation of all the ACLs.
|
|
|
|
OptionalSecurityDescriptorCount - Number of optional security descriptors
|
|
This does not include the Primary security descriptor.
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results.
|
|
|
|
LocalTypeListLength - Length of the array represnting the object.
|
|
|
|
ObjectTypeListPresent - Whether the called supplied an object type list.
|
|
|
|
Restricted - Whether this is an access check on the restricted part of the
|
|
client context.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i = 0;
|
|
DWORD j = 0;
|
|
DWORD SidCount = 0;
|
|
BOOL b = TRUE;
|
|
PSID_AND_ATTRIBUTES pSidAttr = NULL;
|
|
PACL pAcl = NULL;
|
|
PSID pOwnerSid = RtlpOwnerAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) pSecurityDescriptor);
|
|
PAUTHZI_SID_HASH_ENTRY pSidHash = NULL;
|
|
|
|
if (Restricted)
|
|
{
|
|
//
|
|
// Access granted is a bitwise AND of both (normal & restricted) part
|
|
// of the client context.
|
|
//
|
|
|
|
for (j = 0; j < LocalTypeListLength; j++)
|
|
{
|
|
LocalTypeList[j].Remaining = LocalTypeList[j].CurrentGranted;
|
|
LocalTypeList[j].CurrentGranted = 0;
|
|
}
|
|
|
|
pSidAttr = pCC->RestrictedSids;
|
|
SidCount = pCC->RestrictedSidCount;
|
|
pSidHash = pCC->RestrictedSidHash;
|
|
}
|
|
else
|
|
{
|
|
pSidAttr = pCC->Sids;
|
|
SidCount = pCC->SidCount;
|
|
pSidHash = pCC->SidHash;
|
|
}
|
|
|
|
pAcl = RtlpDaclAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) pSecurityDescriptor);
|
|
|
|
b = AuthzpAllowOnlyMaximumAllowedSingleAclAccessCheck(
|
|
pCC,
|
|
pSidAttr,
|
|
SidCount,
|
|
pSidHash,
|
|
pRequest,
|
|
pAcl,
|
|
pOwnerSid,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
ObjectTypeListPresent
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < OptionalSecurityDescriptorCount; i++)
|
|
{
|
|
pAcl = RtlpDaclAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) OptionalSecurityDescriptorArray[i]);
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(pAcl))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
b = AuthzpAllowOnlyMaximumAllowedSingleAclAccessCheck(
|
|
pCC,
|
|
pSidAttr,
|
|
SidCount,
|
|
pSidHash,
|
|
pRequest,
|
|
pAcl,
|
|
pOwnerSid,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
ObjectTypeListPresent
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
AuthzpAllowOnlyMaximumAllowedSingleAclAccessCheck(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PSID_AND_ATTRIBUTES pSidAttr,
|
|
IN DWORD SidCount,
|
|
IN PAUTHZI_SID_HASH_ENTRY pSidHash,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN PACL pAcl,
|
|
IN PSID pOwnerSid,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList,
|
|
IN DWORD LocalTypeListLength,
|
|
IN BOOL ObjectTypeListPresent
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine loops thru the entire acl and evaluates only those Grant aces
|
|
that have principal self sid in them or are callback aces.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pSidAttr - Sids and attributes array to compare ace sids.
|
|
|
|
SidCount - Number of elements in the array.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pAcl - Sacl to be used to make the decision about audit generation.
|
|
|
|
pOwnerSid - The owner sid in the primary security descriptor. This will be
|
|
needed after we implement single instancing.
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results.
|
|
|
|
LocalTypeListLength - Length of the array represnting the object.
|
|
|
|
ObjectTypeListPresent - Whether the called supplied an object type list.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD AceCount = 0;
|
|
DWORD i = 0;
|
|
DWORD Index = 0;
|
|
PVOID Ace = NULL;
|
|
GUID * ObjectTypeInAce = NULL;
|
|
BOOL bAceApplicable = FALSE;
|
|
BOOL bCallbackMayApply = FALSE;
|
|
PSID pSid = NULL;
|
|
|
|
UNREFERENCED_PARAMETER(pOwnerSid);
|
|
|
|
AceCount = pAcl->AceCount;
|
|
|
|
for (i = 0, Ace = FirstAce(pAcl); i < AceCount; i++, Ace = NextAce(Ace))
|
|
{
|
|
//
|
|
// Skip INHERIT_ONLY aces.
|
|
//
|
|
|
|
if (FLAG_ON(((PACE_HEADER) Ace)->AceFlags, INHERIT_ONLY_ACE))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
switch (((PACE_HEADER) Ace)->AceType)
|
|
{
|
|
|
|
case ACCESS_ALLOWED_ACE_TYPE:
|
|
|
|
//
|
|
// We have determined that only dynamic aces should be evaluated.
|
|
// For non-callback aces, the dynamic factor arises from Principal
|
|
// self sid. Skip the ace if the ace sid is not Principal Self sid.
|
|
//
|
|
|
|
if (!AUTHZ_IS_PRINCIPAL_SELF_SID(&((PKNOWN_ACE) Ace)->SidStart))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check if the caller supplied Principal self sid is present in the
|
|
// client context and is enabled.
|
|
//
|
|
|
|
if (!AuthzpAllowOnlySidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
pRequest->PrincipalSelfSid
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Grant access bits that have not already been denied.
|
|
//
|
|
|
|
LocalTypeList->CurrentGranted |= (((PKNOWN_ACE) Ace)->Mask & ~LocalTypeList->CurrentDenied);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate grant bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_COMPOUND_ACE_TYPE:
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(pCC->Server))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We have determined that only dynamic aces should be evaluated.
|
|
// For non-callback aces, the dynamic factor arises from Principal
|
|
// self sid. Skip the ace if the ace sid is not Principal Self sid.
|
|
//
|
|
|
|
if (!AUTHZ_IS_PRINCIPAL_SELF_SID(RtlCompoundAceClientSid(Ace)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (!AuthzpAllowOnlySidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
pRequest->PrincipalSelfSid
|
|
) ||
|
|
!AuthzpAllowOnlySidApplicable(
|
|
pCC->Server->SidCount,
|
|
pCC->Server->Sids,
|
|
pCC->Server->SidHash,
|
|
RtlCompoundAceServerSid(Ace)
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Grant access bits that have not already been denied.
|
|
//
|
|
|
|
LocalTypeList->CurrentGranted |= (((PCOMPOUND_ACCESS_ALLOWED_ACE) Ace)->Mask & ~LocalTypeList->CurrentDenied);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate grant bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PCOMPOUND_ACCESS_ALLOWED_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_OBJECT_ACE_TYPE:
|
|
|
|
//
|
|
// We have determined that only dynamic aces should be evaluated.
|
|
// For non-callback aces, the dynamic factor arises from Principal
|
|
// self sid. Skip the ace if the ace sid is not Principal Self sid.
|
|
//
|
|
|
|
if (!AUTHZ_IS_PRINCIPAL_SELF_SID(RtlObjectAceSid(Ace)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check if the caller supplied Principal self sid is present in the
|
|
// client context and is enabled.
|
|
//
|
|
|
|
if (!AuthzpAllowOnlySidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
pRequest->PrincipalSelfSid
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
ObjectTypeInAce = RtlObjectAceObjectType(Ace);
|
|
|
|
//
|
|
// An object type ace with a NULL Object Type guid is the same as a
|
|
// normal ace.
|
|
//
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(ObjectTypeInAce))
|
|
{
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Grant access bits that have not already been denied.
|
|
//
|
|
|
|
LocalTypeList->CurrentGranted |= (((PKNOWN_OBJECT_ACE) Ace)->Mask & ~LocalTypeList->CurrentDenied);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate grant bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// So, it is a true object type ace. Proceed only if we are doing
|
|
// access check on a object type list.
|
|
//
|
|
|
|
else if (ObjectTypeListPresent)
|
|
{
|
|
//
|
|
// Look for a matching object type guid that matches the one in
|
|
// the ace.
|
|
//
|
|
|
|
if (AuthzpObjectInTypeList(
|
|
ObjectTypeInAce,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
&Index
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// Propagate grant bits down the tree starting at the
|
|
// index at which the guids matched.
|
|
// In the case when this is the last of the siblings to be
|
|
// granted access, the parent also is granted access.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
Index,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_CALLBACK_ACE_TYPE:
|
|
|
|
//
|
|
// If the ace sid is Principal Self, replace it with the caller
|
|
// supplied Principal sid.
|
|
//
|
|
|
|
if (!AUTHZ_IS_PRINCIPAL_SELF_SID(&((PKNOWN_ACE) Ace)->SidStart))
|
|
{
|
|
pSid = (PSID) &(((PKNOWN_ACE) Ace)->SidStart);
|
|
}
|
|
else
|
|
{
|
|
pSid = pRequest->PrincipalSelfSid;
|
|
}
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled.
|
|
//
|
|
|
|
if (!AuthzpAllowOnlySidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
pSid
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// If the all the bits granted by this ace have already been granted
|
|
// or denied, then there is no need to evaluate this callback ace.
|
|
//
|
|
|
|
if (0 == ((((PKNOWN_ACE) Ace)->Mask) &
|
|
~(LocalTypeList->CurrentGranted | LocalTypeList->CurrentDenied)))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We have an object type list. Compute whether we need to break
|
|
// out early.
|
|
//
|
|
|
|
bCallbackMayApply = AuthzpIsAccessMaskApplicable(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask
|
|
);
|
|
|
|
if (!bCallbackMayApply)
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
bAceApplicable = FALSE;
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Clear the granted bits from the remaining.
|
|
//
|
|
|
|
LocalTypeList->CurrentGranted |= (((PKNOWN_ACE) Ace)->Mask & ~LocalTypeList->CurrentDenied);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate grant bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE:
|
|
|
|
//
|
|
// If the ace sid is Principal Self, replace it with the caller
|
|
// supplied Principal sid.
|
|
//
|
|
|
|
if (!AUTHZ_IS_PRINCIPAL_SELF_SID(RtlObjectAceSid(Ace)))
|
|
{
|
|
pSid = RtlObjectAceSid(Ace);
|
|
}
|
|
else
|
|
{
|
|
pSid = pRequest->PrincipalSelfSid;
|
|
}
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled.
|
|
//
|
|
|
|
if (!AuthzpAllowOnlySidApplicable(
|
|
SidCount,
|
|
pSidAttr,
|
|
pSidHash,
|
|
pSid
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
|
|
bAceApplicable = FALSE;
|
|
|
|
ObjectTypeInAce = RtlObjectAceObjectType(Ace);
|
|
|
|
//
|
|
// An object type ace with a NULL Object Type guid is the same as a
|
|
// normal ace.
|
|
//
|
|
|
|
if (!AUTHZ_NON_NULL_PTR(ObjectTypeInAce))
|
|
{
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// If the all the bits granted by this ace have already been
|
|
// granted, then there is no need to evaluate this callback
|
|
// ace.
|
|
//
|
|
|
|
if (0 == ((~LocalTypeList->CurrentGranted) & (((PKNOWN_ACE) Ace)->Mask & ~LocalTypeList->CurrentDenied)))
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We have an object type list. Compute whether we need to break
|
|
// out early.
|
|
//
|
|
|
|
bCallbackMayApply = AuthzpIsAccessMaskApplicable(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask
|
|
);
|
|
|
|
if (!bCallbackMayApply)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Optimize the common case instead of calling a subroutine.
|
|
//
|
|
|
|
if (1 == LocalTypeListLength)
|
|
{
|
|
//
|
|
// Grant access bits that have not already been denied.
|
|
//
|
|
|
|
LocalTypeList->CurrentGranted |= (((PKNOWN_OBJECT_ACE) Ace)->Mask & ~LocalTypeList->CurrentDenied);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Propagate grant bits down the tree starting at the root.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
0,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// So, it is a true object type ace. Proceed only if we are doing
|
|
// access check on a object type list.
|
|
//
|
|
|
|
else if (ObjectTypeListPresent)
|
|
{
|
|
//
|
|
// Look for a matching object type guid that matches the one in
|
|
// the ace.
|
|
//
|
|
|
|
if (AuthzpObjectInTypeList(
|
|
ObjectTypeInAce,
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
&Index
|
|
)
|
|
)
|
|
{
|
|
//
|
|
// We have an object type list. Compute whether we need to break
|
|
// out early.
|
|
//
|
|
|
|
bCallbackMayApply = AuthzpIsAccessMaskApplicable(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
Index,
|
|
((PKNOWN_ACE) Ace)->Mask
|
|
);
|
|
|
|
if (!bCallbackMayApply)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Propagate grant bits down the tree starting at the
|
|
// index at which the guids matched.
|
|
// In the case when this is the last of the siblings to be
|
|
// granted access, the parent also is granted access.
|
|
//
|
|
|
|
AuthzpAddAccessTypeList(
|
|
LocalTypeList,
|
|
LocalTypeListLength,
|
|
Index,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
AuthzUpdateCurrentGranted
|
|
);
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case ACCESS_DENIED_OBJECT_ACE_TYPE:
|
|
case ACCESS_DENIED_ACE_TYPE:
|
|
case ACCESS_DENIED_CALLBACK_ACE_TYPE:
|
|
case ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE:
|
|
ASSERT(FALSE);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpObjectInTypeList (
|
|
IN GUID *ObjectType,
|
|
IN PIOBJECT_TYPE_LIST ObjectTypeList,
|
|
IN DWORD ObjectTypeListLength,
|
|
OUT PDWORD ReturnedIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine searches an ObjectTypeList to determine if the specified object
|
|
type is in the list. It returns the index of the node if a match is found.
|
|
|
|
Arguments:
|
|
|
|
ObjectType - Object Type guid to search for.
|
|
|
|
ObjectTypeList - The object type list to search in.
|
|
|
|
ObjectTypeListLength - Number of elements in ObjectTypeList.
|
|
|
|
ReturnedIndex - Index to the element ObjectType was found in.
|
|
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Index = 0;
|
|
GUID * LocalObjectType = NULL;
|
|
|
|
for (Index = 0; Index < ObjectTypeListLength; Index++)
|
|
{
|
|
LocalObjectType = &ObjectTypeList[Index].ObjectType;
|
|
|
|
if (RtlpIsEqualGuid(ObjectType, LocalObjectType))
|
|
{
|
|
*ReturnedIndex = Index;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
VOID
|
|
AuthzpUpdateParentTypeList(
|
|
IN OUT PIOBJECT_TYPE_LIST ObjectTypeList,
|
|
IN DWORD ObjectTypeListLength,
|
|
IN DWORD StartIndex
|
|
)
|
|
|
|
/*++
|
|
|
|
This routine updates the Access fields of the parent object of the specified
|
|
object.
|
|
|
|
The "remaining" field of a parent object is the logical OR of
|
|
the remaining field of all of its children.
|
|
|
|
The CurrentGranted field of the parent is the collection of bits
|
|
granted to every one of its children..
|
|
|
|
The CurrentDenied fields of the parent is the logical OR of
|
|
the bits denied to any of its children.
|
|
|
|
This routine takes an index to one of the children and updates the
|
|
remaining field of the parent (and grandparents recursively).
|
|
|
|
Arguments:
|
|
|
|
ObjectTypeList - The object type list to update.
|
|
|
|
ObjectTypeListLength - Number of elements in ObjectTypeList
|
|
|
|
StartIndex - Index to the "child" element whose parents are to be updated.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Index = 0;
|
|
DWORD ParentIndex = 0;
|
|
DWORD Level = 0;
|
|
ACCESS_MASK NewRemaining = 0;
|
|
ACCESS_MASK NewCurrentDenied = 0;
|
|
ACCESS_MASK NewCurrentGranted = 0xFFFFFFFF;
|
|
|
|
//
|
|
// If the target node is at the root,
|
|
// we're all done.
|
|
//
|
|
|
|
if (-1 == ObjectTypeList[StartIndex].ParentIndex)
|
|
{
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the index to the parent that needs updating and the level of
|
|
// the siblings.
|
|
//
|
|
|
|
ParentIndex = ObjectTypeList[StartIndex].ParentIndex;
|
|
Level = ObjectTypeList[StartIndex].Level;
|
|
|
|
//
|
|
// Loop through all the children.
|
|
//
|
|
|
|
for (Index = ParentIndex + 1; Index < ObjectTypeListLength; Index++)
|
|
{
|
|
|
|
//
|
|
// By definition, the children of an object are all those entries
|
|
// immediately following the target. The list of children (or
|
|
// grandchildren) stops as soon as we reach an entry the has the
|
|
// same level as the target (a sibling) or lower than the target
|
|
// (an uncle).
|
|
//
|
|
|
|
if (ObjectTypeList[Index].Level <= ObjectTypeList[ParentIndex].Level)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Only handle direct children of the parent.
|
|
//
|
|
|
|
if (ObjectTypeList[Index].Level != Level)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Compute the new bits for the parent.
|
|
//
|
|
|
|
NewRemaining |= ObjectTypeList[Index].Remaining;
|
|
NewCurrentGranted &= ObjectTypeList[Index].CurrentGranted;
|
|
NewCurrentDenied |= ObjectTypeList[Index].CurrentDenied;
|
|
|
|
}
|
|
|
|
//
|
|
// If we've not changed the access to the parent,
|
|
// we're done.
|
|
//
|
|
|
|
if ((NewRemaining == ObjectTypeList[ParentIndex].Remaining) &&
|
|
(NewCurrentGranted == ObjectTypeList[ParentIndex].CurrentGranted) &&
|
|
(NewCurrentDenied == ObjectTypeList[ParentIndex].CurrentDenied))
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Change the parent.
|
|
//
|
|
|
|
ObjectTypeList[ParentIndex].Remaining = NewRemaining;
|
|
ObjectTypeList[ParentIndex].CurrentGranted = NewCurrentGranted;
|
|
ObjectTypeList[ParentIndex].CurrentDenied = NewCurrentDenied;
|
|
|
|
//
|
|
// Go update the grand parents.
|
|
//
|
|
|
|
AuthzpUpdateParentTypeList(
|
|
ObjectTypeList,
|
|
ObjectTypeListLength,
|
|
ParentIndex
|
|
);
|
|
}
|
|
|
|
|
|
VOID
|
|
AuthzpAddAccessTypeList (
|
|
IN OUT PIOBJECT_TYPE_LIST ObjectTypeList,
|
|
IN DWORD ObjectTypeListLength,
|
|
IN DWORD StartIndex,
|
|
IN ACCESS_MASK AccessMask,
|
|
IN ACCESS_MASK_FIELD_TO_UPDATE FieldToUpdate
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine grants the specified AccessMask to all of the objects that
|
|
are descendents of the object specified by StartIndex.
|
|
|
|
The Access fields of the parent objects are also recomputed as needed.
|
|
|
|
For example, if an ACE granting access to a Property Set is found,
|
|
that access is granted to all the Properties in the Property Set.
|
|
|
|
Arguments:
|
|
|
|
ObjectTypeList - The object type list to update.
|
|
|
|
ObjectTypeListLength - Number of elements in ObjectTypeList
|
|
|
|
StartIndex - Index to the target element to update.
|
|
|
|
AccessMask - Mask of access to grant to the target element and
|
|
all of its decendents
|
|
|
|
FieldToUpdate - Indicate which fields to update in object type list
|
|
|
|
Updation cases:
|
|
|
|
A
|
|
/ \
|
|
/ \
|
|
B E
|
|
/ \ / \
|
|
C D F G
|
|
|
|
Consider an object type list of length 7, with two property sets, each with
|
|
two properties. In array form this is represented as
|
|
{(A, 0), (B, 1), (C, 2), (D, 2), (E, 1), (F, 2), (G, 2)}
|
|
|
|
The following diagrams explain access granted/denied at each node.
|
|
|
|
Access may be granted because of one (or more) of the following reasons:
|
|
a = Access granted by an ace on the node
|
|
ca = Access granted by ORing of the children
|
|
pa = Access granted by an ancestor
|
|
|
|
Access may be denied because of one (or more) of the following reasons.
|
|
|
|
d = Access explicitly denied by an ace on the node
|
|
cd = Access explicitly denied by child, grandchild, etc
|
|
pd = Access explicitly denied by an ancestor
|
|
id = Access implicitly denied because no ace was applicable
|
|
|
|
case 1: case 2: case 3:
|
|
Deny D Deny B Grant B
|
|
Grant A Grant E Grant F
|
|
Grant D Grant G
|
|
|
|
cd cd ca
|
|
/ \ / \ / \
|
|
/ \ / \ / \
|
|
cd pa d a a ca
|
|
/ \ / \ / \ / \ / \ / \
|
|
pa d pa pa pd pd pa pa pa pa a a
|
|
|
|
|
|
case 4: case 5: case 6:
|
|
Grant A Grant C Grant B
|
|
Deny B Grant D Grant F
|
|
Deny F Grant F
|
|
Grant G
|
|
Deny A
|
|
|
|
a ca id
|
|
/ \ / \ / \
|
|
/ \ / \ / \
|
|
pa pa ca ca a id
|
|
/ \ / \ / \ / \ / \ / \
|
|
pa pa pa pa a a a a pa pa a id
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD Index;
|
|
ACCESS_MASK OldRemaining;
|
|
ACCESS_MASK OldCurrentGranted;
|
|
ACCESS_MASK OldCurrentDenied;
|
|
BOOL AvoidParent = FALSE;
|
|
|
|
//
|
|
// Update the requested field.
|
|
//
|
|
// Always handle the target entry.
|
|
//
|
|
// If we've not actually changed the bits, early out.
|
|
//
|
|
|
|
switch (FieldToUpdate)
|
|
{
|
|
case AuthzUpdateRemaining:
|
|
|
|
OldRemaining = ObjectTypeList[StartIndex].Remaining;
|
|
ObjectTypeList[StartIndex].Remaining = OldRemaining & ~AccessMask;
|
|
|
|
if (OldRemaining == ObjectTypeList[StartIndex].Remaining)
|
|
{
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
case AuthzUpdateCurrentGranted:
|
|
|
|
OldCurrentGranted = ObjectTypeList[StartIndex].CurrentGranted;
|
|
ObjectTypeList[StartIndex].CurrentGranted |=
|
|
AccessMask & ~ObjectTypeList[StartIndex].CurrentDenied;
|
|
|
|
if (OldCurrentGranted == ObjectTypeList[StartIndex].CurrentGranted)
|
|
{
|
|
|
|
//
|
|
// We can't simply return here.
|
|
// We have to visit our children. Consider the case where there
|
|
// was a previous deny ACE on a child. That deny would have
|
|
// propagated up the tree to this entry. However, this allow ACE
|
|
// needs to be added all of the children that haven't been
|
|
// explictly denied.
|
|
//
|
|
|
|
AvoidParent = TRUE;
|
|
}
|
|
|
|
break;
|
|
|
|
case AuthzUpdateCurrentDenied:
|
|
|
|
OldCurrentDenied = ObjectTypeList[StartIndex].CurrentDenied;
|
|
ObjectTypeList[StartIndex].CurrentDenied |=
|
|
AccessMask & ~ObjectTypeList[StartIndex].CurrentGranted;
|
|
|
|
if (OldCurrentDenied == ObjectTypeList[StartIndex].CurrentDenied)
|
|
{
|
|
return;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
|
|
|
|
//
|
|
// Go update parent of the target.
|
|
//
|
|
|
|
if (!AvoidParent)
|
|
{
|
|
AuthzpUpdateParentTypeList(
|
|
ObjectTypeList,
|
|
ObjectTypeListLength,
|
|
StartIndex
|
|
);
|
|
}
|
|
|
|
//
|
|
// Loop handling all children of the target.
|
|
//
|
|
|
|
for (Index = StartIndex + 1; Index < ObjectTypeListLength; Index++)
|
|
{
|
|
|
|
//
|
|
// By definition, the children of an object are all those entries
|
|
// immediately following the target. The list of children (or
|
|
// grandchildren) stops as soon as we reach an entry the has the
|
|
// same level as the target (a sibling) or lower than the target
|
|
// (an uncle).
|
|
//
|
|
|
|
if (ObjectTypeList[Index].Level <= ObjectTypeList[StartIndex].Level)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Grant access to the children
|
|
//
|
|
|
|
switch (FieldToUpdate)
|
|
{
|
|
case AuthzUpdateRemaining:
|
|
|
|
ObjectTypeList[Index].Remaining &= ~AccessMask;
|
|
|
|
break;
|
|
|
|
case AuthzUpdateCurrentGranted:
|
|
|
|
ObjectTypeList[Index].CurrentGranted |=
|
|
AccessMask & ~ObjectTypeList[Index].CurrentDenied;
|
|
|
|
break;
|
|
|
|
case AuthzUpdateCurrentDenied:
|
|
|
|
ObjectTypeList[Index].CurrentDenied |=
|
|
AccessMask & ~ObjectTypeList[Index].CurrentGranted;
|
|
|
|
break;
|
|
|
|
default:
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpCacheResults(
|
|
IN DWORD Flags,
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PIOBJECT_TYPE_LIST LocalCachingTypeList,
|
|
IN DWORD LocalTypeListLength,
|
|
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
IN PSECURITY_DESCRIPTOR *OptionalSecurityDescriptorArray OPTIONAL,
|
|
IN DWORD OptionalSecurityDescriptorCount,
|
|
IN DWORD CachingFlags,
|
|
OUT PAUTHZ_ACCESS_CHECK_RESULTS_HANDLE phAccessCheckResults
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine allocates a handle node, stores the results of static access
|
|
check in it and inserts it into the list of handles.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
LocalCachingTypeList - Internal object type list structure used to hold
|
|
intermediate results for static granted bits.
|
|
|
|
LocalTypeListLength - Length of the array represnting the object.
|
|
|
|
pSecurityDescriptor - Primary security descriptor to be used for access
|
|
checks.
|
|
|
|
OptionalSecurityDescriptorArray - The caller may optionally specify a list
|
|
of security descriptors. NULL ACLs in these security descriptors are
|
|
treated as EMPTY ACLS and the ACL for the entire object is the logical
|
|
concatenation of all the ACLs.
|
|
|
|
OptionalSecurityDescriptorCount - Number of optional security descriptors
|
|
This does not include the Primary security descriptor.
|
|
|
|
CachingFlags - Flags that will be stored in the caching handle.
|
|
|
|
phAccessCheckResults - To return the newly allocated handle.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i = 0;
|
|
DWORD Size = 0;
|
|
DWORD SDSize = 0;
|
|
DWORD SDArraySize = 0;
|
|
ULONG LengthNeeded = 0;
|
|
PAUTHZI_HANDLE pHandle = NULL;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
PUCHAR pWrite = NULL;
|
|
BOOL b = TRUE;
|
|
|
|
Size = PtrAlignSize((LocalTypeListLength - 1) * sizeof(ACCESS_MASK) + sizeof(AUTHZI_HANDLE));
|
|
|
|
//
|
|
// If we are going to copy the SDs we will need some space for the pointers.
|
|
//
|
|
|
|
if (!FLAG_ON(Flags, AUTHZ_ACCESS_CHECK_NO_DEEP_COPY_SD))
|
|
{
|
|
SDSize = PtrAlignSize(RtlLengthSecurityDescriptor(pSecurityDescriptor));
|
|
SDArraySize = PtrAlignSize(sizeof(ULONG_PTR) * OptionalSecurityDescriptorCount);
|
|
|
|
for (i = 0; i < OptionalSecurityDescriptorCount; i++)
|
|
{
|
|
SDArraySize += PtrAlignSize(RtlLengthSecurityDescriptor(OptionalSecurityDescriptorArray[i]));
|
|
}
|
|
}
|
|
|
|
pHandle = (PAUTHZI_HANDLE) AuthzpAlloc(Size + SDSize + SDArraySize);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pHandle))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
return FALSE;
|
|
}
|
|
|
|
if (FLAG_ON(Flags, AUTHZ_ACCESS_CHECK_NO_DEEP_COPY_SD))
|
|
{
|
|
pHandle->pSecurityDescriptor = pSecurityDescriptor;
|
|
pHandle->OptionalSecurityDescriptorArray = OptionalSecurityDescriptorArray;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Put the primary SD immediately after the pHandle
|
|
//
|
|
|
|
pWrite = ((PUCHAR)pHandle + Size);
|
|
pHandle->pSecurityDescriptor = (PSECURITY_DESCRIPTOR) pWrite;
|
|
|
|
LengthNeeded = SDSize;
|
|
|
|
Status = RtlMakeSelfRelativeSD(
|
|
pSecurityDescriptor,
|
|
pHandle->pSecurityDescriptor,
|
|
&LengthNeeded
|
|
);
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pWrite += PtrAlignSize(LengthNeeded);
|
|
|
|
//
|
|
// After the primary SD we put the Optional SD array
|
|
//
|
|
|
|
if (OptionalSecurityDescriptorCount == 0)
|
|
{
|
|
pHandle->OptionalSecurityDescriptorArray = NULL;
|
|
}
|
|
else
|
|
{
|
|
pHandle->OptionalSecurityDescriptorArray = (PSECURITY_DESCRIPTOR *) pWrite;
|
|
pWrite += (sizeof(ULONG_PTR) * OptionalSecurityDescriptorCount);
|
|
|
|
for (i = 0; i < OptionalSecurityDescriptorCount; i++)
|
|
{
|
|
//
|
|
// After the array put in each optional SD, and point an array element at the SD
|
|
//
|
|
|
|
pHandle->OptionalSecurityDescriptorArray[i] = pWrite;
|
|
|
|
LengthNeeded = PtrAlignSize(RtlLengthSecurityDescriptor(OptionalSecurityDescriptorArray[i]));
|
|
|
|
Status = RtlMakeSelfRelativeSD(
|
|
OptionalSecurityDescriptorArray[i],
|
|
pHandle->OptionalSecurityDescriptorArray[i],
|
|
&LengthNeeded
|
|
);
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pWrite += PtrAlignSize(LengthNeeded);
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT(((PUCHAR)pHandle + Size + SDSize + SDArraySize) == pWrite);
|
|
|
|
pHandle->Flags = CachingFlags;
|
|
pHandle->pAuthzClientContext = pCC;
|
|
pHandle->ResultListLength = LocalTypeListLength;
|
|
pHandle->OptionalSecurityDescriptorCount = OptionalSecurityDescriptorCount;
|
|
|
|
//
|
|
// Store the static access check results in the handle.
|
|
//
|
|
|
|
for (i = 0; i < LocalTypeListLength; i++)
|
|
{
|
|
pHandle->GrantedAccessMask[i] = LocalCachingTypeList[i].CurrentGranted;
|
|
}
|
|
|
|
AuthzpAcquireClientCacheWriteLock(pCC);
|
|
|
|
pHandle->next = pCC->AuthzHandleHead;
|
|
pCC->AuthzHandleHead = pHandle;
|
|
|
|
AuthzpReleaseClientCacheLock(pCC);
|
|
|
|
*phAccessCheckResults = (AUTHZ_ACCESS_CHECK_RESULTS_HANDLE) pHandle;
|
|
|
|
Cleanup:
|
|
|
|
if (!b)
|
|
{
|
|
AuthzpFreeNonNull(pHandle);
|
|
SetLastError(RtlNtStatusToDosError(Status));
|
|
}
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpDefaultAccessCheck(
|
|
IN AUTHZ_CLIENT_CONTEXT_HANDLE pAuthzClientContext,
|
|
IN PACE_HEADER pAce,
|
|
IN PVOID pArgs OPTIONAL,
|
|
IN OUT PBOOL pbAceApplicable
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine is the default function to be used for callback aces if none
|
|
has been specified by the resource manager.
|
|
|
|
Returns AceApplicable = TRUE for DENY aces.
|
|
FALSE for Grant and audit aces.
|
|
|
|
Arguments:
|
|
Ignores all arguments other than pAce->AceType.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
UNREFERENCED_PARAMETER(pArgs);
|
|
UNREFERENCED_PARAMETER(pAuthzClientContext);
|
|
|
|
switch (pAce->AceType)
|
|
{
|
|
case ACCESS_ALLOWED_CALLBACK_ACE_TYPE:
|
|
case ACCESS_ALLOWED_CALLBACK_OBJECT_ACE_TYPE:
|
|
case SYSTEM_AUDIT_CALLBACK_ACE_TYPE:
|
|
case SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE:
|
|
|
|
*pbAceApplicable = FALSE;
|
|
|
|
break;
|
|
|
|
case ACCESS_DENIED_CALLBACK_ACE_TYPE:
|
|
case ACCESS_DENIED_CALLBACK_OBJECT_ACE_TYPE:
|
|
|
|
*pbAceApplicable = TRUE;
|
|
|
|
break;
|
|
|
|
default:
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpGenerateAudit(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN PAUTHZI_AUDIT_EVENT pAuditEvent,
|
|
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
IN PSECURITY_DESCRIPTOR *OptionalSecurityDescriptorArray OPTIONAL,
|
|
IN DWORD OptionalSecurityDescriptorCount,
|
|
IN OUT PAUTHZ_ACCESS_REPLY pReply,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine decides whether an audit needs to be generated. It is called
|
|
at the end of the access check.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pAuditEvent - Audit info to be in case we decide to generate an audit.
|
|
|
|
pSecurityDescriptor - Primary security descriptor to be used for access
|
|
checks. The owner sid for the object is picked from this one. A NULL
|
|
DACL in this security descriptor represents a NULL DACL for the entire
|
|
object. A NULL SACL in this security descriptor is treated the same way
|
|
as an EMPTY SACL.
|
|
|
|
OptionalSecurityDescriptorArray - The caller may optionally specify a list
|
|
of security descriptors. NULL ACLs in these security descriptors are
|
|
treated as EMPTY ACLS and the ACL for the entire object is the logical
|
|
concatenation of all the ACLs.
|
|
|
|
OptionalSecurityDescriptorCount - Number of optional security descriptors
|
|
This does not include the Primary security descriptor.
|
|
|
|
pReply - The reply structure holding the results of the access check.
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results.
|
|
|
|
LocalTypeListLength - Length of the array represnting the object.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b = FALSE;
|
|
BOOL bGenerateAudit = FALSE;
|
|
BOOL bGenerateSuccessAudit = FALSE;
|
|
BOOL bGenerateFailureAudit = FALSE;
|
|
|
|
//
|
|
// if the caller is interested in the whole object
|
|
// generate a normal audit
|
|
// else
|
|
// generate an audit for the entire tree - DS style
|
|
//
|
|
|
|
if ((1 == pReply->ResultListLength) && (NULL == pRequest->ObjectTypeList))
|
|
{
|
|
b = AuthzpExamineSacl(
|
|
pCC,
|
|
pRequest,
|
|
pSecurityDescriptor,
|
|
OptionalSecurityDescriptorArray,
|
|
OptionalSecurityDescriptorCount,
|
|
pReply,
|
|
&bGenerateAudit
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (bGenerateAudit)
|
|
{
|
|
if (ERROR_SUCCESS == pReply->Error[0])
|
|
{
|
|
LocalTypeList->Flags |= AUTHZ_OBJECT_SUCCESS_AUDIT;
|
|
|
|
b = AuthzpCreateAndLogAudit(
|
|
AUTHZ_OBJECT_SUCCESS_AUDIT,
|
|
pCC,
|
|
pAuditEvent,
|
|
(PAUTHZI_RESOURCE_MANAGER)pCC->pResourceManager,
|
|
LocalTypeList,
|
|
pRequest,
|
|
pReply
|
|
);
|
|
}
|
|
else
|
|
{
|
|
LocalTypeList->Flags |= AUTHZ_OBJECT_FAILURE_AUDIT;
|
|
|
|
b = AuthzpCreateAndLogAudit(
|
|
AUTHZ_OBJECT_FAILURE_AUDIT,
|
|
pCC,
|
|
pAuditEvent,
|
|
(PAUTHZI_RESOURCE_MANAGER)pCC->pResourceManager,
|
|
LocalTypeList,
|
|
pRequest,
|
|
pReply
|
|
);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
b = AuthzpExamineSaclForObjectTypeList(
|
|
pCC,
|
|
pRequest,
|
|
pSecurityDescriptor,
|
|
OptionalSecurityDescriptorArray,
|
|
OptionalSecurityDescriptorCount,
|
|
pReply,
|
|
LocalTypeList,
|
|
&bGenerateSuccessAudit,
|
|
&bGenerateFailureAudit
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (bGenerateSuccessAudit)
|
|
{
|
|
b = AuthzpCreateAndLogAudit(
|
|
AUTHZ_OBJECT_SUCCESS_AUDIT,
|
|
pCC,
|
|
pAuditEvent,
|
|
(PAUTHZI_RESOURCE_MANAGER)pCC->pResourceManager,
|
|
LocalTypeList,
|
|
pRequest,
|
|
pReply
|
|
);
|
|
}
|
|
if (bGenerateFailureAudit)
|
|
{
|
|
b = AuthzpCreateAndLogAudit(
|
|
AUTHZ_OBJECT_FAILURE_AUDIT,
|
|
pCC,
|
|
pAuditEvent,
|
|
(PAUTHZI_RESOURCE_MANAGER)pCC->pResourceManager,
|
|
LocalTypeList,
|
|
pRequest,
|
|
pReply
|
|
);
|
|
}
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
AuthzpExamineSacl(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
IN PSECURITY_DESCRIPTOR *OptionalSecurityDescriptorArray OPTIONAL,
|
|
IN DWORD OptionalSecurityDescriptorCount,
|
|
IN PAUTHZ_ACCESS_REPLY pReply,
|
|
OUT PBOOL pbGenerateAudit
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine loops thru the entire list of security descriptors and call
|
|
AuthzpExamineSingleSacl for each security descriptor.
|
|
|
|
Called in one of the following cases:
|
|
- the caller has not passed in an object type list
|
|
- the caller has passed in an object type list and asked for access at
|
|
the root of the tree instead of the whole tree.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pSecurityDescriptor - Primary security descriptor to be used for access
|
|
checks. The owner sid for the object is picked from this one. A NULL
|
|
DACL in this security descriptor represents a NULL DACL for the entire
|
|
object. A NULL SACL in this security descriptor is treated the same way
|
|
as an EMPTY SACL.
|
|
|
|
OptionalSecurityDescriptorArray - The caller may optionally specify a list
|
|
of security descriptors. NULL ACLs in these security descriptors are
|
|
treated as EMPTY ACLS and the ACL for the entire object is the logical
|
|
concatenation of all the ACLs.
|
|
|
|
OptionalSecurityDescriptorCount - Number of optional security descriptors
|
|
This does not include the Primary security descriptor.
|
|
|
|
pReply - The reply structure holding the results of the access check.
|
|
|
|
pbGenerateAudit - To return whether an audit needs to be generated.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PACL pAcl = NULL;
|
|
PSID pOwnerSid = NULL;
|
|
BOOL b = FALSE;
|
|
BOOL bMaximumFailed = FALSE;
|
|
UCHAR AuditMaskType = SUCCESSFUL_ACCESS_ACE_FLAG;
|
|
ACCESS_MASK AccessMask = 0;
|
|
DWORD i = 0;
|
|
|
|
if (0 == pReply->GrantedAccessMask[0])
|
|
{
|
|
AuditMaskType = FAILED_ACCESS_ACE_FLAG;
|
|
|
|
if (FLAG_ON(pRequest->DesiredAccess, MAXIMUM_ALLOWED))
|
|
{
|
|
bMaximumFailed = TRUE;
|
|
}
|
|
|
|
AccessMask = pRequest->DesiredAccess;
|
|
}
|
|
else
|
|
{
|
|
AccessMask = pReply->GrantedAccessMask[0];
|
|
}
|
|
|
|
pAcl = RtlpSaclAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) pSecurityDescriptor);
|
|
pOwnerSid = RtlpOwnerAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) pSecurityDescriptor);
|
|
|
|
b = AuthzpExamineSingleSacl(
|
|
pCC,
|
|
pRequest,
|
|
AccessMask,
|
|
pAcl,
|
|
pOwnerSid,
|
|
AuditMaskType,
|
|
bMaximumFailed,
|
|
pReply,
|
|
pbGenerateAudit
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; (i < OptionalSecurityDescriptorCount) && (!*pbGenerateAudit); i++)
|
|
{
|
|
pAcl = RtlpSaclAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) OptionalSecurityDescriptorArray[i]);
|
|
|
|
b = AuthzpExamineSingleSacl(
|
|
pCC,
|
|
pRequest,
|
|
AccessMask,
|
|
pAcl,
|
|
pOwnerSid,
|
|
AuditMaskType,
|
|
bMaximumFailed,
|
|
pReply,
|
|
pbGenerateAudit
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpExamineSingleSacl(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN ACCESS_MASK AccessMask,
|
|
IN PACL pAcl,
|
|
IN PSID pOwnerSid,
|
|
IN UCHAR AuditMaskType,
|
|
IN BOOL bMaximumFailed,
|
|
OUT PAUTHZ_ACCESS_REPLY pReply,
|
|
OUT PBOOL pbGenerateAudit
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine walk the sacl till we hit an ace that is applicable for the
|
|
access check result.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list structure (if any).
|
|
|
|
AccessMask - Desired access mask in case of failure,
|
|
Granted access mask in case of success
|
|
|
|
pAcl - The sacl against which granted/desired access bits will be matched
|
|
|
|
pOwnerSid - Need for single instance work later on
|
|
|
|
pReply - Results of the access check.
|
|
|
|
AuditMaskType - SUCCESSFUL_ACCESS_ACE_FLAG if access check succeeded
|
|
FAILED_ACCESS_ACE_FLAG otherwise
|
|
|
|
bMaximumFailed - Whether the caller asked for MaximumAllowed and access
|
|
check failed
|
|
|
|
pbGenerateAudit - To return whether an audit should be generated
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD AceCount = 0;
|
|
DWORD i = 0;
|
|
PVOID Ace = NULL;
|
|
BOOL bAceApplicable = FALSE;
|
|
DWORD Ignore = 0;
|
|
BOOL bIsAnonymous = FALSE;
|
|
|
|
//
|
|
// Ignore NULL as well as Empty sacls.
|
|
//
|
|
|
|
if ((!ARGUMENT_PRESENT(pAcl)) || (0 == (AceCount = pAcl->AceCount)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Check if this is Anonymous. We audit Anonymous when we see an ACE for
|
|
// Anonymous as well as Everyone.
|
|
//
|
|
|
|
if (AUTHZ_EQUAL_SID(pCC->Sids[0].Sid, pAuthzAnonymousSid, AuthzpAnonymousSidLen))
|
|
{
|
|
bIsAnonymous = TRUE;
|
|
}
|
|
|
|
for (i = 0, Ace = FirstAce(pAcl); i < AceCount; i++, Ace = NextAce(Ace))
|
|
{
|
|
//
|
|
// We are not interested in the ace if one of the following is true
|
|
// - inherit only ace
|
|
// - AuditMaskType does not match
|
|
// - AccessMask has no matching bits with the Mask in the acl and
|
|
// we are not looking for a MaximumAllowed failure audit
|
|
//
|
|
|
|
if (FLAG_ON(((PACE_HEADER) Ace)->AceFlags, INHERIT_ONLY_ACE) ||
|
|
!FLAG_ON(((PACE_HEADER) Ace)->AceFlags, AuditMaskType) ||
|
|
(!FLAG_ON(((PKNOWN_ACE) Ace)->Mask, AccessMask) && !bMaximumFailed))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
switch(((PACE_HEADER) Ace)->AceType)
|
|
{
|
|
case SYSTEM_AUDIT_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled or enabled for deny only if FAILED_ACCESS_ACE_FLAG
|
|
// is on. S-1-5-A is replaced by the principal sid supplied by the
|
|
// caller. In future, Creator Owner will be replaced by the owner
|
|
// sid in the primary security descriptor.
|
|
//
|
|
|
|
bAceApplicable = AuthzpSidApplicable(
|
|
pCC->SidCount,
|
|
pCC->Sids,
|
|
pCC->SidHash,
|
|
AuthzAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
FLAG_ON(((PACE_HEADER) Ace)->AceFlags, FAILED_ACCESS_ACE_FLAG),
|
|
&Ignore
|
|
);
|
|
|
|
//
|
|
// The AceSid may not be directly applicable. However, if the user
|
|
// is Anonymous, and the ACE is applicable to Everyone, we use this
|
|
// ACE is SACL evaluation.
|
|
//
|
|
//
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
if (bIsAnonymous)
|
|
{
|
|
if (!AUTHZ_EQUAL_SID(AuthzAceSid(Ace), pAuthzEveryoneSid, AuthzpEveryoneSidLen))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
*pbGenerateAudit = TRUE;
|
|
if (NULL != pReply->SaclEvaluationResults)
|
|
{
|
|
pReply->SaclEvaluationResults[0] |=
|
|
(pReply->Error[0] == ERROR_SUCCESS ? AUTHZ_GENERATE_SUCCESS_AUDIT : AUTHZ_GENERATE_FAILURE_AUDIT);
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
case SYSTEM_AUDIT_CALLBACK_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled or enabled for deny only if FAILED_ACCESS_ACE_FLAG
|
|
// is on. S-1-5-A is replaced by the principal sid supplied by the
|
|
// caller. In future, Creator Owner will be replaced by the owner
|
|
// sid in the primary security descriptor.
|
|
//
|
|
|
|
bAceApplicable = AuthzpSidApplicable(
|
|
pCC->SidCount,
|
|
pCC->Sids,
|
|
pCC->SidHash,
|
|
AuthzCallbackAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
FLAG_ON(((PACE_HEADER) Ace)->AceFlags, FAILED_ACCESS_ACE_FLAG),
|
|
&Ignore
|
|
);
|
|
|
|
//
|
|
// The AceSid may not be directly applicable. However, if the user
|
|
// is Anonymous, and the ACE is applicable to Everyone, we use this
|
|
// ACE is SACL evaluation.
|
|
//
|
|
//
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
if (bIsAnonymous)
|
|
{
|
|
if (!AUTHZ_EQUAL_SID(AuthzCallbackAceSid(Ace), pAuthzEveryoneSid, AuthzpEveryoneSidLen))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
bAceApplicable = FALSE;
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
*pbGenerateAudit = TRUE;
|
|
if (NULL != pReply->SaclEvaluationResults)
|
|
{
|
|
pReply->SaclEvaluationResults[0] |=
|
|
(pReply->Error[0] == ERROR_SUCCESS ? AUTHZ_GENERATE_SUCCESS_AUDIT : AUTHZ_GENERATE_FAILURE_AUDIT);
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpExamineSaclForObjectTypeList(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN PSECURITY_DESCRIPTOR pSecurityDescriptor,
|
|
IN PSECURITY_DESCRIPTOR *OptionalSecurityDescriptorArray OPTIONAL,
|
|
IN DWORD OptionalSecurityDescriptorCount,
|
|
IN OUT PAUTHZ_ACCESS_REPLY pReply,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList,
|
|
OUT PBOOL pbGenerateSuccessAudit,
|
|
OUT PBOOL pbGenerateFailureAudit
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine walks thru the entire list of security descriptors and calls
|
|
AuthzpExamineSingleSaclForObjectTypeList for each security descriptor.
|
|
Called when an object type list is passed and the caller has asked for
|
|
the results at each node.
|
|
|
|
Arguments:
|
|
|
|
pCC - Pointer to the client context.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pSecurityDescriptor - Primary security descriptor to be used for access
|
|
checks. The owner sid for the object is picked from this one. A NULL
|
|
DACL in this security descriptor represents a NULL DACL for the entire
|
|
object. A NULL SACL in this security descriptor is treated the same way
|
|
as an EMPTY SACL.
|
|
|
|
OptionalSecurityDescriptorArray - The caller may optionally specify a list
|
|
of security descriptors. NULL ACLs in these security descriptors are
|
|
treated as EMPTY ACLS and the ACL for the entire object is the logical
|
|
concatenation of all the ACLs.
|
|
|
|
OptionalSecurityDescriptorCount - Number of optional security descriptors
|
|
This does not include the Primary security descriptor.
|
|
|
|
pReply - The reply structure to return the results.
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results.
|
|
|
|
LocalTypeListLength - Length of the array represnting the object.
|
|
|
|
pbGenerateSuccessAudit - Returns whether a success audit should be
|
|
generated.
|
|
|
|
pbGenerateFailureAudit - Returns whether a failure audit should be
|
|
generated.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PACL pAcl = NULL;
|
|
PSID pOwnerSid = NULL;
|
|
BOOL b = FALSE;
|
|
DWORD i = 0;
|
|
|
|
pAcl = RtlpSaclAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) pSecurityDescriptor);
|
|
pOwnerSid = RtlpOwnerAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) pSecurityDescriptor);
|
|
|
|
b = AuthzpExamineSingleSaclForObjectTypeList(
|
|
pCC,
|
|
pRequest,
|
|
pAcl,
|
|
pOwnerSid,
|
|
pReply,
|
|
LocalTypeList,
|
|
pbGenerateSuccessAudit,
|
|
pbGenerateFailureAudit
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
for (i = 0; i < OptionalSecurityDescriptorCount; i++)
|
|
{
|
|
pAcl = RtlpSaclAddrSecurityDescriptor((PISECURITY_DESCRIPTOR) OptionalSecurityDescriptorArray[i]);
|
|
|
|
b = AuthzpExamineSingleSaclForObjectTypeList(
|
|
pCC,
|
|
pRequest,
|
|
pAcl,
|
|
pOwnerSid,
|
|
pReply,
|
|
LocalTypeList,
|
|
pbGenerateSuccessAudit,
|
|
pbGenerateFailureAudit
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
AuthzpExamineSingleSaclForObjectTypeList(
|
|
IN PAUTHZI_CLIENT_CONTEXT pCC,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN PACL pAcl,
|
|
IN PSID pOwnerSid,
|
|
IN PAUTHZ_ACCESS_REPLY pReply,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList,
|
|
OUT PBOOL pbGenerateSuccessAudit,
|
|
OUT PBOOL pbGenerateFailureAudit
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine walk thru the entire sacl and mark those nodes in the tree that
|
|
need be dumped to the audit log. It colors the whole subtree including the
|
|
node at which the ace is applicable. A normal ace is applicable at the root
|
|
of the tree.
|
|
|
|
Arguments:
|
|
pCC - Pointer to the client context to be audited.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pAcl - Sacl to be used to make the decision about audit generation.
|
|
|
|
pOwnerSid - The owner sid in the primary security descriptor. This will be
|
|
needed after we implement single instancing.
|
|
|
|
pReply - Supplies a pointer to a reply structure used to return the results.
|
|
|
|
LocalTypeList - Internal object type list structure used to hold
|
|
intermediate results.
|
|
|
|
pbGenerateSuccessAudit - Returns whether a success audit should be
|
|
generated.
|
|
|
|
pbGenerateFailureAudit - Returns whether a failure audit should be
|
|
generated.
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD AceCount = 0;
|
|
DWORD i = 0;
|
|
DWORD Ignore = 0;
|
|
DWORD Index = 0;
|
|
PVOID Ace = NULL;
|
|
GUID * ObjectTypeInAce = NULL;
|
|
BOOL bAceApplicable = FALSE;
|
|
BOOL bIsAnonymous = FALSE;
|
|
|
|
//
|
|
// Ignore NULL as well as Empty sacls.
|
|
//
|
|
|
|
if ((!ARGUMENT_PRESENT(pAcl)) || (0 == (AceCount = pAcl->AceCount)))
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Check if this is Anonymous. We audit Anonymous when we see an ACE for
|
|
// Anonymous as well as Everyone.
|
|
//
|
|
|
|
if (AUTHZ_EQUAL_SID(pCC->Sids[0].Sid, pAuthzAnonymousSid, AuthzpAnonymousSidLen))
|
|
{
|
|
bIsAnonymous = TRUE;
|
|
}
|
|
|
|
for (i = 0, Ace = FirstAce(pAcl); i < AceCount; i++, Ace = NextAce(Ace))
|
|
{
|
|
//
|
|
// Skip INHERIT_ONLY aces.
|
|
//
|
|
|
|
if (FLAG_ON(((PACE_HEADER) Ace)->AceFlags, INHERIT_ONLY_ACE))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
switch(((PACE_HEADER) Ace)->AceType)
|
|
{
|
|
case SYSTEM_AUDIT_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled or enabled for deny only if FAILED_ACCESS_ACE_FLAG
|
|
// is on. S-1-5-A is replaced by the principal sid supplied by the
|
|
// caller. In future, Creator Owner will be replaced by the owner
|
|
// sid in the primary security descriptor.
|
|
//
|
|
|
|
bAceApplicable = AuthzpSidApplicable(
|
|
pCC->SidCount,
|
|
pCC->Sids,
|
|
pCC->SidHash,
|
|
AuthzAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
FLAG_ON(((PACE_HEADER) Ace)->AceFlags, FAILED_ACCESS_ACE_FLAG),
|
|
&Ignore
|
|
);
|
|
|
|
//
|
|
// The AceSid may not be directly applicable. However, if the user
|
|
// is Anonymous, and the ACE is applicable to Everyone, we use this
|
|
// ACE is SACL evaluation.
|
|
//
|
|
//
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
if (bIsAnonymous)
|
|
{
|
|
if (!AUTHZ_EQUAL_SID(AuthzAceSid(Ace), pAuthzEveryoneSid, AuthzpEveryoneSidLen))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// We have found an ace that is applicable. Walk the tree to decide
|
|
// if an audit should be generated. Also, mark the nodes that need
|
|
// be dumped to the audit log.
|
|
//
|
|
|
|
AuthzpSetAuditInfoForObjectType(
|
|
pReply,
|
|
LocalTypeList,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask,
|
|
pRequest->DesiredAccess,
|
|
((PACE_HEADER) Ace)->AceFlags,
|
|
pbGenerateSuccessAudit,
|
|
pbGenerateFailureAudit
|
|
);
|
|
|
|
break;
|
|
|
|
case SYSTEM_AUDIT_CALLBACK_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled or enabled for deny only if FAILED_ACCESS_ACE_FLAG
|
|
// is on. S-1-5-A is replaced by the principal sid supplied by the
|
|
// caller. In future, Creator Owner will be replaced by the owner
|
|
// sid in the primary security descriptor.
|
|
//
|
|
|
|
bAceApplicable = AuthzpSidApplicable(
|
|
pCC->SidCount,
|
|
pCC->Sids,
|
|
pCC->SidHash,
|
|
AuthzAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
FLAG_ON(((PACE_HEADER) Ace)->AceFlags, FAILED_ACCESS_ACE_FLAG),
|
|
&Ignore
|
|
);
|
|
|
|
//
|
|
// The AceSid may not be directly applicable. However, if the user
|
|
// is Anonymous, and the ACE is applicable to Everyone, we use this
|
|
// ACE is SACL evaluation.
|
|
//
|
|
//
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
if (bIsAnonymous)
|
|
{
|
|
if (!AUTHZ_EQUAL_SID(AuthzAceSid(Ace), pAuthzEveryoneSid, AuthzpEveryoneSidLen))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
bAceApplicable = FALSE;
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
//
|
|
// We have found an ace that is applicable. Walk the tree to decide
|
|
// if an audit should be generated. Also, mark the nodes that need
|
|
// be dumped to the audit log.
|
|
//
|
|
|
|
AuthzpSetAuditInfoForObjectType(
|
|
pReply,
|
|
LocalTypeList,
|
|
0,
|
|
((PKNOWN_ACE) Ace)->Mask,
|
|
pRequest->DesiredAccess,
|
|
((PACE_HEADER) Ace)->AceFlags,
|
|
pbGenerateSuccessAudit,
|
|
pbGenerateFailureAudit
|
|
);
|
|
|
|
break;
|
|
|
|
case SYSTEM_AUDIT_OBJECT_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled or enabled for deny only if FAILED_ACCESS_ACE_FLAG
|
|
// is on. S-1-5-A is replaced by the principal sid supplied by the
|
|
// caller. In future, Creator Owner will be replaced by the owner
|
|
// sid in the primary security descriptor.
|
|
//
|
|
|
|
bAceApplicable = AuthzpSidApplicable(
|
|
pCC->SidCount,
|
|
pCC->Sids,
|
|
pCC->SidHash,
|
|
RtlObjectAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
FLAG_ON(((PACE_HEADER) Ace)->AceFlags, FAILED_ACCESS_ACE_FLAG),
|
|
&Ignore
|
|
);
|
|
|
|
//
|
|
// The AceSid may not be directly applicable. However, if the user
|
|
// is Anonymous, and the ACE is applicable to Everyone, we use this
|
|
// ACE is SACL evaluation.
|
|
//
|
|
//
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
if (bIsAnonymous)
|
|
{
|
|
if (!AUTHZ_EQUAL_SID(RtlObjectAceSid(Ace), pAuthzEveryoneSid, AuthzpEveryoneSidLen))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
ObjectTypeInAce = RtlObjectAceObjectType(Ace);
|
|
|
|
Index = 0;
|
|
|
|
if (AUTHZ_NON_NULL_PTR(ObjectTypeInAce))
|
|
{
|
|
//
|
|
// Look for a matching object type guid that matches the one in
|
|
// the ace.
|
|
//
|
|
|
|
if (!AuthzpObjectInTypeList(
|
|
ObjectTypeInAce,
|
|
LocalTypeList,
|
|
pReply->ResultListLength,
|
|
&Index
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We have found an ace that is applicable. Walk the tree to decide
|
|
// if an audit should be generated. Also, mark the nodes that need
|
|
// be dumped to the audit log.
|
|
//
|
|
|
|
AuthzpSetAuditInfoForObjectType(
|
|
pReply,
|
|
LocalTypeList,
|
|
Index,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
pRequest->DesiredAccess,
|
|
((PACE_HEADER) Ace)->AceFlags,
|
|
pbGenerateSuccessAudit,
|
|
pbGenerateFailureAudit
|
|
);
|
|
|
|
break;
|
|
|
|
case SYSTEM_AUDIT_CALLBACK_OBJECT_ACE_TYPE:
|
|
|
|
//
|
|
// Check if the effective ace sid is present in the client context
|
|
// and is enabled or enabled for deny only if FAILED_ACCESS_ACE_FLAG
|
|
// is on. S-1-5-A is replaced by the principal sid supplied by the
|
|
// caller. In future, Creator Owner will be replaced by the owner
|
|
// sid in the primary security descriptor.
|
|
//
|
|
|
|
bAceApplicable = AuthzpSidApplicable(
|
|
pCC->SidCount,
|
|
pCC->Sids,
|
|
pCC->SidHash,
|
|
AuthzObjectAceSid(Ace),
|
|
pRequest->PrincipalSelfSid,
|
|
pOwnerSid,
|
|
FLAG_ON(((PACE_HEADER) Ace)->AceFlags, FAILED_ACCESS_ACE_FLAG),
|
|
&Ignore
|
|
);
|
|
|
|
//
|
|
// The AceSid may not be directly applicable. However, if the user
|
|
// is Anonymous, and the ACE is applicable to Everyone, we use this
|
|
// ACE is SACL evaluation.
|
|
//
|
|
//
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
if (bIsAnonymous)
|
|
{
|
|
if (!AUTHZ_EQUAL_SID(AuthzObjectAceSid(Ace), pAuthzEveryoneSid, AuthzpEveryoneSidLen))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
bAceApplicable = FALSE;
|
|
|
|
//
|
|
// Make a call to the resource manager to get his opinion. His
|
|
// evaluation is returned in bAceApplicalble.
|
|
//
|
|
// Note: The return value of the callback is used to decide whether
|
|
// the API failed/succeeded. On a failure, we exit out of access
|
|
// check. On success, we check the boolean returned by
|
|
// bAceApplicable to decide whether to use the current ace.
|
|
//
|
|
|
|
if (!((*(pCC->pResourceManager->pfnDynamicAccessCheck)) (
|
|
(AUTHZ_CLIENT_CONTEXT_HANDLE) pCC,
|
|
Ace,
|
|
pRequest->OptionalArguments,
|
|
&bAceApplicable
|
|
)))
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
if (!bAceApplicable)
|
|
{
|
|
break;
|
|
}
|
|
|
|
ObjectTypeInAce = RtlObjectAceObjectType(Ace);
|
|
|
|
Index = 0;
|
|
|
|
if (AUTHZ_NON_NULL_PTR(ObjectTypeInAce))
|
|
{
|
|
//
|
|
// Look for a matching object type guid that matches the one in
|
|
// the ace.
|
|
//
|
|
|
|
if (!AuthzpObjectInTypeList(
|
|
ObjectTypeInAce,
|
|
LocalTypeList,
|
|
pReply->ResultListLength,
|
|
&Index
|
|
))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// We have found an ace that is applicable. Walk the tree to decide
|
|
// if an audit should be generated. Also, mark the nodes that need
|
|
// be dumped to the audit log.
|
|
//
|
|
|
|
AuthzpSetAuditInfoForObjectType(
|
|
pReply,
|
|
LocalTypeList,
|
|
Index,
|
|
((PKNOWN_OBJECT_ACE) Ace)->Mask,
|
|
pRequest->DesiredAccess,
|
|
((PACE_HEADER) Ace)->AceFlags,
|
|
pbGenerateSuccessAudit,
|
|
pbGenerateFailureAudit
|
|
);
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
AuthzpSetAuditInfoForObjectType(
|
|
IN PAUTHZ_ACCESS_REPLY pReply,
|
|
IN OUT PIOBJECT_TYPE_LIST LocalTypeList,
|
|
IN DWORD StartIndex,
|
|
IN ACCESS_MASK AceAccessMask,
|
|
IN ACCESS_MASK DesiredAccessMask,
|
|
IN UCHAR AceFlags,
|
|
OUT PBOOL pbGenerateSuccessAudit,
|
|
OUT PBOOL pbGenerateFailureAudit
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine propagates the audit decision up and down the subtree starting at
|
|
StartIndex.
|
|
|
|
Arguments:
|
|
|
|
pReply - This has been filled by access check at this point. We just read
|
|
the values to make audit decision.
|
|
|
|
LocalTypeList - Decision to audit a node is stored into Flags corresponding
|
|
member of this array.
|
|
|
|
StartIndex - The index in the array at which the coloring should start.
|
|
|
|
AceAccessMask - Access mask in the audit ace.
|
|
|
|
DesiredAccessMask - Desired access mask in the access request.
|
|
|
|
AceFlags - AceFlags member of the ace header. We are interested in the
|
|
audit flags.
|
|
|
|
pbGenerateSuccessAudit - Returns whether a success audit should be
|
|
generated.
|
|
|
|
pbGenerateFailureAudit - Returns whether a failure audit should be
|
|
generated.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i = StartIndex;
|
|
LONG ParentIndex;
|
|
LONG ChildIndex;
|
|
|
|
do
|
|
{
|
|
//
|
|
// ChildIndex will always be the index of the last object in the list
|
|
// that we examined.
|
|
//
|
|
|
|
ChildIndex = i;
|
|
|
|
//
|
|
// Store the decision to audit in the local type list if there is a
|
|
// match of access bits.
|
|
//
|
|
|
|
if (ERROR_SUCCESS == pReply->Error[i])
|
|
{
|
|
if (FLAG_ON(AceFlags, SUCCESSFUL_ACCESS_ACE_FLAG) &&
|
|
FLAG_ON(pReply->GrantedAccessMask[i], AceAccessMask))
|
|
{
|
|
*pbGenerateSuccessAudit = TRUE;
|
|
LocalTypeList[i].Flags |= AUTHZ_OBJECT_SUCCESS_AUDIT;
|
|
if (NULL != pReply->SaclEvaluationResults)
|
|
{
|
|
pReply->SaclEvaluationResults[i] |= AUTHZ_GENERATE_SUCCESS_AUDIT;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Failure audit is generated even if the bits do not match if the
|
|
// caller asked for MAXIMUM_ALLOWED.
|
|
//
|
|
|
|
if (FLAG_ON(AceFlags, FAILED_ACCESS_ACE_FLAG) &&
|
|
FLAG_ON(DesiredAccessMask, (AceAccessMask | MAXIMUM_ALLOWED)))
|
|
{
|
|
*pbGenerateFailureAudit = TRUE;
|
|
LocalTypeList[i].Flags |= AUTHZ_OBJECT_FAILURE_AUDIT;
|
|
if (NULL != pReply->SaclEvaluationResults)
|
|
{
|
|
pReply->SaclEvaluationResults[i] |= AUTHZ_GENERATE_FAILURE_AUDIT;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
//
|
|
// Stop the traversal when the list is exhausted or when we have hit a
|
|
// sibling of the starting node.
|
|
//
|
|
|
|
} while ((i < pReply->ResultListLength) &&
|
|
(LocalTypeList[i].Level > LocalTypeList[StartIndex].Level));
|
|
|
|
//
|
|
// Now propagate the audit bits up through to the parents.
|
|
//
|
|
|
|
for (ChildIndex; ChildIndex > 0; ChildIndex--)
|
|
{
|
|
ParentIndex = LocalTypeList[ChildIndex].ParentIndex;
|
|
LocalTypeList[ParentIndex].Flags |= LocalTypeList[ChildIndex].Flags;
|
|
if (pReply->SaclEvaluationResults)
|
|
{
|
|
pReply->SaclEvaluationResults[ParentIndex] |= pReply->SaclEvaluationResults[ChildIndex];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
BOOL
|
|
AuthzpVerifyCachedAccessCheckArguments(
|
|
IN PAUTHZI_HANDLE pAH,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN OUT PAUTHZ_ACCESS_REPLY pReply
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine verifies arguments for the cached access check call.
|
|
|
|
Arguments:
|
|
|
|
pAH - Pointer to the authz handle structure.
|
|
|
|
pRequest - Access request specifies the desired access mask, principal self
|
|
sid, the object type list strucutre (if any).
|
|
|
|
pReply - Supplies a pointer to a reply structure used to return the results
|
|
|
|
Return Value:
|
|
|
|
A value of TRUE is returned if the routine is successful. Otherwise,
|
|
a value of FALSE is returned. In the failure case, error value may be
|
|
retrieved using GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
if (!ARGUMENT_PRESENT(pAH))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!ARGUMENT_PRESENT(pRequest))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!ARGUMENT_PRESENT(pReply))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// The caller can specify one of the two values for Reply->ResultListLength
|
|
// a. 1 - representing the whole object.
|
|
// b. pRequest->ObjectTypeListLength - for every node in the type list.
|
|
//
|
|
|
|
if ((1 != pReply->ResultListLength) &&
|
|
(pReply->ResultListLength != pRequest->ObjectTypeListLength))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
AuthzpFillReplyStructureFromCachedGrantedAccessMask(
|
|
IN OUT PAUTHZ_ACCESS_REPLY pReply,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN PACCESS_MASK GrantedAccessMask
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine description:
|
|
|
|
This routine fills the reply structure from the granted access mask array
|
|
in the cache.
|
|
|
|
Arguments:
|
|
|
|
pReply - The reply structure to fill.
|
|
|
|
DesiredAccess - Access mask desired.
|
|
|
|
GrantedAccessMask - Array of granted masks from the cache.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i = 0;
|
|
|
|
for (i = 0; i < pReply->ResultListLength; i++)
|
|
{
|
|
if (FLAG_ON(DesiredAccess, ~(GrantedAccessMask[i])))
|
|
{
|
|
pReply->GrantedAccessMask[i] = 0;
|
|
pReply->Error[i] = ERROR_ACCESS_DENIED;
|
|
}
|
|
else
|
|
{
|
|
pReply->GrantedAccessMask[i] = DesiredAccess;
|
|
pReply->Error[i] = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
AuthzpReferenceAuditEventType(
|
|
IN AUTHZ_AUDIT_EVENT_TYPE_HANDLE hAET
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This references an AUTHZ_AUDIT_EVENT_TYPE_HANDLE. The handle is referenced whenever
|
|
it is used in a situation where it will be 'unused.' For instance, when an audit is placed
|
|
on the audit queue, we reference hAET. When we take that audit off of the queue, we deref
|
|
hAET. This allows the user to not have to concern himself with sync issues revolving around
|
|
the implementation of the hAET.
|
|
|
|
Arguments
|
|
|
|
hAET - the AUTHZ_AUDIT_EVENT_TYPE_HANDLE to reference.
|
|
|
|
Return Value
|
|
|
|
Boolean: TRUE on success, FALSE on fail. Extended information is available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PAUTHZ_AUDIT_EVENT_TYPE_OLD pAAETO = (PAUTHZ_AUDIT_EVENT_TYPE_OLD)hAET;
|
|
InterlockedIncrement(&pAAETO->RefCount);
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpDereferenceAuditEventType(
|
|
IN OUT AUTHZ_AUDIT_EVENT_TYPE_HANDLE hAET
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Dereferences and AUTHZ_AUDIT_EVENT_TYPE_HANDLE.
|
|
|
|
Arguments
|
|
|
|
hAET - handle to dereference.
|
|
|
|
Return Value
|
|
|
|
Boolean: TRUE on success, FALSE on fail. Extended information is available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PAUTHZ_AUDIT_EVENT_TYPE_OLD pAAETO = (PAUTHZ_AUDIT_EVENT_TYPE_OLD)hAET;
|
|
LONG Refs = 0;
|
|
BOOL b = TRUE;
|
|
|
|
Refs = InterlockedDecrement(&pAAETO->RefCount);
|
|
|
|
ASSERT(Refs >= 0);
|
|
|
|
if (Refs == 0)
|
|
{
|
|
b = AuthzpUnregisterAuditEvent((PAUDIT_HANDLE)&(pAAETO->hAudit));
|
|
|
|
//
|
|
// If we fail because the server is busy, then try once more.
|
|
// We don't want to sit here forever, however.
|
|
//
|
|
|
|
if (!b && (RPC_S_SERVER_TOO_BUSY == GetLastError()))
|
|
{
|
|
Sleep(10000);
|
|
b = AuthzpUnregisterAuditEvent((PAUDIT_HANDLE)&(pAAETO->hAudit));
|
|
}
|
|
|
|
//
|
|
// Don't assert if the server was simply too busy (which usually
|
|
// indicates that we are shutting down).
|
|
//
|
|
|
|
if (!b && (RPC_S_SERVER_TOO_BUSY != GetLastError()))
|
|
{
|
|
ASSERT(L"AuthzpUnregisterAuditEvent failed for reason != server busy" && FALSE);
|
|
}
|
|
AuthzpFree(hAET);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpEveryoneIncludesAnonymous(
|
|
OUT PBOOL pbInclude
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks to see if we should include Everyone Sid in Anonymous
|
|
contexts.
|
|
|
|
The reg key under system\currentcontrolset\Control\Lsa\
|
|
AnonymousIncludesEveryone indicates whether or not to include the group.
|
|
If the value is zero (or doesn't exist), we restrict Anonymous context by
|
|
not giving it the Everyone Sid.
|
|
|
|
Arguments:
|
|
|
|
bInclude - TRUE if Everyone Sid should be in the context, false otherwise.
|
|
|
|
Return Value:
|
|
|
|
Boolean: TRUE on success, FALSE on fail. Extended information is available with GetLastError().
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS NtStatus = STATUS_SUCCESS;
|
|
UNICODE_STRING KeyName = {0};
|
|
OBJECT_ATTRIBUTES ObjectAttributes = {0};
|
|
HANDLE KeyHandle = NULL;
|
|
UCHAR Buffer[100];
|
|
PKEY_VALUE_PARTIAL_INFORMATION KeyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION) Buffer;
|
|
ULONG KeyValueLength = 100;
|
|
ULONG ResultLength = 0;
|
|
PULONG Flag = NULL;
|
|
BOOL b = TRUE;
|
|
|
|
*pbInclude = FALSE;
|
|
|
|
//
|
|
// Open the Lsa key in the registry
|
|
//
|
|
|
|
RtlInitUnicodeString(
|
|
&KeyName,
|
|
L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa"
|
|
);
|
|
|
|
InitializeObjectAttributes(
|
|
&ObjectAttributes,
|
|
&KeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
NtStatus = NtOpenKey(
|
|
&KeyHandle,
|
|
KEY_READ,
|
|
&ObjectAttributes
|
|
);
|
|
|
|
if (!NT_SUCCESS(NtStatus)) {
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlInitUnicodeString(
|
|
&KeyName,
|
|
L"EveryoneIncludesAnonymous"
|
|
);
|
|
|
|
NtStatus = NtQueryValueKey(
|
|
KeyHandle,
|
|
&KeyName,
|
|
KeyValuePartialInformation,
|
|
KeyValueInformation,
|
|
KeyValueLength,
|
|
&ResultLength
|
|
);
|
|
|
|
if (NT_SUCCESS(NtStatus)) {
|
|
|
|
//
|
|
// Check that the data is the correct size and type - a ULONG.
|
|
//
|
|
|
|
if ((KeyValueInformation->DataLength >= sizeof(ULONG)) &&
|
|
(KeyValueInformation->Type == REG_DWORD)) {
|
|
|
|
|
|
Flag = (PULONG) KeyValueInformation->Data;
|
|
|
|
if (*Flag != 0 ) {
|
|
*pbInclude = TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
NtClose(KeyHandle);
|
|
|
|
Cleanup:
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpConstructRegistryPolicyPerUserAuditing(
|
|
IN PTOKEN_AUDIT_POLICY pPolicy,
|
|
OUT PULONGLONG pRegPolicy
|
|
)
|
|
|
|
/**
|
|
|
|
Routine Description:
|
|
|
|
This constructs a per user policy suitable for setting in the registry key.
|
|
|
|
Arguments:
|
|
|
|
pPolicy - the policy to convert into the registry setting.
|
|
|
|
pRegPolicy - the registry format of the policy.
|
|
|
|
Return Value:
|
|
|
|
Boolean: TRUE on success, FALSE on fail. Extended information is available with GetLastError().
|
|
|
|
**/
|
|
|
|
{
|
|
ULONG i;
|
|
ULONGLONG RegPolicy = 0;
|
|
DWORD PolicyBits;
|
|
DWORD Category;
|
|
|
|
*pRegPolicy = 0;
|
|
|
|
//
|
|
// For each policy entry in the array, extract the 4 legal policy bits
|
|
// and place them in the proper nibble in the 64bit registry policy.
|
|
//
|
|
|
|
ASSERT(pPolicy->PolicyCount <= POLICY_AUDIT_EVENT_TYPE_COUNT);
|
|
|
|
for (i = 0; i < pPolicy->PolicyCount; i++)
|
|
{
|
|
PolicyBits = pPolicy->Policy[i].PolicyMask;
|
|
Category = pPolicy->Policy[i].Category;
|
|
ASSERT(Category <= POLICY_AUDIT_EVENT_TYPE_COUNT);
|
|
|
|
if (Category > POLICY_AUDIT_EVENT_TYPE_COUNT)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// 4 bits per category.
|
|
//
|
|
|
|
RegPolicy |= ((ULONGLONG)PolicyBits << (4 * Category));
|
|
}
|
|
|
|
*pRegPolicy = RegPolicy;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpConstructPolicyPerUserAuditing(
|
|
IN ULONGLONG RawPolicy,
|
|
OUT PTOKEN_AUDIT_POLICY pTokenPolicy,
|
|
IN OUT PULONG TokenPolicyLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This constructs a human parsable audit policy (policy appropriate for passing
|
|
to NtSetTokenInformation). It converts the raw registry policy into a TOKEN_AUDIT_POLICY.
|
|
|
|
Arguments
|
|
|
|
RawPolicy - a 64 bit quantity describing a user's audit policy settings.
|
|
|
|
pTokenPolicy - points to memory that receives a more presentable form of the RawPolicy.
|
|
|
|
TokenPolicyLength - The length of the pTokenPolicy buffer. Receives the necessary length
|
|
in the case that the buffer is insufficient.
|
|
|
|
Return Value
|
|
|
|
Appropriate NTSTATUS value.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
ULONG j;
|
|
ULONG PolicyBits;
|
|
ULONG CategoryCount;
|
|
ULONG LengthNeeded;
|
|
|
|
//
|
|
// First calculate the number of category settings in the RawPolicy
|
|
// This will reveal if we have enough space to construct the pTokenPolicy.
|
|
//
|
|
|
|
for (CategoryCount = 0, i = 0; i < POLICY_AUDIT_EVENT_TYPE_COUNT; i++)
|
|
{
|
|
if ((RawPolicy >> (4 * i)) & VALID_AUDIT_POLICY_BITS)
|
|
{
|
|
CategoryCount++;
|
|
}
|
|
}
|
|
|
|
LengthNeeded = PER_USER_AUDITING_POLICY_SIZE_BY_COUNT(CategoryCount);
|
|
|
|
//
|
|
// Check if the passed buffer is large enough.
|
|
//
|
|
|
|
if (*TokenPolicyLength < LengthNeeded)
|
|
{
|
|
*TokenPolicyLength = LengthNeeded;
|
|
SetLastError(ERROR_INSUFFICIENT_BUFFER);
|
|
return FALSE;
|
|
}
|
|
|
|
*TokenPolicyLength = LengthNeeded;
|
|
|
|
//
|
|
// Build the policy.
|
|
//
|
|
|
|
pTokenPolicy->PolicyCount = CategoryCount;
|
|
|
|
for (j = 0, i = 0; i < POLICY_AUDIT_EVENT_TYPE_COUNT; i++)
|
|
{
|
|
PolicyBits = (ULONG)((RawPolicy >> (4 * i)) & VALID_AUDIT_POLICY_BITS);
|
|
|
|
if (PolicyBits)
|
|
{
|
|
pTokenPolicy->Policy[j].Category = i;
|
|
pTokenPolicy->Policy[j].PolicyMask = PolicyBits;
|
|
j++;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpInitializeAuditParamsV(
|
|
IN DWORD dwFlags,
|
|
OUT PAUDIT_PARAMS pParams,
|
|
IN OUT PSID* ppUserSid,
|
|
IN PCWSTR SubsystemName,
|
|
IN USHORT AuditId,
|
|
IN USHORT NumParams,
|
|
IN va_list arglist
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the AUDIT_PARAMS structure based on the passed
|
|
data.
|
|
|
|
Arguments:
|
|
|
|
dwFlags - control flags. one or more of the following:
|
|
APF_AuditSuccess
|
|
AUTHZP_INIT_PARAMS_SKIP_HEADER
|
|
AUTHZP_INIT_PARAMS_SOURCE_INFO
|
|
|
|
pParams - pointer to AUDIT_PARAMS structure to be initialized
|
|
the size of pParams->Parameters member must be big enough
|
|
to hold NumParams elements. The caller must allocate
|
|
memory for this structure and its members.
|
|
|
|
ppUserSid - pointer to user sid. This sid is referenced
|
|
by the first parameter (index 0) in AUDIT_PARAMS
|
|
structure.
|
|
If dwFlags contains the AUTHZP_INIT_PARAMS_SKIP_HEADER
|
|
flag, this parameter is completely ignored.
|
|
If a NULL sid is passed in, the function will allocate
|
|
the sid. The caller should free this by calling LocalFree
|
|
*after* freeing the AUDIT_PARAMS structure.
|
|
If a non-NULL sid is passed in the function will just
|
|
use the passed-in value and not allocate anything..
|
|
|
|
SubsystemName - name of Subsystem that is generating audit
|
|
|
|
|
|
ap - The format of the variable arg portion is as follows:
|
|
|
|
<one of APT_ * flags> <zero or more values>
|
|
|
|
APT_String <pointer to null terminated string>
|
|
|
|
Flags:
|
|
AP_Filespec : treats the string as a filename
|
|
|
|
APT_Ulong <ulong value> [<object-type-index>]
|
|
|
|
Flags:
|
|
AP_FormatHex : number appears in hex
|
|
in eventlog
|
|
AP_AccessMask: number is treated as
|
|
an access-mask
|
|
Index to object type must follow
|
|
|
|
APT_Pointer <pointer/handle>
|
|
32 bit on 32 bit systems and
|
|
64 bit on 64 bit systems
|
|
|
|
APT_Sid <pointer to sid>
|
|
|
|
APT_Luid <Luid>
|
|
|
|
APT_Guid <pointer to guid>
|
|
|
|
APT_LogonId [<logon-id>]
|
|
|
|
Flags:
|
|
AP_PrimaryLogonId : logon-id is captured
|
|
from the process token.
|
|
No need to specify one.
|
|
|
|
AP_ClientLogonId : logon-id is captured
|
|
from the thread token.
|
|
No need to specify one.
|
|
|
|
no flags : need to supply a logon-id
|
|
|
|
APT_ObjectTypeList <ptr to obj type list> <obj-type-index>
|
|
Index to object type must be specified
|
|
|
|
APT_Time <filetime>
|
|
|
|
Return Value:
|
|
TRUE on success
|
|
FALSE otherwise
|
|
|
|
call GetLastError() to retrieve the errorcode,
|
|
which will be one of the following:
|
|
|
|
ERROR_INVALID_PARAMETER if one of the params is incorrect
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD dwError = NO_ERROR;
|
|
BOOL fResult = TRUE;
|
|
USHORT i;
|
|
AUDIT_PARAM_TYPE ParamType;
|
|
AUDIT_PARAM* pParam;
|
|
LUID Luid;
|
|
LUID AuthIdThread = {0};
|
|
LUID AuthIdProcess = {0};
|
|
BOOL bGotTokenInfo = FALSE;
|
|
BOOL bGotThreadTokenInfo = FALSE;
|
|
BOOL fImpersonating=TRUE;
|
|
FILETIME FileTime;
|
|
ULONG TypeFlags;
|
|
ULONG ParamFlags;
|
|
ULONG ObjectTypeIndex;
|
|
ULONG ObjectTypeIndexAdjustment = 0;
|
|
PSID pUserSid = NULL;
|
|
|
|
if (!FLAG_ON(dwFlags, AUTHZP_INIT_PARAMS_SKIP_HEADER))
|
|
{
|
|
//
|
|
// the first two params are always fixed. thus the total number
|
|
// is 2 + the passed number.
|
|
//
|
|
|
|
if (( (NumParams+AUTHZP_NUM_FIXED_HEADER_PARAMS) > SE_MAX_AUDIT_PARAMETERS ) ||
|
|
( pParams == NULL ) ||
|
|
( ppUserSid == NULL ) ||
|
|
( dwFlags & ~(APF_ValidFlags | AUTHZP_INIT_PARAMS_SOURCE_INFO | AUTHZP_INIT_PARAMS_SKIP_HEADER | AUTHZP_INIT_PARAMS_SOURCE_DS) )
|
|
)
|
|
{
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (*ppUserSid == NULL)
|
|
{
|
|
if (!AuthzpGetThreadTokenInfo( &pUserSid, &AuthIdThread ))
|
|
{
|
|
dwError = GetLastError();
|
|
|
|
if (dwError == ERROR_NO_TOKEN)
|
|
{
|
|
fImpersonating = FALSE;
|
|
dwError = NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
bGotThreadTokenInfo = TRUE;
|
|
|
|
if (!fImpersonating)
|
|
{
|
|
if (!AuthzpGetProcessTokenInfo( &pUserSid, &AuthIdProcess ))
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
bGotTokenInfo = TRUE;
|
|
}
|
|
}
|
|
|
|
pParams->Length = 0;
|
|
pParams->Count = NumParams+AUTHZP_NUM_FIXED_HEADER_PARAMS;
|
|
pParam = pParams->Parameters;
|
|
pParams->Flags = dwFlags & APF_ValidFlags; // strip out any flags that were intended for this API's
|
|
// consumption (AUTHZP_INIT_PARAMS_*)
|
|
|
|
//
|
|
// the first param is always the sid of the user in thread token
|
|
// if thread is not impersonating, sid in the primary token is used.
|
|
//
|
|
|
|
pParam->Type = APT_Sid;
|
|
pParam->Data0 = (ULONG_PTR) (pUserSid ? pUserSid : *ppUserSid);
|
|
|
|
pParam++;
|
|
|
|
//
|
|
// the second param is always the sub-system name "Security"
|
|
//
|
|
|
|
pParam->Type = APT_String;
|
|
if (FLAG_ON(dwFlags, AUTHZP_INIT_PARAMS_SOURCE_DS))
|
|
{
|
|
pParam->Data0 = (ULONG_PTR) L"DS";
|
|
}
|
|
else
|
|
{
|
|
pParam->Data0 = (ULONG_PTR) L"Security";
|
|
}
|
|
|
|
pParam++;
|
|
|
|
//
|
|
// Since we have prepended 2 parameters, any ObjectTypeIndex calculation
|
|
// must reflect this offset.
|
|
//
|
|
|
|
ObjectTypeIndexAdjustment += 2;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We are not initializing the params with the first 2 params
|
|
// as the SID and the Security string.
|
|
//
|
|
|
|
if (((NumParams+AUTHZP_NUM_FIXED_HEADER_PARAMS) > SE_MAX_AUDIT_PARAMETERS) ||
|
|
(pParams == NULL)
|
|
)
|
|
{
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pParams->Length = 0;
|
|
pParams->Count = NumParams;
|
|
pParam = pParams->Parameters;
|
|
pParams->Flags = dwFlags & APF_ValidFlags; // strip out any flags that were intended for this API's
|
|
// consumption (AUTHZP_INIT_PARAMS_*)
|
|
}
|
|
|
|
if (FLAG_ON(AUTHZP_INIT_PARAMS_SOURCE_INFO, dwFlags))
|
|
{
|
|
//
|
|
// Create information for eventlog to parse this event properly.
|
|
// We prepend 4 parameters to the final AUDIT_PARAMS structure.
|
|
//
|
|
|
|
pParam->Type = APT_String;
|
|
pParam->Data0 = (ULONG_PTR) SubsystemName;
|
|
pParams->Count++;
|
|
pParam++;
|
|
|
|
pParam->Type = APT_Ulong;
|
|
pParam->Data0 = (ULONG_PTR) AuditId;
|
|
pParams->Count++;
|
|
pParam++;
|
|
|
|
//
|
|
// Since we have prepended 2 parameters, any ObjectTypeIndex calculation
|
|
// must reflect this offset.
|
|
//
|
|
|
|
ObjectTypeIndexAdjustment += 2;
|
|
}
|
|
|
|
//
|
|
// now initialize the rest using the var-arg portion
|
|
//
|
|
|
|
for (i = 0; i < NumParams; i++, pParam++)
|
|
{
|
|
TypeFlags = va_arg(arglist, ULONG);
|
|
|
|
ParamType = ApExtractType(TypeFlags);
|
|
ParamFlags = ApExtractFlags(TypeFlags);
|
|
|
|
pParam->Type = ParamType;
|
|
|
|
switch( ParamType )
|
|
{
|
|
default:
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
|
|
case APT_Pointer:
|
|
case APT_String:
|
|
case APT_Sid:
|
|
case APT_Guid:
|
|
pParam->Data0 = (ULONG_PTR) va_arg(arglist, PVOID);
|
|
break;
|
|
|
|
case APT_Ulong:
|
|
pParam->Data0 = va_arg(arglist, ULONG);
|
|
|
|
//
|
|
// in case of an access-mask, store the object-type index
|
|
// This is because, the meaning of the access-mask bits
|
|
// cannot be determined without knowing the object type.
|
|
//
|
|
|
|
if (ParamFlags & AP_AccessMask)
|
|
{
|
|
ObjectTypeIndex = va_arg(arglist, ULONG);
|
|
//
|
|
// The object-type-index:
|
|
// - must have been specified earlier
|
|
// - must be specified as a string.
|
|
//
|
|
|
|
if ( ( ObjectTypeIndex >= i ) ||
|
|
( pParams->Parameters[ObjectTypeIndex].Type !=
|
|
APT_String ) )
|
|
{
|
|
dwError = ERROR_INVALID_PARAMETER;
|
|
}
|
|
else
|
|
{
|
|
pParam->Data1 = ObjectTypeIndex + ObjectTypeIndexAdjustment;
|
|
}
|
|
}
|
|
pParam->Flags = ParamFlags;
|
|
break;
|
|
|
|
case APT_Luid:
|
|
Luid = va_arg(arglist, LUID);
|
|
pParam->Data0 = Luid.LowPart;
|
|
pParam->Data1 = Luid.HighPart;
|
|
break;
|
|
|
|
case APT_LogonId:
|
|
if (ParamFlags & AP_PrimaryLogonId)
|
|
{
|
|
//
|
|
// use the captured process token info
|
|
//
|
|
|
|
if (!bGotTokenInfo)
|
|
{
|
|
if (!AuthzpGetProcessTokenInfo(
|
|
NULL,
|
|
&AuthIdProcess
|
|
))
|
|
{
|
|
dwError = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
bGotTokenInfo = TRUE;
|
|
}
|
|
pParam->Data0 = AuthIdProcess.LowPart;
|
|
pParam->Data1 = AuthIdProcess.HighPart;
|
|
}
|
|
else if (ParamFlags & AP_ClientLogonId)
|
|
{
|
|
//
|
|
// use the captured thread token info
|
|
//
|
|
|
|
if (!bGotThreadTokenInfo)
|
|
{
|
|
if (!AuthzpGetThreadTokenInfo(
|
|
NULL,
|
|
&AuthIdThread
|
|
))
|
|
{
|
|
dwError = GetLastError();
|
|
|
|
if (dwError == ERROR_NO_TOKEN)
|
|
{
|
|
fImpersonating = FALSE;
|
|
dwError = NO_ERROR;
|
|
}
|
|
else
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
bGotThreadTokenInfo = TRUE;
|
|
}
|
|
pParam->Data0 = AuthIdThread.LowPart;
|
|
pParam->Data1 = AuthIdThread.HighPart;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// no flag is specified, use the supplied logon-id
|
|
//
|
|
|
|
Luid = va_arg(arglist, LUID);
|
|
pParam->Data0 = Luid.LowPart;
|
|
pParam->Data1 = Luid.HighPart;
|
|
}
|
|
break;
|
|
|
|
case APT_ObjectTypeList:
|
|
pParam->Data0 = (ULONG_PTR) va_arg(arglist,
|
|
AUDIT_OBJECT_TYPES*);
|
|
//
|
|
// index of object type
|
|
//
|
|
pParam->Data1 = va_arg(arglist, ULONG) + ObjectTypeIndexAdjustment;
|
|
break;
|
|
|
|
case APT_Time:
|
|
FileTime = va_arg(arglist, FILETIME);
|
|
pParam->Data0 = FileTime.dwLowDateTime;
|
|
pParam->Data1 = FileTime.dwHighDateTime;
|
|
break;
|
|
}
|
|
|
|
if (dwError != NO_ERROR)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if ( dwError != NO_ERROR )
|
|
{
|
|
SetLastError( dwError );
|
|
fResult = FALSE;
|
|
|
|
if (pUserSid)
|
|
{
|
|
AuthzpFree(pUserSid);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (ppUserSid && *ppUserSid == NULL)
|
|
{
|
|
*ppUserSid = pUserSid;
|
|
}
|
|
}
|
|
|
|
return fResult;
|
|
}
|