mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4049 lines
117 KiB
4049 lines
117 KiB
/*++
|
|
|
|
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 "tokenp.h"
|
|
#include "adt.h"
|
|
#include "adtp.h"
|
|
|
|
VOID
|
|
SepProbeAndCaptureString_U (
|
|
IN PUNICODE_STRING SourceString,
|
|
OUT PUNICODE_STRING *DestString
|
|
);
|
|
|
|
VOID
|
|
SepFreeCapturedString(
|
|
IN PUNICODE_STRING CapturedString
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE,NtAccessCheckAndAuditAlarm)
|
|
#pragma alloc_text(PAGE,NtCloseObjectAuditAlarm)
|
|
#pragma alloc_text(PAGE,NtDeleteObjectAuditAlarm)
|
|
#pragma alloc_text(PAGE,NtOpenObjectAuditAlarm)
|
|
#pragma alloc_text(PAGE,NtPrivilegeObjectAuditAlarm)
|
|
#pragma alloc_text(PAGE,NtPrivilegedServiceAuditAlarm)
|
|
#pragma alloc_text(PAGE,SeAuditHandleCreation)
|
|
#pragma alloc_text(PAGE,SeAuditingFileEvents)
|
|
#pragma alloc_text(PAGE,SeCheckAuditPrivilege)
|
|
#pragma alloc_text(PAGE,SeCloseObjectAuditAlarm)
|
|
#pragma alloc_text(PAGE,SeDeleteObjectAuditAlarm)
|
|
#pragma alloc_text(PAGE,SeCreateObjectAuditAlarm)
|
|
#pragma alloc_text(PAGE,SeObjectReferenceAuditAlarm)
|
|
#pragma alloc_text(PAGE,SeOpenObjectAuditAlarm)
|
|
#pragma alloc_text(PAGE,SePrivilegeObjectAuditAlarm)
|
|
#pragma alloc_text(PAGE,SePrivilegedServiceAuditAlarm)
|
|
#pragma alloc_text(PAGE,SeTraverseAuditAlarm)
|
|
#pragma alloc_text(PAGE,SepExamineSacl)
|
|
#pragma alloc_text(PAGE,SepFilterPrivilegeAudits)
|
|
#pragma alloc_text(PAGE,SepFreeCapturedString)
|
|
#pragma alloc_text(PAGE,SepProbeAndCaptureString_U)
|
|
#pragma alloc_text(PAGE,SepSinglePrivilegeCheck)
|
|
#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, // BUGWARNING need service name
|
|
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.
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
ProbeForRead( SourceString, sizeof( UNICODE_STRING ), sizeof( ULONG ) );
|
|
|
|
*DestString = NULL;
|
|
|
|
//
|
|
// Probe the pointer to the buffer to ensure that the
|
|
// characters in the buffer are readable by the caller.
|
|
// If so, then try to allocate a pool buffer and copy the
|
|
// buffer into it.
|
|
//
|
|
|
|
ProbeForRead((PVOID)SourceString->Buffer,
|
|
SourceString->Length,
|
|
sizeof(WCHAR));
|
|
|
|
|
|
*DestString = ExAllocatePoolWithTag( PagedPool,
|
|
sizeof( UNICODE_STRING ) + SourceString->MaximumLength,
|
|
'sUeS'
|
|
);
|
|
|
|
if ( *DestString == NULL ) {
|
|
return;
|
|
}
|
|
|
|
(*DestString)->Buffer = (PWSTR)((PCHAR)(*DestString) + sizeof( UNICODE_STRING ));
|
|
(*DestString)->MaximumLength = SourceString->MaximumLength;
|
|
|
|
RtlCopyUnicodeString(
|
|
*DestString,
|
|
SourceString
|
|
);
|
|
|
|
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;
|
|
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
|
|
SepTokenObjectType, // 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 );
|
|
|
|
ProbeForRead(
|
|
Privileges,
|
|
sizeof(PRIVILEGE_SET),
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
PrivilegeParameterLength = (ULONG)sizeof(PRIVILEGE_SET) +
|
|
((Privileges->PrivilegeCount - ANYSIZE_ARRAY) *
|
|
(ULONG)sizeof(LUID_AND_ATTRIBUTES) );
|
|
|
|
ProbeForRead(
|
|
Privileges,
|
|
PrivilegeParameterLength,
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
CapturedPrivileges = ExAllocatePoolWithTag( PagedPool,
|
|
PrivilegeParameterLength,
|
|
'rPeS'
|
|
);
|
|
|
|
if (CapturedPrivileges != NULL) {
|
|
|
|
RtlMoveMemory ( CapturedPrivileges,
|
|
Privileges,
|
|
PrivilegeParameterLength );
|
|
}
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
if (CapturedPrivileges != NULL) {
|
|
ExFreePool( CapturedPrivileges );
|
|
}
|
|
|
|
if (CapturedSubsystemName != NULL) {
|
|
SepFreeCapturedString ( CapturedSubsystemName );
|
|
}
|
|
|
|
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
|
|
|
ObDereferenceObject( (PVOID)Token );
|
|
|
|
return GetExceptionCode();
|
|
|
|
}
|
|
|
|
//
|
|
// 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 (
|
|
&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;
|
|
|
|
PAGED_CODE();
|
|
|
|
PreviousMode = KeGetPreviousMode();
|
|
|
|
ASSERT(PreviousMode != KernelMode);
|
|
|
|
Status = ObReferenceObjectByHandle(
|
|
ClientToken, // Handle
|
|
TOKEN_QUERY, // DesiredAccess
|
|
SepTokenObjectType, // 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 );
|
|
|
|
}
|
|
|
|
ProbeForRead(
|
|
Privileges,
|
|
sizeof(PRIVILEGE_SET),
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
PrivilegeParameterLength = (ULONG)sizeof(PRIVILEGE_SET) +
|
|
((Privileges->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) {
|
|
|
|
RtlMoveMemory ( CapturedPrivileges,
|
|
Privileges,
|
|
PrivilegeParameterLength );
|
|
|
|
}
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
if (CapturedSubsystemName != NULL) {
|
|
SepFreeCapturedString ( CapturedSubsystemName );
|
|
}
|
|
|
|
if (CapturedServiceName != NULL) {
|
|
SepFreeCapturedString ( CapturedServiceName );
|
|
}
|
|
|
|
if (CapturedPrivileges != NULL) {
|
|
ExFreePool ( CapturedPrivileges );
|
|
}
|
|
|
|
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
|
|
|
ObDereferenceObject( (PVOID)Token );
|
|
|
|
return GetExceptionCode();
|
|
|
|
}
|
|
|
|
//
|
|
// 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 (
|
|
&SeSubsystemName,
|
|
ServiceName,
|
|
SubjectSecurityContext->ClientToken,
|
|
SubjectSecurityContext->PrimaryToken,
|
|
Privileges,
|
|
AccessGranted
|
|
);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
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:
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
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.
|
|
|
|
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;
|
|
|
|
ACCESS_MASK LocalGrantedAccess = (ACCESS_MASK)0;
|
|
BOOLEAN LocalGenerateOnClose = FALSE;
|
|
|
|
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;
|
|
|
|
ACCESS_MASK PreviouslyGrantedAccess = (ACCESS_MASK)0;
|
|
GENERIC_MAPPING LocalGenericMapping;
|
|
|
|
PPRIVILEGE_SET PrivilegeSet = NULL;
|
|
|
|
BOOLEAN Result;
|
|
|
|
BOOLEAN AccessGranted = FALSE;
|
|
BOOLEAN GenerateAudit = FALSE;
|
|
BOOLEAN GenerateAlarm = FALSE;
|
|
LUID OperationId;
|
|
BOOLEAN AuditPerformed;
|
|
|
|
PAGED_CODE();
|
|
|
|
PreviousMode = KeGetPreviousMode();
|
|
|
|
ASSERT( PreviousMode != KernelMode );
|
|
|
|
//
|
|
// Capture the subject Context
|
|
//
|
|
|
|
SeCaptureSubjectContext ( &SubjectSecurityContext );
|
|
|
|
//
|
|
// Make sure we're impersonating a client...
|
|
//
|
|
|
|
if ( (SubjectSecurityContext.ClientToken == NULL) ) {
|
|
|
|
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
|
return(STATUS_NO_IMPERSONATION_TOKEN);
|
|
|
|
}
|
|
|
|
//
|
|
// ...and at a high enough impersonation level
|
|
//
|
|
|
|
if (SubjectSecurityContext.ImpersonationLevel < SecurityIdentification) {
|
|
|
|
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
|
return(STATUS_BAD_IMPERSONATION_LEVEL);
|
|
|
|
}
|
|
|
|
try {
|
|
|
|
ProbeForWriteUlong((PULONG)AccessStatus);
|
|
ProbeForWriteUlong((PULONG)GrantedAccess);
|
|
|
|
ProbeForRead(
|
|
GenericMapping,
|
|
sizeof(GENERIC_MAPPING),
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
LocalGenericMapping = *GenericMapping;
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
SeReleaseSubjectContext( &SubjectSecurityContext );
|
|
return( GetExceptionCode() );
|
|
|
|
}
|
|
|
|
//
|
|
// Check for SeAuditPrivilege
|
|
//
|
|
|
|
Result = SeCheckAuditPrivilege (
|
|
&SubjectSecurityContext,
|
|
PreviousMode
|
|
);
|
|
|
|
if (!Result) {
|
|
|
|
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
|
|
|
try {
|
|
|
|
*AccessStatus = STATUS_ACCESS_DENIED;
|
|
*GrantedAccess = (ACCESS_MASK)0;
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
return( GetExceptionCode() );
|
|
|
|
}
|
|
|
|
return(STATUS_PRIVILEGE_NOT_HELD);
|
|
}
|
|
|
|
if (DesiredAccess &
|
|
( GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL )) {
|
|
|
|
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
|
|
|
return(STATUS_GENERIC_NOT_MAPPED);
|
|
}
|
|
|
|
//
|
|
// 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) {
|
|
|
|
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
|
|
|
try {
|
|
|
|
*AccessStatus = Status;
|
|
*GrantedAccess = (ACCESS_MASK)0;
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
return( GetExceptionCode() );
|
|
|
|
}
|
|
|
|
if ( CapturedSecurityDescriptor == NULL ) {
|
|
|
|
return( STATUS_INVALID_SECURITY_DESCR );
|
|
}
|
|
|
|
return( STATUS_SUCCESS );
|
|
}
|
|
|
|
//
|
|
// A valid security descriptor must have an owner and a group
|
|
//
|
|
|
|
if ( SepOwnerAddrSecurityDescriptor(
|
|
(PISECURITY_DESCRIPTOR)CapturedSecurityDescriptor
|
|
) == NULL ||
|
|
SepGroupAddrSecurityDescriptor(
|
|
(PISECURITY_DESCRIPTOR)CapturedSecurityDescriptor
|
|
) == NULL ) {
|
|
|
|
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
|
|
|
SeReleaseSecurityDescriptor (
|
|
CapturedSecurityDescriptor,
|
|
PreviousMode,
|
|
FALSE
|
|
);
|
|
|
|
return( STATUS_INVALID_SECURITY_DESCR );
|
|
}
|
|
|
|
//
|
|
// 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) {
|
|
|
|
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
|
|
|
if (CapturedSubsystemName != NULL) {
|
|
SepFreeCapturedString( CapturedSubsystemName );
|
|
}
|
|
|
|
if (CapturedObjectTypeName != NULL) {
|
|
SepFreeCapturedString( CapturedObjectTypeName );
|
|
}
|
|
|
|
if (CapturedObjectName != NULL) {
|
|
SepFreeCapturedString( CapturedObjectName );
|
|
}
|
|
|
|
SeReleaseSecurityDescriptor (
|
|
CapturedSecurityDescriptor,
|
|
PreviousMode,
|
|
FALSE
|
|
);
|
|
|
|
return( GetExceptionCode() );
|
|
|
|
}
|
|
|
|
//
|
|
// 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 )) {
|
|
|
|
//
|
|
// 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;
|
|
Status = STATUS_SUCCESS;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Finally, do the access check
|
|
//
|
|
|
|
AccessGranted = SepAccessCheck ( CapturedSecurityDescriptor,
|
|
SubjectSecurityContext.PrimaryToken,
|
|
SubjectSecurityContext.ClientToken,
|
|
DesiredAccess,
|
|
&LocalGenericMapping,
|
|
PreviouslyGrantedAccess,
|
|
PreviousMode,
|
|
&LocalGrantedAccess,
|
|
NULL,
|
|
&Status
|
|
);
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// sound the alarms...
|
|
//
|
|
|
|
if ( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted )) {
|
|
|
|
SepExamineSacl(
|
|
SepSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)CapturedSecurityDescriptor ),
|
|
EffectiveToken( &SubjectSecurityContext ),
|
|
(AccessGranted ? LocalGrantedAccess : (DesiredAccess | PreviouslyGrantedAccess)),
|
|
AccessGranted,
|
|
&GenerateAudit,
|
|
&GenerateAlarm
|
|
);
|
|
|
|
}
|
|
|
|
if (GenerateAudit || GenerateAlarm) {
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
ExAllocateLocallyUniqueId( &OperationId );
|
|
|
|
AuditPerformed = SepAdtOpenObjectAuditAlarm (
|
|
CapturedSubsystemName,
|
|
AccessGranted ? &HandleId : NULL, // Don't audit handle if failure
|
|
CapturedObjectTypeName,
|
|
0, // IN PVOID Object OPTIONAL,
|
|
CapturedObjectName,
|
|
SubjectSecurityContext.ClientToken,
|
|
SubjectSecurityContext.PrimaryToken,
|
|
DesiredAccess,
|
|
LocalGrantedAccess,
|
|
&OperationId,
|
|
PrivilegeSet,
|
|
ObjectCreation,
|
|
AccessGranted,
|
|
GenerateAudit,
|
|
GenerateAlarm,
|
|
PsProcessAuditId( PsGetCurrentProcess() )
|
|
);
|
|
} 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 );
|
|
|
|
//
|
|
// 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 );
|
|
}
|
|
|
|
try {
|
|
*AccessStatus = Status;
|
|
*GrantedAccess = LocalGrantedAccess;
|
|
*GenerateOnClose = LocalGenerateOnClose;
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
return( GetExceptionCode() );
|
|
}
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
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;
|
|
|
|
PTOKEN Token;
|
|
|
|
PAGED_CODE();
|
|
|
|
PreviousMode = KeGetPreviousMode();
|
|
|
|
ASSERT( PreviousMode != KernelMode );
|
|
|
|
Status = ObReferenceObjectByHandle( ClientToken, // Handle
|
|
TOKEN_QUERY, // DesiredAccess
|
|
SepTokenObjectType, // 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 {
|
|
|
|
if (ARGUMENT_PRESENT(Privileges)) {
|
|
|
|
ProbeForRead(
|
|
Privileges,
|
|
sizeof(PRIVILEGE_SET),
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
PrivilegeParameterLength = (ULONG)sizeof(PRIVILEGE_SET) +
|
|
((Privileges->PrivilegeCount - ANYSIZE_ARRAY) *
|
|
(ULONG)sizeof(LUID_AND_ATTRIBUTES) );
|
|
|
|
ProbeForRead(
|
|
Privileges,
|
|
PrivilegeParameterLength,
|
|
sizeof(ULONG)
|
|
);
|
|
|
|
CapturedPrivileges = ExAllocatePoolWithTag( PagedPool,
|
|
PrivilegeParameterLength,
|
|
'rPeS'
|
|
);
|
|
|
|
if (CapturedPrivileges != NULL) {
|
|
|
|
RtlMoveMemory ( CapturedPrivileges,
|
|
Privileges,
|
|
PrivilegeParameterLength );
|
|
} else {
|
|
|
|
SeReleaseSecurityDescriptor ( CapturedSecurityDescriptor,
|
|
PreviousMode,
|
|
FALSE );
|
|
|
|
ObDereferenceObject( (PVOID)Token );
|
|
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
|
return( STATUS_INSUFFICIENT_RESOURCES );
|
|
}
|
|
|
|
|
|
}
|
|
|
|
if (ARGUMENT_PRESENT( HandleId )) {
|
|
|
|
ProbeForRead( (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) {
|
|
|
|
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 GetExceptionCode();
|
|
|
|
}
|
|
|
|
if ( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted) ) {
|
|
|
|
SepExamineSacl(
|
|
SepSaclAddrSecurityDescriptor( (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,
|
|
NULL,
|
|
CapturedObjectName,
|
|
Token,
|
|
SubjectSecurityContext.PrimaryToken,
|
|
DesiredAccess,
|
|
GrantedAccess,
|
|
NULL,
|
|
CapturedPrivileges,
|
|
ObjectCreation,
|
|
AccessGranted,
|
|
GenerateAudit,
|
|
GenerateAlarm,
|
|
PsProcessAuditId( PsGetCurrentProcess() )
|
|
);
|
|
|
|
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;
|
|
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 ));
|
|
|
|
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
|
|
|
try {
|
|
|
|
SepProbeAndCaptureString_U ( SubsystemName,
|
|
&CapturedSubsystemName );
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
if ( CapturedSubsystemName != NULL ) {
|
|
SepFreeCapturedString( CapturedSubsystemName );
|
|
}
|
|
|
|
ExFreePool( CapturedUserSid );
|
|
return GetExceptionCode();
|
|
|
|
}
|
|
|
|
//
|
|
// This routine will check to see if auditing is enabled
|
|
//
|
|
|
|
SepAdtCloseObjectAuditAlarm ( CapturedSubsystemName,
|
|
HandleId,
|
|
NULL,
|
|
CapturedUserSid,
|
|
SepTokenAuthenticationId( EffectiveToken( &SubjectSecurityContext ))
|
|
);
|
|
|
|
SepFreeCapturedString( CapturedSubsystemName );
|
|
|
|
ExFreePool( CapturedUserSid );
|
|
|
|
return(STATUS_SUCCESS);
|
|
}
|
|
|
|
|
|
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 ));
|
|
|
|
SeReleaseSubjectContext ( &SubjectSecurityContext );
|
|
|
|
try {
|
|
|
|
SepProbeAndCaptureString_U ( SubsystemName,
|
|
&CapturedSubsystemName );
|
|
|
|
} except (EXCEPTION_EXECUTE_HANDLER) {
|
|
|
|
if ( CapturedSubsystemName != NULL ) {
|
|
SepFreeCapturedString( CapturedSubsystemName );
|
|
}
|
|
|
|
ExFreePool( CapturedUserSid );
|
|
return GetExceptionCode();
|
|
|
|
}
|
|
|
|
//
|
|
// This routine will check to see if auditing is enabled
|
|
//
|
|
|
|
SepAdtDeleteObjectAuditAlarm ( CapturedSubsystemName,
|
|
HandleId,
|
|
NULL,
|
|
CapturedUserSid,
|
|
SepTokenAuthenticationId( EffectiveToken( &SubjectSecurityContext ))
|
|
);
|
|
|
|
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();
|
|
|
|
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(
|
|
SepSaclAddrSecurityDescriptor( (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 ( &SeSubsystemName,
|
|
NULL,
|
|
LocalObjectTypeName,
|
|
NULL,
|
|
ObjectName,
|
|
AccessState->SubjectSecurityContext.ClientToken,
|
|
AccessState->SubjectSecurityContext.PrimaryToken,
|
|
AccessState->OriginalDesiredAccess,
|
|
AccessState->PreviouslyGrantedAccess,
|
|
&AccessState->OperationID,
|
|
AuxData->PrivilegesUsed,
|
|
FALSE,
|
|
FALSE,
|
|
TRUE,
|
|
FALSE,
|
|
AccessState->SubjectSecurityContext.ProcessAuditId );
|
|
} else {
|
|
|
|
//
|
|
// Copy all the stuff we're going to need into the
|
|
// AccessState and return.
|
|
//
|
|
|
|
if ( ObjectName != NULL ) {
|
|
|
|
AccessState->ObjectName.Buffer = ExAllocatePool( PagedPool,ObjectName->MaximumLength );
|
|
if (AccessState->ObjectName.Buffer != NULL) {
|
|
|
|
AccessState->ObjectName.MaximumLength = ObjectName->MaximumLength;
|
|
RtlCopyUnicodeString( &AccessState->ObjectName, ObjectName );
|
|
}
|
|
}
|
|
|
|
if ( LocalObjectTypeName != NULL ) {
|
|
|
|
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();
|
|
|
|
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(
|
|
SepSaclAddrSecurityDescriptor( (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 ( &SeSubsystemName,
|
|
NULL,
|
|
LocalObjectTypeName,
|
|
NULL,
|
|
ObjectName,
|
|
AccessState->SubjectSecurityContext.ClientToken,
|
|
AccessState->SubjectSecurityContext.PrimaryToken,
|
|
AccessState->OriginalDesiredAccess,
|
|
AccessState->PreviouslyGrantedAccess,
|
|
&AccessState->OperationID,
|
|
AuxData->PrivilegesUsed,
|
|
FALSE,
|
|
FALSE,
|
|
TRUE,
|
|
FALSE,
|
|
AccessState->SubjectSecurityContext.ProcessAuditId );
|
|
} else {
|
|
|
|
//
|
|
// Generate the delete audit first
|
|
//
|
|
|
|
SepAdtOpenObjectForDeleteAuditAlarm ( &SeSubsystemName,
|
|
NULL,
|
|
LocalObjectTypeName,
|
|
NULL,
|
|
ObjectName,
|
|
AccessState->SubjectSecurityContext.ClientToken,
|
|
AccessState->SubjectSecurityContext.PrimaryToken,
|
|
AccessState->OriginalDesiredAccess,
|
|
AccessState->PreviouslyGrantedAccess,
|
|
&AccessState->OperationID,
|
|
AuxData->PrivilegesUsed,
|
|
FALSE,
|
|
TRUE,
|
|
TRUE,
|
|
FALSE,
|
|
AccessState->SubjectSecurityContext.ProcessAuditId );
|
|
|
|
//
|
|
// Copy all the stuff we're going to need into the
|
|
// AccessState and return.
|
|
//
|
|
|
|
if ( ObjectName != NULL ) {
|
|
|
|
AccessState->ObjectName.Buffer = ExAllocatePool( PagedPool,ObjectName->MaximumLength );
|
|
if (AccessState->ObjectName.Buffer != NULL) {
|
|
|
|
AccessState->ObjectName.MaximumLength = ObjectName->MaximumLength;
|
|
RtlCopyUnicodeString( &AccessState->ObjectName, ObjectName );
|
|
}
|
|
}
|
|
|
|
if ( LocalObjectTypeName != NULL ) {
|
|
|
|
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;
|
|
}
|
|
|
|
#if 0
|
|
|
|
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:
|
|
|
|
SeOpenObjectAuditForDeleteAlarm is used by the file systems for files
|
|
that are opened with the FILE_DELETE_ON_CLOSE bit specified. Since a
|
|
handle may not be created, it is important to generate the deletiong
|
|
audit when the file is opened. No messages will be
|
|
generated for Kernel mode accesses.
|
|
|
|
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;
|
|
PTOKEN Token;
|
|
ACCESS_MASK MappedGrantMask = (ACCESS_MASK)0;
|
|
ACCESS_MASK MappedDenyMask = (ACCESS_MASK)0;
|
|
PAUX_ACCESS_DATA AuxData;
|
|
|
|
PAGED_CODE();
|
|
|
|
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(
|
|
SepSaclAddrSecurityDescriptor( (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;
|
|
}
|
|
|
|
//
|
|
// Do the audit here.
|
|
//
|
|
|
|
//
|
|
// BUGBUG: call SepAdtOpenObjectForDeleteAuditAlarm here
|
|
// instead.
|
|
//
|
|
|
|
SepAdtOpenObjectAuditAlarm ( &SeSubsystemName,
|
|
NULL,
|
|
LocalObjectTypeName,
|
|
NULL,
|
|
ObjectName,
|
|
AccessState->SubjectSecurityContext.ClientToken,
|
|
AccessState->SubjectSecurityContext.PrimaryToken,
|
|
AccessState->OriginalDesiredAccess,
|
|
AccessState->PreviouslyGrantedAccess,
|
|
&AccessState->OperationID,
|
|
AuxData->PrivilegesUsed,
|
|
FALSE,
|
|
FALSE,
|
|
TRUE,
|
|
FALSE,
|
|
AccessState->SubjectSecurityContext.ProcessAuditId );
|
|
|
|
if ( ObjectNameInfo != NULL ) {
|
|
|
|
ExFreePool( ObjectNameInfo );
|
|
}
|
|
|
|
if ( ObjectTypeNameInfo != NULL ) {
|
|
|
|
ExFreePool( ObjectTypeNameInfo );
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
|
|
VOID
|
|
SeTraverseAuditAlarm(
|
|
IN PLUID OperationID,
|
|
IN PVOID DirectoryObject,
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
|
|
IN BOOLEAN SubjectContextLocked,
|
|
IN ACCESS_MASK TraverseAccess,
|
|
IN PPRIVILEGE_SET Privileges OPTIONAL,
|
|
IN BOOLEAN AccessGranted,
|
|
IN KPROCESSOR_MODE AccessMode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to audit directory traverse operations
|
|
specifically. It should be called by parse procedures as they traverse
|
|
directories as part of their operation.
|
|
|
|
Arguments:
|
|
|
|
OperationID - LUID identifying the operation in progress
|
|
|
|
DirectoryObject - Pointer to the directory being traversed.
|
|
|
|
SecurityDescriptor - The security descriptor (if any) attached to the
|
|
directory being traversed.
|
|
|
|
SubjectSecurityContext - Security context of the client.
|
|
|
|
SubjectContextLocked - Supplies whether the SubjectContext is locked
|
|
for shared access.
|
|
|
|
TraverseAccess - Mask to indicate the traverse access for this object
|
|
type.
|
|
|
|
Privileges - Optional parameter to indicate any privilges that the
|
|
subject may have used to gain access to the object.
|
|
|
|
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.
|
|
|
|
Return value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
#if 0
|
|
BOOLEAN GenerateAudit = FALSE;
|
|
BOOLEAN GenerateAlarm = FALSE;
|
|
|
|
if (AccessMode == KernelMode) {
|
|
return;
|
|
}
|
|
|
|
if ((SeAuditingState[AuditEventTraverse].AuditOnSuccess && AccessGranted) ||
|
|
SeAuditingState[AuditEventTraverse].AuditOnFailure && !AccessGranted) {
|
|
|
|
if ( SecurityDescriptor != NULL ) {
|
|
|
|
if ( !SubjectContextLocked ) {
|
|
SeLockSubjectContext( SubjectSecurityContext );
|
|
}
|
|
|
|
SepExamineSacl(
|
|
SepSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor ),
|
|
EffectiveToken( SubjectSecurityContext ),
|
|
TraverseAccess,
|
|
AccessGranted,
|
|
&GenerateAudit,
|
|
&GenerateAlarm
|
|
);
|
|
|
|
if (GenerateAudit || GenerateAlarm) {
|
|
|
|
SepAdtTraverseAuditAlarm(
|
|
OperationID,
|
|
DirectoryObject,
|
|
SepTokenUserSid(EffectiveToken( SubjectSecurityContext )),
|
|
SepTokenAuthenticationId(EffectiveToken( SubjectSecurityContext )),
|
|
TraverseAccess,
|
|
Privileges,
|
|
AccessGranted,
|
|
GenerateAudit,
|
|
GenerateAlarm
|
|
);
|
|
}
|
|
|
|
if ( !SubjectContextLocked ) {
|
|
SeUnlockSubjectContext( SubjectSecurityContext );
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
//
|
|
#if 0
|
|
|
|
VOID
|
|
SeCreateInstanceAuditAlarm(
|
|
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;
|
|
|
|
if ( AccessMode == KernelMode ) {
|
|
return;
|
|
}
|
|
|
|
if ( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted )) {
|
|
|
|
if ( SecurityDescriptor != NULL ) {
|
|
|
|
SepExamineSacl(
|
|
SepSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor ),
|
|
EffectiveToken( SubjectSecurityContext ),
|
|
DesiredAccess,
|
|
AccessGranted,
|
|
&GenerateAudit,
|
|
&GenerateAlarm
|
|
);
|
|
|
|
if ( GenerateAudit || GenerateAlarm ) {
|
|
|
|
SepAdtCreateInstanceAuditAlarm(
|
|
OperationID,
|
|
Object,
|
|
SepTokenUserSid(EffectiveToken( SubjectSecurityContext )),
|
|
SepTokenAuthenticationId(EffectiveToken( SubjectSecurityContext )),
|
|
DesiredAccess,
|
|
Privileges,
|
|
AccessGranted,
|
|
GenerateAudit,
|
|
GenerateAlarm
|
|
);
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
VOID
|
|
SeCreateObjectAuditAlarm(
|
|
IN PLUID OperationID OPTIONAL,
|
|
IN PVOID DirectoryObject,
|
|
IN PUNICODE_STRING ComponentName,
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN PPRIVILEGE_SET Privileges OPTIONAL,
|
|
IN BOOLEAN AccessGranted,
|
|
OUT PBOOLEAN AuditPerformed,
|
|
IN KPROCESSOR_MODE AccessMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Audits the creation of an object in a directory.
|
|
|
|
Arguments:
|
|
|
|
OperationID - Optionally supplies the LUID representing the operation
|
|
id for this operation.
|
|
|
|
DirectoryObject - Provides a pointer to the directory object being
|
|
examined.
|
|
|
|
ComponentName - Provides a pointer to a Unicode string containing the
|
|
relative name of the object being created.
|
|
|
|
SecurityDescriptor - The security descriptor for the passed direcctory.
|
|
|
|
SubjectSecurityContext - The current subject context.
|
|
|
|
DesiredAccess - The desired access to the directory.
|
|
|
|
Privileges - Returns any privileges that were used for the access attempt.
|
|
|
|
AccessGranted - Returns whether or not the access was successful.
|
|
|
|
AuditPerformed - Returns whether or not auditing was performed.
|
|
|
|
AccessMode - The previous mode.
|
|
|
|
Return Value:
|
|
|
|
return-value - Description of conditions needed to return value. - or -
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
#if 0
|
|
|
|
BOOLEAN GenerateAudit = FALSE;
|
|
BOOLEAN GenerateAlarm = FALSE;
|
|
PUNICODE_STRING DirectoryName;
|
|
POBJECT_NAME_INFORMATION ObjectNameInformation = NULL;
|
|
|
|
UNREFERENCED_PARAMETER( DirectoryObject );
|
|
UNREFERENCED_PARAMETER( Privileges );
|
|
|
|
if (AccessMode == KernelMode) {
|
|
return;
|
|
}
|
|
|
|
if ( SecurityDescriptor != NULL ) {
|
|
|
|
if ( SepAdtAuditThisEvent( AuditCategoryObjectAccess, &AccessGranted )) {
|
|
|
|
SepExamineSacl(
|
|
SepSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor ),
|
|
EffectiveToken( SubjectSecurityContext ),
|
|
DesiredAccess,
|
|
AccessGranted,
|
|
&GenerateAudit,
|
|
&GenerateAlarm
|
|
);
|
|
|
|
if ( GenerateAudit || GenerateAlarm ) {
|
|
|
|
//
|
|
// Call ob for the name of the directory.
|
|
//
|
|
|
|
ObjectNameInformation = SepQueryNameString( DirectoryObject );
|
|
|
|
if ( ObjectNameInformation != NULL ) {
|
|
|
|
DirectoryName = &ObjectNameInformation->Name;
|
|
}
|
|
|
|
SepAdtCreateObjectAuditAlarm(
|
|
OperationID,
|
|
DirectoryName,
|
|
ComponentName,
|
|
SepTokenUserSid(EffectiveToken( SubjectSecurityContext )),
|
|
SepTokenAuthenticationId( EffectiveToken( SubjectSecurityContext )),
|
|
DesiredAccess,
|
|
AccessGranted,
|
|
GenerateAudit,
|
|
GenerateAlarm
|
|
);
|
|
|
|
*AuditPerformed = TRUE;
|
|
|
|
if ( DirectoryName != NULL ) {
|
|
|
|
ExFreePool( DirectoryName );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
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();
|
|
|
|
if (AccessMode == KernelMode) {
|
|
return;
|
|
}
|
|
|
|
if ( SecurityDescriptor != NULL ) {
|
|
|
|
if ( SepAdtAuditThisEvent( AuditCategoryDetailedTracking, &AccessGranted )) {
|
|
|
|
SepExamineSacl(
|
|
SepSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor ),
|
|
EffectiveToken( SubjectSecurityContext ),
|
|
DesiredAccess,
|
|
AccessGranted,
|
|
&GenerateAudit,
|
|
&GenerateAlarm
|
|
);
|
|
|
|
if ( GenerateAudit || GenerateAlarm ) {
|
|
|
|
SepAdtObjectReferenceAuditAlarm(
|
|
OperationID,
|
|
Object,
|
|
SubjectSecurityContext,
|
|
DesiredAccess,
|
|
Privileges,
|
|
AccessGranted,
|
|
GenerateAudit,
|
|
GenerateAlarm
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
//
|
|
#if 0
|
|
|
|
VOID
|
|
SeImplicitObjectAuditAlarm(
|
|
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:
|
|
|
|
This routine is used to generate audit messages for implicit
|
|
references to objects. This includes both objects that may
|
|
be referenced without either a name or a handle (such as
|
|
the current process), and references to objects where access
|
|
checking is being performed but the object is not being
|
|
opened or created.
|
|
|
|
For example, the file system may wish to determine if a
|
|
user has a FILE_LIST_DIRECTORY access to a directory, but
|
|
it is not opening the directory or creating a handle to it.
|
|
|
|
Arguments:
|
|
|
|
OperationID - If this reference is part of another, more complex
|
|
object access, pass in the OperationID corresponding to that access.
|
|
|
|
Object - The object being accessed.
|
|
|
|
SecurityDescriptor - The security descriptor of the object being accessed.
|
|
|
|
SubjectSecurityContext - The current subject context.
|
|
|
|
DesiredAccess - Access mask describing the desired access to the object.
|
|
|
|
Privileges - Any privileges that have been invoked as part of gaining
|
|
access to this object.
|
|
|
|
AccessGranted - Boolean describing whether access was granted or not.
|
|
|
|
AccessMode - Previous processor mode.
|
|
|
|
Return value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
BOOLEAN GenerateAudit = FALSE;
|
|
BOOLEAN GenerateAlarm = FALSE;
|
|
|
|
if (AccessMode == KernelMode) {
|
|
return;
|
|
}
|
|
|
|
if ( SecurityDescriptor != NULL ) {
|
|
|
|
if ((SeAuditingState[AuditEventImplicitAccess].AuditOnSuccess && AccessGranted) ||
|
|
SeAuditingState[AuditEventImplicitAccess].AuditOnFailure && !AccessGranted) {
|
|
|
|
SepExamineSacl(
|
|
SepSaclAddrSecurityDescriptor( (PISECURITY_DESCRIPTOR)SecurityDescriptor ),
|
|
EffectiveToken( SubjectSecurityContext ),
|
|
DesiredAccess,
|
|
AccessGranted,
|
|
&GenerateAudit,
|
|
&GenerateAlarm
|
|
);
|
|
|
|
if ( GenerateAudit || GenerateAlarm ) {
|
|
|
|
SepAdtImplicitObjectAuditAlarm(
|
|
OperationID,
|
|
Object,
|
|
SepTokenUserSid(EffectiveToken( SubjectSecurityContext )),
|
|
DesiredAccess,
|
|
Privileges,
|
|
AccessGranted,
|
|
GenerateAudit,
|
|
GenerateAlarm
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
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;
|
|
PAUX_ACCESS_DATA AuxData;
|
|
|
|
PAGED_CODE();
|
|
|
|
AuxData = (PAUX_ACCESS_DATA)AccessState->AuxData;
|
|
|
|
if ( AccessState->GenerateAudit ) {
|
|
|
|
if ( AccessState->AuditPrivileges ) {
|
|
|
|
AuditPerformed = SepAdtPrivilegeObjectAuditAlarm (
|
|
&SeSubsystemName,
|
|
Handle,
|
|
(PTOKEN)AccessState->SubjectSecurityContext.ClientToken,
|
|
(PTOKEN)AccessState->SubjectSecurityContext.PrimaryToken,
|
|
&AccessState->SubjectSecurityContext.ProcessAuditId,
|
|
AccessState->PreviouslyGrantedAccess,
|
|
AuxData->PrivilegesUsed,
|
|
TRUE
|
|
);
|
|
} else {
|
|
|
|
AuditPerformed = SepAdtOpenObjectAuditAlarm ( &SeSubsystemName,
|
|
&Handle,
|
|
&AccessState->ObjectTypeName,
|
|
NULL,
|
|
&AccessState->ObjectName,
|
|
AccessState->SubjectSecurityContext.ClientToken,
|
|
AccessState->SubjectSecurityContext.PrimaryToken,
|
|
AccessState->OriginalDesiredAccess,
|
|
AccessState->PreviouslyGrantedAccess,
|
|
&AccessState->OperationID,
|
|
AuxData->PrivilegesUsed,
|
|
FALSE,
|
|
TRUE,
|
|
TRUE,
|
|
FALSE,
|
|
AccessState->SubjectSecurityContext.ProcessAuditId );
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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();
|
|
|
|
if (GenerateOnClose) {
|
|
|
|
SeCaptureSubjectContext ( &SubjectSecurityContext );
|
|
|
|
UserSid = SepTokenUserSid( EffectiveToken (&SubjectSecurityContext));
|
|
|
|
|
|
SepAdtCloseObjectAuditAlarm (
|
|
&SeSubsystemName,
|
|
(PVOID)Handle,
|
|
Object,
|
|
UserSid,
|
|
SepTokenAuthenticationId( EffectiveToken (&SubjectSecurityContext))
|
|
);
|
|
|
|
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();
|
|
|
|
SeCaptureSubjectContext ( &SubjectSecurityContext );
|
|
|
|
UserSid = SepTokenUserSid( EffectiveToken (&SubjectSecurityContext));
|
|
|
|
|
|
|
|
SepAdtDeleteObjectAuditAlarm (
|
|
&SeSubsystemName,
|
|
(PVOID)Handle,
|
|
Object,
|
|
UserSid,
|
|
SepTokenAuthenticationId( EffectiveToken (&SubjectSecurityContext))
|
|
);
|
|
|
|
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, &((PSYSTEM_AUDIT_ACE)Ace)->SidStart ) ) {
|
|
|
|
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, &((PSYSTEM_ALARM_ACE)Ace)->SidStart ) ) {
|
|
|
|
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;
|
|
}
|
|
|
|
|
|
/******************************************************************************
|
|
* *
|
|
* 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). *
|
|
* *
|
|
******************************************************************************/
|
|
|
|
PLUID *SepFilterPrivileges = NULL;
|
|
|
|
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. *
|
|
* *
|
|
******************************************************************************/
|
|
|
|
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 *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 ( SepSaclAddrSecurityDescriptor( 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 ) );
|
|
}
|