/* * PROJECT: ReactOS Kernel * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) * PURPOSE: Security auditing functions * COPYRIGHT: Copyright Eric Kohl * Copyright Timo Kreuzer */ /* INCLUDES *******************************************************************/ #include #define NDEBUG #include #define SEP_PRIVILEGE_SET_MAX_COUNT 60 UNICODE_STRING SeSubsystemName = RTL_CONSTANT_STRING(L"Security"); /* PRIVATE FUNCTIONS ***********************************************************/ /** * @unimplemented * @brief * Peforms a detailed security auditing with an access token. * * @param[in] Token * A valid token object. * * @return * To be added... */ BOOLEAN NTAPI SeDetailedAuditingWithToken( _In_ PTOKEN Token) { /* FIXME */ return FALSE; } /** * @unimplemented * @brief * Peforms a security auditing against a process that is about to * be created. * * @param[in] Process * An object that points to a process which is in process of * creation. * * @return * Nothing. */ VOID NTAPI SeAuditProcessCreate( _In_ PEPROCESS Process) { /* FIXME */ } /** * @unimplemented * @brief * Peforms a security auditing against a process that is about to * be terminated. * * @param[in] Process * An object that points to a process which is in process of * termination. * * @return * Nothing. */ VOID NTAPI SeAuditProcessExit( _In_ PEPROCESS Process) { /* FIXME */ } /** * @brief * Initializes a process audit name and returns it to the caller. * * @param[in] FileObject * File object that points to a name to be queried. * * @param[in] DoAudit * If set to TRUE, the function will perform various security * auditing onto the audit name. * * @param[out] AuditInfo * The returned audit info data. * * @return * Returns STATUS_SUCCESS if process audit name initialization * has completed successfully. STATUS_NO_MEMORY is returned if * pool allocation for object name info has failed. A failure * NTSTATUS code is returned otherwise. */ 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; } /** * @brief * Finds the process image name of a specific process. * * @param[in] Process * Process object submitted by the caller, where the image name * is to be located. * * @param[out] ProcessImageName * An output Unicode string structure with the located process * image name. * * @return * Returns STATUS_SUCCESS if process image name has been located * successfully. STATUS_NO_MEMORY is returned if pool allocation * for the image name has failed. A failure NTSTATUS code is * returned otherwise. */ 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; } /** * @brief * Closes an audit alarm event of an object. * * @param[in] SubsystemName * A Unicode string pointing to the name of the subsystem where auditing * alarm event has to be closed. * * @param[in] HandleId * A handle to an ID where such ID represents the identification of the * object where audit alarm is to be closed. * * @param[in] Sid * A SID that represents the user who attempted to close the audit * alarm. * * @return * Nothing. */ VOID NTAPI SepAdtCloseObjectAuditAlarm( _In_ PUNICODE_STRING SubsystemName, _In_ PVOID HandleId, _In_ PSID Sid) { UNIMPLEMENTED; } /** * @brief * Performs an audit alarm to a privileged service request. * This is a worker function. * * @param[in] SubjectContext * A security subject context used for the auditing process. * * @param[in] SubsystemName * A Unicode string that represents the name of a subsystem that * actuated the procedure of alarm auditing of a privileged * service. * * @param[in] ServiceName * A Unicode string that represents the name of a privileged * service request for auditing. * * @param[in] Token * An access token. * * @param[in] PrimaryToken * A primary access token. * * @param[in] Privileges * An array set of privileges used to check if the privileged * service does actually have all the required set of privileges * for security access. * * @param[in] AccessGranted * When auditing is done, the function will return TRUE to the caller * if access is granted, FALSE otherwise. * * @return * Nothing. */ VOID NTAPI SepAdtPrivilegedServiceAuditAlarm( _In_ 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"); } /** * @brief * Performs an audit alarm to a privileged service request. * * @param[in] ServiceName * A Unicode string that represents the name of a privileged * service request for auditing. * * @param[in] SubjectContext * A security subject context used for the auditing process. * * @param[in] PrivilegeSet * An array set of privileges used to check if the privileged * service does actually have all the required set of privileges * for security access. * * @param[in] AccessGranted * When auditing is done, the function will return TRUE to the caller * if access is granted, FALSE otherwise. * * @return * Nothing. */ 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); } /** * @unimplemented * @brief * Worker function that serves as the main heart and brain of the whole * concept and implementation of auditing in the kernel. * * @param[in] SubsystemName * A Unicode string that represents the name of a subsystem that * actuates the auditing process. * * @param[in] HandleId * A handle to an ID used to identify an object where auditing * is to be done. * * @param[in] SubjectContext * Security subject context. * * @param[in] ObjectTypeName * A Unicode string that represents the name of an object type. * * @param[in] ObjectName * The name of the object. * * @param[in] SecurityDescriptor * A security descriptor with internal security information details * for audit. * * @param[in] PrincipalSelfSid * A principal self user SID. * * @param[in] DesiredAccess * The desired access rights masks requested by the caller. * * @param[in] AuditType * Type of audit to start. This parameter influences how an audit * should be done. * * @param[in] HaveAuditPrivilege * If set to TRUE, the security subject context has the audit privilege thus * it is allowed the ability to perform the audit. * * @param[in] ObjectTypeList * A list of object types. * * @param[in] ObjectTypeListLength * The length size of the list. * * @param[in] GenericMapping * The generic mapping table of access rights used whilst performing auditing * sequence procedure. * * @param[out] GrantedAccessList * This parameter is used to return to the caller a list of actual granted access * rights masks that the audited object has. * * @param[out] AccessStatusList * This parameter is used to return to the caller a list of status return codes. * The function may actually return a single NTSTATUS code if the calling thread * sets UseResultList parameter to FALSE. * * @param[out] GenerateOnClose * Returns TRUE if the function has generated a list of granted access rights and * status codes on termination, FALSE otherwise. * * @param[in] UseResultList * If set to TRUE, the caller wants that the function should only return a single * NTSTATUS code. * * @return * Returns STATUS_SUCCESS if the function has completed the whole internal * auditing procedure mechanism with success. */ _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_INTERNAL 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; } /** * @brief * Performs security auditing, if the specific object can be granted * security access or not. * * @param[in] SubsystemName * A Unicode string that represents the name of a subsystem that * actuates the auditing process. * * @param[in] HandleId * A handle to an ID used to identify an object where auditing * is to be done. * * @param[in] SubjectContext * Security subject context. * * @param[in] ObjectTypeName * A Unicode string that represents the name of an object type. * * @param[in] ObjectName * The name of the object. * * @param[in] SecurityDescriptor * A security descriptor with internal security information details * for audit. * * @param[in] PrincipalSelfSid * A principal self user SID. * * @param[in] DesiredAccess * The desired access rights masks requested by the caller. * * @param[in] AuditType * Type of audit to start. This parameter influences how an audit * should be done. * * @param[in] Flags * Flag bitmask parameter. * * @param[in] HaveAuditPrivilege * If set to TRUE, the security subject context has the audit privilege thus * it is allowed the ability to perform the audit. * * @param[in] ObjectTypeList * A list of object types. * * @param[in] ObjectTypeListLength * The length size of the list. * * @param[in] GenericMapping * The generic mapping table of access rights used whilst performing auditing * sequence procedure. * * @param[out] GrantedAccessList * This parameter is used to return to the caller a list of actual granted access * rights masks that the audited object has. * * @param[out] AccessStatusList * This parameter is used to return to the caller a list of status return codes. * The function may actually return a single NTSTATUS code if the calling thread * sets UseResultList parameter to FALSE. * * @param[out] GenerateOnClose * Returns TRUE if the function has generated a list of granted access rights and * status codes on termination, FALSE otherwise. * * @param[in] UseResultList * If set to TRUE, the caller wants that the function should only return a single * NTSTATUS code. * * @return * Returns STATUS_SUCCESS if the function has completed the whole internal * auditing procedure mechanism with success. STATUS_INVALID_PARAMETER is * returned if one of the parameters do not satisfy the general requirements * by the function. STATUS_INSUFFICIENT_RESOURCES is returned if pool memory * allocation has failed. STATUS_PRIVILEGE_NOT_HELD is returned if the current * security subject context does not have the required audit privilege to actually * perform auditing in the first place. STATUS_INVALID_SECURITY_DESCR is returned * if the security descriptor provided by the caller is not valid, that is, such * descriptor doesn't belong to the main user (owner) and current group. * STATUS_GENERIC_NOT_MAPPED is returned if the access rights masks aren't actually * mapped. A failure NTSTATUS code is returned otherwise. */ _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_INTERNAL 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 */ Status = SepAccessCheckAndAuditAlarmWorker(&CapturedSubsystemName, HandleId, &SubjectContext, &CapturedObjectTypeName, &CapturedObjectName, CapturedSecurityDescriptor, CapturedPrincipalSelfSid, DesiredAccess, AuditType, HaveAuditPrivilege, CapturedObjectTypeList, ObjectTypeListLength, &LocalGenericMapping, SafeGrantedAccessList, SafeAccessStatusList, &LocalGenerateOnClose, UseResultList); if (!NT_SUCCESS(Status)) goto Cleanup; /* 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 * @brief * Performs an audit against a hard link creation. * * @param[in] FileName * A Unicode string that points to the name of the file. * * @param[in] LinkName * A Unicode string that points to a link. * * @param[out] bSuccess * If TRUE, the function has successfully audited * the hard link and security access can be granted, * FALSE otherwise. * * @return * Nothing. */ VOID NTAPI SeAuditHardLinkCreation( _In_ PUNICODE_STRING FileName, _In_ PUNICODE_STRING LinkName, _In_ BOOLEAN bSuccess) { UNIMPLEMENTED; } /** * @unimplemented * @brief * Determines whether auditing against file events is being * done or not. * * @param[in] AccessGranted * If set to TRUE, the access attempt is deemed as successful * otherwise set it to FALSE. * * @param[in] SecurityDescriptor * A security descriptor. * * @return * Returns TRUE if auditing is being currently done, FALSE otherwise. */ BOOLEAN NTAPI SeAuditingFileEvents( _In_ BOOLEAN AccessGranted, _In_ PSECURITY_DESCRIPTOR SecurityDescriptor) { UNIMPLEMENTED; return FALSE; } /** * @unimplemented * @brief * Determines whether auditing against file events with subject context * is being done or not. * * @param[in] AccessGranted * If set to TRUE, the access attempt is deemed as successful * otherwise set it to FALSE. * * @param[in] SecurityDescriptor * A security descriptor. * * @param[in] SubjectSecurityContext * If specified, the function will check if security auditing is currently * being done with this context. * * @return * Returns TRUE if auditing is being currently done, FALSE otherwise. */ BOOLEAN NTAPI SeAuditingFileEventsWithContext( _In_ BOOLEAN AccessGranted, _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, _In_opt_ PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext) { UNIMPLEMENTED_ONCE; return FALSE; } /** * @unimplemented * @brief * Determines whether auditing against hard links events is being * done or not. * * @param[in] AccessGranted * If set to TRUE, the access attempt is deemed as successful * otherwise set it to FALSE. * * @param[in] SecurityDescriptor * A security descriptor. * * @return * Returns TRUE if auditing is being currently done, FALSE otherwise. */ BOOLEAN NTAPI SeAuditingHardLinkEvents( _In_ BOOLEAN AccessGranted, _In_ PSECURITY_DESCRIPTOR SecurityDescriptor) { UNIMPLEMENTED; return FALSE; } /** * @unimplemented * @brief * Determines whether auditing against hard links events with subject context * is being done or not. * * @param[in] AccessGranted * If set to TRUE, the access attempt is deemed as successful * otherwise set it to FALSE. * * @param[in] SecurityDescriptor * A security descriptor. * * @param[in] SubjectSecurityContext * If specified, the function will check if security auditing is currently * being done with this context. * * @return * Returns TRUE if auditing is being currently done, FALSE otherwise. */ BOOLEAN NTAPI SeAuditingHardLinkEventsWithContext( _In_ BOOLEAN AccessGranted, _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, _In_opt_ PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext) { UNIMPLEMENTED; return FALSE; } /** * @unimplemented * @brief * Determines whether auditing against files or global events with * subject context is being done or not. * * @param[in] AccessGranted * If set to TRUE, the access attempt is deemed as successful * otherwise set it to FALSE. * * @param[in] SecurityDescriptor * A security descriptor. * * @param[in] SubjectSecurityContext * If specified, the function will check if security auditing is currently * being done with this context. * * @return * Returns TRUE if auditing is being currently done, FALSE otherwise. */ BOOLEAN NTAPI SeAuditingFileOrGlobalEvents( _In_ BOOLEAN AccessGranted, _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, _In_ PSECURITY_SUBJECT_CONTEXT SubjectSecurityContext) { UNIMPLEMENTED; return FALSE; } /** * @unimplemented * @brief * Closes an alarm audit of an object. * * @param[in] Object * An arbitrary pointer data that points to the object. * * @param[in] Handle * A handle of the said object. * * @param[in] PerformAction * Set this to TRUE to perform any auxiliary action, otherwise * set to FALSE. * * @return * Nothing. */ VOID NTAPI SeCloseObjectAuditAlarm( _In_ PVOID Object, _In_ HANDLE Handle, _In_ BOOLEAN PerformAction) { UNIMPLEMENTED; } /** * @unimplemented * @brief * Deletes an alarm audit of an object. * * @param[in] Object * An arbitrary pointer data that points to the object. * * @param[in] Handle * A handle of the said object. * * @return * Nothing. */ VOID NTAPI SeDeleteObjectAuditAlarm( _In_ PVOID Object, _In_ HANDLE Handle) { UNIMPLEMENTED; } /** * @unimplemented * @brief * Creates an audit with alarm notification of an object * that is being opened. * * @param[in] ObjectTypeName * A Unicode string that points to the object type name. * * @param[in] Object * If specified, the function will use this parameter to * directly open the object. * * @param[in] AbsoluteObjectName * If specified, the function will use this parameter to * directly open the object through the absolute name * of the object. * * @param[in] SecurityDescriptor * A security descriptor. * * @param[in] AccessState * An access state right mask when opening the object. * * @param[in] ObjectCreated * Set this to TRUE if the object has been fully created, * FALSE otherwise. * * @param[in] AccessGranted * Set this to TRUE if access was deemed as granted. * * @param[in] AccessMode * Processor level access mode. * * @param[out] GenerateOnClose * A boolean flag returned to the caller once audit generation procedure * finishes. * * @return * Nothing. */ VOID NTAPI SeOpenObjectAuditAlarm( _In_ PUNICODE_STRING ObjectTypeName, _In_opt_ PVOID Object, _In_opt_ PUNICODE_STRING AbsoluteObjectName, _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 * @brief * Creates an audit with alarm notification of an object * that is being opened for deletion. * * @param[in] ObjectTypeName * A Unicode string that points to the object type name. * * @param[in] Object * If specified, the function will use this parameter to * directly open the object. * * @param[in] AbsoluteObjectName * If specified, the function will use this parameter to * directly open the object through the absolute name * of the object. * * @param[in] SecurityDescriptor * A security descriptor. * * @param[in] AccessState * An access state right mask when opening the object. * * @param[in] ObjectCreated * Set this to TRUE if the object has been fully created, * FALSE otherwise. * * @param[in] AccessGranted * Set this to TRUE if access was deemed as granted. * * @param[in] AccessMode * Processor level access mode. * * @param[out] GenerateOnClose * A boolean flag returned to the caller once audit generation procedure * finishes. * * @return * Nothing. */ VOID NTAPI SeOpenObjectForDeleteAuditAlarm( _In_ PUNICODE_STRING ObjectTypeName, _In_opt_ PVOID Object, _In_opt_ PUNICODE_STRING AbsoluteObjectName, _In_ PSECURITY_DESCRIPTOR SecurityDescriptor, _In_ PACCESS_STATE AccessState, _In_ BOOLEAN ObjectCreated, _In_ BOOLEAN AccessGranted, _In_ KPROCESSOR_MODE AccessMode, _Out_ PBOOLEAN GenerateOnClose) { UNIMPLEMENTED; } /** * @unimplemented * @brief * Raises an audit with alarm notification message * when an object tries to acquire this privilege. * * @param[in] Handle * A handle to an object. * * @param[in] SubjectContext * The security subject context for auditing. * * @param[in] DesiredAccess * The desired right access masks requested by the caller. * * @param[in] Privileges * An array set of privileges for auditing. * * @param[out] AccessGranted * When the auditing procedure routine ends, it returns TRUE to the * caller if the object has the required privileges for access, * FALSE otherwise. * * @param[in] CurrentMode * Processor level access mode. * * @return * Nothing. */ 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 ***************************************************************/ /** * @brief * Raises an alarm audit message when an object is about * to be closed. * * @param[in] SubsystemName * A Unicode string that points to the name of the subsystem. * * @param[in] HandleId * A handle of an ID used for identification instance for auditing. * * @param[in] GenerateOnClose * A boolean value previously created by the "open" equivalent of this * function. If the caller explicitly sets this to FALSE, the function * assumes that the object is not opened. * * @return * Returns STATUS_SUCCESS if all the operations have completed successfully. * STATUS_PRIVILEGE_NOT_HELD is returned if the security subject context * does not have the audit privilege to actually begin auditing procedures * in the first place. */ NTSTATUS NTAPI NtCloseObjectAuditAlarm( _In_ PUNICODE_STRING SubsystemName, _In_ PVOID HandleId, _In_ 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; } /** * @unimplemented * @brief * Raises an alarm audit message when an object is about * to be deleted. * * @param[in] SubsystemName * A Unicode string that points to the name of the subsystem. * * @param[in] HandleId * A handle of an ID used for identification instance for auditing. * * @param[in] GenerateOnClose * A boolean value previously created by the "open" equivalent of this * function. If the caller explicitly sets this to FALSE, the function * assumes that the object is not opened. * * @return * To be added... */ NTSTATUS NTAPI NtDeleteObjectAuditAlarm( _In_ PUNICODE_STRING SubsystemName, _In_ PVOID HandleId, _In_ BOOLEAN GenerateOnClose) { UNIMPLEMENTED; return STATUS_NOT_IMPLEMENTED; } /** * @unimplemented * @brief * Raises an alarm audit message when an object is about * to be opened. * * @param[in] SubjectContext * A security subject context for auditing. * * @param[in] SubsystemName * A Unicode string that points to a name of the subsystem. * * @param[in] HandleId * A handle to an ID used for identification instance for auditing. * * @param[in] ObjectTypeName * A Unicode string that points to an object type name. * * @param[in] ObjectName * The name of the object. * * @param[in] SecurityDescriptor * A security descriptor. * * @param[in] ClientToken * A client access token, representing the client we want to impersonate. * * @param[in] DesiredAccess * The desired access rights masks requested by the caller. * * @param[in] GrantedAccess * The granted access mask rights. * * @param[in] Privileges * If specified, the function will use this set of privileges to audit. * * @param[in] ObjectCreation * Set this to TRUE if the object has just been created. * * @param[in] AccessGranted * Set this to TRUE if the access attempt was deemed as granted. * * @param[out] GenerateOnClose * A boolean flag returned to the caller once audit generation procedure * finishes. * * @return * Nothing. */ 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; } /** * @brief * Raises an alarm audit message when an object is about * to be opened. * * @param[in] SubsystemName * A Unicode string that points to a name of the subsystem. * * @param[in] HandleId * A handle to an ID used for identification instance for auditing. * * @param[in] ObjectTypeName * A Unicode string that points to an object type name. * * @param[in] ObjectName * The name of the object. * * @param[in] SecurityDescriptor * A security descriptor. * * @param[in] ClientTokenHandle * A handle to a client access token. * * @param[in] DesiredAccess * The desired access rights masks requested by the caller. * * @param[in] GrantedAccess * The granted access mask rights. * * @param[in] PrivilegeSet * If specified, the function will use this set of privileges to audit. * * @param[in] ObjectCreation * Set this to TRUE if the object has just been created. * * @param[in] AccessGranted * Set this to TRUE if the access attempt was deemed as granted. * * @param[out] GenerateOnClose * A boolean flag returned to the caller once audit generation procedure * finishes. * * @return * Returns STATUS_SUCCESS if all the operations have been completed successfully. * STATUS_PRIVILEGE_NOT_HELD is returned if the given subject context does not * hold the required audit privilege to actually begin auditing in the first place. * STATUS_BAD_IMPERSONATION_LEVEL is returned if the security impersonation level * of the client token is not on par with the impersonation level that alllows * impersonation. STATUS_INVALID_PARAMETER is returned if the caller has * submitted a bogus set of privileges as such array set exceeds the maximum * count of privileges that the kernel can accept. A failure NTSTATUS code * is returned otherwise. */ __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; } /** * @brief * Raises an alarm audit message when a caller attempts to request * a privileged service call. * * @param[in] SubsystemName * A Unicode string that points to a name of the subsystem. * * @param[in] ServiceName * A Unicode string that points to a name of the privileged service. * * @param[in] ClientTokenHandle * A handle to a client access token. * * @param[in] Privileges * An array set of privileges. * * @param[in] AccessGranted * Set this to TRUE if the access attempt was deemed as granted. * * @return * Returns STATUS_SUCCESS if all the operations have been completed successfully. * STATUS_PRIVILEGE_NOT_HELD is returned if the given subject context does not * hold the required audit privilege to actually begin auditing in the first place. * STATUS_BAD_IMPERSONATION_LEVEL is returned if the security impersonation level * of the client token is not on par with the impersonation level that alllows * impersonation. STATUS_INVALID_PARAMETER is returned if the caller has * submitted a bogus set of privileges as such array set exceeds the maximum * count of privileges that the kernel can accept. A failure NTSTATUS code * is returned otherwise. */ __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; } /** * @brief * Raises an alarm audit message when a caller attempts to access a * privileged object. * * @param[in] SubsystemName * A Unicode string that points to a name of the subsystem. * * @param[in] HandleId * A handle to an ID that is used as identification instance for auditing. * * @param[in] ClientToken * A handle to a client access token. * * @param[in] DesiredAccess * A handle to a client access token. * * @param[in] Privileges * An array set of privileges. * * @param[in] AccessGranted * Set this to TRUE if the access attempt was deemed as granted. * * @return * To be added... */ 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; } /** * @brief * Raises an alarm audit message when a caller attempts to access an * object and determine if the access can be made. * * @param[in] SubsystemName * A Unicode string that points to a name of the subsystem. * * @param[in] HandleId * A handle to an ID that is used as identification instance for auditing. * * @param[in] ObjectTypeName * The name of the object type. * * @param[in] ObjectName * The object name. * * @param[in] SecurityDescriptor * A security descriptor. * * @param[in] DesiredAccess * The desired access rights masks requested by the caller. * * @param[in] GenericMapping * The generic mapping of access mask rights. * * @param[in] ObjectCreation * Set this to TRUE if the object has just been created. * * @param[out] GrantedAccess * Returns the granted access rights. * * @param[out] AccessStatus * Returns a NTSTATUS status code indicating whether access check * can be granted or not. * * @param[out] GenerateOnClose * Returns TRUE if the function has generated a list of granted access rights and * status codes on termination, FALSE otherwise. * * @return * See SepAccessCheckAndAuditAlarm. */ _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); } /** * @brief * Raises an alarm audit message when a caller attempts to access an * object and determine if the access can be made by type. * * @param[in] SubsystemName * A Unicode string that points to a name of the subsystem. * * @param[in] HandleId * A handle to an ID that is used as identification instance for auditing. * * @param[in] ObjectTypeName * The name of the object type. * * @param[in] ObjectName * The object name. * * @param[in] SecurityDescriptor * A security descriptor. * * @param[in] PrincipalSelfSid * A principal self user SID. * * @param[in] DesiredAccess * The desired access rights masks requested by the caller. * * @param[in] AuditType * Type of audit to start, influencing how the audit should * be done. * * @param[in] Flags * Flag bitmask, used to check if auditing can be done * without privileges. * * @param[in] ObjectTypeList * A list of object types. * * @param[in] ObjectTypeLength * The length size of the list. * * @param[in] GenericMapping * The generic mapping of access mask rights. * * @param[in] ObjectCreation * Set this to TRUE if the object has just been created. * * @param[out] GrantedAccess * Returns the granted access rights. * * @param[out] AccessStatus * Returns a NTSTATUS status code indicating whether access check * can be granted or not. * * @param[out] GenerateOnClose * Returns TRUE if the function has generated a list of granted access rights and * status codes on termination, FALSE otherwise. * * @return * See SepAccessCheckAndAuditAlarm. */ _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); } /** * @brief * Raises an alarm audit message when a caller attempts to access an * object and determine if the access can be made by given type result. * * @param[in] SubsystemName * A Unicode string that points to a name of the subsystem. * * @param[in] HandleId * A handle to an ID that is used as identification instance for auditing. * * @param[in] ObjectTypeName * The name of the object type. * * @param[in] ObjectName * The object name. * * @param[in] SecurityDescriptor * A security descriptor. * * @param[in] PrincipalSelfSid * A principal self user SID. * * @param[in] DesiredAccess * The desired access rights masks requested by the caller. * * @param[in] AuditType * Type of audit to start, influencing how the audit should * be done. * * @param[in] Flags * Flag bitmask, used to check if auditing can be done * without privileges. * * @param[in] ObjectTypeList * A list of object types. * * @param[in] ObjectTypeLength * The length size of the list. * * @param[in] GenericMapping * The generic mapping of access mask rights. * * @param[in] ObjectCreation * Set this to TRUE if the object has just been created. * * @param[out] GrantedAccessList * Returns the granted access rights. * * @param[out] AccessStatusList * Returns a NTSTATUS status code indicating whether access check * can be granted or not. * * @param[out] GenerateOnClose * Returns TRUE if the function has generated a list of granted access rights and * status codes on termination, FALSE otherwise. * * @return * See SepAccessCheckAndAuditAlarm. */ _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); } /** * @brief * Raises an alarm audit message when a caller attempts to access an * object and determine if the access can be made by given type result * and a token handle. * * @param[in] SubsystemName * A Unicode string that points to a name of the subsystem. * * @param[in] HandleId * A handle to an ID that is used as identification instance for auditing. * * @param[in] ClientToken * A handle to a client access token. * * @param[in] ObjectTypeName * The name of the object type. * * @param[in] ObjectName * The object name. * * @param[in] SecurityDescriptor * A security descriptor. * * @param[in] PrincipalSelfSid * A principal self user SID. * * @param[in] DesiredAccess * The desired access rights masks requested by the caller. * * @param[in] AuditType * Type of audit to start, influencing how the audit should * be done. * * @param[in] Flags * Flag bitmask, used to check if auditing can be done * without privileges. * * @param[in] ObjectTypeList * A list of object types. * * @param[in] ObjectTypeLength * The length size of the list. * * @param[in] GenericMapping * The generic mapping of access mask rights. * * @param[in] ObjectCreation * Set this to TRUE if the object has just been created. * * @param[out] GrantedAccessList * Returns the granted access rights. * * @param[out] AccessStatusList * Returns a NTSTATUS status code indicating whether access check * can be granted or not. * * @param[out] GenerateOnClose * Returns TRUE if the function has generated a list of granted access rights and * status codes on termination, FALSE otherwise. * * @return * See SepAccessCheckAndAuditAlarm. */ _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 */