summaryrefslogtreecommitdiffstats
path: root/private/ntos/se/seaudit.c
diff options
context:
space:
mode:
authorAdam <you@example.com>2020-05-17 05:51:50 +0200
committerAdam <you@example.com>2020-05-17 05:51:50 +0200
commite611b132f9b8abe35b362e5870b74bce94a1e58e (patch)
treea5781d2ec0e085eeca33cf350cf878f2efea6fe5 /private/ntos/se/seaudit.c
downloadNT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.gz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.bz2
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.lz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.xz
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.tar.zst
NT4.0-e611b132f9b8abe35b362e5870b74bce94a1e58e.zip
Diffstat (limited to 'private/ntos/se/seaudit.c')
-rw-r--r--private/ntos/se/seaudit.c4049
1 files changed, 4049 insertions, 0 deletions
diff --git a/private/ntos/se/seaudit.c b/private/ntos/se/seaudit.c
new file mode 100644
index 000000000..a0a90c39c
--- /dev/null
+++ b/private/ntos/se/seaudit.c
@@ -0,0 +1,4049 @@
+/*++
+
+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 ) );
+}