mirror of
https://github.com/reactos/reactos.git
synced 2024-08-27 13:48:42 +00:00
![Oleg Dubinskiy](/assets/img/avatar_default.png)
Mute debug prints of MmDoesFileHaveUserWritableReferences and SeAuditingFileEventsWithContext stubs. These stubs are very noisy and create a lot of spam in the log when using Microsoft NTFS driver in ReactOS (with some other improvements applied). Implementing those functions isn't badly required for the proper work of this driver, so better way for now is just mute these stubs a bit. After my changes, they will be displayed only once, and the log will be more clear, so it will seem to be enough to understand that the driver calls these routines. CORE-17409
1569 lines
49 KiB
C
1569 lines
49 KiB
C
/*
|
|
* COPYRIGHT: See COPYING in the top level directory
|
|
* PROJECT: ReactOS kernel
|
|
* FILE: ntoskrnl/se/audit.c
|
|
* PURPOSE: Audit functions
|
|
*
|
|
* PROGRAMMERS: Eric Kohl
|
|
* Timo Kreuzer (timo.kreuzer@reactos.org)
|
|
*/
|
|
|
|
/* INCLUDES *******************************************************************/
|
|
|
|
#include <ntoskrnl.h>
|
|
#define NDEBUG
|
|
#include <debug.h>
|
|
|
|
#define SEP_PRIVILEGE_SET_MAX_COUNT 60
|
|
|
|
UNICODE_STRING SeSubsystemName = RTL_CONSTANT_STRING(L"Security");
|
|
|
|
/* PRIVATE FUNCTIONS***********************************************************/
|
|
|
|
BOOLEAN
|
|
NTAPI
|
|
SeDetailedAuditingWithToken(IN PTOKEN Token)
|
|
{
|
|
/* FIXME */
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
SeAuditProcessCreate(IN PEPROCESS Process)
|
|
{
|
|
/* FIXME */
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
SeAuditProcessExit(IN PEPROCESS Process)
|
|
{
|
|
/* FIXME */
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SeInitializeProcessAuditName(IN PFILE_OBJECT FileObject,
|
|
IN BOOLEAN DoAudit,
|
|
OUT POBJECT_NAME_INFORMATION *AuditInfo)
|
|
{
|
|
OBJECT_NAME_INFORMATION LocalNameInfo;
|
|
POBJECT_NAME_INFORMATION ObjectNameInfo = NULL;
|
|
ULONG ReturnLength = 8;
|
|
NTSTATUS Status;
|
|
|
|
PAGED_CODE();
|
|
ASSERT(AuditInfo);
|
|
|
|
/* Check if we should do auditing */
|
|
if (DoAudit)
|
|
{
|
|
/* FIXME: TODO */
|
|
}
|
|
|
|
/* Now query the name */
|
|
Status = ObQueryNameString(FileObject,
|
|
&LocalNameInfo,
|
|
sizeof(LocalNameInfo),
|
|
&ReturnLength);
|
|
if (((Status == STATUS_BUFFER_OVERFLOW) ||
|
|
(Status == STATUS_BUFFER_TOO_SMALL) ||
|
|
(Status == STATUS_INFO_LENGTH_MISMATCH)) &&
|
|
(ReturnLength != sizeof(LocalNameInfo)))
|
|
{
|
|
/* Allocate required size */
|
|
ObjectNameInfo = ExAllocatePoolWithTag(NonPagedPool,
|
|
ReturnLength,
|
|
TAG_SEPA);
|
|
if (ObjectNameInfo)
|
|
{
|
|
/* Query the name again */
|
|
Status = ObQueryNameString(FileObject,
|
|
ObjectNameInfo,
|
|
ReturnLength,
|
|
&ReturnLength);
|
|
}
|
|
}
|
|
|
|
/* Check if we got here due to failure */
|
|
if ((ObjectNameInfo) &&
|
|
(!(NT_SUCCESS(Status)) || (ReturnLength == sizeof(LocalNameInfo))))
|
|
{
|
|
/* First, free any buffer we might've allocated */
|
|
ASSERT(FALSE);
|
|
if (ObjectNameInfo) ExFreePool(ObjectNameInfo);
|
|
|
|
/* Now allocate a temporary one */
|
|
ReturnLength = sizeof(OBJECT_NAME_INFORMATION);
|
|
ObjectNameInfo = ExAllocatePoolWithTag(NonPagedPool,
|
|
sizeof(OBJECT_NAME_INFORMATION),
|
|
TAG_SEPA);
|
|
if (ObjectNameInfo)
|
|
{
|
|
/* Clear it */
|
|
RtlZeroMemory(ObjectNameInfo, ReturnLength);
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
/* Check if memory allocation failed */
|
|
if (!ObjectNameInfo) Status = STATUS_NO_MEMORY;
|
|
|
|
/* Return the audit name */
|
|
*AuditInfo = ObjectNameInfo;
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
SeLocateProcessImageName(IN PEPROCESS Process,
|
|
OUT PUNICODE_STRING *ProcessImageName)
|
|
{
|
|
POBJECT_NAME_INFORMATION AuditName;
|
|
PUNICODE_STRING ImageName;
|
|
PFILE_OBJECT FileObject;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
/* Assume failure */
|
|
*ProcessImageName = NULL;
|
|
|
|
/* Check if we have audit info */
|
|
AuditName = Process->SeAuditProcessCreationInfo.ImageFileName;
|
|
if (!AuditName)
|
|
{
|
|
/* Get the file object */
|
|
Status = PsReferenceProcessFilePointer(Process, &FileObject);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
|
|
/* Initialize the audit structure */
|
|
Status = SeInitializeProcessAuditName(FileObject, TRUE, &AuditName);
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
/* Set it */
|
|
if (InterlockedCompareExchangePointer((PVOID*)&Process->
|
|
SeAuditProcessCreationInfo.ImageFileName,
|
|
AuditName,
|
|
NULL))
|
|
{
|
|
/* Someone beat us to it, deallocate our copy */
|
|
ExFreePool(AuditName);
|
|
}
|
|
}
|
|
|
|
/* Dereference the file object */
|
|
ObDereferenceObject(FileObject);
|
|
if (!NT_SUCCESS(Status)) return Status;
|
|
}
|
|
|
|
/* Get audit info again, now we have it for sure */
|
|
AuditName = Process->SeAuditProcessCreationInfo.ImageFileName;
|
|
|
|
/* Allocate the output string */
|
|
ImageName = ExAllocatePoolWithTag(NonPagedPool,
|
|
AuditName->Name.MaximumLength +
|
|
sizeof(UNICODE_STRING),
|
|
TAG_SEPA);
|
|
if (!ImageName) return STATUS_NO_MEMORY;
|
|
|
|
/* Make a copy of it */
|
|
RtlCopyMemory(ImageName,
|
|
&AuditName->Name,
|
|
AuditName->Name.MaximumLength + sizeof(UNICODE_STRING));
|
|
|
|
/* Fix up the buffer */
|
|
ImageName->Buffer = (PWSTR)(ImageName + 1);
|
|
|
|
/* Return it */
|
|
*ProcessImageName = ImageName;
|
|
|
|
/* Return status */
|
|
return Status;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
SepAdtCloseObjectAuditAlarm(
|
|
PUNICODE_STRING SubsystemName,
|
|
PVOID HandleId,
|
|
PSID Sid)
|
|
{
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
SepAdtPrivilegedServiceAuditAlarm(
|
|
PSECURITY_SUBJECT_CONTEXT SubjectContext,
|
|
_In_opt_ PUNICODE_STRING SubsystemName,
|
|
_In_opt_ PUNICODE_STRING ServiceName,
|
|
_In_ PTOKEN Token,
|
|
_In_ PTOKEN PrimaryToken,
|
|
_In_ PPRIVILEGE_SET Privileges,
|
|
_In_ BOOLEAN AccessGranted)
|
|
{
|
|
DPRINT("SepAdtPrivilegedServiceAuditAlarm is unimplemented\n");
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
SePrivilegedServiceAuditAlarm(
|
|
_In_opt_ PUNICODE_STRING ServiceName,
|
|
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
|
|
_In_ PPRIVILEGE_SET PrivilegeSet,
|
|
_In_ BOOLEAN AccessGranted)
|
|
{
|
|
PTOKEN EffectiveToken;
|
|
PSID UserSid;
|
|
PAGED_CODE();
|
|
|
|
/* Get the effective token */
|
|
if (SubjectContext->ClientToken != NULL)
|
|
EffectiveToken = SubjectContext->ClientToken;
|
|
else
|
|
EffectiveToken = SubjectContext->PrimaryToken;
|
|
|
|
/* Get the user SID */
|
|
UserSid = EffectiveToken->UserAndGroups->Sid;
|
|
|
|
/* Check if this is the local system SID */
|
|
if (RtlEqualSid(UserSid, SeLocalSystemSid))
|
|
{
|
|
/* Nothing to do */
|
|
return;
|
|
}
|
|
|
|
/* Check if this is the network service or local service SID */
|
|
if (RtlEqualSid(UserSid, SeExports->SeNetworkServiceSid) ||
|
|
RtlEqualSid(UserSid, SeExports->SeLocalServiceSid))
|
|
{
|
|
// FIXME: should continue for a certain set of privileges
|
|
return;
|
|
}
|
|
|
|
/* Call the worker function */
|
|
SepAdtPrivilegedServiceAuditAlarm(SubjectContext,
|
|
&SeSubsystemName,
|
|
ServiceName,
|
|
SubjectContext->ClientToken,
|
|
SubjectContext->PrimaryToken,
|
|
PrivilegeSet,
|
|
AccessGranted);
|
|
|
|
}
|
|
|
|
|
|
static
|
|
NTSTATUS
|
|
SeCaptureObjectTypeList(
|
|
_In_reads_opt_(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
|
|
_In_ ULONG ObjectTypeListLength,
|
|
_In_ KPROCESSOR_MODE PreviousMode,
|
|
_Out_ POBJECT_TYPE_LIST *CapturedObjectTypeList)
|
|
{
|
|
SIZE_T Size;
|
|
|
|
if (PreviousMode == KernelMode)
|
|
{
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
if (ObjectTypeListLength == 0)
|
|
{
|
|
*CapturedObjectTypeList = NULL;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (ObjectTypeList == NULL)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Calculate the list size and check for integer overflow */
|
|
Size = ObjectTypeListLength * sizeof(OBJECT_TYPE_LIST);
|
|
if (Size == 0)
|
|
{
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Allocate a new list */
|
|
*CapturedObjectTypeList = ExAllocatePoolWithTag(PagedPool, Size, TAG_SEPA);
|
|
if (*CapturedObjectTypeList == NULL)
|
|
{
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
_SEH2_TRY
|
|
{
|
|
ProbeForRead(ObjectTypeList, Size, sizeof(ULONG));
|
|
RtlCopyMemory(*CapturedObjectTypeList, ObjectTypeList, Size);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
ExFreePoolWithTag(*CapturedObjectTypeList, TAG_SEPA);
|
|
*CapturedObjectTypeList = NULL;
|
|
_SEH2_YIELD(return _SEH2_GetExceptionCode());
|
|
}
|
|
_SEH2_END;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
static
|
|
VOID
|
|
SeReleaseObjectTypeList(
|
|
_In_ _Post_invalid_ POBJECT_TYPE_LIST CapturedObjectTypeList,
|
|
_In_ KPROCESSOR_MODE PreviousMode)
|
|
{
|
|
if ((PreviousMode != KernelMode) && (CapturedObjectTypeList != NULL))
|
|
ExFreePoolWithTag(CapturedObjectTypeList, TAG_SEPA);
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
static
|
|
NTSTATUS
|
|
SepAccessCheckAndAuditAlarmWorker(
|
|
_In_ PUNICODE_STRING SubsystemName,
|
|
_In_opt_ PVOID HandleId,
|
|
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
|
|
_In_ PUNICODE_STRING ObjectTypeName,
|
|
_In_ PUNICODE_STRING ObjectName,
|
|
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
_In_opt_ PSID PrincipalSelfSid,
|
|
_In_ ACCESS_MASK DesiredAccess,
|
|
_In_ AUDIT_EVENT_TYPE AuditType,
|
|
_In_ BOOLEAN HaveAuditPrivilege,
|
|
_In_reads_opt_(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
|
|
_In_ ULONG ObjectTypeListLength,
|
|
_In_ PGENERIC_MAPPING GenericMapping,
|
|
_Out_writes_(ObjectTypeListLength) PACCESS_MASK GrantedAccessList,
|
|
_Out_writes_(ObjectTypeListLength) PNTSTATUS AccessStatusList,
|
|
_Out_ PBOOLEAN GenerateOnClose,
|
|
_In_ BOOLEAN UseResultList)
|
|
{
|
|
ULONG ResultListLength, i;
|
|
|
|
/* Get the length of the result list */
|
|
ResultListLength = UseResultList ? ObjectTypeListLength : 1;
|
|
|
|
/// FIXME: we should do some real work here...
|
|
UNIMPLEMENTED;
|
|
|
|
/// HACK: we just pretend all access is granted!
|
|
for (i = 0; i < ResultListLength; i++)
|
|
{
|
|
GrantedAccessList[i] = DesiredAccess;
|
|
AccessStatusList[i] = STATUS_SUCCESS;
|
|
}
|
|
|
|
*GenerateOnClose = FALSE;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
NTSTATUS
|
|
NTAPI
|
|
SepAccessCheckAndAuditAlarm(
|
|
_In_ PUNICODE_STRING SubsystemName,
|
|
_In_opt_ PVOID HandleId,
|
|
_In_ PHANDLE ClientTokenHandle,
|
|
_In_ PUNICODE_STRING ObjectTypeName,
|
|
_In_ PUNICODE_STRING ObjectName,
|
|
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
_In_opt_ PSID PrincipalSelfSid,
|
|
_In_ ACCESS_MASK DesiredAccess,
|
|
_In_ AUDIT_EVENT_TYPE AuditType,
|
|
_In_ ULONG Flags,
|
|
_In_reads_opt_(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
|
|
_In_ ULONG ObjectTypeListLength,
|
|
_In_ PGENERIC_MAPPING GenericMapping,
|
|
_Out_writes_(ObjectTypeListLength) PACCESS_MASK GrantedAccessList,
|
|
_Out_writes_(ObjectTypeListLength) PNTSTATUS AccessStatusList,
|
|
_Out_ PBOOLEAN GenerateOnClose,
|
|
_In_ BOOLEAN UseResultList)
|
|
{
|
|
SECURITY_SUBJECT_CONTEXT SubjectContext;
|
|
ULONG ResultListLength;
|
|
GENERIC_MAPPING LocalGenericMapping;
|
|
PTOKEN SubjectContextToken, ClientToken;
|
|
BOOLEAN AllocatedResultLists;
|
|
BOOLEAN HaveAuditPrivilege;
|
|
PSECURITY_DESCRIPTOR CapturedSecurityDescriptor;
|
|
UNICODE_STRING CapturedSubsystemName, CapturedObjectTypeName, CapturedObjectName;
|
|
ACCESS_MASK GrantedAccess, *SafeGrantedAccessList;
|
|
NTSTATUS AccessStatus, *SafeAccessStatusList;
|
|
PSID CapturedPrincipalSelfSid;
|
|
POBJECT_TYPE_LIST CapturedObjectTypeList;
|
|
ULONG i;
|
|
BOOLEAN LocalGenerateOnClose;
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
|
|
/* Only user mode is supported! */
|
|
ASSERT(ExGetPreviousMode() != KernelMode);
|
|
|
|
/* Start clean */
|
|
AllocatedResultLists = FALSE;
|
|
ClientToken = NULL;
|
|
CapturedSecurityDescriptor = NULL;
|
|
CapturedSubsystemName.Buffer = NULL;
|
|
CapturedObjectTypeName.Buffer = NULL;
|
|
CapturedObjectName.Buffer = NULL;
|
|
CapturedPrincipalSelfSid = NULL;
|
|
CapturedObjectTypeList = NULL;
|
|
|
|
/* Validate AuditType */
|
|
if ((AuditType != AuditEventObjectAccess) &&
|
|
(AuditType != AuditEventDirectoryServiceAccess))
|
|
{
|
|
DPRINT1("Invalid audit type: %u\n", AuditType);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
/* Capture the security subject context */
|
|
SeCaptureSubjectContext(&SubjectContext);
|
|
|
|
/* Did the caller pass a token handle? */
|
|
if (ClientTokenHandle == NULL)
|
|
{
|
|
/* Check if we have a token in the subject context */
|
|
if (SubjectContext.ClientToken == NULL)
|
|
{
|
|
Status = STATUS_NO_IMPERSONATION_TOKEN;
|
|
DPRINT1("No token\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Check if we have a valid impersonation level */
|
|
if (SubjectContext.ImpersonationLevel < SecurityIdentification)
|
|
{
|
|
Status = STATUS_BAD_IMPERSONATION_LEVEL;
|
|
DPRINT1("Invalid impersonation level 0x%lx\n",
|
|
SubjectContext.ImpersonationLevel);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
/* Are we using a result list? */
|
|
if (UseResultList)
|
|
{
|
|
/* The list length equals the object type list length */
|
|
ResultListLength = ObjectTypeListLength;
|
|
if ((ResultListLength == 0) || (ResultListLength > 0x1000))
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
DPRINT1("Invalid ResultListLength: 0x%lx\n", ResultListLength);
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Allocate a safe buffer from paged pool */
|
|
SafeGrantedAccessList = ExAllocatePoolWithTag(PagedPool,
|
|
2 * ResultListLength * sizeof(ULONG),
|
|
TAG_SEPA);
|
|
if (SafeGrantedAccessList == NULL)
|
|
{
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
DPRINT1("Failed to allocate access lists\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
SafeAccessStatusList = (PNTSTATUS)&SafeGrantedAccessList[ResultListLength];
|
|
AllocatedResultLists = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* List length is 1 */
|
|
ResultListLength = 1;
|
|
SafeGrantedAccessList = &GrantedAccess;
|
|
SafeAccessStatusList = &AccessStatus;
|
|
}
|
|
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe output buffers */
|
|
ProbeForWrite(AccessStatusList,
|
|
ResultListLength * sizeof(*AccessStatusList),
|
|
sizeof(*AccessStatusList));
|
|
ProbeForWrite(GrantedAccessList,
|
|
ResultListLength * sizeof(*GrantedAccessList),
|
|
sizeof(*GrantedAccessList));
|
|
|
|
/* Probe generic mapping and make a local copy */
|
|
ProbeForRead(GenericMapping, sizeof(*GenericMapping), sizeof(ULONG));
|
|
LocalGenericMapping = * GenericMapping;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
DPRINT1("Exception while probing parameters: 0x%lx\n", Status);
|
|
_SEH2_YIELD(goto Cleanup);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Do we have a client token? */
|
|
if (ClientTokenHandle != NULL)
|
|
{
|
|
/* Reference the client token */
|
|
Status = ObReferenceObjectByHandle(*ClientTokenHandle,
|
|
TOKEN_QUERY,
|
|
SeTokenObjectType,
|
|
UserMode,
|
|
(PVOID*)&ClientToken,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to reference token handle %p: %lx\n",
|
|
*ClientTokenHandle, Status);
|
|
goto Cleanup;
|
|
}
|
|
|
|
SubjectContextToken = SubjectContext.ClientToken;
|
|
SubjectContext.ClientToken = ClientToken;
|
|
}
|
|
|
|
/* Check for audit privilege */
|
|
HaveAuditPrivilege = SeCheckAuditPrivilege(&SubjectContext, UserMode);
|
|
if (!HaveAuditPrivilege && !(Flags & AUDIT_ALLOW_NO_PRIVILEGE))
|
|
{
|
|
DPRINT1("Caller does not have SeAuditPrivilege\n");
|
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Generic access must already be mapped to non-generic access types! */
|
|
if (DesiredAccess & (GENERIC_READ | GENERIC_WRITE | GENERIC_EXECUTE | GENERIC_ALL))
|
|
{
|
|
DPRINT1("Generic access rights requested: 0x%lx\n", DesiredAccess);
|
|
Status = STATUS_GENERIC_NOT_MAPPED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Capture the security descriptor */
|
|
Status = SeCaptureSecurityDescriptor(SecurityDescriptor,
|
|
UserMode,
|
|
PagedPool,
|
|
FALSE,
|
|
&CapturedSecurityDescriptor);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to capture security descriptor!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Validate the Security descriptor */
|
|
if ((SepGetOwnerFromDescriptor(CapturedSecurityDescriptor) == NULL) ||
|
|
(SepGetGroupFromDescriptor(CapturedSecurityDescriptor) == NULL))
|
|
{
|
|
Status = STATUS_INVALID_SECURITY_DESCR;
|
|
DPRINT1("Invalid security descriptor\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Probe and capture the subsystem name */
|
|
Status = ProbeAndCaptureUnicodeString(&CapturedSubsystemName,
|
|
UserMode,
|
|
SubsystemName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to capture subsystem name!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Probe and capture the object type name */
|
|
Status = ProbeAndCaptureUnicodeString(&CapturedObjectTypeName,
|
|
UserMode,
|
|
ObjectTypeName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to capture object type name!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Probe and capture the object name */
|
|
Status = ProbeAndCaptureUnicodeString(&CapturedObjectName,
|
|
UserMode,
|
|
ObjectName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to capture object name!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Check if we have a PrincipalSelfSid */
|
|
if (PrincipalSelfSid != NULL)
|
|
{
|
|
/* Capture it */
|
|
Status = SepCaptureSid(PrincipalSelfSid,
|
|
UserMode,
|
|
PagedPool,
|
|
FALSE,
|
|
&CapturedPrincipalSelfSid);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to capture PrincipalSelfSid!\n");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
/* Capture the object type list */
|
|
Status = SeCaptureObjectTypeList(ObjectTypeList,
|
|
ObjectTypeListLength,
|
|
UserMode,
|
|
&CapturedObjectTypeList);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to capture object type list!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Call the worker routine with the captured buffers */
|
|
SepAccessCheckAndAuditAlarmWorker(&CapturedSubsystemName,
|
|
HandleId,
|
|
&SubjectContext,
|
|
&CapturedObjectTypeName,
|
|
&CapturedObjectName,
|
|
CapturedSecurityDescriptor,
|
|
CapturedPrincipalSelfSid,
|
|
DesiredAccess,
|
|
AuditType,
|
|
HaveAuditPrivilege,
|
|
CapturedObjectTypeList,
|
|
ObjectTypeListLength,
|
|
&LocalGenericMapping,
|
|
SafeGrantedAccessList,
|
|
SafeAccessStatusList,
|
|
&LocalGenerateOnClose,
|
|
UseResultList);
|
|
|
|
/* Enter SEH to copy the data back to user mode */
|
|
_SEH2_TRY
|
|
{
|
|
/* Loop all result entries (only 1 when no list was requested) */
|
|
ASSERT(UseResultList || (ResultListLength == 1));
|
|
for (i = 0; i < ResultListLength; i++)
|
|
{
|
|
AccessStatusList[i] = SafeAccessStatusList[i];
|
|
GrantedAccessList[i] = SafeGrantedAccessList[i];
|
|
}
|
|
|
|
*GenerateOnClose = LocalGenerateOnClose;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
DPRINT1("Exception while copying back data: 0x%lx\n", Status);
|
|
}
|
|
_SEH2_END;
|
|
|
|
Cleanup:
|
|
|
|
if (CapturedObjectTypeList != NULL)
|
|
SeReleaseObjectTypeList(CapturedObjectTypeList, UserMode);
|
|
|
|
if (CapturedPrincipalSelfSid != NULL)
|
|
SepReleaseSid(CapturedPrincipalSelfSid, UserMode, FALSE);
|
|
|
|
if (CapturedObjectName.Buffer != NULL)
|
|
ReleaseCapturedUnicodeString(&CapturedObjectName, UserMode);
|
|
|
|
if (CapturedObjectTypeName.Buffer != NULL)
|
|
ReleaseCapturedUnicodeString(&CapturedObjectTypeName, UserMode);
|
|
|
|
if (CapturedSubsystemName.Buffer != NULL)
|
|
ReleaseCapturedUnicodeString(&CapturedSubsystemName, UserMode);
|
|
|
|
if (CapturedSecurityDescriptor != NULL)
|
|
SeReleaseSecurityDescriptor(CapturedSecurityDescriptor, UserMode, FALSE);
|
|
|
|
if (ClientToken != NULL)
|
|
{
|
|
ObDereferenceObject(ClientToken);
|
|
SubjectContext.ClientToken = SubjectContextToken;
|
|
}
|
|
|
|
if (AllocatedResultLists)
|
|
ExFreePoolWithTag(SafeGrantedAccessList, TAG_SEPA);
|
|
|
|
/* Release the security subject context */
|
|
SeReleaseSubjectContext(&SubjectContext);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
/* PUBLIC FUNCTIONS ***********************************************************/
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
SeAuditHardLinkCreation(IN PUNICODE_STRING FileName,
|
|
IN PUNICODE_STRING LinkName,
|
|
IN BOOLEAN bSuccess)
|
|
{
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
SeAuditingFileEvents(IN BOOLEAN AccessGranted,
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
SeAuditingFileEventsWithContext(IN BOOLEAN AccessGranted,
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext OPTIONAL)
|
|
{
|
|
UNIMPLEMENTED_ONCE;
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
SeAuditingHardLinkEvents(IN BOOLEAN AccessGranted,
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
SeAuditingHardLinkEventsWithContext(IN BOOLEAN AccessGranted,
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext OPTIONAL)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
BOOLEAN
|
|
NTAPI
|
|
SeAuditingFileOrGlobalEvents(IN BOOLEAN AccessGranted,
|
|
IN PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
IN PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
SeCloseObjectAuditAlarm(IN PVOID Object,
|
|
IN HANDLE Handle,
|
|
IN BOOLEAN PerformAction)
|
|
{
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
VOID NTAPI
|
|
SeDeleteObjectAuditAlarm(IN PVOID Object,
|
|
IN HANDLE Handle)
|
|
{
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
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)
|
|
{
|
|
PAGED_CODE();
|
|
|
|
/* Audits aren't done on kernel-mode access */
|
|
if (AccessMode == KernelMode) return;
|
|
|
|
/* Otherwise, unimplemented! */
|
|
//UNIMPLEMENTED;
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
VOID NTAPI
|
|
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)
|
|
{
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/*
|
|
* @unimplemented
|
|
*/
|
|
VOID
|
|
NTAPI
|
|
SePrivilegeObjectAuditAlarm(IN HANDLE Handle,
|
|
IN PSECURITY_SUBJECT_CONTEXT SubjectContext,
|
|
IN ACCESS_MASK DesiredAccess,
|
|
IN PPRIVILEGE_SET Privileges,
|
|
IN BOOLEAN AccessGranted,
|
|
IN KPROCESSOR_MODE CurrentMode)
|
|
{
|
|
UNIMPLEMENTED;
|
|
}
|
|
|
|
/* SYSTEM CALLS ***************************************************************/
|
|
|
|
NTSTATUS
|
|
NTAPI
|
|
NtCloseObjectAuditAlarm(
|
|
PUNICODE_STRING SubsystemName,
|
|
PVOID HandleId,
|
|
BOOLEAN GenerateOnClose)
|
|
{
|
|
SECURITY_SUBJECT_CONTEXT SubjectContext;
|
|
UNICODE_STRING CapturedSubsystemName;
|
|
KPROCESSOR_MODE PreviousMode;
|
|
BOOLEAN UseImpersonationToken;
|
|
PETHREAD CurrentThread;
|
|
BOOLEAN CopyOnOpen, EffectiveOnly;
|
|
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
|
|
NTSTATUS Status;
|
|
PTOKEN Token;
|
|
PAGED_CODE();
|
|
|
|
/* Get the previous mode (only user mode is supported!) */
|
|
PreviousMode = ExGetPreviousMode();
|
|
ASSERT(PreviousMode != KernelMode);
|
|
|
|
/* Do we even need to do anything? */
|
|
if (!GenerateOnClose)
|
|
{
|
|
/* Nothing to do, return success */
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
/* Capture the security subject context */
|
|
SeCaptureSubjectContext(&SubjectContext);
|
|
|
|
/* Check for audit privilege */
|
|
if (!SeCheckAuditPrivilege(&SubjectContext, PreviousMode))
|
|
{
|
|
DPRINT1("Caller does not have SeAuditPrivilege\n");
|
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Probe and capture the subsystem name */
|
|
Status = ProbeAndCaptureUnicodeString(&CapturedSubsystemName,
|
|
PreviousMode,
|
|
SubsystemName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to capture subsystem name!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Get the current thread and check if it's impersonating */
|
|
CurrentThread = PsGetCurrentThread();
|
|
if (PsIsThreadImpersonating(CurrentThread))
|
|
{
|
|
/* Get the impersonation token */
|
|
Token = PsReferenceImpersonationToken(CurrentThread,
|
|
&CopyOnOpen,
|
|
&EffectiveOnly,
|
|
&ImpersonationLevel);
|
|
UseImpersonationToken = TRUE;
|
|
}
|
|
else
|
|
{
|
|
/* Get the primary token */
|
|
Token = PsReferencePrimaryToken(PsGetCurrentProcess());
|
|
UseImpersonationToken = FALSE;
|
|
}
|
|
|
|
/* Call the internal function */
|
|
SepAdtCloseObjectAuditAlarm(&CapturedSubsystemName,
|
|
HandleId,
|
|
Token->UserAndGroups->Sid);
|
|
|
|
/* Release the captured subsystem name */
|
|
ReleaseCapturedUnicodeString(&CapturedSubsystemName, PreviousMode);
|
|
|
|
/* Check what token we used */
|
|
if (UseImpersonationToken)
|
|
{
|
|
/* Release impersonation token */
|
|
PsDereferenceImpersonationToken(Token);
|
|
}
|
|
else
|
|
{
|
|
/* Release primary token */
|
|
PsDereferencePrimaryToken(Token);
|
|
}
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
/* Release the security subject context */
|
|
SeReleaseSubjectContext(&SubjectContext);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
NtDeleteObjectAuditAlarm(IN PUNICODE_STRING SubsystemName,
|
|
IN PVOID HandleId,
|
|
IN BOOLEAN GenerateOnClose)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
VOID
|
|
NTAPI
|
|
SepOpenObjectAuditAlarm(
|
|
_In_ PSECURITY_SUBJECT_CONTEXT SubjectContext,
|
|
_In_ PUNICODE_STRING SubsystemName,
|
|
_In_opt_ PVOID HandleId,
|
|
_In_ PUNICODE_STRING ObjectTypeName,
|
|
_In_ PUNICODE_STRING ObjectName,
|
|
_In_opt_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
_In_ PTOKEN ClientToken,
|
|
_In_ ACCESS_MASK DesiredAccess,
|
|
_In_ ACCESS_MASK GrantedAccess,
|
|
_In_opt_ PPRIVILEGE_SET Privileges,
|
|
_In_ BOOLEAN ObjectCreation,
|
|
_In_ BOOLEAN AccessGranted,
|
|
_Out_ PBOOLEAN GenerateOnClose)
|
|
{
|
|
DBG_UNREFERENCED_PARAMETER(SubjectContext);
|
|
DBG_UNREFERENCED_PARAMETER(SubsystemName);
|
|
DBG_UNREFERENCED_PARAMETER(HandleId);
|
|
DBG_UNREFERENCED_PARAMETER(ObjectTypeName);
|
|
DBG_UNREFERENCED_PARAMETER(ObjectName);
|
|
DBG_UNREFERENCED_PARAMETER(SecurityDescriptor);
|
|
DBG_UNREFERENCED_PARAMETER(ClientToken);
|
|
DBG_UNREFERENCED_PARAMETER(DesiredAccess);
|
|
DBG_UNREFERENCED_PARAMETER(GrantedAccess);
|
|
DBG_UNREFERENCED_PARAMETER(Privileges);
|
|
DBG_UNREFERENCED_PARAMETER(ObjectCreation);
|
|
DBG_UNREFERENCED_PARAMETER(AccessGranted);
|
|
UNIMPLEMENTED;
|
|
*GenerateOnClose = FALSE;
|
|
}
|
|
|
|
__kernel_entry
|
|
NTSTATUS
|
|
NTAPI
|
|
NtOpenObjectAuditAlarm(
|
|
_In_ PUNICODE_STRING SubsystemName,
|
|
_In_opt_ PVOID HandleId,
|
|
_In_ PUNICODE_STRING ObjectTypeName,
|
|
_In_ PUNICODE_STRING ObjectName,
|
|
_In_opt_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
_In_ HANDLE ClientTokenHandle,
|
|
_In_ ACCESS_MASK DesiredAccess,
|
|
_In_ ACCESS_MASK GrantedAccess,
|
|
_In_opt_ PPRIVILEGE_SET PrivilegeSet,
|
|
_In_ BOOLEAN ObjectCreation,
|
|
_In_ BOOLEAN AccessGranted,
|
|
_Out_ PBOOLEAN GenerateOnClose)
|
|
{
|
|
PTOKEN ClientToken;
|
|
PSECURITY_DESCRIPTOR CapturedSecurityDescriptor;
|
|
UNICODE_STRING CapturedSubsystemName, CapturedObjectTypeName, CapturedObjectName;
|
|
ULONG PrivilegeCount, PrivilegeSetSize;
|
|
volatile PPRIVILEGE_SET CapturedPrivilegeSet;
|
|
BOOLEAN LocalGenerateOnClose;
|
|
PVOID CapturedHandleId;
|
|
SECURITY_SUBJECT_CONTEXT SubjectContext;
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
|
|
/* Only user mode is supported! */
|
|
ASSERT(ExGetPreviousMode() != KernelMode);
|
|
|
|
/* Start clean */
|
|
ClientToken = NULL;
|
|
CapturedSecurityDescriptor = NULL;
|
|
CapturedPrivilegeSet = NULL;
|
|
CapturedSubsystemName.Buffer = NULL;
|
|
CapturedObjectTypeName.Buffer = NULL;
|
|
CapturedObjectName.Buffer = NULL;
|
|
|
|
/* Reference the client token */
|
|
Status = ObReferenceObjectByHandle(ClientTokenHandle,
|
|
TOKEN_QUERY,
|
|
SeTokenObjectType,
|
|
UserMode,
|
|
(PVOID*)&ClientToken,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to reference token handle %p: %lx\n",
|
|
ClientTokenHandle, Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Capture the security subject context */
|
|
SeCaptureSubjectContext(&SubjectContext);
|
|
|
|
/* Validate the token's impersonation level */
|
|
if ((ClientToken->TokenType == TokenImpersonation) &&
|
|
(ClientToken->ImpersonationLevel < SecurityIdentification))
|
|
{
|
|
DPRINT1("Invalid impersonation level (%u)\n", ClientToken->ImpersonationLevel);
|
|
Status = STATUS_BAD_IMPERSONATION_LEVEL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Check for audit privilege */
|
|
if (!SeCheckAuditPrivilege(&SubjectContext, UserMode))
|
|
{
|
|
DPRINT1("Caller does not have SeAuditPrivilege\n");
|
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Check for NULL SecurityDescriptor */
|
|
if (SecurityDescriptor == NULL)
|
|
{
|
|
/* Nothing to do */
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Capture the security descriptor */
|
|
Status = SeCaptureSecurityDescriptor(SecurityDescriptor,
|
|
UserMode,
|
|
PagedPool,
|
|
FALSE,
|
|
&CapturedSecurityDescriptor);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to capture security descriptor!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
_SEH2_TRY
|
|
{
|
|
/* Check if we have a privilege set */
|
|
if (PrivilegeSet != NULL)
|
|
{
|
|
/* Probe the basic privilege set structure */
|
|
ProbeForRead(PrivilegeSet, sizeof(PRIVILEGE_SET), sizeof(ULONG));
|
|
|
|
/* Validate privilege count */
|
|
PrivilegeCount = PrivilegeSet->PrivilegeCount;
|
|
if (PrivilegeCount > SEP_PRIVILEGE_SET_MAX_COUNT)
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
_SEH2_YIELD(goto Cleanup);
|
|
}
|
|
|
|
/* Calculate the size of the PrivilegeSet structure */
|
|
PrivilegeSetSize = FIELD_OFFSET(PRIVILEGE_SET, Privilege[PrivilegeCount]);
|
|
|
|
/* Probe the whole structure */
|
|
ProbeForRead(PrivilegeSet, PrivilegeSetSize, sizeof(ULONG));
|
|
|
|
/* Allocate a temp buffer */
|
|
CapturedPrivilegeSet = ExAllocatePoolWithTag(PagedPool,
|
|
PrivilegeSetSize,
|
|
TAG_PRIVILEGE_SET);
|
|
if (CapturedPrivilegeSet == NULL)
|
|
{
|
|
DPRINT1("Failed to allocate %u bytes\n", PrivilegeSetSize);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
_SEH2_YIELD(goto Cleanup);
|
|
}
|
|
|
|
/* Copy the privileges */
|
|
RtlCopyMemory(CapturedPrivilegeSet, PrivilegeSet, PrivilegeSetSize);
|
|
}
|
|
|
|
if (HandleId != NULL)
|
|
{
|
|
ProbeForRead(HandleId, sizeof(PVOID), sizeof(PVOID));
|
|
CapturedHandleId = *(PVOID*)HandleId;
|
|
}
|
|
|
|
ProbeForWrite(GenerateOnClose, sizeof(BOOLEAN), sizeof(BOOLEAN));
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
DPRINT1("Exception while probing parameters: 0x%lx\n", Status);
|
|
_SEH2_YIELD(goto Cleanup);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Probe and capture the subsystem name */
|
|
Status = ProbeAndCaptureUnicodeString(&CapturedSubsystemName,
|
|
UserMode,
|
|
SubsystemName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to capture subsystem name!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Probe and capture the object type name */
|
|
Status = ProbeAndCaptureUnicodeString(&CapturedObjectTypeName,
|
|
UserMode,
|
|
ObjectTypeName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to capture object type name!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Probe and capture the object name */
|
|
Status = ProbeAndCaptureUnicodeString(&CapturedObjectName,
|
|
UserMode,
|
|
ObjectName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to capture object name!\n");
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Call the internal function */
|
|
SepOpenObjectAuditAlarm(&SubjectContext,
|
|
&CapturedSubsystemName,
|
|
CapturedHandleId,
|
|
&CapturedObjectTypeName,
|
|
&CapturedObjectName,
|
|
CapturedSecurityDescriptor,
|
|
ClientToken,
|
|
DesiredAccess,
|
|
GrantedAccess,
|
|
CapturedPrivilegeSet,
|
|
ObjectCreation,
|
|
AccessGranted,
|
|
&LocalGenerateOnClose);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
/* Enter SEH to copy the data back to user mode */
|
|
_SEH2_TRY
|
|
{
|
|
*GenerateOnClose = LocalGenerateOnClose;
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
DPRINT1("Exception while copying back data: 0x%lx\n", Status);
|
|
}
|
|
_SEH2_END;
|
|
|
|
Cleanup:
|
|
|
|
if (CapturedObjectName.Buffer != NULL)
|
|
ReleaseCapturedUnicodeString(&CapturedObjectName, UserMode);
|
|
|
|
if (CapturedObjectTypeName.Buffer != NULL)
|
|
ReleaseCapturedUnicodeString(&CapturedObjectTypeName, UserMode);
|
|
|
|
if (CapturedSubsystemName.Buffer != NULL)
|
|
ReleaseCapturedUnicodeString(&CapturedSubsystemName, UserMode);
|
|
|
|
if (CapturedSecurityDescriptor != NULL)
|
|
SeReleaseSecurityDescriptor(CapturedSecurityDescriptor, UserMode, FALSE);
|
|
|
|
if (CapturedPrivilegeSet != NULL)
|
|
ExFreePoolWithTag(CapturedPrivilegeSet, TAG_PRIVILEGE_SET);
|
|
|
|
/* Release the security subject context */
|
|
SeReleaseSubjectContext(&SubjectContext);
|
|
|
|
ObDereferenceObject(ClientToken);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
__kernel_entry
|
|
NTSTATUS
|
|
NTAPI
|
|
NtPrivilegedServiceAuditAlarm(
|
|
_In_opt_ PUNICODE_STRING SubsystemName,
|
|
_In_opt_ PUNICODE_STRING ServiceName,
|
|
_In_ HANDLE ClientTokenHandle,
|
|
_In_ PPRIVILEGE_SET Privileges,
|
|
_In_ BOOLEAN AccessGranted )
|
|
{
|
|
KPROCESSOR_MODE PreviousMode;
|
|
PTOKEN ClientToken;
|
|
volatile PPRIVILEGE_SET CapturedPrivileges = NULL;
|
|
UNICODE_STRING CapturedSubsystemName;
|
|
UNICODE_STRING CapturedServiceName;
|
|
ULONG PrivilegeCount, PrivilegesSize;
|
|
SECURITY_SUBJECT_CONTEXT SubjectContext;
|
|
NTSTATUS Status;
|
|
PAGED_CODE();
|
|
|
|
/* Get the previous mode (only user mode is supported!) */
|
|
PreviousMode = ExGetPreviousMode();
|
|
ASSERT(PreviousMode != KernelMode);
|
|
|
|
CapturedSubsystemName.Buffer = NULL;
|
|
CapturedServiceName.Buffer = NULL;
|
|
|
|
/* Reference the client token */
|
|
Status = ObReferenceObjectByHandle(ClientTokenHandle,
|
|
TOKEN_QUERY,
|
|
SeTokenObjectType,
|
|
PreviousMode,
|
|
(PVOID*)&ClientToken,
|
|
NULL);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to reference client token: 0x%lx\n", Status);
|
|
return Status;
|
|
}
|
|
|
|
/* Validate the token's impersonation level */
|
|
if ((ClientToken->TokenType == TokenImpersonation) &&
|
|
(ClientToken->ImpersonationLevel < SecurityIdentification))
|
|
{
|
|
DPRINT1("Invalid impersonation level (%u)\n", ClientToken->ImpersonationLevel);
|
|
ObDereferenceObject(ClientToken);
|
|
return STATUS_BAD_IMPERSONATION_LEVEL;
|
|
}
|
|
|
|
/* Capture the security subject context */
|
|
SeCaptureSubjectContext(&SubjectContext);
|
|
|
|
/* Check for audit privilege */
|
|
if (!SeCheckAuditPrivilege(&SubjectContext, PreviousMode))
|
|
{
|
|
DPRINT1("Caller does not have SeAuditPrivilege\n");
|
|
Status = STATUS_PRIVILEGE_NOT_HELD;
|
|
goto Cleanup;
|
|
}
|
|
|
|
/* Do we have a subsystem name? */
|
|
if (SubsystemName != NULL)
|
|
{
|
|
/* Probe and capture the subsystem name */
|
|
Status = ProbeAndCaptureUnicodeString(&CapturedSubsystemName,
|
|
PreviousMode,
|
|
SubsystemName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to capture subsystem name!\n");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
/* Do we have a service name? */
|
|
if (ServiceName != NULL)
|
|
{
|
|
/* Probe and capture the service name */
|
|
Status = ProbeAndCaptureUnicodeString(&CapturedServiceName,
|
|
PreviousMode,
|
|
ServiceName);
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
DPRINT1("Failed to capture service name!\n");
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
_SEH2_TRY
|
|
{
|
|
/* Probe the basic privilege set structure */
|
|
ProbeForRead(Privileges, sizeof(PRIVILEGE_SET), sizeof(ULONG));
|
|
|
|
/* Validate privilege count */
|
|
PrivilegeCount = Privileges->PrivilegeCount;
|
|
if (PrivilegeCount > SEP_PRIVILEGE_SET_MAX_COUNT)
|
|
{
|
|
Status = STATUS_INVALID_PARAMETER;
|
|
_SEH2_YIELD(goto Cleanup);
|
|
}
|
|
|
|
/* Calculate the size of the Privileges structure */
|
|
PrivilegesSize = FIELD_OFFSET(PRIVILEGE_SET, Privilege[PrivilegeCount]);
|
|
|
|
/* Probe the whole structure */
|
|
ProbeForRead(Privileges, PrivilegesSize, sizeof(ULONG));
|
|
|
|
/* Allocate a temp buffer */
|
|
CapturedPrivileges = ExAllocatePoolWithTag(PagedPool,
|
|
PrivilegesSize,
|
|
TAG_PRIVILEGE_SET);
|
|
if (CapturedPrivileges == NULL)
|
|
{
|
|
DPRINT1("Failed to allocate %u bytes\n", PrivilegesSize);
|
|
Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
_SEH2_YIELD(goto Cleanup);
|
|
}
|
|
|
|
/* Copy the privileges */
|
|
RtlCopyMemory(CapturedPrivileges, Privileges, PrivilegesSize);
|
|
}
|
|
_SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
Status = _SEH2_GetExceptionCode();
|
|
DPRINT1("Got exception 0x%lx\n", Status);
|
|
_SEH2_YIELD(goto Cleanup);
|
|
}
|
|
_SEH2_END;
|
|
|
|
/* Call the internal function */
|
|
SepAdtPrivilegedServiceAuditAlarm(&SubjectContext,
|
|
SubsystemName ? &CapturedSubsystemName : NULL,
|
|
ServiceName ? &CapturedServiceName : NULL,
|
|
ClientToken,
|
|
SubjectContext.PrimaryToken,
|
|
CapturedPrivileges,
|
|
AccessGranted);
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Cleanup:
|
|
/* Cleanup resources */
|
|
if (CapturedSubsystemName.Buffer != NULL)
|
|
ReleaseCapturedUnicodeString(&CapturedSubsystemName, PreviousMode);
|
|
|
|
if (CapturedServiceName.Buffer != NULL)
|
|
ReleaseCapturedUnicodeString(&CapturedServiceName, PreviousMode);
|
|
|
|
if (CapturedPrivileges != NULL)
|
|
ExFreePoolWithTag(CapturedPrivileges, TAG_PRIVILEGE_SET);
|
|
|
|
/* Release the security subject context */
|
|
SeReleaseSubjectContext(&SubjectContext);
|
|
|
|
ObDereferenceObject(ClientToken);
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS NTAPI
|
|
NtPrivilegeObjectAuditAlarm(IN PUNICODE_STRING SubsystemName,
|
|
IN PVOID HandleId,
|
|
IN HANDLE ClientToken,
|
|
IN ULONG DesiredAccess,
|
|
IN PPRIVILEGE_SET Privileges,
|
|
IN BOOLEAN AccessGranted)
|
|
{
|
|
UNIMPLEMENTED;
|
|
return STATUS_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
|
|
_Must_inspect_result_
|
|
__kernel_entry
|
|
NTSTATUS
|
|
NTAPI
|
|
NtAccessCheckAndAuditAlarm(
|
|
_In_ PUNICODE_STRING SubsystemName,
|
|
_In_opt_ 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)
|
|
{
|
|
/* Call the internal function */
|
|
return SepAccessCheckAndAuditAlarm(SubsystemName,
|
|
HandleId,
|
|
NULL,
|
|
ObjectTypeName,
|
|
ObjectName,
|
|
SecurityDescriptor,
|
|
NULL,
|
|
DesiredAccess,
|
|
AuditEventObjectAccess,
|
|
0,
|
|
NULL,
|
|
0,
|
|
GenericMapping,
|
|
GrantedAccess,
|
|
AccessStatus,
|
|
GenerateOnClose,
|
|
FALSE);
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
__kernel_entry
|
|
NTSTATUS
|
|
NTAPI
|
|
NtAccessCheckByTypeAndAuditAlarm(
|
|
_In_ PUNICODE_STRING SubsystemName,
|
|
_In_opt_ PVOID HandleId,
|
|
_In_ PUNICODE_STRING ObjectTypeName,
|
|
_In_ PUNICODE_STRING ObjectName,
|
|
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
_In_opt_ PSID PrincipalSelfSid,
|
|
_In_ ACCESS_MASK DesiredAccess,
|
|
_In_ AUDIT_EVENT_TYPE AuditType,
|
|
_In_ ULONG Flags,
|
|
_In_reads_opt_(ObjectTypeLength) POBJECT_TYPE_LIST ObjectTypeList,
|
|
_In_ ULONG ObjectTypeLength,
|
|
_In_ PGENERIC_MAPPING GenericMapping,
|
|
_In_ BOOLEAN ObjectCreation,
|
|
_Out_ PACCESS_MASK GrantedAccess,
|
|
_Out_ PNTSTATUS AccessStatus,
|
|
_Out_ PBOOLEAN GenerateOnClose)
|
|
{
|
|
/* Call the internal function */
|
|
return SepAccessCheckAndAuditAlarm(SubsystemName,
|
|
HandleId,
|
|
NULL,
|
|
ObjectTypeName,
|
|
ObjectName,
|
|
SecurityDescriptor,
|
|
PrincipalSelfSid,
|
|
DesiredAccess,
|
|
AuditType,
|
|
Flags,
|
|
ObjectTypeList,
|
|
ObjectTypeLength,
|
|
GenericMapping,
|
|
GrantedAccess,
|
|
AccessStatus,
|
|
GenerateOnClose,
|
|
FALSE);
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
__kernel_entry
|
|
NTSTATUS
|
|
NTAPI
|
|
NtAccessCheckByTypeResultListAndAuditAlarm(
|
|
_In_ PUNICODE_STRING SubsystemName,
|
|
_In_opt_ PVOID HandleId,
|
|
_In_ PUNICODE_STRING ObjectTypeName,
|
|
_In_ PUNICODE_STRING ObjectName,
|
|
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
_In_opt_ PSID PrincipalSelfSid,
|
|
_In_ ACCESS_MASK DesiredAccess,
|
|
_In_ AUDIT_EVENT_TYPE AuditType,
|
|
_In_ ULONG Flags,
|
|
_In_reads_opt_(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
|
|
_In_ ULONG ObjectTypeListLength,
|
|
_In_ PGENERIC_MAPPING GenericMapping,
|
|
_In_ BOOLEAN ObjectCreation,
|
|
_Out_writes_(ObjectTypeListLength) PACCESS_MASK GrantedAccessList,
|
|
_Out_writes_(ObjectTypeListLength) PNTSTATUS AccessStatusList,
|
|
_Out_ PBOOLEAN GenerateOnClose)
|
|
{
|
|
/* Call the internal function */
|
|
return SepAccessCheckAndAuditAlarm(SubsystemName,
|
|
HandleId,
|
|
NULL,
|
|
ObjectTypeName,
|
|
ObjectName,
|
|
SecurityDescriptor,
|
|
PrincipalSelfSid,
|
|
DesiredAccess,
|
|
AuditType,
|
|
Flags,
|
|
ObjectTypeList,
|
|
ObjectTypeListLength,
|
|
GenericMapping,
|
|
GrantedAccessList,
|
|
AccessStatusList,
|
|
GenerateOnClose,
|
|
TRUE);
|
|
}
|
|
|
|
_Must_inspect_result_
|
|
__kernel_entry
|
|
NTSTATUS
|
|
NTAPI
|
|
NtAccessCheckByTypeResultListAndAuditAlarmByHandle(
|
|
_In_ PUNICODE_STRING SubsystemName,
|
|
_In_opt_ PVOID HandleId,
|
|
_In_ HANDLE ClientToken,
|
|
_In_ PUNICODE_STRING ObjectTypeName,
|
|
_In_ PUNICODE_STRING ObjectName,
|
|
_In_ PSECURITY_DESCRIPTOR SecurityDescriptor,
|
|
_In_opt_ PSID PrincipalSelfSid,
|
|
_In_ ACCESS_MASK DesiredAccess,
|
|
_In_ AUDIT_EVENT_TYPE AuditType,
|
|
_In_ ULONG Flags,
|
|
_In_reads_opt_(ObjectTypeListLength) POBJECT_TYPE_LIST ObjectTypeList,
|
|
_In_ ULONG ObjectTypeListLength,
|
|
_In_ PGENERIC_MAPPING GenericMapping,
|
|
_In_ BOOLEAN ObjectCreation,
|
|
_Out_writes_(ObjectTypeListLength) PACCESS_MASK GrantedAccessList,
|
|
_Out_writes_(ObjectTypeListLength) PNTSTATUS AccessStatusList,
|
|
_Out_ PBOOLEAN GenerateOnClose)
|
|
{
|
|
UNREFERENCED_PARAMETER(ObjectCreation);
|
|
|
|
/* Call the internal function */
|
|
return SepAccessCheckAndAuditAlarm(SubsystemName,
|
|
HandleId,
|
|
&ClientToken,
|
|
ObjectTypeName,
|
|
ObjectName,
|
|
SecurityDescriptor,
|
|
PrincipalSelfSid,
|
|
DesiredAccess,
|
|
AuditType,
|
|
Flags,
|
|
ObjectTypeList,
|
|
ObjectTypeListLength,
|
|
GenericMapping,
|
|
GrantedAccessList,
|
|
AccessStatusList,
|
|
GenerateOnClose,
|
|
TRUE);
|
|
}
|
|
|
|
/* EOF */
|