4596 lines
135 KiB
C
4596 lines
135 KiB
C
/*++
|
||
|
||
Copyright (c) 1989 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
seaudit.c
|
||
|
||
Abstract:
|
||
|
||
This Module implements the audit and alarm procedures.
|
||
|
||
Author:
|
||
|
||
Robert Reichel (robertre) 26-Nov-90
|
||
Scott Birrell (ScottBi) 17-Jan-92
|
||
|
||
Environment:
|
||
|
||
Kernel Mode
|
||
|
||
Revision History:
|
||
|
||
Richard Ward (richardw) 14-Apr-92
|
||
|
||
--*/
|
||
|
||
#include "pch.h"
|
||
|
||
#pragma hdrstop
|
||
|
||
VOID
|
||
SepProbeAndCaptureString_U (
|
||
IN PUNICODE_STRING SourceString,
|
||
OUT PUNICODE_STRING *DestString
|
||
);
|
||
|
||
VOID
|
||
SepFreeCapturedString(
|
||
IN PUNICODE_STRING CapturedString
|
||
);
|
||
|
||
VOID
|
||
SepAuditTypeList (
|
||
IN PIOBJECT_TYPE_LIST ObjectTypeList,
|
||
IN ULONG ObjectTypeListLength,
|
||
IN PNTSTATUS AccessStatus,
|
||
IN ULONG StartIndex,
|
||
OUT PBOOLEAN GenerateSuccessAudit,
|
||
OUT PBOOLEAN GenerateFailureAudit
|
||
);
|
||
|
||
VOID
|
||
SepExamineSaclEx(
|
||
IN PACL Sacl,
|
||
IN PACCESS_TOKEN Token,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN PIOBJECT_TYPE_LIST ObjectTypeList OPTIONAL,
|
||
IN ULONG ObjectTypeListLength,
|
||
IN BOOLEAN ReturnResultList,
|
||
IN PNTSTATUS AccessStatus,
|
||
IN PACCESS_MASK GrantedAccess,
|
||
IN PSID PrincipalSelfSid,
|
||
OUT PBOOLEAN GenerateSuccessAudit,
|
||
OUT PBOOLEAN GenerateFailureAudit
|
||
);
|
||
|
||
NTSTATUS
|
||
SepAccessCheckAndAuditAlarm (
|
||
IN PUNICODE_STRING SubsystemName,
|
||
IN PVOID HandleId,
|
||
IN PHANDLE ClientToken OPTIONAL,
|
||
IN PUNICODE_STRING ObjectTypeName,
|
||
IN PUNICODE_STRING ObjectName,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN PSID PrincipalSelfSid,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN AUDIT_EVENT_TYPE AuditType,
|
||
IN ULONG Flags,
|
||
IN POBJECT_TYPE_LIST ObjectTypeList OPTIONAL,
|
||
IN ULONG ObjectTypeListLength,
|
||
IN PGENERIC_MAPPING GenericMapping,
|
||
OUT PACCESS_MASK GrantedAccess,
|
||
OUT PNTSTATUS AccessStatus,
|
||
OUT PBOOLEAN GenerateOnClose,
|
||
IN BOOLEAN ReturnResultList
|
||
);
|
||
|
||
#ifdef ALLOC_PRAGMA
|
||
VOID
|
||
SepSetAuditInfoForObjectType(
|
||
IN UCHAR AceFlags,
|
||
IN ACCESS_MASK AccessMask,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN PIOBJECT_TYPE_LIST ObjectTypeList,
|
||
IN ULONG ObjectTypeListLength,
|
||
IN BOOLEAN ReturnResultList,
|
||
IN ULONG ObjectTypeIndex,
|
||
IN PNTSTATUS AccessStatus,
|
||
IN PACCESS_MASK GrantedAccess,
|
||
IN BOOLEAN FailedMaximumAllowed,
|
||
OUT PBOOLEAN GenerateSuccessAudit,
|
||
OUT PBOOLEAN GenerateFailureAudit
|
||
);
|
||
#pragma alloc_text(PAGE,SepSinglePrivilegeCheck)
|
||
#pragma alloc_text(PAGE,SeCheckAuditPrivilege)
|
||
#pragma alloc_text(PAGE,SepProbeAndCaptureString_U)
|
||
#pragma alloc_text(PAGE,SepFreeCapturedString)
|
||
#pragma alloc_text(PAGE,NtPrivilegeObjectAuditAlarm)
|
||
#pragma alloc_text(PAGE,SePrivilegeObjectAuditAlarm)
|
||
#pragma alloc_text(PAGE,NtPrivilegedServiceAuditAlarm)
|
||
#pragma alloc_text(PAGE,SePrivilegedServiceAuditAlarm)
|
||
#pragma alloc_text(PAGE,SepAccessCheckAndAuditAlarm)
|
||
#pragma alloc_text(PAGE,NtAccessCheckAndAuditAlarm)
|
||
#pragma alloc_text(PAGE,NtAccessCheckByTypeAndAuditAlarm)
|
||
#pragma alloc_text(PAGE,NtAccessCheckByTypeResultListAndAuditAlarm)
|
||
#pragma alloc_text(PAGE,NtAccessCheckByTypeResultListAndAuditAlarmByHandle)
|
||
#pragma alloc_text(PAGE,NtOpenObjectAuditAlarm)
|
||
#pragma alloc_text(PAGE,NtCloseObjectAuditAlarm)
|
||
#pragma alloc_text(PAGE,NtDeleteObjectAuditAlarm)
|
||
#pragma alloc_text(PAGE,SeOpenObjectAuditAlarm)
|
||
#pragma alloc_text(PAGE,SeOpenObjectForDeleteAuditAlarm)
|
||
#pragma alloc_text(PAGE,SeObjectReferenceAuditAlarm)
|
||
#pragma alloc_text(PAGE,SeAuditHandleCreation)
|
||
#pragma alloc_text(PAGE,SeCloseObjectAuditAlarm)
|
||
#pragma alloc_text(PAGE,SeDeleteObjectAuditAlarm)
|
||
#pragma alloc_text(PAGE,SepExamineSacl)
|
||
#pragma alloc_text(PAGE,SepAuditTypeList)
|
||
#pragma alloc_text(PAGE,SepSetAuditInfoForObjectType)
|
||
#pragma alloc_text(PAGE,SepExamineSaclEx)
|
||
#pragma alloc_text(INIT,SepInitializePrivilegeFilter)
|
||
#pragma alloc_text(PAGE,SepFilterPrivilegeAudits)
|
||
#pragma alloc_text(PAGE,SeAuditingFileOrGlobalEvents)
|
||
#pragma alloc_text(PAGE,SeAuditingFileEvents)
|
||
#pragma alloc_text(PAGE,SeAuditingHardLinkEvents)
|
||
#endif
|
||
|
||
//
|
||
// Flag to tell us if we are auditing shutdown events.
|
||
//
|
||
// Move this to seglobal.c
|
||
//
|
||
|
||
//BOOLEAN SepAuditShutdownEvents = FALSE;
|
||
|
||
|
||
//
|
||
// Private useful routines
|
||
//
|
||
|
||
//
|
||
// This routine is to be called to do simple checks of single privileges
|
||
// against the passed token.
|
||
//
|
||
// DO NOT CALL THIS TO CHECK FOR SeTcbPrivilege SINCE THAT MUST
|
||
// BE CHECKED AGAINST THE PRIMARY TOKEN ONLY!
|
||
//
|
||
|
||
BOOLEAN
|
||
SepSinglePrivilegeCheck (
|
||
LUID DesiredPrivilege,
|
||
IN PACCESS_TOKEN Token,
|
||
IN KPROCESSOR_MODE PreviousMode
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determines if the passed token has the passed privilege.
|
||
|
||
Arguments:
|
||
|
||
DesiredPrivilege - The privilege to be tested for.
|
||
|
||
Token - The token being examined.
|
||
|
||
PreviousMode - The previous processor mode.
|
||
|
||
Return Value:
|
||
|
||
Returns TRUE of the subject has the passed privilege, FALSE otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
LUID_AND_ATTRIBUTES Privilege;
|
||
BOOLEAN Result;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Don't let anyone call this to test for SeTcbPrivilege
|
||
//
|
||
|
||
ASSERT(!((DesiredPrivilege.LowPart == SeTcbPrivilege.LowPart) &&
|
||
(DesiredPrivilege.HighPart == SeTcbPrivilege.HighPart)));
|
||
|
||
Privilege.Luid = DesiredPrivilege;
|
||
Privilege.Attributes = 0;
|
||
|
||
Result = SepPrivilegeCheck(
|
||
Token,
|
||
&Privilege,
|
||
1,
|
||
PRIVILEGE_SET_ALL_NECESSARY,
|
||
PreviousMode
|
||
);
|
||
|
||
return(Result);
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
SeCheckAuditPrivilege (
|
||
IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
|
||
IN KPROCESSOR_MODE PreviousMode
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine specifically searches the primary token (rather than
|
||
the effective token) of the calling process for SeAuditPrivilege.
|
||
In order to do this it must call the underlying worker
|
||
SepPrivilegeCheck directly, to ensure that the correct token is
|
||
searched
|
||
|
||
Arguments:
|
||
|
||
SubjectSecurityContext - The subject being examined.
|
||
|
||
PreviousMode - The previous processor mode.
|
||
|
||
Return Value:
|
||
|
||
Returns TRUE if the subject has SeAuditPrivilege, FALSE otherwise.
|
||
|
||
--*/
|
||
{
|
||
|
||
PRIVILEGE_SET RequiredPrivileges;
|
||
BOOLEAN AccessGranted;
|
||
|
||
PAGED_CODE();
|
||
|
||
RequiredPrivileges.PrivilegeCount = 1;
|
||
RequiredPrivileges.Control = PRIVILEGE_SET_ALL_NECESSARY;
|
||
RequiredPrivileges.Privilege[0].Luid = SeAuditPrivilege;
|
||
RequiredPrivileges.Privilege[0].Attributes = 0;
|
||
|
||
AccessGranted = SepPrivilegeCheck(
|
||
SubjectSecurityContext->PrimaryToken, // token
|
||
RequiredPrivileges.Privilege, // privilege set
|
||
RequiredPrivileges.PrivilegeCount, // privilege count
|
||
PRIVILEGE_SET_ALL_NECESSARY, // privilege control
|
||
PreviousMode // previous mode
|
||
);
|
||
|
||
if ( PreviousMode != KernelMode ) {
|
||
|
||
SePrivilegedServiceAuditAlarm (
|
||
NULL,
|
||
SubjectSecurityContext,
|
||
&RequiredPrivileges,
|
||
AccessGranted
|
||
);
|
||
}
|
||
|
||
return( AccessGranted );
|
||
}
|
||
|
||
|
||
VOID
|
||
SepProbeAndCaptureString_U (
|
||
IN PUNICODE_STRING SourceString,
|
||
OUT PUNICODE_STRING *DestString
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Helper routine to probe and capture a Unicode string argument.
|
||
|
||
This routine may fail due to lack of memory, in which case,
|
||
it will return a NULL pointer in the output parameter.
|
||
|
||
Arguments:
|
||
|
||
SourceString - Pointer to a Unicode string to be captured.
|
||
|
||
DestString - Returns a pointer to a captured Unicode string. This
|
||
will be one contiguous structure, and thus may be freed by
|
||
a single call to ExFreePool().
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
|
||
UNICODE_STRING InputString;
|
||
ULONG Length;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Initialize the object name descriptor and capture the specified name
|
||
// string.
|
||
//
|
||
|
||
*DestString = NULL;
|
||
|
||
Status = STATUS_SUCCESS;
|
||
try {
|
||
|
||
//
|
||
// Probe and capture the name string descriptor and probe the
|
||
// name string, if necessary.
|
||
//
|
||
|
||
InputString = ProbeAndReadUnicodeString(SourceString);
|
||
ProbeForRead(InputString.Buffer,
|
||
InputString.Length,
|
||
sizeof(WCHAR));
|
||
|
||
|
||
|
||
//
|
||
// If the length of the string is not an even multiple of the
|
||
// size of a UNICODE character or cannot be zero terminated,
|
||
// then return an error.
|
||
//
|
||
|
||
Length = InputString.Length;
|
||
if (((Length & (sizeof(WCHAR) - 1)) != 0) ||
|
||
(Length == (MAXUSHORT - sizeof(WCHAR) + 1))) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Allocate a buffer for the specified name string.
|
||
//
|
||
|
||
*DestString = ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
InputString.Length + sizeof(UNICODE_STRING),
|
||
'sUeS');
|
||
|
||
if (*DestString == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
|
||
} else {
|
||
(*DestString)->Length = InputString.Length;
|
||
(*DestString)->MaximumLength = InputString.Length;
|
||
(*DestString)->Buffer = (PWSTR) ((*DestString) + 1);
|
||
|
||
if (InputString.Length != 0) {
|
||
|
||
RtlCopyMemory(
|
||
(*DestString)->Buffer,
|
||
InputString.Buffer,
|
||
InputString.Length);
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
} except(ExSystemExceptionFilter()) {
|
||
Status = GetExceptionCode();
|
||
if (*DestString != NULL) {
|
||
ExFreePool(*DestString);
|
||
*DestString = NULL;
|
||
}
|
||
}
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
|
||
VOID
|
||
SepFreeCapturedString(
|
||
IN PUNICODE_STRING CapturedString
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Frees a string captured by SepProbeAndCaptureString.
|
||
|
||
Arguments:
|
||
|
||
CapturedString - Supplies a pointer to a string previously captured
|
||
by SepProbeAndCaptureString.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
ExFreePool( CapturedString );
|
||
return;
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Privileged Object Audit Alarms //
|
||
// //
|
||
////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
NTSTATUS
|
||
NtPrivilegeObjectAuditAlarm (
|
||
IN PUNICODE_STRING SubsystemName,
|
||
IN PVOID HandleId,
|
||
IN HANDLE ClientToken,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN PPRIVILEGE_SET Privileges,
|
||
IN BOOLEAN AccessGranted
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to generate audit and alarm messages when an
|
||
attempt is made to perform privileged operations on a protected
|
||
subsystem object after the object is already opened. This routine may
|
||
result in several messages being generated and sent to Port objects.
|
||
This may result in a significant latency before returning. Design of
|
||
routines that must call this routine must take this potential latency
|
||
into account. This may have an impact on the approach taken for data
|
||
structure mutex locking, for example.
|
||
|
||
This API requires the caller have SeAuditPrivilege privilege. The test
|
||
for this privilege is always against the primary token of the calling
|
||
process, allowing the caller to be impersonating a client during the
|
||
call with no ill effects.
|
||
|
||
Arguments:
|
||
|
||
SubsystemName - Supplies a name string identifying the subsystem
|
||
calling the routine.
|
||
|
||
HandleId - A unique value representing the client's handle to the
|
||
object.
|
||
|
||
ClientToken - A handle to a token object representing the client that
|
||
requested the operation. This handle must be obtained from a
|
||
communication session layer, such as from an LPC Port or Local
|
||
Named Pipe, to prevent possible security policy violations.
|
||
|
||
DesiredAccess - The desired access mask. This mask must have been
|
||
previously mapped to contain no generic accesses.
|
||
|
||
Privileges - The set of privileges required for the requested
|
||
operation. Those privileges that were held by the subject are
|
||
marked using the UsedForAccess flag of the attributes
|
||
associated with each privilege.
|
||
|
||
AccessGranted - Indicates whether the requested access was granted or
|
||
not. A value of TRUE indicates the access was granted. A value of
|
||
FALSE indicates the access was not granted.
|
||
|
||
Return value:
|
||
|
||
--*/
|
||
{
|
||
|
||
KPROCESSOR_MODE PreviousMode;
|
||
PUNICODE_STRING CapturedSubsystemName = NULL;
|
||
PPRIVILEGE_SET CapturedPrivileges = NULL;
|
||
ULONG PrivilegeParameterLength;
|
||
ULONG PrivilegeCount;
|
||
SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
|
||
BOOLEAN Result;
|
||
PTOKEN Token;
|
||
NTSTATUS Status;
|
||
BOOLEAN AuditPerformed;
|
||
|
||
PAGED_CODE();
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
ASSERT(PreviousMode != KernelMode);
|
||
|
||
Status = ObReferenceObjectByHandle(
|
||
ClientToken, // Handle
|
||
TOKEN_QUERY, // DesiredAccess
|
||
SeTokenObjectType, // ObjectType
|
||
PreviousMode, // AccessMode
|
||
(PVOID *)&Token, // Object
|
||
NULL // GrantedAccess
|
||
);
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
return( Status );
|
||
}
|
||
|
||
//
|
||
// If the passed token is an impersonation token, make sure
|
||
// it is at SecurityIdentification or above.
|
||
//
|
||
|
||
if (Token->TokenType == TokenImpersonation) {
|
||
|
||
if (Token->ImpersonationLevel < SecurityIdentification) {
|
||
|
||
ObDereferenceObject( (PVOID)Token );
|
||
|
||
return( STATUS_BAD_IMPERSONATION_LEVEL );
|
||
|
||
}
|
||
}
|
||
|
||
// //
|
||
// // Make sure the passed token is an impersonation token...
|
||
// //
|
||
//
|
||
// if (Token->TokenType != TokenImpersonation) {
|
||
//
|
||
// ObDereferenceObject( (PVOID)Token );
|
||
//
|
||
// return( STATUS_NO_IMPERSONATION_TOKEN );
|
||
//
|
||
// }
|
||
//
|
||
// //
|
||
// // ...and at a high enough impersonation level
|
||
// //
|
||
//
|
||
// if (Token->ImpersonationLevel < SecurityIdentification) {
|
||
//
|
||
// ObDereferenceObject( (PVOID)Token );
|
||
//
|
||
// return( STATUS_BAD_IMPERSONATION_LEVEL );
|
||
//
|
||
// }
|
||
|
||
//
|
||
// Check for SeAuditPrivilege
|
||
//
|
||
|
||
SeCaptureSubjectContext ( &SubjectSecurityContext );
|
||
|
||
Result = SeCheckAuditPrivilege (
|
||
&SubjectSecurityContext,
|
||
PreviousMode
|
||
);
|
||
|
||
if (!Result) {
|
||
|
||
ObDereferenceObject( (PVOID)Token );
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
return(STATUS_PRIVILEGE_NOT_HELD);
|
||
|
||
}
|
||
|
||
try {
|
||
|
||
SepProbeAndCaptureString_U ( SubsystemName,
|
||
&CapturedSubsystemName );
|
||
|
||
ProbeForReadSmallStructure(
|
||
Privileges,
|
||
sizeof(PRIVILEGE_SET),
|
||
sizeof(ULONG)
|
||
);
|
||
PrivilegeCount = Privileges->PrivilegeCount;
|
||
|
||
if (!IsValidElementCount(PrivilegeCount, LUID_AND_ATTRIBUTES)) {
|
||
Status= STATUS_INVALID_PARAMETER;
|
||
leave ;
|
||
}
|
||
PrivilegeParameterLength = (ULONG)sizeof(PRIVILEGE_SET) +
|
||
((PrivilegeCount - ANYSIZE_ARRAY) *
|
||
(ULONG)sizeof(LUID_AND_ATTRIBUTES) );
|
||
|
||
ProbeForRead(
|
||
Privileges,
|
||
PrivilegeParameterLength,
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
CapturedPrivileges = ExAllocatePoolWithTag( PagedPool,
|
||
PrivilegeParameterLength,
|
||
'rPeS'
|
||
);
|
||
|
||
if (CapturedPrivileges != NULL) {
|
||
|
||
RtlCopyMemory ( CapturedPrivileges,
|
||
Privileges,
|
||
PrivilegeParameterLength );
|
||
CapturedPrivileges->PrivilegeCount = PrivilegeCount;
|
||
}
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
Status = GetExceptionCode();
|
||
}
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
if (CapturedPrivileges != NULL) {
|
||
ExFreePool( CapturedPrivileges );
|
||
}
|
||
|
||
if (CapturedSubsystemName != NULL) {
|
||
SepFreeCapturedString ( CapturedSubsystemName );
|
||
}
|
||
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
|
||
ObDereferenceObject( (PVOID)Token );
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
//
|
||
// No need to lock the token, because the only thing we're going
|
||
// to reference in it is the User's Sid, which cannot be changed.
|
||
//
|
||
|
||
//
|
||
// SepPrivilegeObjectAuditAlarm will check the global flags
|
||
// to determine if we're supposed to be auditing here.
|
||
//
|
||
|
||
AuditPerformed = SepAdtPrivilegeObjectAuditAlarm (
|
||
CapturedSubsystemName,
|
||
HandleId,
|
||
Token, // ClientToken
|
||
SubjectSecurityContext.PrimaryToken, // PrimaryToken
|
||
SubjectSecurityContext.ProcessAuditId,
|
||
DesiredAccess,
|
||
CapturedPrivileges,
|
||
AccessGranted
|
||
);
|
||
|
||
if (CapturedPrivileges != NULL) {
|
||
ExFreePool( CapturedPrivileges );
|
||
}
|
||
|
||
if (CapturedSubsystemName != NULL) {
|
||
SepFreeCapturedString ( CapturedSubsystemName );
|
||
}
|
||
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
|
||
ObDereferenceObject( (PVOID)Token );
|
||
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
VOID
|
||
SePrivilegeObjectAuditAlarm(
|
||
IN HANDLE Handle,
|
||
IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN PPRIVILEGE_SET Privileges,
|
||
IN BOOLEAN AccessGranted,
|
||
IN KPROCESSOR_MODE AccessMode
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used by object methods that perform privileged
|
||
operations to generate audit and alarm messages related to the use
|
||
of privileges, or attempts to use privileges.
|
||
|
||
Arguments:
|
||
|
||
Object - Address of the object accessed. This value will not be
|
||
used as a pointer (referenced). It is necessary only to enter
|
||
into log messages.
|
||
|
||
Handle - Provides the handle value assigned for the open.
|
||
|
||
SecurityDescriptor - A pointer to the security descriptor of the
|
||
object being accessed.
|
||
|
||
SubjectSecurityContext - A pointer to the captured security
|
||
context of the subject attempting to open the object.
|
||
|
||
DesiredAccess - The desired access mask. This mask must have been
|
||
previously mapped to contain no generic accesses.
|
||
|
||
Privileges - Points to a set of privileges required for the access
|
||
attempt. Those privileges that were held by the subject are
|
||
marked using the UsedForAccess flag of the PRIVILEGE_ATTRIBUTES
|
||
associated with each privilege.
|
||
|
||
AccessGranted - Indicates whether the access was granted or
|
||
denied. A value of TRUE indicates the access was allowed. A
|
||
value of FALSE indicates the access was denied.
|
||
|
||
AccessMode - Indicates the access mode used for the access check.
|
||
Messages will not be generated by kernel mode accesses.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN AuditPerformed;
|
||
|
||
PAGED_CODE();
|
||
|
||
if (AccessMode != KernelMode) {
|
||
|
||
AuditPerformed = SepAdtPrivilegeObjectAuditAlarm (
|
||
(PUNICODE_STRING)&SeSubsystemName,
|
||
Handle,
|
||
SubjectSecurityContext->ClientToken,
|
||
SubjectSecurityContext->PrimaryToken,
|
||
SubjectSecurityContext->ProcessAuditId,
|
||
DesiredAccess,
|
||
Privileges,
|
||
AccessGranted
|
||
);
|
||
}
|
||
}
|
||
|
||
|
||
////////////////////////////////////////////////////////////////////////
|
||
// //
|
||
// Privileged Service Audit Alarms //
|
||
// //
|
||
////////////////////////////////////////////////////////////////////////
|
||
|
||
|
||
NTSTATUS
|
||
NtPrivilegedServiceAuditAlarm (
|
||
IN PUNICODE_STRING SubsystemName,
|
||
IN PUNICODE_STRING ServiceName,
|
||
IN HANDLE ClientToken,
|
||
IN PPRIVILEGE_SET Privileges,
|
||
IN BOOLEAN AccessGranted
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to generate audit and alarm messages when an
|
||
attempt is made to perform privileged system service operations. This
|
||
routine may result in several messages being generated and sent to Port
|
||
objects. This may result in a significant latency before returning.
|
||
Design of routines that must call this routine must take this potential
|
||
latency into account. This may have an impact on the approach taken
|
||
for data structure mutex locking, for example.
|
||
|
||
This API requires the caller have SeAuditPrivilege privilege. The test
|
||
for this privilege is always against the primary token of the calling
|
||
process, allowing the caller to be impersonating a client during the
|
||
call with no ill effects
|
||
|
||
Arguments:
|
||
|
||
SubsystemName - Supplies a name string identifying the subsystem
|
||
calling the routine.
|
||
|
||
ServiceName - Supplies a name of the privileged subsystem service. For
|
||
example, "RESET RUNTIME LOCAL SECURITY POLICY" might be specified
|
||
by a Local Security Authority service used to update the local
|
||
security policy database.
|
||
|
||
ClientToken - A handle to a token object representing the client that
|
||
requested the operation. This handle must be obtained from a
|
||
communication session layer, such as from an LPC Port or Local
|
||
Named Pipe, to prevent possible security policy violations.
|
||
|
||
Privileges - Points to a set of privileges required to perform the
|
||
privileged operation. Those privileges that were held by the
|
||
subject are marked using the UsedForAccess flag of the
|
||
attributes associated with each privilege.
|
||
|
||
AccessGranted - Indicates whether the requested access was granted or
|
||
not. A value of TRUE indicates the access was granted. A value of
|
||
FALSE indicates the access was not granted.
|
||
|
||
Return value:
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PPRIVILEGE_SET CapturedPrivileges = NULL;
|
||
ULONG PrivilegeParameterLength = 0;
|
||
BOOLEAN Result;
|
||
SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
PUNICODE_STRING CapturedSubsystemName = NULL;
|
||
PUNICODE_STRING CapturedServiceName = NULL;
|
||
NTSTATUS Status;
|
||
PTOKEN Token;
|
||
ULONG PrivilegeCount;
|
||
|
||
PAGED_CODE();
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
ASSERT(PreviousMode != KernelMode);
|
||
|
||
Status = ObReferenceObjectByHandle(
|
||
ClientToken, // Handle
|
||
TOKEN_QUERY, // DesiredAccess
|
||
SeTokenObjectType, // ObjectType
|
||
PreviousMode, // AccessMode
|
||
(PVOID *)&Token, // Object
|
||
NULL // GrantedAccess
|
||
);
|
||
|
||
if ( !NT_SUCCESS( Status )) {
|
||
return( Status );
|
||
}
|
||
|
||
//
|
||
// If the passed token is an impersonation token, make sure
|
||
// it is at SecurityIdentification or above.
|
||
//
|
||
|
||
if (Token->TokenType == TokenImpersonation) {
|
||
|
||
if (Token->ImpersonationLevel < SecurityIdentification) {
|
||
|
||
ObDereferenceObject( (PVOID)Token );
|
||
|
||
return( STATUS_BAD_IMPERSONATION_LEVEL );
|
||
|
||
}
|
||
}
|
||
|
||
// //
|
||
// // Make sure the passed token is an impersonation token...
|
||
// //
|
||
//
|
||
// if (Token->TokenType != TokenImpersonation) {
|
||
//
|
||
// ObDereferenceObject( (PVOID)Token );
|
||
//
|
||
// return( STATUS_NO_IMPERSONATION_TOKEN );
|
||
//
|
||
// }
|
||
//
|
||
// //
|
||
// // ...and at a high enough impersonation level
|
||
// //
|
||
//
|
||
// if (Token->ImpersonationLevel < SecurityIdentification) {
|
||
//
|
||
// ObDereferenceObject( (PVOID)Token );
|
||
//
|
||
// return( STATUS_BAD_IMPERSONATION_LEVEL );
|
||
//
|
||
// }
|
||
|
||
//
|
||
// Check for SeAuditPrivilege
|
||
//
|
||
|
||
SeCaptureSubjectContext ( &SubjectSecurityContext );
|
||
|
||
Result = SeCheckAuditPrivilege (
|
||
&SubjectSecurityContext,
|
||
PreviousMode
|
||
);
|
||
|
||
if (!Result) {
|
||
|
||
ObDereferenceObject( (PVOID)Token );
|
||
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
|
||
return(STATUS_PRIVILEGE_NOT_HELD);
|
||
}
|
||
|
||
try {
|
||
|
||
if ( ARGUMENT_PRESENT( SubsystemName )) {
|
||
SepProbeAndCaptureString_U ( SubsystemName,
|
||
&CapturedSubsystemName );
|
||
}
|
||
|
||
if ( ARGUMENT_PRESENT( ServiceName )) {
|
||
SepProbeAndCaptureString_U ( ServiceName,
|
||
&CapturedServiceName );
|
||
|
||
}
|
||
|
||
ProbeForReadSmallStructure(
|
||
Privileges,
|
||
sizeof(PRIVILEGE_SET),
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
PrivilegeCount = Privileges->PrivilegeCount;
|
||
|
||
if (!IsValidElementCount( PrivilegeCount, LUID_AND_ATTRIBUTES ) ) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
leave ;
|
||
}
|
||
PrivilegeParameterLength = (ULONG)sizeof(PRIVILEGE_SET) +
|
||
((PrivilegeCount - ANYSIZE_ARRAY) *
|
||
(ULONG)sizeof(LUID_AND_ATTRIBUTES) );
|
||
|
||
ProbeForRead(
|
||
Privileges,
|
||
PrivilegeParameterLength,
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
CapturedPrivileges = ExAllocatePoolWithTag( PagedPool,
|
||
PrivilegeParameterLength,
|
||
'rPeS'
|
||
);
|
||
|
||
//
|
||
// If ExAllocatePool has failed, too bad. Carry on and do as much of the
|
||
// audit as we can.
|
||
//
|
||
|
||
if (CapturedPrivileges != NULL) {
|
||
|
||
RtlCopyMemory ( CapturedPrivileges,
|
||
Privileges,
|
||
PrivilegeParameterLength );
|
||
CapturedPrivileges->PrivilegeCount = PrivilegeCount;
|
||
|
||
}
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
Status = GetExceptionCode();
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
if (CapturedSubsystemName != NULL) {
|
||
SepFreeCapturedString ( CapturedSubsystemName );
|
||
}
|
||
|
||
if (CapturedServiceName != NULL) {
|
||
SepFreeCapturedString ( CapturedServiceName );
|
||
}
|
||
|
||
if (CapturedPrivileges != NULL) {
|
||
ExFreePool ( CapturedPrivileges );
|
||
}
|
||
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
|
||
ObDereferenceObject( (PVOID)Token );
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
//
|
||
// The AuthenticationId is in the read-only part of the token,
|
||
// so we may reference it without having the token read-locked.
|
||
//
|
||
|
||
SepAdtPrivilegedServiceAuditAlarm ( CapturedSubsystemName,
|
||
CapturedServiceName,
|
||
Token,
|
||
SubjectSecurityContext.PrimaryToken,
|
||
CapturedPrivileges,
|
||
AccessGranted );
|
||
|
||
if (CapturedSubsystemName != NULL) {
|
||
SepFreeCapturedString ( CapturedSubsystemName );
|
||
}
|
||
|
||
if (CapturedServiceName != NULL) {
|
||
SepFreeCapturedString ( CapturedServiceName );
|
||
}
|
||
|
||
if (CapturedPrivileges != NULL) {
|
||
ExFreePool ( CapturedPrivileges );
|
||
}
|
||
|
||
ObDereferenceObject( (PVOID)Token );
|
||
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
VOID
|
||
SePrivilegedServiceAuditAlarm (
|
||
IN PUNICODE_STRING ServiceName,
|
||
IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
|
||
IN PPRIVILEGE_SET Privileges,
|
||
IN BOOLEAN AccessGranted
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is to be called whenever a privileged system service
|
||
is attempted. It should be called immediately after the privilege
|
||
check regardless of whether or not the test succeeds.
|
||
|
||
Arguments:
|
||
|
||
ServiceName - Supplies the name of the privileged system service.
|
||
|
||
SubjectSecurityContext - The subject security context representing
|
||
the caller of the system service.
|
||
|
||
Privileges - Supplies a privilge set containing the privilege(s)
|
||
required for the access.
|
||
|
||
AccessGranted - Supplies the results of the privilege test.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
PTOKEN Token;
|
||
|
||
PAGED_CODE();
|
||
|
||
if ( SepAdtAuditThisEvent( AuditCategoryPrivilegeUse, &AccessGranted ) &&
|
||
SepFilterPrivilegeAudits( Privileges )) {
|
||
|
||
Token = (PTOKEN)EffectiveToken( SubjectSecurityContext );
|
||
|
||
if ( RtlEqualSid( SeLocalSystemSid, SepTokenUserSid( Token ))) {
|
||
return;
|
||
}
|
||
|
||
SepAdtPrivilegedServiceAuditAlarm (
|
||
(PUNICODE_STRING)&SeSubsystemName,
|
||
ServiceName,
|
||
SubjectSecurityContext->ClientToken,
|
||
SubjectSecurityContext->PrimaryToken,
|
||
Privileges,
|
||
AccessGranted
|
||
);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
SepAccessCheckAndAuditAlarm (
|
||
IN PUNICODE_STRING SubsystemName,
|
||
IN PVOID HandleId,
|
||
IN PHANDLE ClientToken OPTIONAL,
|
||
IN PUNICODE_STRING ObjectTypeName,
|
||
IN PUNICODE_STRING ObjectName,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN PSID PrincipalSelfSid,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN AUDIT_EVENT_TYPE AuditType,
|
||
IN ULONG Flags,
|
||
IN POBJECT_TYPE_LIST ObjectTypeList OPTIONAL,
|
||
IN ULONG ObjectTypeListLength,
|
||
IN PGENERIC_MAPPING GenericMapping,
|
||
OUT PACCESS_MASK GrantedAccess,
|
||
OUT PNTSTATUS AccessStatus,
|
||
OUT PBOOLEAN GenerateOnClose,
|
||
IN BOOLEAN ReturnResultList
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This system service is used to perform both an access validation and
|
||
generate the corresponding audit and alarm messages. This service may
|
||
only be used by a protected server that chooses to impersonate its
|
||
client and thereby specifies the client security context implicitly.
|
||
|
||
Arguments:
|
||
|
||
SubsystemName - Supplies a name string identifying the subsystem
|
||
calling the routine.
|
||
|
||
HandleId - A unique value that will be used to represent the client's
|
||
handle to the object. This value is ignored (and may be re-used)
|
||
if the access is denied.
|
||
|
||
ClientToken - Supplies the client token so that the caller does not have
|
||
to impersonate before making the kernel call.
|
||
|
||
ObjectTypeName - Supplies the name of the type of the object being
|
||
created or accessed.
|
||
|
||
ObjectName - Supplies the name of the object being created or accessed.
|
||
|
||
SecurityDescriptor - A pointer to the Security Descriptor against which
|
||
acccess is to be checked.
|
||
|
||
DesiredAccess - The desired acccess mask. This mask must have been
|
||
previously mapped to contain no generic accesses.
|
||
|
||
AuditType - Specifies the type of audit to be generated. Valid values
|
||
are: AuditEventObjectAccess and AuditEventDirectoryServiceAccess.
|
||
|
||
Flags - Flags modifying the execution of the API:
|
||
|
||
AUDIT_ALLOW_NO_PRIVILEGE - If the called does not have AuditPrivilege,
|
||
the call will silently continue to check access and will
|
||
generate no audit.
|
||
|
||
ObjectTypeList - Supplies a list of GUIDs representing the object (and
|
||
sub-objects) being accessed. If no list is present, AccessCheckByType
|
||
behaves identically to AccessCheck.
|
||
|
||
ObjectTypeListLength - Specifies the number of elements in the ObjectTypeList.
|
||
|
||
GenericMapping - Supplies a pointer to the generic mapping associated
|
||
with this object type.
|
||
|
||
ObjectCreation - A boolean flag indicated whether the access will
|
||
result in a new object being created if granted. A value of TRUE
|
||
indicates an object will be created, FALSE indicates an existing
|
||
object will be opened.
|
||
|
||
GrantedAccess - Receives a masking indicating which accesses have been
|
||
granted.
|
||
|
||
AccessStatus - Receives an indication of the success or failure of the
|
||
access check. If access is granted, STATUS_SUCCESS is returned.
|
||
If access is denied, a value appropriate for return to the client
|
||
is returned. This will be STATUS_ACCESS_DENIED or, when mandatory
|
||
access controls are implemented, STATUS_OBJECT_NOT_FOUND.
|
||
|
||
GenerateOnClose - Points to a boolean that is set by the audity
|
||
generation routine and must be passed to NtCloseObjectAuditAlarm
|
||
when the object handle is closed.
|
||
|
||
ReturnResultList - If true, GrantedAccess and AccessStatus are actually
|
||
arrays of entries ObjectTypeListLength elements long.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the call completed successfully. In this
|
||
case, ClientStatus receives the result of the access check.
|
||
|
||
STATUS_PRIVILEGE_NOT_HELD - Indicates the caller does not have
|
||
sufficient privilege to use this privileged system service.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
|
||
|
||
NTSTATUS Status = STATUS_SUCCESS;
|
||
|
||
ACCESS_MASK LocalGrantedAccess = (ACCESS_MASK)0;
|
||
PACCESS_MASK LocalGrantedAccessPointer = NULL;
|
||
BOOLEAN LocalGrantedAccessAllocated = FALSE;
|
||
NTSTATUS LocalAccessStatus = STATUS_UNSUCCESSFUL;
|
||
PNTSTATUS LocalAccessStatusPointer = NULL;
|
||
BOOLEAN LocalGenerateOnClose = FALSE;
|
||
POLICY_AUDIT_EVENT_TYPE NtAuditType;
|
||
|
||
KPROCESSOR_MODE PreviousMode;
|
||
|
||
PUNICODE_STRING CapturedSubsystemName = (PUNICODE_STRING) NULL;
|
||
PUNICODE_STRING CapturedObjectTypeName = (PUNICODE_STRING) NULL;
|
||
PUNICODE_STRING CapturedObjectName = (PUNICODE_STRING) NULL;
|
||
PSECURITY_DESCRIPTOR CapturedSecurityDescriptor = (PSECURITY_DESCRIPTOR) NULL;
|
||
PSID CapturedPrincipalSelfSid = NULL;
|
||
PIOBJECT_TYPE_LIST LocalObjectTypeList = NULL;
|
||
|
||
ACCESS_MASK PreviouslyGrantedAccess = (ACCESS_MASK)0;
|
||
GENERIC_MAPPING LocalGenericMapping;
|
||
|
||
PPRIVILEGE_SET PrivilegeSet = NULL;
|
||
|
||
BOOLEAN Result;
|
||
|
||
BOOLEAN AccessGranted;
|
||
BOOLEAN AccessDenied;
|
||
BOOLEAN GenerateSuccessAudit = FALSE;
|
||
BOOLEAN GenerateFailureAudit = FALSE;
|
||
LUID OperationId;
|
||
BOOLEAN AuditPerformed;
|
||
BOOLEAN AvoidAudit = FALSE;
|
||
|
||
PTOKEN NewToken = NULL;
|
||
PTOKEN OldToken = NULL;
|
||
BOOLEAN TokenSwapped = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
ASSERT( PreviousMode != KernelMode );
|
||
|
||
//
|
||
// Capture the subject Context
|
||
//
|
||
|
||
SeCaptureSubjectContext ( &SubjectSecurityContext );
|
||
|
||
//
|
||
// Convert AuditType
|
||
//
|
||
|
||
if ( AuditType == AuditEventObjectAccess ) {
|
||
NtAuditType = AuditCategoryObjectAccess;
|
||
} else if ( AuditType == AuditEventDirectoryServiceAccess ) {
|
||
NtAuditType = AuditCategoryDirectoryServiceAccess;
|
||
} else {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Impersonation checks should be done only if the ClientToken is NULL.
|
||
//
|
||
|
||
if ( !ARGUMENT_PRESENT( ClientToken ) ) {
|
||
|
||
//
|
||
// Make sure we're impersonating a client...
|
||
//
|
||
|
||
if ( (SubjectSecurityContext.ClientToken == NULL) ) {
|
||
Status = STATUS_NO_IMPERSONATION_TOKEN;
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
//
|
||
// ...and at a high enough impersonation level
|
||
//
|
||
|
||
if (SubjectSecurityContext.ImpersonationLevel < SecurityIdentification) {
|
||
Status = STATUS_BAD_IMPERSONATION_LEVEL;
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
try {
|
||
|
||
if ( ReturnResultList ) {
|
||
|
||
if ( ObjectTypeListLength == 0 ) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
leave;
|
||
}
|
||
|
||
if (!IsValidElementCount(ObjectTypeListLength, ULONG)) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
leave;
|
||
}
|
||
ProbeForWrite(
|
||
AccessStatus,
|
||
sizeof(NTSTATUS) * ObjectTypeListLength,
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
ProbeForWrite(
|
||
GrantedAccess,
|
||
sizeof(ACCESS_MASK) * ObjectTypeListLength,
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
} else {
|
||
ProbeForWriteUlong((PULONG)AccessStatus);
|
||
ProbeForWriteUlong((PULONG)GrantedAccess);
|
||
}
|
||
|
||
ProbeForReadSmallStructure(
|
||
GenericMapping,
|
||
sizeof(GENERIC_MAPPING),
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
LocalGenericMapping = *GenericMapping;
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
Status = GetExceptionCode();
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
if ( ARGUMENT_PRESENT( ClientToken ) ) {
|
||
|
||
Status = ObReferenceObjectByHandle(
|
||
*ClientToken, // Handle
|
||
(ACCESS_MASK)TOKEN_QUERY, // DesiredAccess
|
||
SeTokenObjectType, // ObjectType
|
||
PreviousMode, // AccessMode
|
||
(PVOID *)&NewToken, // Object
|
||
NULL // GrantedAccess
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
NewToken = NULL;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Save the old token so that it can be recovered before
|
||
// SeReleaseSubjectContext.
|
||
//
|
||
|
||
OldToken = SubjectSecurityContext.ClientToken;
|
||
|
||
//
|
||
// Set the impersonation token to the one that has been obtained thru
|
||
// ClientToken handle. This must be freed later in Cleanup.
|
||
//
|
||
|
||
SubjectSecurityContext.ClientToken = NewToken;
|
||
|
||
TokenSwapped = TRUE;
|
||
}
|
||
|
||
//
|
||
// Check for SeAuditPrivilege
|
||
//
|
||
|
||
Result = SeCheckAuditPrivilege (
|
||
&SubjectSecurityContext,
|
||
PreviousMode
|
||
);
|
||
|
||
if (!Result) {
|
||
if ( Flags & AUDIT_ALLOW_NO_PRIVILEGE ) {
|
||
AvoidAudit = TRUE;
|
||
} else {
|
||
Status = STATUS_PRIVILEGE_NOT_HELD;
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
if (DesiredAccess &
|
||
( GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL )) {
|
||
|
||
Status = STATUS_GENERIC_NOT_MAPPED;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Capture the passed security descriptor.
|
||
//
|
||
// SeCaptureSecurityDescriptor probes the input security descriptor,
|
||
// so we don't have to
|
||
//
|
||
|
||
Status = SeCaptureSecurityDescriptor (
|
||
SecurityDescriptor,
|
||
PreviousMode,
|
||
PagedPool,
|
||
FALSE,
|
||
&CapturedSecurityDescriptor
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status) ) {
|
||
CapturedSecurityDescriptor = NULL;
|
||
goto Cleanup;
|
||
}
|
||
|
||
if ( CapturedSecurityDescriptor == NULL ) {
|
||
Status = STATUS_INVALID_SECURITY_DESCR;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// A valid security descriptor must have an owner and a group
|
||
//
|
||
|
||
if ( RtlpOwnerAddrSecurityDescriptor(
|
||
(PISECURITY_DESCRIPTOR)CapturedSecurityDescriptor
|
||
) == NULL ||
|
||
RtlpGroupAddrSecurityDescriptor(
|
||
(PISECURITY_DESCRIPTOR)CapturedSecurityDescriptor
|
||
) == NULL ) {
|
||
|
||
Status = STATUS_INVALID_SECURITY_DESCR;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Probe and capture the STRING arguments
|
||
//
|
||
|
||
try {
|
||
|
||
ProbeForWriteBoolean(GenerateOnClose);
|
||
|
||
SepProbeAndCaptureString_U ( SubsystemName, &CapturedSubsystemName );
|
||
|
||
SepProbeAndCaptureString_U ( ObjectTypeName, &CapturedObjectTypeName );
|
||
|
||
SepProbeAndCaptureString_U ( ObjectName, &CapturedObjectName );
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
Status = GetExceptionCode();
|
||
goto Cleanup;
|
||
|
||
}
|
||
|
||
//
|
||
// Capture the PrincipalSelfSid.
|
||
//
|
||
|
||
if ( PrincipalSelfSid != NULL ) {
|
||
Status = SeCaptureSid(
|
||
PrincipalSelfSid,
|
||
PreviousMode,
|
||
NULL, 0,
|
||
PagedPool,
|
||
TRUE,
|
||
&CapturedPrincipalSelfSid );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
CapturedPrincipalSelfSid = NULL;
|
||
goto Cleanup;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Capture any Object type list
|
||
//
|
||
|
||
Status = SeCaptureObjectTypeList( ObjectTypeList,
|
||
ObjectTypeListLength,
|
||
PreviousMode,
|
||
&LocalObjectTypeList );
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// See if anything (or everything) in the desired access can be
|
||
// satisfied by privileges.
|
||
//
|
||
|
||
Status = SePrivilegePolicyCheck(
|
||
&DesiredAccess,
|
||
&PreviouslyGrantedAccess,
|
||
&SubjectSecurityContext,
|
||
NULL,
|
||
&PrivilegeSet,
|
||
PreviousMode
|
||
);
|
||
|
||
SeLockSubjectContext( &SubjectSecurityContext );
|
||
|
||
if (!NT_SUCCESS( Status )) {
|
||
AccessGranted = FALSE;
|
||
AccessDenied = TRUE;
|
||
LocalAccessStatus = Status;
|
||
|
||
if ( ReturnResultList ) {
|
||
ULONG ResultListIndex;
|
||
LocalGrantedAccessPointer =
|
||
ExAllocatePoolWithTag( PagedPool, (sizeof(ACCESS_MASK)+sizeof(NTSTATUS)) * ObjectTypeListLength, 'aGeS' );
|
||
|
||
if (LocalGrantedAccessPointer == NULL) {
|
||
SeUnlockSubjectContext( &SubjectSecurityContext );
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
LocalGrantedAccessAllocated = TRUE;
|
||
LocalAccessStatusPointer = (PNTSTATUS)(LocalGrantedAccessPointer + ObjectTypeListLength);
|
||
|
||
for ( ResultListIndex=0; ResultListIndex<ObjectTypeListLength; ResultListIndex++ ) {
|
||
LocalGrantedAccessPointer[ResultListIndex] = LocalGrantedAccess;
|
||
LocalAccessStatusPointer[ResultListIndex] = LocalAccessStatus;
|
||
}
|
||
|
||
} else {
|
||
LocalGrantedAccessPointer = &LocalGrantedAccess;
|
||
LocalAccessStatusPointer = &LocalAccessStatus;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// If the user in the token is the owner of the object, we
|
||
// must automatically grant ReadControl and WriteDac access
|
||
// if desired. If the DesiredAccess mask is empty after
|
||
// these bits are turned off, we don't have to do any more
|
||
// access checking (ref section 4, DSA ACL Arch)
|
||
//
|
||
|
||
if ( DesiredAccess & (WRITE_DAC | READ_CONTROL | MAXIMUM_ALLOWED) ) {
|
||
|
||
if (SepTokenIsOwner( SubjectSecurityContext.ClientToken, CapturedSecurityDescriptor, TRUE )) {
|
||
|
||
if ( DesiredAccess & MAXIMUM_ALLOWED ) {
|
||
|
||
PreviouslyGrantedAccess |= ( WRITE_DAC | READ_CONTROL );
|
||
|
||
} else {
|
||
|
||
PreviouslyGrantedAccess |= (DesiredAccess & (WRITE_DAC | READ_CONTROL));
|
||
}
|
||
|
||
DesiredAccess &= ~(WRITE_DAC | READ_CONTROL);
|
||
}
|
||
|
||
}
|
||
|
||
if (DesiredAccess == 0) {
|
||
|
||
LocalGrantedAccess = PreviouslyGrantedAccess;
|
||
AccessGranted = TRUE;
|
||
AccessDenied = FALSE;
|
||
LocalAccessStatus = STATUS_SUCCESS;
|
||
|
||
if ( ReturnResultList ) {
|
||
ULONG ResultListIndex;
|
||
LocalGrantedAccessPointer =
|
||
ExAllocatePoolWithTag( PagedPool, (sizeof(ACCESS_MASK)+sizeof(NTSTATUS)) * ObjectTypeListLength, 'aGeS' );
|
||
|
||
if (LocalGrantedAccessPointer == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
SeUnlockSubjectContext( &SubjectSecurityContext );
|
||
goto Cleanup;
|
||
}
|
||
LocalGrantedAccessAllocated = TRUE;
|
||
LocalAccessStatusPointer = (PNTSTATUS)(LocalGrantedAccessPointer + ObjectTypeListLength);
|
||
|
||
for ( ResultListIndex=0; ResultListIndex<ObjectTypeListLength; ResultListIndex++ ) {
|
||
LocalGrantedAccessPointer[ResultListIndex] = LocalGrantedAccess;
|
||
LocalAccessStatusPointer[ResultListIndex] = LocalAccessStatus;
|
||
}
|
||
|
||
} else {
|
||
LocalGrantedAccessPointer = &LocalGrantedAccess;
|
||
LocalAccessStatusPointer = &LocalAccessStatus;
|
||
}
|
||
|
||
} else {
|
||
|
||
//
|
||
// Finally, do the access check
|
||
//
|
||
|
||
if ( ReturnResultList ) {
|
||
LocalGrantedAccessPointer =
|
||
ExAllocatePoolWithTag( PagedPool, (sizeof(ACCESS_MASK)+sizeof(NTSTATUS)) * ObjectTypeListLength, 'aGeS' );
|
||
|
||
if (LocalGrantedAccessPointer == NULL) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
SeUnlockSubjectContext( &SubjectSecurityContext );
|
||
goto Cleanup;
|
||
}
|
||
LocalGrantedAccessAllocated = TRUE;
|
||
LocalAccessStatusPointer = (PNTSTATUS)(LocalGrantedAccessPointer + ObjectTypeListLength);
|
||
|
||
} else {
|
||
LocalGrantedAccessPointer = &LocalGrantedAccess;
|
||
LocalAccessStatusPointer = &LocalAccessStatus;
|
||
}
|
||
|
||
|
||
//
|
||
// This does not ask for privilege set to be returned so we can ignore
|
||
// the return value of the call.
|
||
//
|
||
|
||
(VOID) SepAccessCheck (
|
||
CapturedSecurityDescriptor,
|
||
CapturedPrincipalSelfSid,
|
||
SubjectSecurityContext.PrimaryToken,
|
||
SubjectSecurityContext.ClientToken,
|
||
DesiredAccess,
|
||
LocalObjectTypeList,
|
||
ObjectTypeListLength,
|
||
&LocalGenericMapping,
|
||
PreviouslyGrantedAccess,
|
||
PreviousMode,
|
||
LocalGrantedAccessPointer,
|
||
NULL, // Privileges already checked
|
||
LocalAccessStatusPointer,
|
||
ReturnResultList,
|
||
&AccessGranted,
|
||
&AccessDenied
|
||
);
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// sound the alarms...
|
||
//
|
||
|
||
if ( !AvoidAudit ) {
|
||
if ( SepAdtAuditThisEventEx( NtAuditType, AccessGranted, AccessDenied )) {
|
||
|
||
SepExamineSaclEx(
|
||
RtlpSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)CapturedSecurityDescriptor ),
|
||
EffectiveToken( &SubjectSecurityContext ),
|
||
DesiredAccess | PreviouslyGrantedAccess,
|
||
LocalObjectTypeList,
|
||
ObjectTypeListLength,
|
||
ReturnResultList,
|
||
LocalAccessStatusPointer,
|
||
LocalGrantedAccessPointer,
|
||
CapturedPrincipalSelfSid,
|
||
&GenerateSuccessAudit,
|
||
&GenerateFailureAudit
|
||
);
|
||
}
|
||
|
||
if ( GenerateSuccessAudit ||
|
||
GenerateFailureAudit ) {
|
||
|
||
//
|
||
// Save this to a local here, so we don't
|
||
// have to risk accessing user memory and
|
||
// potentially having to exit before the audit
|
||
//
|
||
|
||
if ( AccessGranted ) {
|
||
|
||
//
|
||
// SAM calls NtCloseObjectAuditAlarm despite the fact that it may not
|
||
// have successfully opened the object, causing a spurious close audit.
|
||
// Since no one should rely on this anyway if their access attempt
|
||
// failed, make sure it's false and SAM will work properly.
|
||
//
|
||
|
||
LocalGenerateOnClose = TRUE;
|
||
}
|
||
|
||
//
|
||
// Generate the success audit if needed.
|
||
//
|
||
if ( GenerateSuccessAudit ) {
|
||
ExAllocateLocallyUniqueId( &OperationId );
|
||
|
||
// ??
|
||
ASSERT( AccessGranted );
|
||
AuditPerformed = SepAdtOpenObjectAuditAlarm (
|
||
CapturedSubsystemName,
|
||
AccessGranted ? &HandleId : NULL, // Don't audit handle if failure
|
||
CapturedObjectTypeName,
|
||
CapturedObjectName,
|
||
SubjectSecurityContext.ClientToken,
|
||
SubjectSecurityContext.PrimaryToken,
|
||
*LocalGrantedAccessPointer,
|
||
*LocalGrantedAccessPointer,
|
||
&OperationId,
|
||
PrivilegeSet,
|
||
TRUE, // Generate success case
|
||
PsProcessAuditId( PsGetCurrentProcess() ),
|
||
NtAuditType,
|
||
LocalObjectTypeList,
|
||
ObjectTypeListLength,
|
||
ReturnResultList ? LocalGrantedAccessPointer : NULL
|
||
);
|
||
|
||
}
|
||
|
||
//
|
||
// Generate failure audit if it is needed.
|
||
//
|
||
if ( GenerateFailureAudit ) {
|
||
ExAllocateLocallyUniqueId( &OperationId );
|
||
|
||
// ??
|
||
ASSERT( AccessDenied );
|
||
AuditPerformed = SepAdtOpenObjectAuditAlarm (
|
||
CapturedSubsystemName,
|
||
AccessGranted ? &HandleId : NULL, // Don't audit handle if failure
|
||
CapturedObjectTypeName,
|
||
CapturedObjectName,
|
||
SubjectSecurityContext.ClientToken,
|
||
SubjectSecurityContext.PrimaryToken,
|
||
DesiredAccess,
|
||
DesiredAccess,
|
||
&OperationId,
|
||
PrivilegeSet,
|
||
FALSE, // Generate failure case
|
||
PsProcessAuditId( PsGetCurrentProcess() ),
|
||
NtAuditType,
|
||
LocalObjectTypeList,
|
||
ObjectTypeListLength,
|
||
ReturnResultList ? LocalGrantedAccessPointer : NULL
|
||
);
|
||
}
|
||
} else {
|
||
|
||
//
|
||
// We didn't generate an audit due to the SACL. If privileges were used, we need
|
||
// to audit that.
|
||
//
|
||
|
||
if ( PrivilegeSet != NULL ) {
|
||
|
||
if ( SepAdtAuditThisEvent( AuditCategoryPrivilegeUse, &AccessGranted) ) {
|
||
|
||
AuditPerformed = SepAdtPrivilegeObjectAuditAlarm ( CapturedSubsystemName,
|
||
&HandleId,
|
||
SubjectSecurityContext.ClientToken,
|
||
SubjectSecurityContext.PrimaryToken,
|
||
PsProcessAuditId( PsGetCurrentProcess() ),
|
||
DesiredAccess,
|
||
PrivilegeSet,
|
||
AccessGranted
|
||
);
|
||
|
||
//
|
||
// We don't want close audits to be generated. May need to revisit this.
|
||
//
|
||
|
||
LocalGenerateOnClose = FALSE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
SeUnlockSubjectContext( &SubjectSecurityContext );
|
||
|
||
try {
|
||
if ( ReturnResultList ) {
|
||
ULONG ResultListIndex;
|
||
if ( LocalAccessStatusPointer == NULL ) {
|
||
for ( ResultListIndex=0; ResultListIndex<ObjectTypeListLength; ResultListIndex++ ) {
|
||
AccessStatus[ResultListIndex] = LocalAccessStatus;
|
||
GrantedAccess[ResultListIndex] = LocalGrantedAccess;
|
||
}
|
||
} else {
|
||
for ( ResultListIndex=0; ResultListIndex<ObjectTypeListLength; ResultListIndex++ ) {
|
||
AccessStatus[ResultListIndex] = LocalAccessStatusPointer[ResultListIndex];
|
||
GrantedAccess[ResultListIndex] = LocalGrantedAccessPointer[ResultListIndex];
|
||
}
|
||
}
|
||
|
||
} else {
|
||
*AccessStatus = LocalAccessStatus;
|
||
*GrantedAccess = LocalGrantedAccess;
|
||
}
|
||
*GenerateOnClose = LocalGenerateOnClose;
|
||
Status = STATUS_SUCCESS;
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
Status = GetExceptionCode();
|
||
}
|
||
|
||
//
|
||
// Free locally used resources.
|
||
//
|
||
Cleanup:
|
||
|
||
if ( TokenSwapped ) {
|
||
|
||
//
|
||
// Decrement the reference count for the ClientToken that was passed in.
|
||
//
|
||
|
||
ObDereferenceObject( (PVOID)NewToken );
|
||
|
||
//
|
||
// Reset the value of the token from saved value.
|
||
//
|
||
|
||
SubjectSecurityContext.ClientToken = OldToken;
|
||
}
|
||
|
||
//
|
||
// Free any privileges allocated as part of the access check
|
||
//
|
||
|
||
if (PrivilegeSet != NULL) {
|
||
ExFreePool( PrivilegeSet );
|
||
}
|
||
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
|
||
SeReleaseSecurityDescriptor ( CapturedSecurityDescriptor,
|
||
PreviousMode,
|
||
FALSE );
|
||
|
||
if (CapturedSubsystemName != NULL) {
|
||
SepFreeCapturedString( CapturedSubsystemName );
|
||
}
|
||
|
||
if (CapturedObjectTypeName != NULL) {
|
||
SepFreeCapturedString( CapturedObjectTypeName );
|
||
}
|
||
|
||
if (CapturedObjectName != NULL) {
|
||
SepFreeCapturedString( CapturedObjectName );
|
||
}
|
||
|
||
if (CapturedPrincipalSelfSid != NULL) {
|
||
SeReleaseSid( CapturedPrincipalSelfSid, PreviousMode, TRUE);
|
||
}
|
||
|
||
if ( LocalObjectTypeList != NULL ) {
|
||
SeFreeCapturedObjectTypeList( LocalObjectTypeList );
|
||
}
|
||
|
||
if ( LocalGrantedAccessAllocated ) {
|
||
if ( LocalGrantedAccessPointer != NULL ) {
|
||
ExFreePool( LocalGrantedAccessPointer );
|
||
}
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtAccessCheckAndAuditAlarm (
|
||
IN PUNICODE_STRING SubsystemName,
|
||
IN PVOID HandleId,
|
||
IN PUNICODE_STRING ObjectTypeName,
|
||
IN PUNICODE_STRING ObjectName,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN PGENERIC_MAPPING GenericMapping,
|
||
IN BOOLEAN ObjectCreation,
|
||
OUT PACCESS_MASK GrantedAccess,
|
||
OUT PNTSTATUS AccessStatus,
|
||
OUT PBOOLEAN GenerateOnClose
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
See SepAccessCheckAndAuditAlarm.
|
||
|
||
Arguments:
|
||
|
||
See SepAccessCheckAndAuditAlarm.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the call completed successfully. In this
|
||
case, ClientStatus receives the result of the access check.
|
||
|
||
STATUS_PRIVILEGE_NOT_HELD - Indicates the caller does not have
|
||
sufficient privilege to use this privileged system service.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( ObjectCreation );
|
||
|
||
return SepAccessCheckAndAuditAlarm(
|
||
SubsystemName,
|
||
HandleId,
|
||
NULL,
|
||
ObjectTypeName,
|
||
ObjectName,
|
||
SecurityDescriptor,
|
||
NULL, // No Principal Self sid
|
||
DesiredAccess,
|
||
AuditEventObjectAccess, // Default to ObjectAccess
|
||
0, // No Flags
|
||
NULL, // No ObjectType List
|
||
0, // No ObjectType List
|
||
GenericMapping,
|
||
GrantedAccess,
|
||
AccessStatus,
|
||
GenerateOnClose,
|
||
FALSE ); // Return a single GrantedAccess and AccessStatus
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtAccessCheckByTypeAndAuditAlarm (
|
||
IN PUNICODE_STRING SubsystemName,
|
||
IN PVOID HandleId,
|
||
IN PUNICODE_STRING ObjectTypeName,
|
||
IN PUNICODE_STRING ObjectName,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN PSID PrincipalSelfSid,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN AUDIT_EVENT_TYPE AuditType,
|
||
IN ULONG Flags,
|
||
IN POBJECT_TYPE_LIST ObjectTypeList OPTIONAL,
|
||
IN ULONG ObjectTypeListLength,
|
||
IN PGENERIC_MAPPING GenericMapping,
|
||
IN BOOLEAN ObjectCreation,
|
||
OUT PACCESS_MASK GrantedAccess,
|
||
OUT PNTSTATUS AccessStatus,
|
||
OUT PBOOLEAN GenerateOnClose
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
See SepAccessCheckAndAuditAlarm.
|
||
|
||
Arguments:
|
||
|
||
See SepAccessCheckAndAuditAlarm.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the call completed successfully. In this
|
||
case, ClientStatus receives the result of the access check.
|
||
|
||
STATUS_PRIVILEGE_NOT_HELD - Indicates the caller does not have
|
||
sufficient privilege to use this privileged system service.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( ObjectCreation );
|
||
|
||
return SepAccessCheckAndAuditAlarm(
|
||
SubsystemName,
|
||
HandleId,
|
||
NULL,
|
||
ObjectTypeName,
|
||
ObjectName,
|
||
SecurityDescriptor,
|
||
PrincipalSelfSid,
|
||
DesiredAccess,
|
||
AuditType,
|
||
Flags,
|
||
ObjectTypeList,
|
||
ObjectTypeListLength,
|
||
GenericMapping,
|
||
GrantedAccess,
|
||
AccessStatus,
|
||
GenerateOnClose,
|
||
FALSE ); // Return a single GrantedAccess and AccessStatus
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtAccessCheckByTypeResultListAndAuditAlarm (
|
||
IN PUNICODE_STRING SubsystemName,
|
||
IN PVOID HandleId,
|
||
IN PUNICODE_STRING ObjectTypeName,
|
||
IN PUNICODE_STRING ObjectName,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN PSID PrincipalSelfSid,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN AUDIT_EVENT_TYPE AuditType,
|
||
IN ULONG Flags,
|
||
IN POBJECT_TYPE_LIST ObjectTypeList OPTIONAL,
|
||
IN ULONG ObjectTypeListLength,
|
||
IN PGENERIC_MAPPING GenericMapping,
|
||
IN BOOLEAN ObjectCreation,
|
||
OUT PACCESS_MASK GrantedAccess,
|
||
OUT PNTSTATUS AccessStatus,
|
||
OUT PBOOLEAN GenerateOnClose
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
See SepAccessCheckAndAuditAlarm.
|
||
|
||
Arguments:
|
||
|
||
See SepAccessCheckAndAuditAlarm.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the call completed successfully. In this
|
||
case, ClientStatus receives the result of the access check.
|
||
|
||
STATUS_PRIVILEGE_NOT_HELD - Indicates the caller does not have
|
||
sufficient privilege to use this privileged system service.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( ObjectCreation );
|
||
|
||
return SepAccessCheckAndAuditAlarm(
|
||
SubsystemName,
|
||
HandleId,
|
||
NULL,
|
||
ObjectTypeName,
|
||
ObjectName,
|
||
SecurityDescriptor,
|
||
PrincipalSelfSid,
|
||
DesiredAccess,
|
||
AuditType,
|
||
Flags,
|
||
ObjectTypeList,
|
||
ObjectTypeListLength,
|
||
GenericMapping,
|
||
GrantedAccess,
|
||
AccessStatus,
|
||
GenerateOnClose,
|
||
TRUE ); // Return an array of GrantedAccess and AccessStatus
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtAccessCheckByTypeResultListAndAuditAlarmByHandle (
|
||
IN PUNICODE_STRING SubsystemName,
|
||
IN PVOID HandleId,
|
||
IN HANDLE ClientToken,
|
||
IN PUNICODE_STRING ObjectTypeName,
|
||
IN PUNICODE_STRING ObjectName,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN PSID PrincipalSelfSid,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN AUDIT_EVENT_TYPE AuditType,
|
||
IN ULONG Flags,
|
||
IN POBJECT_TYPE_LIST ObjectTypeList OPTIONAL,
|
||
IN ULONG ObjectTypeListLength,
|
||
IN PGENERIC_MAPPING GenericMapping,
|
||
IN BOOLEAN ObjectCreation,
|
||
OUT PACCESS_MASK GrantedAccess,
|
||
OUT PNTSTATUS AccessStatus,
|
||
OUT PBOOLEAN GenerateOnClose
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
See SepAccessCheckAndAuditAlarm.
|
||
|
||
Arguments:
|
||
|
||
See SepAccessCheckAndAuditAlarm.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - Indicates the call completed successfully. In this
|
||
case, ClientStatus receives the result of the access check.
|
||
|
||
STATUS_PRIVILEGE_NOT_HELD - Indicates the caller does not have
|
||
sufficient privilege to use this privileged system service.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( ObjectCreation );
|
||
|
||
return SepAccessCheckAndAuditAlarm(
|
||
SubsystemName,
|
||
HandleId,
|
||
&ClientToken,
|
||
ObjectTypeName,
|
||
ObjectName,
|
||
SecurityDescriptor,
|
||
PrincipalSelfSid,
|
||
DesiredAccess,
|
||
AuditType,
|
||
Flags,
|
||
ObjectTypeList,
|
||
ObjectTypeListLength,
|
||
GenericMapping,
|
||
GrantedAccess,
|
||
AccessStatus,
|
||
GenerateOnClose,
|
||
TRUE ); // Return an array of GrantedAccess and AccessStatus
|
||
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtOpenObjectAuditAlarm (
|
||
IN PUNICODE_STRING SubsystemName,
|
||
IN PVOID HandleId OPTIONAL,
|
||
IN PUNICODE_STRING ObjectTypeName,
|
||
IN PUNICODE_STRING ObjectName,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor OPTIONAL,
|
||
IN HANDLE ClientToken,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN ACCESS_MASK GrantedAccess,
|
||
IN PPRIVILEGE_SET Privileges OPTIONAL,
|
||
IN BOOLEAN ObjectCreation,
|
||
IN BOOLEAN AccessGranted,
|
||
OUT PBOOLEAN GenerateOnClose
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to generate audit and alarm messages when an
|
||
attempt is made to access an existing protected subsystem object or
|
||
create a new one. This routine may result in several messages being
|
||
generated and sent to Port objects. This may result in a significant
|
||
latency before returning. Design of routines that must call this
|
||
routine must take this potential latency into account. This may have
|
||
an impact on the approach taken for data structure mutex locking, for
|
||
example.
|
||
|
||
This routine may not be able to generate a complete audit record
|
||
due to memory restrictions.
|
||
|
||
This API requires the caller have SeAuditPrivilege privilege. The test
|
||
for this privilege is always against the primary token of the calling
|
||
process, not the impersonation token of the thread.
|
||
|
||
Arguments:
|
||
|
||
SubsystemName - Supplies a name string identifying the
|
||
subsystem calling the routine.
|
||
|
||
HandleId - A unique value representing the client's handle to the
|
||
object. If the access attempt was not successful (AccessGranted is
|
||
FALSE), then this parameter is ignored.
|
||
|
||
ObjectTypeName - Supplies the name of the type of object being
|
||
accessed.
|
||
|
||
ObjectName - Supplies the name of the object the client
|
||
accessed or attempted to access.
|
||
|
||
SecurityDescriptor - An optional pointer to the security descriptor of
|
||
the object being accessed.
|
||
|
||
ClientToken - A handle to a token object representing the client that
|
||
requested the operation. This handle must be obtained from a
|
||
communication session layer, such as from an LPC Port or Local
|
||
Named Pipe, to prevent possible security policy violations.
|
||
|
||
DesiredAccess - The desired access mask. This mask must have been
|
||
previously mapped to contain no generic accesses.
|
||
|
||
GrantedAccess - The mask of accesses that were actually granted.
|
||
|
||
Privileges - Optionally points to a set of privileges that were
|
||
required for the access attempt. Those privileges that were held
|
||
by the subject are marked using the UsedForAccess flag of the
|
||
attributes associated with each privilege.
|
||
|
||
ObjectCreation - A boolean flag indicating whether the access will
|
||
result in a new object being created if granted. A value of TRUE
|
||
indicates an object will be created, FALSE indicates an existing
|
||
object will be opened.
|
||
|
||
AccessGranted - Indicates whether the requested access was granted or
|
||
not. A value of TRUE indicates the access was granted. A value of
|
||
FALSE indicates the access was not granted.
|
||
|
||
GenerateOnClose - Points to a boolean that is set by the audit
|
||
generation routine and must be passed to NtCloseObjectAuditAlarm()
|
||
when the object handle is closed.
|
||
|
||
Return Value:
|
||
|
||
--*/
|
||
{
|
||
|
||
KPROCESSOR_MODE PreviousMode;
|
||
ULONG PrivilegeParameterLength;
|
||
PUNICODE_STRING CapturedSubsystemName = (PUNICODE_STRING) NULL;
|
||
PUNICODE_STRING CapturedObjectTypeName = (PUNICODE_STRING) NULL;
|
||
PUNICODE_STRING CapturedObjectName = (PUNICODE_STRING) NULL;
|
||
PSECURITY_DESCRIPTOR CapturedSecurityDescriptor = (PSECURITY_DESCRIPTOR) NULL;
|
||
PPRIVILEGE_SET CapturedPrivileges = NULL;
|
||
BOOLEAN LocalGenerateOnClose = FALSE;
|
||
SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
|
||
BOOLEAN Result;
|
||
NTSTATUS Status;
|
||
BOOLEAN GenerateAudit = FALSE;
|
||
BOOLEAN GenerateAlarm = FALSE;
|
||
PLUID ClientAuthenticationId = NULL;
|
||
HANDLE CapturedHandleId = NULL;
|
||
BOOLEAN AuditPerformed;
|
||
ULONG PrivilegeCount;
|
||
|
||
PTOKEN Token;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( ObjectCreation );
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
ASSERT( PreviousMode != KernelMode );
|
||
|
||
Status = ObReferenceObjectByHandle( ClientToken, // Handle
|
||
TOKEN_QUERY, // DesiredAccess
|
||
SeTokenObjectType, // ObjectType
|
||
PreviousMode, // AccessMode
|
||
(PVOID *)&Token, // Object
|
||
NULL // GrantedAccess
|
||
);
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
return( Status );
|
||
}
|
||
|
||
//
|
||
// If the passed token is an impersonation token, make sure
|
||
// it is at SecurityIdentification or above.
|
||
//
|
||
|
||
if (Token->TokenType == TokenImpersonation) {
|
||
|
||
if (Token->ImpersonationLevel < SecurityIdentification) {
|
||
|
||
ObDereferenceObject( (PVOID)Token );
|
||
|
||
return( STATUS_BAD_IMPERSONATION_LEVEL );
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// Check for SeAuditPrivilege. This must be tested against
|
||
// the caller's primary token.
|
||
//
|
||
|
||
SeCaptureSubjectContext ( &SubjectSecurityContext );
|
||
|
||
Result = SeCheckAuditPrivilege (
|
||
&SubjectSecurityContext,
|
||
PreviousMode
|
||
);
|
||
|
||
if (!Result) {
|
||
|
||
ObDereferenceObject( (PVOID)Token );
|
||
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
|
||
return(STATUS_PRIVILEGE_NOT_HELD);
|
||
}
|
||
|
||
//
|
||
// This will just return NULL if the input descriptor is NULL
|
||
//
|
||
|
||
Status = SeCaptureSecurityDescriptor ( SecurityDescriptor,
|
||
PreviousMode,
|
||
PagedPool,
|
||
FALSE,
|
||
&CapturedSecurityDescriptor
|
||
);
|
||
|
||
//
|
||
// At this point in time, if there's no security descriptor, there's
|
||
// nothing to do. Return success.
|
||
//
|
||
|
||
if (!NT_SUCCESS( Status ) || CapturedSecurityDescriptor == NULL) {
|
||
|
||
ObDereferenceObject( (PVOID)Token );
|
||
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
|
||
return( Status );
|
||
}
|
||
|
||
try {
|
||
|
||
//
|
||
// Only capture the privileges if we've completed a successful
|
||
// access check. Otherwise they don't mean anything.
|
||
//
|
||
|
||
if (AccessGranted && ARGUMENT_PRESENT(Privileges)) {
|
||
|
||
ProbeForReadSmallStructure(
|
||
Privileges,
|
||
sizeof(PRIVILEGE_SET),
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
PrivilegeCount = Privileges->PrivilegeCount;
|
||
|
||
if (!IsValidPrivilegeCount( PrivilegeCount )) {
|
||
Status = STATUS_INVALID_PARAMETER;
|
||
leave;
|
||
}
|
||
|
||
PrivilegeParameterLength = (ULONG)sizeof(PRIVILEGE_SET) +
|
||
((PrivilegeCount - ANYSIZE_ARRAY) *
|
||
(ULONG)sizeof(LUID_AND_ATTRIBUTES) );
|
||
|
||
ProbeForRead(
|
||
Privileges,
|
||
PrivilegeParameterLength,
|
||
sizeof(ULONG)
|
||
);
|
||
|
||
CapturedPrivileges = ExAllocatePoolWithTag( PagedPool,
|
||
PrivilegeParameterLength,
|
||
'rPeS'
|
||
);
|
||
|
||
if (CapturedPrivileges != NULL) {
|
||
|
||
RtlCopyMemory ( CapturedPrivileges,
|
||
Privileges,
|
||
PrivilegeParameterLength );
|
||
CapturedPrivileges->PrivilegeCount = PrivilegeCount;
|
||
} else {
|
||
|
||
SeReleaseSecurityDescriptor ( CapturedSecurityDescriptor,
|
||
PreviousMode,
|
||
FALSE );
|
||
|
||
ObDereferenceObject( (PVOID)Token );
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
|
||
}
|
||
|
||
if (ARGUMENT_PRESENT( HandleId )) {
|
||
|
||
ProbeForReadSmallStructure( (PHANDLE)HandleId, sizeof(PVOID), sizeof(PVOID) );
|
||
CapturedHandleId = *(PHANDLE)HandleId;
|
||
}
|
||
|
||
ProbeForWriteBoolean(GenerateOnClose);
|
||
|
||
//
|
||
// Probe and Capture the parameter strings.
|
||
// If we run out of memory attempting to capture
|
||
// the strings, the returned pointer will be
|
||
// NULL and we will continue with the audit.
|
||
//
|
||
|
||
SepProbeAndCaptureString_U ( SubsystemName,
|
||
&CapturedSubsystemName );
|
||
|
||
SepProbeAndCaptureString_U ( ObjectTypeName,
|
||
&CapturedObjectTypeName );
|
||
|
||
SepProbeAndCaptureString_U ( ObjectName,
|
||
&CapturedObjectName );
|
||
|
||
} except(EXCEPTION_EXECUTE_HANDLER) {
|
||
Status = GetExceptionCode();
|
||
}
|
||
|
||
if (!NT_SUCCESS(Status)) {
|
||
|
||
if (CapturedSubsystemName != NULL) {
|
||
SepFreeCapturedString( CapturedSubsystemName );
|
||
}
|
||
|
||
if (CapturedObjectTypeName != NULL) {
|
||
SepFreeCapturedString( CapturedObjectTypeName );
|
||
}
|
||
|
||
if (CapturedObjectName != NULL) {
|
||
SepFreeCapturedString( CapturedObjectName );
|
||
}
|
||
|
||
if (CapturedPrivileges != NULL) {
|
||
ExFreePool( CapturedPrivileges );
|
||
}
|
||
|
||
if (CapturedSecurityDescriptor != NULL) {
|
||
|
||
SeReleaseSecurityDescriptor ( CapturedSecurityDescriptor,
|
||
PreviousMode,
|
||
FALSE );
|
||
}
|
||
|
||
ObDereferenceObject( (PVOID)Token );
|
||
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
|
||
return Status;
|
||
|
||
}
|
||
|
||
if ( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted) ) {
|
||
|
||
SepExamineSacl(
|
||
RtlpSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)CapturedSecurityDescriptor ),
|
||
Token,
|
||
DesiredAccess | GrantedAccess,
|
||
AccessGranted,
|
||
&GenerateAudit,
|
||
&GenerateAlarm
|
||
);
|
||
|
||
if (GenerateAudit || GenerateAlarm) {
|
||
|
||
//
|
||
// Take a read lock on the token, because we're going to extract
|
||
// the user's Sid from it.
|
||
//
|
||
|
||
LocalGenerateOnClose = TRUE;
|
||
|
||
AuditPerformed = SepAdtOpenObjectAuditAlarm ( CapturedSubsystemName,
|
||
ARGUMENT_PRESENT(HandleId) ? (PVOID)&CapturedHandleId : NULL,
|
||
CapturedObjectTypeName,
|
||
CapturedObjectName,
|
||
Token,
|
||
SubjectSecurityContext.PrimaryToken,
|
||
DesiredAccess,
|
||
GrantedAccess,
|
||
NULL,
|
||
CapturedPrivileges,
|
||
AccessGranted,
|
||
PsProcessAuditId( PsGetCurrentProcess() ),
|
||
AuditCategoryObjectAccess,
|
||
NULL,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
LocalGenerateOnClose = AuditPerformed;
|
||
}
|
||
}
|
||
|
||
if ( !(GenerateAudit || GenerateAlarm) ) {
|
||
|
||
//
|
||
// We didn't attempt to generate an audit above, so if privileges were used,
|
||
// see if we should generate an audit here.
|
||
//
|
||
|
||
if ( ARGUMENT_PRESENT(Privileges) ) {
|
||
|
||
if ( SepAdtAuditThisEvent( AuditCategoryPrivilegeUse, &AccessGranted) ) {
|
||
|
||
AuditPerformed = SepAdtPrivilegeObjectAuditAlarm ( CapturedSubsystemName,
|
||
CapturedHandleId,
|
||
Token,
|
||
SubjectSecurityContext.PrimaryToken,
|
||
PsProcessAuditId( PsGetCurrentProcess() ),
|
||
DesiredAccess,
|
||
CapturedPrivileges,
|
||
AccessGranted
|
||
);
|
||
//
|
||
// If we generate an audit due to use of privilege, don't set generate on close,
|
||
// because then we'll have a close audit without a corresponding open audit.
|
||
//
|
||
|
||
LocalGenerateOnClose = FALSE;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (CapturedSecurityDescriptor != NULL) {
|
||
|
||
SeReleaseSecurityDescriptor ( CapturedSecurityDescriptor,
|
||
PreviousMode,
|
||
FALSE );
|
||
}
|
||
|
||
if (CapturedSubsystemName != NULL) {
|
||
SepFreeCapturedString( CapturedSubsystemName );
|
||
}
|
||
|
||
if (CapturedObjectTypeName != NULL) {
|
||
SepFreeCapturedString( CapturedObjectTypeName );
|
||
}
|
||
|
||
if (CapturedObjectName != NULL) {
|
||
SepFreeCapturedString( CapturedObjectName );
|
||
}
|
||
|
||
if (CapturedPrivileges != NULL) {
|
||
ExFreePool( CapturedPrivileges );
|
||
}
|
||
|
||
ObDereferenceObject( (PVOID)Token );
|
||
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
|
||
try {
|
||
|
||
*GenerateOnClose = LocalGenerateOnClose;
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
return GetExceptionCode();
|
||
}
|
||
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
|
||
NTSTATUS
|
||
NtCloseObjectAuditAlarm (
|
||
IN PUNICODE_STRING SubsystemName,
|
||
IN PVOID HandleId,
|
||
IN BOOLEAN GenerateOnClose
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to generate audit and alarm messages when a handle
|
||
to a protected subsystem object is deleted. This routine may result in
|
||
several messages being generated and sent to Port objects. This may
|
||
result in a significant latency before returning. Design of routines
|
||
that must call this routine must take this potential latency into
|
||
account. This may have an impact on the approach taken for data
|
||
structure mutex locking, for example.
|
||
|
||
This API requires the caller have SeAuditPrivilege privilege. The test
|
||
for this privilege is always against the primary token of the calling
|
||
process, allowing the caller to be impersonating a client during the
|
||
call with no ill effects.
|
||
|
||
Arguments:
|
||
|
||
SubsystemName - Supplies a name string identifying the subsystem
|
||
calling the routine.
|
||
|
||
HandleId - A unique value representing the client's handle to the
|
||
object.
|
||
|
||
GenerateOnClose - Is a boolean value returned from a corresponding
|
||
NtAccessCheckAndAuditAlarm() call or NtOpenObjectAuditAlarm() call
|
||
when the object handle was created.
|
||
|
||
Return value:
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN Result;
|
||
SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
PUNICODE_STRING CapturedSubsystemName = NULL;
|
||
PSID UserSid;
|
||
PSID CapturedUserSid = NULL;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
ASSERT(PreviousMode != KernelMode);
|
||
|
||
if (!GenerateOnClose) {
|
||
return( STATUS_SUCCESS );
|
||
}
|
||
|
||
//
|
||
// Check for SeAuditPrivilege
|
||
//
|
||
|
||
SeCaptureSubjectContext ( &SubjectSecurityContext );
|
||
|
||
Result = SeCheckAuditPrivilege (
|
||
&SubjectSecurityContext,
|
||
PreviousMode
|
||
);
|
||
|
||
if (!Result) {
|
||
Status = STATUS_PRIVILEGE_NOT_HELD;
|
||
goto Cleanup;
|
||
}
|
||
|
||
UserSid = SepTokenUserSid( EffectiveToken (&SubjectSecurityContext));
|
||
|
||
CapturedUserSid = ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
SeLengthSid( UserSid ),
|
||
'iSeS'
|
||
);
|
||
|
||
if ( CapturedUserSid == NULL ) {
|
||
Status = STATUS_INSUFFICIENT_RESOURCES;
|
||
goto Cleanup;
|
||
}
|
||
|
||
Status = RtlCopySid (
|
||
SeLengthSid( UserSid ),
|
||
CapturedUserSid,
|
||
UserSid
|
||
);
|
||
|
||
ASSERT( NT_SUCCESS( Status ));
|
||
|
||
|
||
try {
|
||
|
||
SepProbeAndCaptureString_U ( SubsystemName,
|
||
&CapturedSubsystemName );
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
Status = GetExceptionCode();
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// This routine will check to see if auditing is enabled
|
||
//
|
||
|
||
SepAdtCloseObjectAuditAlarm( CapturedSubsystemName, HandleId, CapturedUserSid );
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
Cleanup:
|
||
if ( CapturedSubsystemName != NULL ) {
|
||
SepFreeCapturedString( CapturedSubsystemName );
|
||
}
|
||
|
||
if ( CapturedUserSid != NULL ) {
|
||
ExFreePool( CapturedUserSid );
|
||
}
|
||
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
|
||
return Status;
|
||
}
|
||
|
||
|
||
NTSTATUS
|
||
NtDeleteObjectAuditAlarm (
|
||
IN PUNICODE_STRING SubsystemName,
|
||
IN PVOID HandleId,
|
||
IN BOOLEAN GenerateOnClose
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to generate audit and alarm messages when an object
|
||
in a protected subsystem object is deleted. This routine may result in
|
||
several messages being generated and sent to Port objects. This may
|
||
result in a significant latency before returning. Design of routines
|
||
that must call this routine must take this potential latency into
|
||
account. This may have an impact on the approach taken for data
|
||
structure mutex locking, for example.
|
||
|
||
This API requires the caller have SeAuditPrivilege privilege. The test
|
||
for this privilege is always against the primary token of the calling
|
||
process, allowing the caller to be impersonating a client during the
|
||
call with no ill effects.
|
||
|
||
Arguments:
|
||
|
||
SubsystemName - Supplies a name string identifying the subsystem
|
||
calling the routine.
|
||
|
||
HandleId - A unique value representing the client's handle to the
|
||
object.
|
||
|
||
GenerateOnClose - Is a boolean value returned from a corresponding
|
||
NtAccessCheckAndAuditAlarm() call or NtOpenObjectAuditAlarm() call
|
||
when the object handle was created.
|
||
|
||
Return value:
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN Result;
|
||
SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
|
||
KPROCESSOR_MODE PreviousMode;
|
||
PUNICODE_STRING CapturedSubsystemName = NULL;
|
||
PSID UserSid;
|
||
PSID CapturedUserSid;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
PreviousMode = KeGetPreviousMode();
|
||
|
||
ASSERT(PreviousMode != KernelMode);
|
||
|
||
if (!GenerateOnClose) {
|
||
return( STATUS_SUCCESS );
|
||
}
|
||
|
||
//
|
||
// Check for SeAuditPrivilege
|
||
//
|
||
|
||
SeCaptureSubjectContext ( &SubjectSecurityContext );
|
||
|
||
Result = SeCheckAuditPrivilege (
|
||
&SubjectSecurityContext,
|
||
PreviousMode
|
||
);
|
||
|
||
if (!Result) {
|
||
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
return(STATUS_PRIVILEGE_NOT_HELD);
|
||
}
|
||
|
||
UserSid = SepTokenUserSid( EffectiveToken (&SubjectSecurityContext));
|
||
|
||
CapturedUserSid = ExAllocatePoolWithTag(
|
||
PagedPool,
|
||
SeLengthSid( UserSid ),
|
||
'iSeS'
|
||
);
|
||
|
||
if ( CapturedUserSid == NULL ) {
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
return( STATUS_INSUFFICIENT_RESOURCES );
|
||
}
|
||
|
||
Status = RtlCopySid (
|
||
SeLengthSid( UserSid ),
|
||
CapturedUserSid,
|
||
UserSid
|
||
);
|
||
|
||
ASSERT( NT_SUCCESS( Status ));
|
||
|
||
|
||
try {
|
||
|
||
SepProbeAndCaptureString_U ( SubsystemName,
|
||
&CapturedSubsystemName );
|
||
|
||
} except (EXCEPTION_EXECUTE_HANDLER) {
|
||
|
||
if ( CapturedSubsystemName != NULL ) {
|
||
SepFreeCapturedString( CapturedSubsystemName );
|
||
}
|
||
|
||
ExFreePool( CapturedUserSid );
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
return GetExceptionCode();
|
||
|
||
}
|
||
|
||
//
|
||
// This routine will check to see if auditing is enabled
|
||
//
|
||
|
||
SepAdtDeleteObjectAuditAlarm ( CapturedSubsystemName,
|
||
HandleId,
|
||
CapturedUserSid
|
||
);
|
||
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
|
||
if ( CapturedSubsystemName != NULL ) {
|
||
SepFreeCapturedString( CapturedSubsystemName );
|
||
}
|
||
|
||
ExFreePool( CapturedUserSid );
|
||
|
||
return(STATUS_SUCCESS);
|
||
}
|
||
|
||
|
||
VOID
|
||
SeOpenObjectAuditAlarm (
|
||
IN PUNICODE_STRING ObjectTypeName,
|
||
IN PVOID Object OPTIONAL,
|
||
IN PUNICODE_STRING AbsoluteObjectName OPTIONAL,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN PACCESS_STATE AccessState,
|
||
IN BOOLEAN ObjectCreated,
|
||
IN BOOLEAN AccessGranted,
|
||
IN KPROCESSOR_MODE AccessMode,
|
||
OUT PBOOLEAN GenerateOnClose
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
SeOpenObjectAuditAlarm is used by the object manager that open objects
|
||
to generate any necessary audit or alarm messages. The open may be to
|
||
existing objects or for newly created objects. No messages will be
|
||
generated for Kernel mode accesses.
|
||
|
||
This routine is used to generate audit and alarm messages when an
|
||
attempt is made to open an object.
|
||
|
||
This routine may result in several messages being generated and sent to
|
||
Port objects. This may result in a significant latency before
|
||
returning. Design of routines that must call this routine must take
|
||
this potential latency into account. This may have an impact on the
|
||
approach taken for data structure mutex locking, for example.
|
||
|
||
Arguments:
|
||
|
||
ObjectTypeName - Supplies the name of the type of object being
|
||
accessed. This must be the same name provided to the
|
||
ObCreateObjectType service when the object type was created.
|
||
|
||
Object - Address of the object accessed. This value will not be used
|
||
as a pointer (referenced). It is necessary only to enter into log
|
||
messages. If the open was not successful, then this argument is
|
||
ignored. Otherwise, it must be provided.
|
||
|
||
AbsoluteObjectName - Supplies the name of the object being accessed.
|
||
If the object doesn't have a name, then this field is left null.
|
||
Otherwise, it must be provided.
|
||
|
||
SecurityDescriptor - A pointer to the security descriptor of the
|
||
object being accessed.
|
||
|
||
AccessState - A pointer to an access state structure containing the
|
||
subject context, the remaining desired access types, the granted
|
||
access types, and optionally a privilege set to indicate which
|
||
privileges were used to permit the access.
|
||
|
||
ObjectCreated - A boolean flag indicating whether the access resulted
|
||
in a new object being created. A value of TRUE indicates an object
|
||
was created, FALSE indicates an existing object was opened.
|
||
|
||
AccessGranted - Indicates if the access was granted or denied based on
|
||
the access check or privilege check.
|
||
|
||
AccessMode - Indicates the access mode used for the access check. One
|
||
of UserMode or KernelMode. Messages will not be generated by
|
||
kernel mode accesses.
|
||
|
||
GenerateOnClose - Points to a boolean that is set by the audit
|
||
generation routine and must be passed to SeCloseObjectAuditAlarm()
|
||
when the object handle is closed.
|
||
|
||
Return value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN GenerateAudit = FALSE;
|
||
BOOLEAN GenerateAlarm = FALSE;
|
||
ACCESS_MASK RequestedAccess;
|
||
POBJECT_NAME_INFORMATION ObjectNameInfo = NULL;
|
||
PUNICODE_STRING ObjectTypeNameInfo = NULL;
|
||
PUNICODE_STRING ObjectName = NULL;
|
||
PUNICODE_STRING LocalObjectTypeName = NULL;
|
||
PLUID PrimaryAuthenticationId = NULL;
|
||
PLUID ClientAuthenticationId = NULL;
|
||
BOOLEAN AuditPrivileges = FALSE;
|
||
BOOLEAN AuditPerformed;
|
||
PTOKEN Token;
|
||
ACCESS_MASK MappedGrantMask = (ACCESS_MASK)0;
|
||
ACCESS_MASK MappedDenyMask = (ACCESS_MASK)0;
|
||
PAUX_ACCESS_DATA AuxData;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( ObjectCreated );
|
||
|
||
if ( AccessMode == KernelMode ) {
|
||
return;
|
||
}
|
||
|
||
AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
|
||
|
||
Token = EffectiveToken( &AccessState->SubjectSecurityContext );
|
||
|
||
if (ARGUMENT_PRESENT(Token->AuditData)) {
|
||
|
||
MappedGrantMask = Token->AuditData->GrantMask;
|
||
|
||
RtlMapGenericMask(
|
||
&MappedGrantMask,
|
||
&AuxData->GenericMapping
|
||
);
|
||
|
||
MappedDenyMask = Token->AuditData->DenyMask;
|
||
|
||
RtlMapGenericMask(
|
||
&MappedDenyMask,
|
||
&AuxData->GenericMapping
|
||
);
|
||
}
|
||
|
||
if (SecurityDescriptor != NULL) {
|
||
|
||
RequestedAccess = AccessState->RemainingDesiredAccess |
|
||
AccessState->PreviouslyGrantedAccess;
|
||
|
||
if ( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted )) {
|
||
|
||
if ( RequestedAccess & (AccessGranted ? MappedGrantMask : MappedDenyMask)) {
|
||
|
||
GenerateAudit = TRUE;
|
||
|
||
} else {
|
||
|
||
SepExamineSacl(
|
||
RtlpSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor ),
|
||
Token,
|
||
RequestedAccess,
|
||
AccessGranted,
|
||
&GenerateAudit,
|
||
&GenerateAlarm
|
||
);
|
||
}
|
||
|
||
//
|
||
// Only generate an audit on close of we're auditing from SACL
|
||
// settings.
|
||
//
|
||
|
||
if (GenerateAudit) {
|
||
|
||
*GenerateOnClose = TRUE;
|
||
|
||
//
|
||
// Construct the audit mask that will be placed into the handle.
|
||
//
|
||
|
||
if (AccessGranted) {
|
||
|
||
SeMaximumAuditMask(
|
||
RtlpSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor ),
|
||
RequestedAccess,
|
||
Token,
|
||
&AuxData->MaximumAuditMask
|
||
);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we don't generate an audit via the SACL, see if we need to generate
|
||
// one for privilege use.
|
||
//
|
||
// Note that we only audit privileges successfully used to open objects,
|
||
// so we don't care about a failed privilege use here. Therefore, only
|
||
// do this test of access has been granted.
|
||
//
|
||
|
||
if (!GenerateAudit && (AccessGranted == TRUE)) {
|
||
|
||
if ( SepAdtAuditThisEvent( AuditCategoryPrivilegeUse, &AccessGranted )) {
|
||
|
||
if ((AuxData->PrivilegesUsed != NULL) &&
|
||
(AuxData->PrivilegesUsed->PrivilegeCount > 0) ) {
|
||
|
||
//
|
||
// Make sure these are actually privileges that we want to audit
|
||
//
|
||
|
||
if (SepFilterPrivilegeAudits( AuxData->PrivilegesUsed )) {
|
||
|
||
GenerateAudit = TRUE;
|
||
|
||
//
|
||
// When we finally try to generate this audit, this flag
|
||
// will tell us that we need to audit the fact that we
|
||
// used a privilege, as opposed to audit due to the SACL.
|
||
//
|
||
|
||
AccessState->AuditPrivileges = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Set up either to generate an audit (if the access check has failed), or save
|
||
// the stuff that we're going to audit later into the AccessState structure.
|
||
//
|
||
|
||
if (GenerateAudit || GenerateAlarm) {
|
||
|
||
AccessState->GenerateAudit = TRUE;
|
||
|
||
//
|
||
// Figure out what we've been passed, and obtain as much
|
||
// missing information as possible.
|
||
//
|
||
|
||
if ( !ARGUMENT_PRESENT( AbsoluteObjectName )) {
|
||
|
||
if ( ARGUMENT_PRESENT( Object )) {
|
||
|
||
ObjectNameInfo = SepQueryNameString( Object );
|
||
|
||
if ( ObjectNameInfo != NULL ) {
|
||
|
||
ObjectName = &ObjectNameInfo->Name;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
ObjectName = AbsoluteObjectName;
|
||
}
|
||
|
||
if ( !ARGUMENT_PRESENT( ObjectTypeName )) {
|
||
|
||
if ( ARGUMENT_PRESENT( Object )) {
|
||
|
||
ObjectTypeNameInfo = SepQueryTypeString( Object );
|
||
|
||
if ( ObjectTypeNameInfo != NULL ) {
|
||
|
||
LocalObjectTypeName = ObjectTypeNameInfo;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
LocalObjectTypeName = ObjectTypeName;
|
||
}
|
||
|
||
//
|
||
// If the access attempt failed, do the audit here. If it succeeded,
|
||
// we'll do the audit later, when the handle is allocated.
|
||
//
|
||
//
|
||
|
||
if (!AccessGranted) {
|
||
|
||
AuditPerformed = SepAdtOpenObjectAuditAlarm ( (PUNICODE_STRING)&SeSubsystemName,
|
||
NULL,
|
||
LocalObjectTypeName,
|
||
ObjectName,
|
||
AccessState->SubjectSecurityContext.ClientToken,
|
||
AccessState->SubjectSecurityContext.PrimaryToken,
|
||
AccessState->OriginalDesiredAccess,
|
||
AccessState->PreviouslyGrantedAccess,
|
||
&AccessState->OperationID,
|
||
AuxData->PrivilegesUsed,
|
||
FALSE,
|
||
AccessState->SubjectSecurityContext.ProcessAuditId,
|
||
AuditCategoryObjectAccess,
|
||
NULL,
|
||
0,
|
||
NULL );
|
||
} else {
|
||
|
||
//
|
||
// Copy all the stuff we're going to need into the
|
||
// AccessState and return.
|
||
//
|
||
|
||
if ( ObjectName != NULL ) {
|
||
|
||
if ( AccessState->ObjectName.Buffer != NULL ) {
|
||
|
||
ExFreePool( AccessState->ObjectName.Buffer );
|
||
AccessState->ObjectName.Length = 0;
|
||
AccessState->ObjectName.MaximumLength = 0;
|
||
}
|
||
|
||
AccessState->ObjectName.Buffer = ExAllocatePool( PagedPool,ObjectName->MaximumLength );
|
||
if (AccessState->ObjectName.Buffer != NULL) {
|
||
|
||
AccessState->ObjectName.MaximumLength = ObjectName->MaximumLength;
|
||
RtlCopyUnicodeString( &AccessState->ObjectName, ObjectName );
|
||
}
|
||
}
|
||
|
||
if ( LocalObjectTypeName != NULL ) {
|
||
|
||
if ( AccessState->ObjectTypeName.Buffer != NULL ) {
|
||
|
||
ExFreePool( AccessState->ObjectTypeName.Buffer );
|
||
AccessState->ObjectTypeName.Length = 0;
|
||
AccessState->ObjectTypeName.MaximumLength = 0;
|
||
}
|
||
|
||
AccessState->ObjectTypeName.Buffer = ExAllocatePool( PagedPool, LocalObjectTypeName->MaximumLength );
|
||
if (AccessState->ObjectTypeName.Buffer != NULL) {
|
||
|
||
AccessState->ObjectTypeName.MaximumLength = LocalObjectTypeName->MaximumLength;
|
||
RtlCopyUnicodeString( &AccessState->ObjectTypeName, LocalObjectTypeName );
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( ObjectNameInfo != NULL ) {
|
||
|
||
ExFreePool( ObjectNameInfo );
|
||
}
|
||
|
||
if ( ObjectTypeNameInfo != NULL ) {
|
||
|
||
ExFreePool( ObjectTypeNameInfo );
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
SeOpenObjectForDeleteAuditAlarm (
|
||
IN PUNICODE_STRING ObjectTypeName,
|
||
IN PVOID Object OPTIONAL,
|
||
IN PUNICODE_STRING AbsoluteObjectName OPTIONAL,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN PACCESS_STATE AccessState,
|
||
IN BOOLEAN ObjectCreated,
|
||
IN BOOLEAN AccessGranted,
|
||
IN KPROCESSOR_MODE AccessMode,
|
||
OUT PBOOLEAN GenerateOnClose
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
SeOpenObjectForDeleteAuditAlarm is used by the object manager that open
|
||
objects to generate any necessary audit or alarm messages. The open may
|
||
be to existing objects or for newly created objects. No messages will be
|
||
generated for Kernel mode accesses.
|
||
|
||
This routine is used to generate audit and alarm messages when an
|
||
attempt is made to open an object with the intent to delete it.
|
||
Specifically, this is used by file systems when the flag
|
||
FILE_DELETE_ON_CLOSE is specified.
|
||
|
||
This routine may result in several messages being generated and sent to
|
||
Port objects. This may result in a significant latency before
|
||
returning. Design of routines that must call this routine must take
|
||
this potential latency into account. This may have an impact on the
|
||
approach taken for data structure mutex locking, for example.
|
||
|
||
Arguments:
|
||
|
||
ObjectTypeName - Supplies the name of the type of object being
|
||
accessed. This must be the same name provided to the
|
||
ObCreateObjectType service when the object type was created.
|
||
|
||
Object - Address of the object accessed. This value will not be used
|
||
as a pointer (referenced). It is necessary only to enter into log
|
||
messages. If the open was not successful, then this argument is
|
||
ignored. Otherwise, it must be provided.
|
||
|
||
AbsoluteObjectName - Supplies the name of the object being accessed.
|
||
If the object doesn't have a name, then this field is left null.
|
||
Otherwise, it must be provided.
|
||
|
||
SecurityDescriptor - A pointer to the security descriptor of the
|
||
object being accessed.
|
||
|
||
AccessState - A pointer to an access state structure containing the
|
||
subject context, the remaining desired access types, the granted
|
||
access types, and optionally a privilege set to indicate which
|
||
privileges were used to permit the access.
|
||
|
||
ObjectCreated - A boolean flag indicating whether the access resulted
|
||
in a new object being created. A value of TRUE indicates an object
|
||
was created, FALSE indicates an existing object was opened.
|
||
|
||
AccessGranted - Indicates if the access was granted or denied based on
|
||
the access check or privilege check.
|
||
|
||
AccessMode - Indicates the access mode used for the access check. One
|
||
of UserMode or KernelMode. Messages will not be generated by
|
||
kernel mode accesses.
|
||
|
||
GenerateOnClose - Points to a boolean that is set by the audit
|
||
generation routine and must be passed to SeCloseObjectAuditAlarm()
|
||
when the object handle is closed.
|
||
|
||
Return value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
BOOLEAN GenerateAudit = FALSE;
|
||
BOOLEAN GenerateAlarm = FALSE;
|
||
ACCESS_MASK RequestedAccess;
|
||
POBJECT_NAME_INFORMATION ObjectNameInfo = NULL;
|
||
PUNICODE_STRING ObjectTypeNameInfo = NULL;
|
||
PUNICODE_STRING ObjectName = NULL;
|
||
PUNICODE_STRING LocalObjectTypeName = NULL;
|
||
PLUID PrimaryAuthenticationId = NULL;
|
||
PLUID ClientAuthenticationId = NULL;
|
||
BOOLEAN AuditPrivileges = FALSE;
|
||
BOOLEAN AuditPerformed;
|
||
PTOKEN Token;
|
||
ACCESS_MASK MappedGrantMask = (ACCESS_MASK)0;
|
||
ACCESS_MASK MappedDenyMask = (ACCESS_MASK)0;
|
||
PAUX_ACCESS_DATA AuxData;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( ObjectCreated );
|
||
|
||
if ( AccessMode == KernelMode ) {
|
||
return;
|
||
}
|
||
|
||
AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
|
||
|
||
Token = EffectiveToken( &AccessState->SubjectSecurityContext );
|
||
|
||
if (ARGUMENT_PRESENT(Token->AuditData)) {
|
||
|
||
MappedGrantMask = Token->AuditData->GrantMask;
|
||
|
||
RtlMapGenericMask(
|
||
&MappedGrantMask,
|
||
&AuxData->GenericMapping
|
||
);
|
||
|
||
MappedDenyMask = Token->AuditData->DenyMask;
|
||
|
||
RtlMapGenericMask(
|
||
&MappedDenyMask,
|
||
&AuxData->GenericMapping
|
||
);
|
||
}
|
||
|
||
if (SecurityDescriptor != NULL) {
|
||
|
||
RequestedAccess = AccessState->RemainingDesiredAccess |
|
||
AccessState->PreviouslyGrantedAccess;
|
||
|
||
if ( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted )) {
|
||
|
||
if ( RequestedAccess & (AccessGranted ? MappedGrantMask : MappedDenyMask)) {
|
||
|
||
GenerateAudit = TRUE;
|
||
|
||
} else {
|
||
|
||
SepExamineSacl(
|
||
RtlpSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor ),
|
||
Token,
|
||
RequestedAccess,
|
||
AccessGranted,
|
||
&GenerateAudit,
|
||
&GenerateAlarm
|
||
);
|
||
}
|
||
|
||
//
|
||
// Only generate an audit on close of we're auditing from SACL
|
||
// settings.
|
||
//
|
||
|
||
if (GenerateAudit) {
|
||
*GenerateOnClose = TRUE;
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we don't generate an audit via the SACL, see if we need to generate
|
||
// one for privilege use.
|
||
//
|
||
// Note that we only audit privileges successfully used to open objects,
|
||
// so we don't care about a failed privilege use here. Therefore, only
|
||
// do this test of access has been granted.
|
||
//
|
||
|
||
if (!GenerateAudit && (AccessGranted == TRUE)) {
|
||
|
||
if ( SepAdtAuditThisEvent( AuditCategoryPrivilegeUse, &AccessGranted )) {
|
||
|
||
if ((AuxData->PrivilegesUsed != NULL) &&
|
||
(AuxData->PrivilegesUsed->PrivilegeCount > 0) ) {
|
||
|
||
//
|
||
// Make sure these are actually privileges that we want to audit
|
||
//
|
||
|
||
if (SepFilterPrivilegeAudits( AuxData->PrivilegesUsed )) {
|
||
|
||
GenerateAudit = TRUE;
|
||
|
||
//
|
||
// When we finally try to generate this audit, this flag
|
||
// will tell us that we need to audit the fact that we
|
||
// used a privilege, as opposed to audit due to the SACL.
|
||
//
|
||
|
||
AccessState->AuditPrivileges = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Set up either to generate an audit (if the access check has failed), or save
|
||
// the stuff that we're going to audit later into the AccessState structure.
|
||
//
|
||
|
||
if (GenerateAudit || GenerateAlarm) {
|
||
|
||
AccessState->GenerateAudit = TRUE;
|
||
|
||
//
|
||
// Figure out what we've been passed, and obtain as much
|
||
// missing information as possible.
|
||
//
|
||
|
||
if ( !ARGUMENT_PRESENT( AbsoluteObjectName )) {
|
||
|
||
if ( ARGUMENT_PRESENT( Object )) {
|
||
|
||
ObjectNameInfo = SepQueryNameString( Object );
|
||
|
||
if ( ObjectNameInfo != NULL ) {
|
||
|
||
ObjectName = &ObjectNameInfo->Name;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
ObjectName = AbsoluteObjectName;
|
||
}
|
||
|
||
if ( !ARGUMENT_PRESENT( ObjectTypeName )) {
|
||
|
||
if ( ARGUMENT_PRESENT( Object )) {
|
||
|
||
ObjectTypeNameInfo = SepQueryTypeString( Object );
|
||
|
||
if ( ObjectTypeNameInfo != NULL ) {
|
||
|
||
LocalObjectTypeName = ObjectTypeNameInfo;
|
||
}
|
||
}
|
||
|
||
} else {
|
||
|
||
LocalObjectTypeName = ObjectTypeName;
|
||
}
|
||
|
||
//
|
||
// If the access attempt failed, do the audit here. If it succeeded,
|
||
// we'll do the audit later, when the handle is allocated.
|
||
//
|
||
//
|
||
|
||
if (!AccessGranted) {
|
||
|
||
AuditPerformed = SepAdtOpenObjectAuditAlarm ( (PUNICODE_STRING)&SeSubsystemName,
|
||
NULL,
|
||
LocalObjectTypeName,
|
||
ObjectName,
|
||
AccessState->SubjectSecurityContext.ClientToken,
|
||
AccessState->SubjectSecurityContext.PrimaryToken,
|
||
AccessState->OriginalDesiredAccess,
|
||
AccessState->PreviouslyGrantedAccess,
|
||
&AccessState->OperationID,
|
||
AuxData->PrivilegesUsed,
|
||
FALSE,
|
||
AccessState->SubjectSecurityContext.ProcessAuditId,
|
||
AuditCategoryObjectAccess,
|
||
NULL,
|
||
0,
|
||
NULL );
|
||
} else {
|
||
|
||
//
|
||
// Generate the delete audit first
|
||
//
|
||
|
||
SepAdtOpenObjectForDeleteAuditAlarm ( (PUNICODE_STRING)&SeSubsystemName,
|
||
NULL,
|
||
LocalObjectTypeName,
|
||
ObjectName,
|
||
AccessState->SubjectSecurityContext.ClientToken,
|
||
AccessState->SubjectSecurityContext.PrimaryToken,
|
||
AccessState->OriginalDesiredAccess,
|
||
AccessState->PreviouslyGrantedAccess,
|
||
&AccessState->OperationID,
|
||
AuxData->PrivilegesUsed,
|
||
TRUE,
|
||
AccessState->SubjectSecurityContext.ProcessAuditId );
|
||
|
||
//
|
||
// Copy all the stuff we're going to need into the
|
||
// AccessState and return.
|
||
//
|
||
|
||
if ( ObjectName != NULL ) {
|
||
|
||
if ( AccessState->ObjectName.Buffer != NULL ) {
|
||
|
||
ExFreePool( AccessState->ObjectName.Buffer );
|
||
AccessState->ObjectName.Length = 0;
|
||
AccessState->ObjectName.MaximumLength = 0;
|
||
}
|
||
|
||
AccessState->ObjectName.Buffer = ExAllocatePool( PagedPool,ObjectName->MaximumLength );
|
||
if (AccessState->ObjectName.Buffer != NULL) {
|
||
|
||
AccessState->ObjectName.MaximumLength = ObjectName->MaximumLength;
|
||
RtlCopyUnicodeString( &AccessState->ObjectName, ObjectName );
|
||
}
|
||
}
|
||
|
||
if ( LocalObjectTypeName != NULL ) {
|
||
|
||
if ( AccessState->ObjectTypeName.Buffer != NULL ) {
|
||
|
||
ExFreePool( AccessState->ObjectTypeName.Buffer );
|
||
AccessState->ObjectTypeName.Length = 0;
|
||
AccessState->ObjectTypeName.MaximumLength = 0;
|
||
}
|
||
|
||
AccessState->ObjectTypeName.Buffer = ExAllocatePool( PagedPool, LocalObjectTypeName->MaximumLength );
|
||
if (AccessState->ObjectTypeName.Buffer != NULL) {
|
||
|
||
AccessState->ObjectTypeName.MaximumLength = LocalObjectTypeName->MaximumLength;
|
||
RtlCopyUnicodeString( &AccessState->ObjectTypeName, LocalObjectTypeName );
|
||
}
|
||
}
|
||
}
|
||
|
||
if ( ObjectNameInfo != NULL ) {
|
||
|
||
ExFreePool( ObjectNameInfo );
|
||
}
|
||
|
||
if ( ObjectTypeNameInfo != NULL ) {
|
||
|
||
ExFreePool( ObjectTypeNameInfo );
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
SeObjectReferenceAuditAlarm(
|
||
IN PLUID OperationID OPTIONAL,
|
||
IN PVOID Object,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN PPRIVILEGE_SET Privileges OPTIONAL,
|
||
IN BOOLEAN AccessGranted,
|
||
IN KPROCESSOR_MODE AccessMode
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
description-of-function.
|
||
|
||
Arguments:
|
||
|
||
argument-name - Supplies | Returns description of argument.
|
||
.
|
||
.
|
||
|
||
Return Value:
|
||
|
||
return-value - Description of conditions needed to return value. - or -
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN GenerateAudit = FALSE;
|
||
BOOLEAN GenerateAlarm = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( OperationID );
|
||
UNREFERENCED_PARAMETER( Privileges );
|
||
|
||
if (AccessMode == KernelMode) {
|
||
return;
|
||
}
|
||
|
||
if ( SecurityDescriptor != NULL ) {
|
||
|
||
if ( SepAdtAuditThisEvent( AuditCategoryDetailedTracking, &AccessGranted )) {
|
||
|
||
SepExamineSacl(
|
||
RtlpSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor ),
|
||
EffectiveToken( SubjectSecurityContext ),
|
||
DesiredAccess,
|
||
AccessGranted,
|
||
&GenerateAudit,
|
||
&GenerateAlarm
|
||
);
|
||
|
||
if ( GenerateAudit || GenerateAlarm ) {
|
||
|
||
SepAdtObjectReferenceAuditAlarm(
|
||
Object,
|
||
SubjectSecurityContext,
|
||
DesiredAccess,
|
||
AccessGranted
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
|
||
|
||
VOID
|
||
SeAuditHandleCreation(
|
||
IN PACCESS_STATE AccessState,
|
||
IN HANDLE Handle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function audits the creation of a handle.
|
||
|
||
It will examine the AuditHandleCreation field in the passed AccessState,
|
||
which will indicate whether auditing was performed when the object
|
||
was found or created.
|
||
|
||
This routine is necessary because object name decoding and handle
|
||
allocation occur in widely separate places, preventing us from
|
||
auditing everything at once.
|
||
|
||
Arguments:
|
||
|
||
AccessState - Supplies a pointer to the AccessState structure
|
||
representing this access attempt.
|
||
|
||
Handle - The newly allocated handle value.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN AuditPerformed = FALSE;
|
||
PAUX_ACCESS_DATA AuxData;
|
||
PAGED_CODE();
|
||
|
||
AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
|
||
|
||
if ( AccessState->GenerateAudit ) {
|
||
|
||
if ( AccessState->AuditPrivileges ) {
|
||
|
||
AuditPerformed = SepAdtPrivilegeObjectAuditAlarm (
|
||
(PUNICODE_STRING)&SeSubsystemName,
|
||
Handle,
|
||
(PTOKEN)AccessState->SubjectSecurityContext.ClientToken,
|
||
(PTOKEN)AccessState->SubjectSecurityContext.PrimaryToken,
|
||
AccessState->SubjectSecurityContext.ProcessAuditId,
|
||
AccessState->PreviouslyGrantedAccess,
|
||
AuxData->PrivilegesUsed,
|
||
TRUE
|
||
);
|
||
} else {
|
||
|
||
AuditPerformed = SepAdtOpenObjectAuditAlarm (
|
||
(PUNICODE_STRING)&SeSubsystemName,
|
||
&Handle,
|
||
&AccessState->ObjectTypeName,
|
||
&AccessState->ObjectName,
|
||
AccessState->SubjectSecurityContext.ClientToken,
|
||
AccessState->SubjectSecurityContext.PrimaryToken,
|
||
AccessState->OriginalDesiredAccess,
|
||
AccessState->PreviouslyGrantedAccess,
|
||
&AccessState->OperationID,
|
||
AuxData->PrivilegesUsed,
|
||
TRUE,
|
||
PsGetCurrentProcessId(),
|
||
AuditCategoryObjectAccess,
|
||
NULL,
|
||
0,
|
||
NULL );
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we generated an 'open' audit, make sure we generate a close
|
||
//
|
||
|
||
AccessState->GenerateOnClose = AuditPerformed;
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
SeCloseObjectAuditAlarm(
|
||
IN PVOID Object,
|
||
IN HANDLE Handle,
|
||
IN BOOLEAN GenerateOnClose
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to generate audit and alarm messages when a handle
|
||
to an object is deleted.
|
||
|
||
This routine may result in several messages being generated and sent to
|
||
Port objects. This may result in a significant latency before
|
||
returning. Design of routines that must call this routine must take
|
||
this potential latency into account. This may have an impact on the
|
||
approach taken for data structure mutex locking, for example.
|
||
|
||
Arguments:
|
||
|
||
Object - Address of the object being accessed. This value will not be
|
||
used as a pointer (referenced). It is necessary only to enter into
|
||
log messages.
|
||
|
||
Handle - Supplies the handle value assigned to the open.
|
||
|
||
GenerateOnClose - Is a boolean value returned from a corresponding
|
||
SeOpenObjectAuditAlarm() call when the object handle was created.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
|
||
PSID UserSid;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( Object );
|
||
|
||
if (GenerateOnClose) {
|
||
|
||
SeCaptureSubjectContext ( &SubjectSecurityContext );
|
||
|
||
UserSid = SepTokenUserSid( EffectiveToken (&SubjectSecurityContext));
|
||
|
||
|
||
SepAdtCloseObjectAuditAlarm( (PUNICODE_STRING) &SeSubsystemName,
|
||
Handle,
|
||
UserSid );
|
||
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
SeDeleteObjectAuditAlarm(
|
||
IN PVOID Object,
|
||
IN HANDLE Handle
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is used to generate audit and alarm messages when an object
|
||
is marked for deletion.
|
||
|
||
This routine may result in several messages being generated and sent to
|
||
Port objects. This may result in a significant latency before
|
||
returning. Design of routines that must call this routine must take
|
||
this potential latency into account. This may have an impact on the
|
||
approach taken for data structure mutex locking, for example.
|
||
|
||
Arguments:
|
||
|
||
Object - Address of the object being accessed. This value will not be
|
||
used as a pointer (referenced). It is necessary only to enter into
|
||
log messages.
|
||
|
||
Handle - Supplies the handle value assigned to the open.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
SECURITY_SUBJECT_CONTEXT SubjectSecurityContext;
|
||
PSID UserSid;
|
||
NTSTATUS Status;
|
||
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( Object );
|
||
|
||
SeCaptureSubjectContext ( &SubjectSecurityContext );
|
||
|
||
UserSid = SepTokenUserSid( EffectiveToken (&SubjectSecurityContext));
|
||
|
||
|
||
|
||
SepAdtDeleteObjectAuditAlarm (
|
||
(PUNICODE_STRING)&SeSubsystemName,
|
||
(PVOID)Handle,
|
||
UserSid
|
||
);
|
||
|
||
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
SepExamineSacl(
|
||
IN PACL Sacl,
|
||
IN PACCESS_TOKEN Token,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN BOOLEAN AccessGranted,
|
||
OUT PBOOLEAN GenerateAudit,
|
||
OUT PBOOLEAN GenerateAlarm
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will examine the passed Sacl and determine what
|
||
if any action is required based its contents.
|
||
|
||
Note that this routine is not aware of any system state, ie,
|
||
whether or not auditing is currently enabled for either the
|
||
system or this particular object type.
|
||
|
||
Arguments:
|
||
|
||
Sacl - Supplies a pointer to the Sacl to be examined.
|
||
|
||
Token - Supplies the effective token of the caller
|
||
|
||
AccessGranted - Supplies whether or not the access attempt
|
||
was successful.
|
||
|
||
GenerateAudit - Returns a boolean indicating whether or not
|
||
we should generate an audit.
|
||
|
||
GenerateAlarm - Returns a boolean indiciating whether or not
|
||
we should generate an alarm.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - The operation completed successfully.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG i;
|
||
PVOID Ace;
|
||
ULONG AceCount;
|
||
ACCESS_MASK AccessMask;
|
||
UCHAR AceFlags;
|
||
BOOLEAN FailedMaximumAllowed;
|
||
|
||
PAGED_CODE();
|
||
|
||
*GenerateAudit = FALSE;
|
||
*GenerateAlarm = FALSE;
|
||
|
||
//
|
||
// If we failed an attempt to open an object for ONLY maximumum allowed,
|
||
// then we generate an audit if ANY ACCESS_DENIED audit matching this
|
||
// user's list of sids is found
|
||
//
|
||
|
||
FailedMaximumAllowed = FALSE;
|
||
if (!AccessGranted && (DesiredAccess & MAXIMUM_ALLOWED)) {
|
||
FailedMaximumAllowed = TRUE;
|
||
}
|
||
|
||
//
|
||
// If the Sacl is null, do nothing and return
|
||
//
|
||
|
||
if (Sacl == NULL) {
|
||
|
||
return;
|
||
}
|
||
|
||
AceCount = Sacl->AceCount;
|
||
|
||
if (AceCount == 0) {
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Iterate through the ACEs on the Sacl until either we reach
|
||
// the end or discover that we have to take all possible actions,
|
||
// in which case it doesn't pay to look any further
|
||
//
|
||
|
||
for ( i = 0, Ace = FirstAce( Sacl ) ;
|
||
(i < AceCount) && !(*GenerateAudit && *GenerateAlarm);
|
||
i++, Ace = NextAce( Ace ) ) {
|
||
|
||
if ( !(((PACE_HEADER)Ace)->AceFlags & INHERIT_ONLY_ACE)) {
|
||
|
||
if ( (((PACE_HEADER)Ace)->AceType == SYSTEM_AUDIT_ACE_TYPE) ) {
|
||
|
||
if ( SepSidInToken( (PACCESS_TOKEN)Token, NULL, &((PSYSTEM_AUDIT_ACE)Ace)->SidStart, FALSE ) ) {
|
||
|
||
AccessMask = ((PSYSTEM_AUDIT_ACE)Ace)->Mask;
|
||
AceFlags = ((PACE_HEADER)Ace)->AceFlags;
|
||
|
||
if ( AccessMask & DesiredAccess ) {
|
||
|
||
if (((AceFlags & SUCCESSFUL_ACCESS_ACE_FLAG) && AccessGranted) ||
|
||
((AceFlags & FAILED_ACCESS_ACE_FLAG) && !AccessGranted)) {
|
||
|
||
*GenerateAudit = TRUE;
|
||
}
|
||
} else if ( FailedMaximumAllowed && (AceFlags & FAILED_ACCESS_ACE_FLAG) ) {
|
||
*GenerateAudit = TRUE;
|
||
}
|
||
}
|
||
|
||
continue;
|
||
}
|
||
|
||
if ( (((PACE_HEADER)Ace)->AceType == SYSTEM_ALARM_ACE_TYPE) ) {
|
||
|
||
if ( SepSidInToken( (PACCESS_TOKEN)Token, NULL, &((PSYSTEM_ALARM_ACE)Ace)->SidStart, FALSE ) ) {
|
||
|
||
AccessMask = ((PSYSTEM_ALARM_ACE)Ace)->Mask;
|
||
|
||
if ( AccessMask & DesiredAccess ) {
|
||
|
||
AceFlags = ((PACE_HEADER)Ace)->AceFlags;
|
||
|
||
if (((AceFlags & SUCCESSFUL_ACCESS_ACE_FLAG) && AccessGranted) ||
|
||
((AceFlags & FAILED_ACCESS_ACE_FLAG) && !AccessGranted)) {
|
||
|
||
*GenerateAlarm = TRUE;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
SepAuditTypeList (
|
||
IN PIOBJECT_TYPE_LIST ObjectTypeList,
|
||
IN ULONG ObjectTypeListLength,
|
||
IN PNTSTATUS AccessStatus,
|
||
IN ULONG StartIndex,
|
||
OUT PBOOLEAN GenerateSuccessAudit,
|
||
OUT PBOOLEAN GenerateFailureAudit
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines if any children of the object represented by
|
||
StartIndex have a different degree of success than the StartIndex element.
|
||
|
||
Arguments:
|
||
|
||
ObjectTypeList - The object type list to update.
|
||
|
||
ObjectTypeListLength - Number of elements in ObjectTypeList
|
||
|
||
AccessStatus - Specifies STATUS_SUCCESS or other error code to be
|
||
propogated back to the caller
|
||
|
||
StartIndex - Index to the target element to update.
|
||
|
||
GenerateSuccessAudit - Returns a boolean indicating whether or not
|
||
we should generate a success audit.
|
||
|
||
GenerateFailureAudit - Returns a boolean indicating whether or not
|
||
we should generate a failure audit.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG Index;
|
||
BOOLEAN WasSuccess;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Determine if the target was successful.
|
||
//
|
||
|
||
WasSuccess = NT_SUCCESS( AccessStatus[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;
|
||
}
|
||
|
||
//
|
||
// If the child has different access than the target,
|
||
// mark the child.
|
||
//
|
||
|
||
if ( WasSuccess && !NT_SUCCESS( AccessStatus[Index]) ) {
|
||
|
||
*GenerateFailureAudit = TRUE;
|
||
ObjectTypeList[Index].Flags |= OBJECT_FAILURE_AUDIT;
|
||
|
||
} else if ( !WasSuccess && NT_SUCCESS( AccessStatus[Index]) ) {
|
||
|
||
*GenerateSuccessAudit = TRUE;
|
||
ObjectTypeList[Index].Flags |= OBJECT_SUCCESS_AUDIT;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
SepSetAuditInfoForObjectType(
|
||
IN UCHAR AceFlags,
|
||
IN ACCESS_MASK AccessMask,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN PIOBJECT_TYPE_LIST ObjectTypeList,
|
||
IN ULONG ObjectTypeListLength,
|
||
IN BOOLEAN ReturnResultList,
|
||
IN ULONG ObjectTypeIndex,
|
||
IN PNTSTATUS AccessStatus,
|
||
IN PACCESS_MASK GrantedAccess,
|
||
IN BOOLEAN FailedMaximumAllowed,
|
||
OUT PBOOLEAN GenerateSuccessAudit,
|
||
OUT PBOOLEAN GenerateFailureAudit
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Determine if success/failure audit needs to be generated for
|
||
object at ObjectTypeIndex in ObjectTypeList.
|
||
|
||
This helper function is called only by SepExamineSaclEx.
|
||
|
||
Arguments:
|
||
|
||
please refer to arg help for function SepExamineSaclEx
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
if ( AccessMask & (DesiredAccess|GrantedAccess[ObjectTypeIndex]) ) {
|
||
|
||
if ( ( AceFlags & SUCCESSFUL_ACCESS_ACE_FLAG ) &&
|
||
NT_SUCCESS(AccessStatus[ObjectTypeIndex]) ) {
|
||
|
||
*GenerateSuccessAudit = TRUE;
|
||
|
||
if ( ObjectTypeListLength != 0 ) {
|
||
|
||
ObjectTypeList[ObjectTypeIndex].Flags |= OBJECT_SUCCESS_AUDIT;
|
||
|
||
if ( ReturnResultList ) {
|
||
|
||
SepAuditTypeList( ObjectTypeList,
|
||
ObjectTypeListLength,
|
||
AccessStatus,
|
||
ObjectTypeIndex,
|
||
GenerateSuccessAudit,
|
||
GenerateFailureAudit );
|
||
}
|
||
}
|
||
} else if ( ( AceFlags & FAILED_ACCESS_ACE_FLAG ) &&
|
||
!NT_SUCCESS(AccessStatus[ObjectTypeIndex]) ) {
|
||
|
||
*GenerateFailureAudit = TRUE;
|
||
|
||
if ( ObjectTypeListLength != 0 ) {
|
||
|
||
ObjectTypeList[ObjectTypeIndex].Flags |= OBJECT_FAILURE_AUDIT;
|
||
|
||
if ( ReturnResultList ) {
|
||
|
||
SepAuditTypeList( ObjectTypeList,
|
||
ObjectTypeListLength,
|
||
AccessStatus,
|
||
ObjectTypeIndex,
|
||
GenerateSuccessAudit,
|
||
GenerateFailureAudit );
|
||
}
|
||
}
|
||
}
|
||
|
||
} else if ( FailedMaximumAllowed && (AceFlags & FAILED_ACCESS_ACE_FLAG) ) {
|
||
|
||
*GenerateFailureAudit = TRUE;
|
||
if ( ObjectTypeListLength != 0 ) {
|
||
ObjectTypeList[ObjectTypeIndex].Flags |= OBJECT_FAILURE_AUDIT;
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
VOID
|
||
SepExamineSaclEx(
|
||
IN PACL Sacl,
|
||
IN PACCESS_TOKEN Token,
|
||
IN ACCESS_MASK DesiredAccess,
|
||
IN PIOBJECT_TYPE_LIST ObjectTypeList OPTIONAL,
|
||
IN ULONG ObjectTypeListLength,
|
||
IN BOOLEAN ReturnResultList,
|
||
IN PNTSTATUS AccessStatus,
|
||
IN PACCESS_MASK GrantedAccess,
|
||
IN PSID PrincipalSelfSid,
|
||
OUT PBOOLEAN GenerateSuccessAudit,
|
||
OUT PBOOLEAN GenerateFailureAudit
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will examine the passed Sacl and determine what
|
||
if any action is required based its contents.
|
||
|
||
Note that this routine is not aware of any system state, ie,
|
||
whether or not auditing is currently enabled for either the
|
||
system or this particular object type.
|
||
|
||
Arguments:
|
||
|
||
Sacl - Supplies a pointer to the Sacl to be examined.
|
||
|
||
Token - Supplies the effective token of the caller
|
||
|
||
DesiredAccess - Access that the caller wanted to the object
|
||
|
||
ObjectTypeList - Supplies a list of GUIDs representing the object (and
|
||
sub-objects) being accessed.
|
||
|
||
ObjectTypeListLength - Specifies the number of elements in the ObjectTypeList.
|
||
|
||
ReturnResultList - If true, AccessStatus and GrantedAccess is actually
|
||
an array of entries ObjectTypeListLength elements long.
|
||
|
||
AccessStatus - Specifies STATUS_SUCCESS or other error code to be
|
||
propogated back to the caller
|
||
|
||
PrincipalSelfSid - If the security descriptor is associated with an object
|
||
that represents a principal (for example, a user object),
|
||
the PrincipalSelfSid parameter should be the SID of the object.
|
||
When evaluating access, this SID logically replaces the SID in any ACE
|
||
containing the well-known PRINCIPAL_SELF SID (S-1-5-10).
|
||
|
||
GrantedAccess - Specifies the access granted to the caller.
|
||
|
||
GenerateSuccessAudit - Returns a boolean indicating whether or not
|
||
we should generate a success audit.
|
||
|
||
GenerateFailureAudit - Returns a boolean indicating whether or not
|
||
we should generate a failure audit.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - The operation completed successfully.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
ULONG i, j;
|
||
PVOID Ace;
|
||
ULONG AceCount;
|
||
ACCESS_MASK AccessMask=0;
|
||
UCHAR AceFlags;
|
||
BOOLEAN FailedMaximumAllowed;
|
||
ULONG Index;
|
||
ULONG SuccessIndex;
|
||
#define INVALID_OBJECT_TYPE_LIST_INDEX 0xFFFFFFFF
|
||
|
||
PAGED_CODE();
|
||
|
||
*GenerateSuccessAudit = FALSE;
|
||
*GenerateFailureAudit = FALSE;
|
||
|
||
//
|
||
// If we failed an attempt to open an object for maximumum allowed,
|
||
// then we generate an audit if ANY ACCESS_DENIED audit matching this
|
||
// user's list of sids is found
|
||
//
|
||
|
||
FailedMaximumAllowed = FALSE;
|
||
if (!NT_SUCCESS(*AccessStatus) && (DesiredAccess & MAXIMUM_ALLOWED)) {
|
||
FailedMaximumAllowed = TRUE;
|
||
}
|
||
|
||
//
|
||
// If the Sacl is null, do nothing and return
|
||
//
|
||
|
||
if (Sacl == NULL) {
|
||
return;
|
||
}
|
||
|
||
AceCount = Sacl->AceCount;
|
||
|
||
if (AceCount == 0) {
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// Iterate through the ACEs on the Sacl until either we reach
|
||
// the end or discover that we have to take all possible actions,
|
||
// in which case it doesn't pay to look any further
|
||
//
|
||
|
||
for ( i = 0, Ace = FirstAce( Sacl ) ;
|
||
(i < AceCount) && !((*GenerateSuccessAudit || *GenerateFailureAudit) && ObjectTypeListLength <= 1 );
|
||
i++, Ace = NextAce( Ace ) ) {
|
||
|
||
AceFlags = ((PACE_HEADER)Ace)->AceFlags;
|
||
|
||
if ( AceFlags & INHERIT_ONLY_ACE ) {
|
||
|
||
continue;
|
||
}
|
||
|
||
|
||
Index = INVALID_OBJECT_TYPE_LIST_INDEX;
|
||
|
||
if ( (((PACE_HEADER)Ace)->AceType == SYSTEM_AUDIT_ACE_TYPE) ) {
|
||
|
||
if ( SepSidInToken( Token, PrincipalSelfSid, &((PSYSTEM_AUDIT_ACE)Ace)->SidStart, (BOOLEAN) ((AceFlags & FAILED_ACCESS_ACE_FLAG) != 0) ) ) {
|
||
|
||
AccessMask = ((PSYSTEM_AUDIT_ACE)Ace)->Mask;
|
||
|
||
if (ObjectTypeListLength == 0) {
|
||
|
||
if ( NT_SUCCESS(AccessStatus[0]) ) {
|
||
|
||
if ( ( AceFlags & SUCCESSFUL_ACCESS_ACE_FLAG ) &&
|
||
( AccessMask & GrantedAccess[0] ) ) {
|
||
|
||
*GenerateSuccessAudit = TRUE;
|
||
}
|
||
|
||
} else {
|
||
|
||
if ( ( AceFlags & FAILED_ACCESS_ACE_FLAG ) &&
|
||
( AccessMask & DesiredAccess ) ) {
|
||
|
||
*GenerateFailureAudit = TRUE;
|
||
}
|
||
}
|
||
} else {
|
||
for (j=0; j < ObjectTypeListLength; j++)
|
||
{
|
||
SepSetAuditInfoForObjectType(AceFlags,
|
||
AccessMask,
|
||
DesiredAccess,
|
||
ObjectTypeList,
|
||
ObjectTypeListLength,
|
||
ReturnResultList,
|
||
j,
|
||
AccessStatus,
|
||
GrantedAccess,
|
||
FailedMaximumAllowed,
|
||
GenerateSuccessAudit,
|
||
GenerateFailureAudit
|
||
);
|
||
}
|
||
Index = INVALID_OBJECT_TYPE_LIST_INDEX;
|
||
}
|
||
}
|
||
|
||
//
|
||
// Handle an object specific audit ACE
|
||
//
|
||
} else if ( (((PACE_HEADER)Ace)->AceType == SYSTEM_AUDIT_OBJECT_ACE_TYPE) ) {
|
||
GUID *ObjectTypeInAce;
|
||
|
||
//
|
||
// If no object type is in the ACE,
|
||
// treat this as a normal audit ACE.
|
||
//
|
||
|
||
AccessMask = ((PSYSTEM_AUDIT_OBJECT_ACE)Ace)->Mask;
|
||
ObjectTypeInAce = RtlObjectAceObjectType(Ace);
|
||
|
||
if ( ObjectTypeInAce == NULL ) {
|
||
|
||
if ( SepSidInToken( Token, PrincipalSelfSid, RtlObjectAceSid(Ace), (BOOLEAN)((AceFlags & FAILED_ACCESS_ACE_FLAG) != 0) ) ) {
|
||
|
||
for (j=0; j < ObjectTypeListLength; j++)
|
||
{
|
||
SepSetAuditInfoForObjectType(AceFlags,
|
||
AccessMask,
|
||
DesiredAccess,
|
||
ObjectTypeList,
|
||
ObjectTypeListLength,
|
||
ReturnResultList,
|
||
j,
|
||
AccessStatus,
|
||
GrantedAccess,
|
||
FailedMaximumAllowed,
|
||
GenerateSuccessAudit,
|
||
GenerateFailureAudit
|
||
);
|
||
}
|
||
Index = INVALID_OBJECT_TYPE_LIST_INDEX;
|
||
}
|
||
|
||
//
|
||
// If an object type is in the ACE,
|
||
// Find it in the LocalTypeList before using the ACE.
|
||
//
|
||
} else {
|
||
|
||
if ( SepSidInToken( Token, PrincipalSelfSid, RtlObjectAceSid(Ace), (BOOLEAN)((AceFlags & FAILED_ACCESS_ACE_FLAG) != 0) ) ) {
|
||
|
||
if ( !SepObjectInTypeList( ObjectTypeInAce,
|
||
ObjectTypeList,
|
||
ObjectTypeListLength,
|
||
&Index ) ) {
|
||
|
||
Index = INVALID_OBJECT_TYPE_LIST_INDEX;
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// If the ACE has a matched SID and a matched GUID,
|
||
// handle it.
|
||
//
|
||
|
||
if ( Index != INVALID_OBJECT_TYPE_LIST_INDEX ) {
|
||
|
||
//
|
||
// ASSERT: we have an ACE to be audited.
|
||
//
|
||
// Index is an index into ObjectTypeList of the entry to mark
|
||
// as the GUID needs auditing.
|
||
//
|
||
// SuccessIndex is an index into AccessStatus to determine if
|
||
// a success or failure audit is to be generated
|
||
//
|
||
|
||
SepSetAuditInfoForObjectType(AceFlags,
|
||
AccessMask,
|
||
DesiredAccess,
|
||
ObjectTypeList,
|
||
ObjectTypeListLength,
|
||
ReturnResultList,
|
||
Index,
|
||
AccessStatus,
|
||
GrantedAccess,
|
||
FailedMaximumAllowed,
|
||
GenerateSuccessAudit,
|
||
GenerateFailureAudit
|
||
);
|
||
}
|
||
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
|
||
/******************************************************************************
|
||
* *
|
||
* The following list of privileges is checked at high frequency *
|
||
* during normal operation, and tend to clog up the audit log when *
|
||
* privilege auditing is enabled. The use of these privileges will *
|
||
* not be audited when they are checked singly or in combination with *
|
||
* each other. *
|
||
* *
|
||
* When adding new privileges, be careful to preserve the NULL *
|
||
* privilege pointer marking the end of the array. *
|
||
* *
|
||
* Be sure to update the corresponding array in LSA when adding new *
|
||
* privileges to this list (LsaFilterPrivileges). *
|
||
* *
|
||
******************************************************************************/
|
||
|
||
#ifdef ALLOC_DATA_PRAGMA
|
||
#pragma data_seg("PAGEDATA")
|
||
#pragma const_seg("PAGECONST")
|
||
#endif
|
||
|
||
PLUID const * SepFilterPrivileges = NULL;
|
||
|
||
const PLUID SepFilterPrivilegesLong[] =
|
||
{
|
||
&SeChangeNotifyPrivilege,
|
||
&SeAuditPrivilege,
|
||
&SeCreateTokenPrivilege,
|
||
&SeAssignPrimaryTokenPrivilege,
|
||
&SeBackupPrivilege,
|
||
&SeRestorePrivilege,
|
||
&SeDebugPrivilege,
|
||
NULL
|
||
};
|
||
|
||
/******************************************************************************
|
||
* *
|
||
* The following list of privileges is the same as the above list, except *
|
||
* is missing backup and restore privileges. This allows for auditing *
|
||
* the use of those privileges at the time they are used. *
|
||
* *
|
||
* The use of this list or the one above is determined by settings in *
|
||
* the registry. *
|
||
* *
|
||
******************************************************************************/
|
||
|
||
const PLUID SepFilterPrivilegesShort[] =
|
||
{
|
||
&SeChangeNotifyPrivilege,
|
||
&SeAuditPrivilege,
|
||
&SeCreateTokenPrivilege,
|
||
&SeAssignPrimaryTokenPrivilege,
|
||
&SeDebugPrivilege,
|
||
NULL
|
||
};
|
||
|
||
BOOLEAN
|
||
SepInitializePrivilegeFilter(
|
||
BOOLEAN Verbose
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Initializes SepFilterPrivileges for either normal or verbose auditing.
|
||
|
||
Arguments:
|
||
|
||
Verbose - Whether we want to filter by the short or long privileges
|
||
list. Verbose == TRUE means use the short list.
|
||
|
||
Return Value:
|
||
|
||
TRUE for success, FALSE for failure
|
||
|
||
--*/
|
||
{
|
||
if (Verbose) {
|
||
SepFilterPrivileges = SepFilterPrivilegesShort;
|
||
} else {
|
||
SepFilterPrivileges = SepFilterPrivilegesLong;
|
||
}
|
||
|
||
return( TRUE );
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
SepFilterPrivilegeAudits(
|
||
IN PPRIVILEGE_SET PrivilegeSet
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will filter out a list of privileges as listed in the
|
||
SepFilterPrivileges array.
|
||
|
||
Arguments:
|
||
|
||
Privileges - The privilege set to be audited
|
||
|
||
Return Value:
|
||
|
||
FALSE means that this use of privilege is not to be audited.
|
||
TRUE means that the audit should continue normally.
|
||
|
||
--*/
|
||
|
||
{
|
||
PLUID const *Privilege;
|
||
ULONG Match = 0;
|
||
ULONG i;
|
||
|
||
PAGED_CODE();
|
||
|
||
if ( !ARGUMENT_PRESENT(PrivilegeSet) ||
|
||
(PrivilegeSet->PrivilegeCount == 0) ) {
|
||
return( FALSE );
|
||
}
|
||
|
||
for (i=0; i<PrivilegeSet->PrivilegeCount; i++) {
|
||
|
||
Privilege = SepFilterPrivileges;
|
||
|
||
do {
|
||
|
||
if ( RtlEqualLuid( &PrivilegeSet->Privilege[i].Luid, *Privilege )) {
|
||
|
||
Match++;
|
||
break;
|
||
}
|
||
|
||
} while ( *++Privilege != NULL );
|
||
}
|
||
|
||
if ( Match == PrivilegeSet->PrivilegeCount ) {
|
||
|
||
return( FALSE );
|
||
|
||
} else {
|
||
|
||
return( TRUE );
|
||
}
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
SeAuditingFileOrGlobalEvents(
|
||
IN BOOLEAN AccessGranted,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
||
IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is to be called by a file system to quickly determine
|
||
if we are auditing file open events. This allows the file system
|
||
to avoid the often considerable setup involved in generating an audit.
|
||
|
||
Arguments:
|
||
|
||
AccessGranted - Supplies whether the access attempt was successful
|
||
or a failure.
|
||
|
||
Return Value:
|
||
|
||
Boolean - TRUE if events of type AccessGranted are being audited, FALSE
|
||
otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PISECURITY_DESCRIPTOR ISecurityDescriptor = (PISECURITY_DESCRIPTOR) SecurityDescriptor;
|
||
|
||
PAGED_CODE();
|
||
|
||
if ( ((PTOKEN)EffectiveToken( SubjectSecurityContext ))->AuditData != NULL) {
|
||
return( TRUE );
|
||
}
|
||
|
||
if ( RtlpSaclAddrSecurityDescriptor( ISecurityDescriptor ) == NULL ) {
|
||
|
||
return( FALSE );
|
||
}
|
||
|
||
return( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted ) );
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
SeAuditingFileEvents(
|
||
IN BOOLEAN AccessGranted,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is to be called by a file system to quickly determine
|
||
if we are auditing file open events. This allows the file system
|
||
to avoid the often considerable setup involved in generating an audit.
|
||
|
||
Arguments:
|
||
|
||
AccessGranted - Supplies whether the access attempt was successful
|
||
or a failure.
|
||
|
||
Return Value:
|
||
|
||
Boolean - TRUE if events of type AccessGranted are being audited, FALSE
|
||
otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
PAGED_CODE();
|
||
|
||
UNREFERENCED_PARAMETER( SecurityDescriptor );
|
||
|
||
return( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted ) );
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
SeAuditingHardLinkEvents(
|
||
IN BOOLEAN AccessGranted,
|
||
IN PSECURITY_DESCRIPTOR SecurityDescriptor
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is to be called by a file system to quickly determine
|
||
if we are auditing hard link creation.
|
||
|
||
Arguments:
|
||
|
||
AccessGranted - Supplies whether the access attempt was successful
|
||
or a failure.
|
||
|
||
SecurityDescriptor - SD of the linked file.
|
||
|
||
Return Value:
|
||
|
||
Boolean - TRUE if events of type AccessGranted are being audited, FALSE
|
||
otherwise.
|
||
|
||
--*/
|
||
|
||
{
|
||
|
||
PISECURITY_DESCRIPTOR pSD = SecurityDescriptor;
|
||
PACL Sacl = RtlpSaclAddrSecurityDescriptor( pSD );
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Audit hard link creation if object access auditing is on and the original file
|
||
// has a non empty SACL.
|
||
//
|
||
|
||
if ( (SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted )) &&
|
||
(NULL != Sacl) &&
|
||
(0 != Sacl->AceCount)) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|