You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1195 lines
36 KiB
1195 lines
36 KiB
/*++
|
|
|
|
Copyright (c) 2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
adtqueue.c
|
|
|
|
Abstract:
|
|
|
|
This module implements the routines for the Authz audit queue.
|
|
|
|
Author:
|
|
|
|
Jeff Hamblin - May 2000
|
|
|
|
Environment:
|
|
|
|
User mode only.
|
|
|
|
Revision History:
|
|
|
|
Created - May 2000
|
|
|
|
--*/
|
|
|
|
/*++
|
|
|
|
The Authz Audit Queue Algorithm
|
|
|
|
The following are used in the queueing algorithm:
|
|
|
|
hAuthzAuditQueueLowEvent - event that is signalled when threads are free
|
|
to place audits on the queue (queue is below high water mark). Note that
|
|
this is an auto reset event: when the event is signalled, exactly one
|
|
waiting thread is scheduled to run and the event then returns to a
|
|
nonsignalled state.
|
|
|
|
bAuthzAuditQueueHighEvent - boolean indicating that audits may not be
|
|
added (queue is over high water mark).
|
|
|
|
hAuthzAuditAddedEvent - event that is signalled when the queue is empty and an
|
|
audit get placed on the queue. The dequeueing thread runs when this is signalled.
|
|
|
|
hAuthzAuditQueueEmptyEvent - signals when the queue is empty.
|
|
|
|
AuthzAuditQueue - doubly linked list. This is the audit queue.
|
|
|
|
AuthzAuditQueueLength - The current number of audits in the queue.
|
|
|
|
hAuthzAuditThread - the dequeueing thread.
|
|
|
|
AuthzAuditQueueLock - critical section locking the queue and related
|
|
variables.
|
|
|
|
Assume that the Resource Manager wishes to monitor the queue length and
|
|
has specified High and Low water marks to control the growth of the queue.
|
|
If the queue length reaches the High water mark, then all queueing threads
|
|
will be blocked until the dequeueing thread has reduced the queue length
|
|
to the Low water mark.
|
|
|
|
Here is the flow of code for a thread attempting to log an audit (via
|
|
AuthziLogAuditEvent()) when the Resource Manager is monitoring the queue
|
|
length:
|
|
|
|
if QueueLength > .75 * HighWater # this is heuristic to save unnecessary
|
|
wait until the LowEvent is signalled # kernel transitions
|
|
enter queue critical section
|
|
{
|
|
insert audit on queue
|
|
QueueLength ++
|
|
signal AuditAddedEvent # notifying the dequeue thread
|
|
if (QueueLength >= HighWater)
|
|
{
|
|
bHigh = TRUE
|
|
}
|
|
}
|
|
leave critical section
|
|
|
|
...[code overhead, execute cleanup code in AuthziLogAuditEvent ...]
|
|
|
|
enter queue critical section
|
|
{
|
|
if (!bHigh)
|
|
{
|
|
if (QueueLength <= HighWater)
|
|
{
|
|
signal LowEvent #allow other threads to run
|
|
}
|
|
}
|
|
ASSERT(FALSE);
|
|
}
|
|
leave critical section
|
|
|
|
Here is the algorithm for the dequeueing thread:
|
|
|
|
while (TRUE)
|
|
{
|
|
wait for AuditAdded event
|
|
while (QueueLength > 0)
|
|
{
|
|
enter queue critical section
|
|
{
|
|
remove audit from head of list
|
|
QueueLength--
|
|
if (bHigh)
|
|
{
|
|
if (QueueLength <= LowWater)
|
|
{
|
|
bHigh = FALSE
|
|
signal LowEvent # tell threads it is okay to queue
|
|
}
|
|
}
|
|
}
|
|
release critical section
|
|
|
|
Send to LSA
|
|
}
|
|
|
|
enter critical section
|
|
{
|
|
if (QueueLength == 0)
|
|
{
|
|
reset AuditAdded event # make myself wait
|
|
}
|
|
}
|
|
release critical section
|
|
}
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
#pragma hdrstop
|
|
|
|
#include <authzp.h>
|
|
#include <authzi.h>
|
|
|
|
#ifdef AUTHZ_AUDIT_COUNTER
|
|
LONG AuthzpAuditsEnqueued = 0;
|
|
LONG AuthzpAuditsDequeued = 0;
|
|
#endif
|
|
|
|
|
|
BOOL
|
|
AuthzpEnQueueAuditEvent(
|
|
PAUTHZI_AUDIT_QUEUE pQueue,
|
|
PAUTHZ_AUDIT_QUEUE_ENTRY pAudit
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This enqueues an audit without regard to any queue size limits. It does minimal event management.
|
|
|
|
Arguments
|
|
|
|
pQueue - Pointer to the queue to place the audit on.
|
|
pAudit - Pointer to the audit to enqueue.
|
|
|
|
Return Value
|
|
|
|
Boolean, TRUE on success, FALSE on failure.
|
|
Extended information available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b = TRUE;
|
|
|
|
RtlEnterCriticalSection(&pQueue->AuthzAuditQueueLock);
|
|
InsertTailList(&pQueue->AuthzAuditQueue, &pAudit->list);
|
|
pQueue->AuthzAuditQueueLength++;
|
|
|
|
#ifdef AUTHZ_AUDIT_COUNTER
|
|
InterlockedIncrement(&AuthzpAuditsEnqueued);
|
|
#endif
|
|
|
|
//
|
|
// Only set the AuditAdded event if the length goes from 0 to 1. This
|
|
// saves us redundant kernel transitions.
|
|
//
|
|
|
|
if (pQueue->AuthzAuditQueueLength == 1)
|
|
{
|
|
b = SetEvent(pQueue->hAuthzAuditAddedEvent);
|
|
if (!b)
|
|
{
|
|
ASSERT(L"AUTHZ: SetEvent on hAuthzAuditAddedEvent handle failed." && FALSE);
|
|
goto Cleanup;
|
|
}
|
|
|
|
b = ResetEvent(pQueue->hAuthzAuditQueueEmptyEvent);
|
|
if (!b)
|
|
{
|
|
ASSERT(L"AUTHZ: ResetEvent on hAuthzAuditQueueEmptyEvent handle failed." && FALSE);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
RtlLeaveCriticalSection(&pQueue->AuthzAuditQueueLock);
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpEnQueueAuditEventMonitor(
|
|
PAUTHZI_AUDIT_QUEUE pQueue,
|
|
PAUTHZ_AUDIT_QUEUE_ENTRY pAudit
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This enqueues an audit and sets appropriate events for queue size monitoring.
|
|
|
|
Arguments
|
|
|
|
pQueue - pointer to the queue to place audit on.
|
|
pAudit - pointer to the audit to queue.
|
|
|
|
Return Value
|
|
|
|
Boolean, TRUE on success, FALSE on failure.
|
|
Extended information available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b = TRUE;
|
|
|
|
RtlEnterCriticalSection(&pQueue->AuthzAuditQueueLock);
|
|
InsertTailList(&pQueue->AuthzAuditQueue, &pAudit->list);
|
|
pQueue->AuthzAuditQueueLength++;
|
|
|
|
#ifdef AUTHZ_AUDIT_COUNTER
|
|
InterlockedIncrement(&AuthzpAuditsEnqueued);
|
|
#endif
|
|
|
|
//
|
|
// Only set the AuditAdded event if the length goes from 0 to 1. This
|
|
// saves us redundant kernel transitions.
|
|
//
|
|
|
|
if (pQueue->AuthzAuditQueueLength == 1)
|
|
{
|
|
b = SetEvent(pQueue->hAuthzAuditAddedEvent);
|
|
if (!b)
|
|
{
|
|
ASSERT(L"AUTHZ: SetEvent on hAuthzAuditAddedEvent handle failed." && FALSE);
|
|
goto Cleanup;
|
|
}
|
|
|
|
b = ResetEvent(pQueue->hAuthzAuditQueueEmptyEvent);
|
|
if (!b)
|
|
{
|
|
ASSERT(L"AUTHZ: ResetEvent on hAuthzAuditQueueEmptyEvent handle failed." && FALSE);
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if (pQueue->AuthzAuditQueueLength >= pQueue->dwAuditQueueHigh)
|
|
{
|
|
#ifdef AUTHZ_DEBUG_QUEUE
|
|
wprintf(L"___Setting HIGH water mark ON\n");
|
|
fflush(stdout);
|
|
#endif
|
|
pQueue->bAuthzAuditQueueHighEvent = TRUE;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
RtlLeaveCriticalSection(&pQueue->AuthzAuditQueueLock);
|
|
return b;
|
|
}
|
|
|
|
|
|
ULONG
|
|
AuthzpDeQueueThreadWorker(
|
|
LPVOID lpParameter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This is the function run by the dequeueing thread. It pulls audits from the queue
|
|
and sends them to LSA.
|
|
|
|
Arguments
|
|
|
|
lpParameter - generic thread parameter. The actual parameter passed in is of
|
|
type PAUTHZI_AUDIT_QUEUE.
|
|
|
|
Return Value
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL b;
|
|
PAUTHZ_AUDIT_QUEUE_ENTRY pAuditEntry = NULL;
|
|
PAUTHZI_AUDIT_QUEUE pQueue = (PAUTHZI_AUDIT_QUEUE) lpParameter;
|
|
DWORD dwError;
|
|
|
|
while (pQueue->bWorker)
|
|
{
|
|
|
|
//
|
|
// The thread waits until there are audits in the queue.
|
|
//
|
|
|
|
dwError = WaitForSingleObject(
|
|
pQueue->hAuthzAuditAddedEvent,
|
|
INFINITE
|
|
);
|
|
|
|
//
|
|
// If the wait does not succeed either something is very wrong or the hAuthzAuditAddedEvent
|
|
// was closed, indicating that the RM is freeing its hRMAuditInfo. The thread should exit.
|
|
//
|
|
|
|
if (WAIT_OBJECT_0 != dwError)
|
|
{
|
|
ASSERT(L"WaitForSingleObject on hAuthzAuditAddedEvent failed." && FALSE);
|
|
}
|
|
|
|
//
|
|
// The thread remains active while there are audits in the queue.
|
|
//
|
|
|
|
while (pQueue->AuthzAuditQueueLength > 0)
|
|
{
|
|
RtlEnterCriticalSection(&pQueue->AuthzAuditQueueLock);
|
|
pAuditEntry = (PAUTHZ_AUDIT_QUEUE_ENTRY) (pQueue->AuthzAuditQueue).Flink;
|
|
RemoveEntryList(&pAuditEntry->list);
|
|
pQueue->AuthzAuditQueueLength--;
|
|
|
|
#ifdef AUTHZ_AUDIT_COUNTER
|
|
InterlockedIncrement(&AuthzpAuditsDequeued);
|
|
#endif
|
|
|
|
if (FLAG_ON(pQueue->Flags, AUTHZ_MONITOR_AUDIT_QUEUE_SIZE))
|
|
{
|
|
if (TRUE == pQueue->bAuthzAuditQueueHighEvent)
|
|
{
|
|
if (pQueue->AuthzAuditQueueLength <= pQueue->dwAuditQueueLow)
|
|
{
|
|
|
|
//
|
|
// If the High flag is on and the length is now reduced to the low water mark, then
|
|
// set appropriate events.
|
|
//
|
|
|
|
pQueue->bAuthzAuditQueueHighEvent = FALSE;
|
|
b = SetEvent(pQueue->hAuthzAuditQueueLowEvent);
|
|
if (!b)
|
|
{
|
|
ASSERT(L"SetEvent on hAuthzAuditQueueLowEvent failed." && FALSE);
|
|
}
|
|
#ifdef AUTHZ_DEBUG_QUEUE
|
|
wprintf(L"** _____ TURNING HIGH WATER OFF _____\n");
|
|
fflush(stdout);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
RtlLeaveCriticalSection(&pQueue->AuthzAuditQueueLock);
|
|
|
|
b = AuthzpSendAuditToLsa(
|
|
(AUDIT_HANDLE)(pAuditEntry->pAAETO->hAudit),
|
|
pAuditEntry->Flags,
|
|
pAuditEntry->pAuditParams,
|
|
pAuditEntry->pReserved
|
|
);
|
|
|
|
#ifdef AUTHZ_DEBUG_QUEUE
|
|
if (!b)
|
|
{
|
|
DbgPrint("Error in AuthzpSendAuditToLsa() :: Error = %d = 0x%x\n", GetLastError(), GetLastError());
|
|
DbgPrint("Context = 0x%x\n", pAuditEntry->pAAETO->hAudit);
|
|
DbgPrint("Flags = 0x%x\n", pAuditEntry->Flags);
|
|
DbgPrint("Params = 0x%x\n", pAuditEntry->pAuditParams);
|
|
ASSERT(FALSE);
|
|
}
|
|
#endif
|
|
b = AuthzpDereferenceAuditEventType((AUTHZ_AUDIT_EVENT_TYPE_HANDLE)pAuditEntry->pAAETO);
|
|
if (!b)
|
|
{
|
|
ASSERT(FALSE && L"Deref AuditEventType failed.");
|
|
}
|
|
AuthzpFree(pAuditEntry->pAuditParams);
|
|
AuthzpFree(pAuditEntry);
|
|
}
|
|
|
|
RtlEnterCriticalSection(&pQueue->AuthzAuditQueueLock);
|
|
if (0 == pQueue->AuthzAuditQueueLength)
|
|
{
|
|
b = ResetEvent(pQueue->hAuthzAuditAddedEvent);
|
|
if (!b)
|
|
{
|
|
ASSERT(L"ResetEvent on hAuthzAuditAddedEvent failed." && FALSE);
|
|
}
|
|
b = SetEvent(pQueue->hAuthzAuditQueueEmptyEvent);
|
|
if (!b)
|
|
{
|
|
ASSERT(L"SetEvent on hAuthzAuditQueueEmptyEvent failed." && FALSE);
|
|
}
|
|
}
|
|
RtlLeaveCriticalSection(&pQueue->AuthzAuditQueueLock);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpCreateAndLogAudit(
|
|
IN DWORD AuditTypeFlag,
|
|
IN PAUTHZI_CLIENT_CONTEXT pAuthzClientContext,
|
|
IN PAUTHZI_AUDIT_EVENT pAuditEvent,
|
|
IN PAUTHZI_RESOURCE_MANAGER pRM,
|
|
IN PIOBJECT_TYPE_LIST LocalTypeList,
|
|
IN PAUTHZ_ACCESS_REQUEST pRequest,
|
|
IN PAUTHZ_ACCESS_REPLY pReply
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This is called from AuthzpGenerateAudit as a wrapper around LSA and
|
|
AuthziLogAuditEvent functionality. It places the appropriate audit
|
|
information on a queue for sending to LSA.
|
|
|
|
Arguments
|
|
|
|
AuditTypeFlag - mask to specify success | failure audit generation. Only
|
|
one bit at a time.
|
|
|
|
pAuthzClientContext - pointer to Authz context representing the client.
|
|
|
|
pAuditEvent - Object specific audit info will be passed in this structure.
|
|
|
|
pRM - Resource manager that generates the audit.
|
|
|
|
LocalTypeList - Internal object type list structure.
|
|
|
|
pRequest - specifies the desired access mask, principal self sid, the
|
|
object type list structure (if any).
|
|
|
|
pReply - The reply structure to return the results.
|
|
|
|
Return Value
|
|
|
|
TRUE if successful, FALSE if not.
|
|
Extended information available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
#define AUTHZ_BUFFER_CAPTURE_MAX 200
|
|
|
|
BOOL b;
|
|
AUDIT_PARAMS AuditParams = {0};
|
|
AUDIT_PARAM ParamArray[SE_MAX_AUDIT_PARAMETERS] = {0};
|
|
PAUTHZI_AUDIT_EVENT pCapturedAuditEvent = NULL;
|
|
UCHAR pBuffer[AUTHZ_BUFFER_CAPTURE_MAX] = {0};
|
|
AUDIT_OBJECT_TYPE FixedObjectTypeToAudit = {0};
|
|
AUDIT_OBJECT_TYPES ObjectTypeListAudit = {0};
|
|
PAUDIT_OBJECT_TYPE ObjectTypesToAudit = NULL;
|
|
USHORT ObjectTypeAuditCount = 0;
|
|
LONG i = 0;
|
|
LONG j = 0;
|
|
DWORD APF_AuditTypeFlag = 0;
|
|
ACCESS_MASK MaskToAudit = 0;
|
|
|
|
//
|
|
// Capture pAuditEvent, as we may change the pAuditParams member and would like to
|
|
// avoid the inevitable race that would follow.
|
|
//
|
|
|
|
if (AUTHZ_BUFFER_CAPTURE_MAX >= pAuditEvent->dwSize)
|
|
{
|
|
pCapturedAuditEvent = (PAUTHZI_AUDIT_EVENT) pBuffer;
|
|
RtlCopyMemory(
|
|
pCapturedAuditEvent,
|
|
pAuditEvent,
|
|
pAuditEvent->dwSize
|
|
);
|
|
}
|
|
else
|
|
{
|
|
pCapturedAuditEvent = AuthzpAlloc(pAuditEvent->dwSize);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pCapturedAuditEvent))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory(
|
|
pCapturedAuditEvent,
|
|
pAuditEvent,
|
|
pAuditEvent->dwSize
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// Make sure only one valid bit is on in the AuditTypeFlag. If a RM needs to generate
|
|
// both success and failure audits, then two separate calls should be made.
|
|
//
|
|
|
|
ASSERT(!(
|
|
FLAG_ON(AuditTypeFlag, AUTHZ_OBJECT_SUCCESS_AUDIT) &&
|
|
FLAG_ON(AuditTypeFlag, AUTHZ_OBJECT_FAILURE_AUDIT)
|
|
));
|
|
|
|
//
|
|
// Set the APF_AuditTypeFlag. LSA has its own flags for audit success
|
|
// and audit failure. Authz must map the Authz flag to the LSA APF equivalent.
|
|
//
|
|
|
|
if (FLAG_ON(AuditTypeFlag, AUTHZ_OBJECT_SUCCESS_AUDIT))
|
|
{
|
|
APF_AuditTypeFlag = APF_AuditSuccess;
|
|
|
|
//
|
|
// Test if the RM specifically disabled success audits
|
|
//
|
|
|
|
if (FLAG_ON(pCapturedAuditEvent->Flags, AUTHZ_NO_SUCCESS_AUDIT))
|
|
{
|
|
b = TRUE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else if (FLAG_ON(AuditTypeFlag, AUTHZ_OBJECT_FAILURE_AUDIT))
|
|
{
|
|
APF_AuditTypeFlag = APF_AuditFailure;
|
|
|
|
//
|
|
// Test if the RM specifically disabled failure audits
|
|
//
|
|
|
|
if (FLAG_ON(pCapturedAuditEvent->Flags, AUTHZ_NO_FAILURE_AUDIT))
|
|
{
|
|
b = TRUE;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Set the AUTHZ_AUDIT_QUEUE_HANDLE and AUTHZ_AUDIT_EVENT_TYPE_HANDLE of the AuditEvent if they are not yet set.
|
|
//
|
|
|
|
if (NULL == pCapturedAuditEvent->hAET)
|
|
{
|
|
if (FLAG_ON(pCapturedAuditEvent->Flags, AUTHZ_DS_CATEGORY_FLAG))
|
|
{
|
|
pCapturedAuditEvent->hAET = pRM->hAETDS;
|
|
}
|
|
else
|
|
{
|
|
pCapturedAuditEvent->hAET = pRM->hAET;
|
|
}
|
|
}
|
|
|
|
if (NULL == pAuditEvent->hAuditQueue)
|
|
{
|
|
pCapturedAuditEvent->hAuditQueue = pRM->hAuditQueue;
|
|
InterlockedCompareExchangePointer(
|
|
&pAuditEvent->hAuditQueue,
|
|
pRM->hAuditQueue,
|
|
NULL
|
|
);
|
|
}
|
|
|
|
//
|
|
// Decide what access bits we should audit
|
|
//
|
|
|
|
MaskToAudit = (APF_AuditTypeFlag == APF_AuditSuccess) ? pReply->GrantedAccessMask[0] : pRequest->DesiredAccess;
|
|
|
|
//
|
|
// If the RM gives us an AUDIT_PARAMS structure to marshall, then we don't
|
|
// need to generate our own.
|
|
//
|
|
|
|
if (AUTHZ_NON_NULL_PTR(pCapturedAuditEvent->pAuditParams))
|
|
{
|
|
|
|
//
|
|
// Capture the AuditParams so that we can change the User SID without racing.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
&AuditParams,
|
|
pCapturedAuditEvent->pAuditParams,
|
|
sizeof(AUDIT_PARAMS)
|
|
);
|
|
|
|
ASSERT(pCapturedAuditEvent->pAuditParams->Count <= SE_MAX_AUDIT_PARAMETERS);
|
|
|
|
RtlCopyMemory(
|
|
ParamArray,
|
|
pCapturedAuditEvent->pAuditParams->Parameters,
|
|
sizeof(AUDIT_PARAM) * pCapturedAuditEvent->pAuditParams->Count
|
|
);
|
|
|
|
AuditParams.Parameters = ParamArray;
|
|
|
|
//
|
|
// Replace the SID in the AUDIT_PARAMS with the SID of the current Client Context.
|
|
//
|
|
|
|
if (AUTHZ_NON_NULL_PTR(pAuthzClientContext->Sids[0].Sid))
|
|
{
|
|
AuditParams.Parameters[0].Data0 = (ULONG_PTR) pAuthzClientContext->Sids[0].Sid;
|
|
}
|
|
|
|
AuditParams.Flags = APF_AuditTypeFlag;
|
|
|
|
pCapturedAuditEvent->pAuditParams = &AuditParams;
|
|
|
|
b = AuthziLogAuditEvent(
|
|
0,
|
|
(AUTHZ_AUDIT_EVENT_HANDLE)pCapturedAuditEvent,
|
|
0
|
|
);
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// The caller has not given us an audit to generate. We will create one, provided that
|
|
// the AuditID specifies the generic object access (SE_AUDITID_OBJECT_OPERATION)
|
|
//
|
|
|
|
if ((NULL != pCapturedAuditEvent->hAET) &&
|
|
(((PAUTHZ_AUDIT_EVENT_TYPE_OLD)pCapturedAuditEvent->hAET)->u.Legacy.AuditId != SE_AUDITID_OBJECT_OPERATION))
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Create the generic object access audit. There are two codepaths
|
|
// that initialize the AuditParams structure. The first path is taken if
|
|
// there is no ObjectTypeList. The second path is taken if there is an
|
|
// ObjectTypeList.
|
|
//
|
|
|
|
AuditParams.Parameters = ParamArray;
|
|
pCapturedAuditEvent->pAuditParams = &AuditParams;
|
|
|
|
//
|
|
// Check if there is an ObjectTypeList.
|
|
//
|
|
|
|
if (AUTHZ_NON_NULL_PTR(pRequest->ObjectTypeList))
|
|
{
|
|
|
|
//
|
|
// If the length of the structure is 1 then the caller only wants access
|
|
// at the root of the tree.
|
|
//
|
|
|
|
if (1 == pReply->ResultListLength)
|
|
{
|
|
|
|
//
|
|
// Caller only wants access at ObjectTypeList root, so only one ObjectType to
|
|
// audit. For efficiency simply use the stack variable.
|
|
//
|
|
|
|
ObjectTypesToAudit = &FixedObjectTypeToAudit;
|
|
ObjectTypeAuditCount = 1;
|
|
FixedObjectTypeToAudit.AccessMask = pReply->GrantedAccessMask[0];
|
|
|
|
RtlCopyMemory(
|
|
&FixedObjectTypeToAudit.ObjectType,
|
|
&LocalTypeList[0].ObjectType,
|
|
sizeof(GUID)
|
|
);
|
|
}
|
|
else
|
|
{
|
|
|
|
//
|
|
// The caller wants more than access at ObjectTypeList root. He wants the
|
|
// whole thing.
|
|
//
|
|
|
|
//
|
|
// Determine how many GUIDs the client has access to which should be audited
|
|
//
|
|
|
|
for (ObjectTypeAuditCount = 0, i = 0; i < (LONG) pReply->ResultListLength; i++)
|
|
{
|
|
if (FLAG_ON(LocalTypeList[i].Flags, AuditTypeFlag))
|
|
{
|
|
ObjectTypeAuditCount++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate appropriate storage space for GUID list
|
|
//
|
|
|
|
ObjectTypesToAudit = AuthzpAlloc(sizeof(AUDIT_OBJECT_TYPE) * ObjectTypeAuditCount);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(ObjectTypesToAudit))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlZeroMemory(
|
|
ObjectTypesToAudit,
|
|
sizeof(AUDIT_OBJECT_TYPE) * ObjectTypeAuditCount
|
|
);
|
|
|
|
for (i = 0, j = -1; i < ObjectTypeAuditCount; i++)
|
|
{
|
|
|
|
//
|
|
// One counter tracks position in the alloc'ed array of ObjectTypesToAudit.
|
|
// The other counter picks out the indices in the pReply and LocalTypeList
|
|
// structures that need to be audited for success.
|
|
//
|
|
|
|
//
|
|
// find the next GUID to audit in pReply that client was granted access to.
|
|
//
|
|
|
|
do
|
|
{
|
|
j++;
|
|
}
|
|
while (!FLAG_ON(LocalTypeList[j].Flags, AuditTypeFlag));
|
|
|
|
//
|
|
// In the success audit, the AccessMask records the actual
|
|
// granted bits.
|
|
//
|
|
|
|
ObjectTypesToAudit[i].AccessMask = pReply->GrantedAccessMask[j];
|
|
ObjectTypesToAudit[i].Level = LocalTypeList[j].Level;
|
|
ObjectTypesToAudit[i].Flags = 0;
|
|
|
|
RtlCopyMemory(
|
|
&ObjectTypesToAudit[i].ObjectType,
|
|
&LocalTypeList[j].ObjectType,
|
|
sizeof(GUID)
|
|
);
|
|
}
|
|
|
|
}
|
|
|
|
ObjectTypeListAudit.Count = ObjectTypeAuditCount;
|
|
ObjectTypeListAudit.pObjectTypes = ObjectTypesToAudit;
|
|
ObjectTypeListAudit.Flags = 0;
|
|
|
|
b = AuthziInitializeAuditParamsWithRM(
|
|
APF_AuditTypeFlag,
|
|
(AUTHZ_RESOURCE_MANAGER_HANDLE)pRM,
|
|
AUTHZP_NUM_PARAMS_FOR_SE_AUDITID_OBJECT_OPERATION,
|
|
&AuditParams,
|
|
APT_String, pRM->szResourceManagerName,
|
|
APT_String, pCapturedAuditEvent->szOperationType,
|
|
APT_String, pCapturedAuditEvent->szObjectType,
|
|
APT_String, pCapturedAuditEvent->szObjectName,
|
|
APT_String, L"-",
|
|
APT_LogonId | AP_PrimaryLogonId,
|
|
APT_LogonId, pAuthzClientContext->AuthenticationId,
|
|
APT_Ulong | AP_AccessMask, MaskToAudit, 2,
|
|
APT_ObjectTypeList, &ObjectTypeListAudit, 2,
|
|
APT_String, pCapturedAuditEvent->szAdditionalInfo,
|
|
APT_String, pCapturedAuditEvent->szAdditionalInfo2,
|
|
APT_Ulong | AP_FormatHex, MaskToAudit
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
#ifdef AUTHZ_DEBUG_QUEUE
|
|
DbgPrint("AuthzInitializeAuditParams failed %d\n", GetLastError());
|
|
#endif
|
|
goto Cleanup;
|
|
}
|
|
} // matches "if (AUTHZ_NON_NULL_PTR(pRequest->ObjectTypeList))"
|
|
else
|
|
{
|
|
b = AuthziInitializeAuditParamsWithRM(
|
|
APF_AuditTypeFlag,
|
|
(AUTHZ_RESOURCE_MANAGER_HANDLE)pRM,
|
|
AUTHZP_NUM_PARAMS_FOR_SE_AUDITID_OBJECT_OPERATION,
|
|
&AuditParams,
|
|
APT_String, pRM->szResourceManagerName,
|
|
APT_String, pCapturedAuditEvent->szOperationType,
|
|
APT_String, pCapturedAuditEvent->szObjectType,
|
|
APT_String, pCapturedAuditEvent->szObjectName,
|
|
APT_String, L"-",
|
|
APT_LogonId | AP_PrimaryLogonId,
|
|
APT_LogonId, pAuthzClientContext->AuthenticationId,
|
|
APT_Ulong | AP_AccessMask, MaskToAudit, 2,
|
|
APT_String, L"-",
|
|
APT_String, pCapturedAuditEvent->szAdditionalInfo,
|
|
APT_String, pCapturedAuditEvent->szAdditionalInfo2,
|
|
APT_Ulong | AP_FormatHex, MaskToAudit
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
#ifdef AUTHZ_DEBUG_QUEUE
|
|
DbgPrint("AuthzInitializeAuditParams failed %d\n", GetLastError());
|
|
#endif
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Replace the SID in the AUDIT_PARAMS with the SID of the current Client Context.
|
|
//
|
|
|
|
if (AUTHZ_NON_NULL_PTR(pAuthzClientContext->Sids[0].Sid))
|
|
{
|
|
//
|
|
// Free an existing sid if alloc'd from heap
|
|
//
|
|
|
|
if (pCapturedAuditEvent->pAuditParams->Parameters[0].Data0 &&
|
|
pCapturedAuditEvent->pAuditParams->Parameters[0].Type == APT_Sid &&
|
|
(pCapturedAuditEvent->pAuditParams->Parameters[0].Flags & AUTHZP_PARAM_FREE_SID))
|
|
{
|
|
AuthzpFree((PVOID)(pCapturedAuditEvent->pAuditParams->Parameters[0].Data0));
|
|
pCapturedAuditEvent->pAuditParams->Parameters[0].Flags &= ~AUTHZP_PARAM_FREE_SID;
|
|
}
|
|
|
|
pCapturedAuditEvent->pAuditParams->Parameters[0].Data0 = (ULONG_PTR) pAuthzClientContext->Sids[0].Sid;
|
|
}
|
|
|
|
//
|
|
// At this point, AuditParams is initialized for an audit. Send to the LSA.
|
|
//
|
|
|
|
b = AuthziLogAuditEvent(
|
|
0,
|
|
(AUTHZ_AUDIT_EVENT_HANDLE)pCapturedAuditEvent,
|
|
0
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (ObjectTypesToAudit != &FixedObjectTypeToAudit)
|
|
{
|
|
AuthzpFreeNonNull(ObjectTypesToAudit);
|
|
}
|
|
|
|
if (pCapturedAuditEvent != (PAUTHZI_AUDIT_EVENT)pBuffer)
|
|
{
|
|
AuthzpFreeNonNull(pCapturedAuditEvent);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|
|
BOOL
|
|
AuthzpMarshallAuditParams(
|
|
OUT PAUDIT_PARAMS * ppMarshalledAuditParams,
|
|
IN PAUDIT_PARAMS pAuditParams
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will take an AUDIT_PARAMS structure and create a new
|
|
structure that is suitable for sending to LSA. It will be allocated
|
|
as a single chunk of memory.
|
|
|
|
Arguments:
|
|
|
|
ppMarshalledAuditParams - pointer to pointer that will receive the
|
|
marshalled audit parameters. This memory is allocated within the routine.
|
|
The dequeue thread frees this memory.
|
|
|
|
pAuditParams - Original, unmarshalled version of the AUDIT_PARAMS.
|
|
|
|
Return Value:
|
|
|
|
Boolean: TRUE if success, FALSE if failure.
|
|
Extended information available with GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
DWORD i = 0;
|
|
DWORD AuditParamsSize = 0;
|
|
PAUDIT_PARAMS pMarshalledAuditParams = NULL;
|
|
BOOL b = TRUE;
|
|
PUCHAR Base = NULL;
|
|
PUCHAR inData0 = NULL;
|
|
|
|
*ppMarshalledAuditParams = NULL;
|
|
|
|
//
|
|
// Begin calculating the total size required for the marshalled version
|
|
// of pAuditParams.
|
|
//
|
|
|
|
AuditParamsSize = sizeof(AUDIT_PARAMS) + sizeof(AUDIT_PARAM) * pAuditParams->Count;
|
|
AuditParamsSize = PtrAlignSize( AuditParamsSize );
|
|
|
|
//
|
|
// Determine how much memory each parameter requires.
|
|
//
|
|
|
|
for (i = 0; i < pAuditParams->Count; i++)
|
|
{
|
|
inData0 = (PUCHAR) pAuditParams->Parameters[i].Data0;
|
|
|
|
switch (pAuditParams->Parameters[i].Type)
|
|
{
|
|
case APT_String:
|
|
{
|
|
|
|
//
|
|
// wcslen returns the number of characters, excluding the terminating NULL. Must check for NULL
|
|
// because the AdditionalInfo string is OPTIONAL.
|
|
//
|
|
|
|
if (AUTHZ_NON_NULL_PTR(inData0))
|
|
{
|
|
AuditParamsSize += (DWORD)(sizeof(WCHAR) * wcslen((PWSTR) inData0) + sizeof(WCHAR));
|
|
AuditParamsSize = PtrAlignSize( AuditParamsSize );
|
|
}
|
|
break;
|
|
}
|
|
case APT_Pointer:
|
|
case APT_Ulong:
|
|
case APT_Int64:
|
|
case APT_LogonId:
|
|
case APT_Luid:
|
|
case APT_Time:
|
|
{
|
|
break;
|
|
}
|
|
case APT_Sid:
|
|
{
|
|
AuditParamsSize += RtlLengthSid((PSID) inData0);
|
|
AuditParamsSize = PtrAlignSize( AuditParamsSize );
|
|
break;
|
|
}
|
|
case APT_Guid:
|
|
{
|
|
AuditParamsSize += sizeof(GUID);
|
|
AuditParamsSize = PtrAlignSize( AuditParamsSize );
|
|
break;
|
|
}
|
|
case APT_ObjectTypeList:
|
|
{
|
|
AUDIT_OBJECT_TYPES * aot = (AUDIT_OBJECT_TYPES *) inData0;
|
|
|
|
//
|
|
// Need space for AUDIT_OBJECT_TYPES structure, and the AUDIT_OBJECT_TYPE
|
|
// array that it contains.
|
|
//
|
|
|
|
AuditParamsSize += sizeof (AUDIT_OBJECT_TYPES);
|
|
AuditParamsSize = PtrAlignSize( AuditParamsSize );
|
|
AuditParamsSize += sizeof(AUDIT_OBJECT_TYPE) * aot->Count;
|
|
AuditParamsSize = PtrAlignSize( AuditParamsSize );
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
ASSERT(L"Invalid Authz audit parameter" && FALSE);
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
b = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate space for the marshalled blob.
|
|
//
|
|
|
|
pMarshalledAuditParams = (PAUDIT_PARAMS) AuthzpAlloc(AuditParamsSize);
|
|
|
|
if (AUTHZ_ALLOCATION_FAILED(pMarshalledAuditParams))
|
|
{
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
b = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Set the fields of the marshalled AUDIT_PARAMS
|
|
//
|
|
|
|
pMarshalledAuditParams->Count = pAuditParams->Count;
|
|
pMarshalledAuditParams->Flags = pAuditParams->Flags;
|
|
pMarshalledAuditParams->Length = pAuditParams->Length;
|
|
pMarshalledAuditParams->Parameters = (AUDIT_PARAM *)((PUCHAR)pMarshalledAuditParams + sizeof(AUDIT_PARAMS));
|
|
|
|
//
|
|
// Base points to the beginning of the "data" section of the marshalled space,
|
|
// that is, Base is the area to copy member fields in and subsequently point at.
|
|
//
|
|
|
|
Base = (PUCHAR)pMarshalledAuditParams;
|
|
Base += PtrAlignSize( sizeof(AUDIT_PARAMS) + sizeof(AUDIT_PARAM) * pAuditParams->Count );
|
|
|
|
ASSERT(Base > (PUCHAR)pMarshalledAuditParams);
|
|
ASSERT(Base < (PUCHAR)((PUCHAR)pMarshalledAuditParams + AuditParamsSize));
|
|
|
|
//
|
|
// Move the Parameters array into the marshalled blob.
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
pMarshalledAuditParams->Parameters,
|
|
pAuditParams->Parameters,
|
|
sizeof(AUDIT_PARAM) * pAuditParams->Count
|
|
);
|
|
|
|
for (i = 0; i < pMarshalledAuditParams->Count; i++)
|
|
{
|
|
inData0 = (PUCHAR) pAuditParams->Parameters[i].Data0;
|
|
|
|
switch (pMarshalledAuditParams->Parameters[i].Type)
|
|
{
|
|
case APT_String:
|
|
{
|
|
if (AUTHZ_NON_NULL_PTR(inData0))
|
|
{
|
|
DWORD StringLength = (DWORD)(sizeof(WCHAR) * wcslen((PWSTR) inData0) + sizeof(WCHAR));
|
|
pMarshalledAuditParams->Parameters[i].Data0 = (ULONG_PTR) Base;
|
|
|
|
RtlCopyMemory(
|
|
(PVOID) Base,
|
|
(PWSTR) inData0,
|
|
StringLength
|
|
);
|
|
|
|
Base += PtrAlignSize( StringLength );
|
|
ASSERT(Base > (PUCHAR)pMarshalledAuditParams);
|
|
ASSERT(Base <= (PUCHAR)((PUCHAR)pMarshalledAuditParams + AuditParamsSize));
|
|
}
|
|
break;
|
|
}
|
|
case APT_Pointer:
|
|
case APT_Ulong:
|
|
case APT_LogonId:
|
|
case APT_Luid:
|
|
case APT_Time:
|
|
{
|
|
break;
|
|
}
|
|
case APT_Sid:
|
|
{
|
|
DWORD SidLength = RtlLengthSid((PSID) inData0);
|
|
pMarshalledAuditParams->Parameters[i].Data0 = (ULONG_PTR) Base;
|
|
|
|
RtlCopyMemory(
|
|
(PVOID) Base,
|
|
(PSID) inData0,
|
|
SidLength
|
|
);
|
|
Base += PtrAlignSize( SidLength );
|
|
ASSERT(Base > (PUCHAR)pMarshalledAuditParams);
|
|
ASSERT(Base <= (PUCHAR)((PUCHAR)pMarshalledAuditParams + AuditParamsSize));
|
|
break;
|
|
}
|
|
case APT_Guid:
|
|
{
|
|
pMarshalledAuditParams->Parameters[i].Data0 = (ULONG_PTR) Base;
|
|
|
|
RtlCopyMemory(
|
|
(PVOID) Base,
|
|
(GUID *) inData0,
|
|
sizeof(GUID)
|
|
);
|
|
Base += PtrAlignSize( sizeof(GUID) );
|
|
ASSERT(Base > (PUCHAR)pMarshalledAuditParams);
|
|
ASSERT(Base <= (PUCHAR)((PUCHAR)pMarshalledAuditParams + AuditParamsSize));
|
|
break;
|
|
}
|
|
case APT_ObjectTypeList:
|
|
{
|
|
AUDIT_OBJECT_TYPES *aot = (AUDIT_OBJECT_TYPES *) inData0;
|
|
DWORD OTLength = sizeof(AUDIT_OBJECT_TYPE) * aot->Count;
|
|
|
|
pMarshalledAuditParams->Parameters[i].Data0 = (ULONG_PTR) Base;
|
|
|
|
//
|
|
// Copy the AUDIT_OBJECT_TYPES structure
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
(PVOID) Base,
|
|
aot,
|
|
sizeof(AUDIT_OBJECT_TYPES)
|
|
);
|
|
|
|
Base += PtrAlignSize( sizeof(AUDIT_OBJECT_TYPES) );
|
|
|
|
//
|
|
// Point the pObjectTypes field at the end of the copied blob.
|
|
//
|
|
|
|
((AUDIT_OBJECT_TYPES *)pMarshalledAuditParams->Parameters[i].Data0)->pObjectTypes = (AUDIT_OBJECT_TYPE *) Base;
|
|
|
|
//
|
|
// Copy the AUDIT_OBJECT_TYPE array (pObjectTypes)
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
(PVOID) Base,
|
|
(AUDIT_OBJECT_TYPE *) aot->pObjectTypes,
|
|
OTLength
|
|
);
|
|
|
|
Base += PtrAlignSize( OTLength );
|
|
ASSERT(Base > (PUCHAR)pMarshalledAuditParams);
|
|
ASSERT(Base <= (PUCHAR)((PUCHAR)pMarshalledAuditParams + AuditParamsSize));
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
ASSERT(L"Invalid Authz audit parameter" && FALSE);
|
|
b = FALSE;
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!b)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Sanity check on the Base value. If this assertion passes, then I have
|
|
// not exceeded my allocated space.
|
|
//
|
|
|
|
ASSERT(Base == ((PUCHAR)pMarshalledAuditParams + AuditParamsSize));
|
|
|
|
Cleanup:
|
|
|
|
if (b)
|
|
{
|
|
*ppMarshalledAuditParams = pMarshalledAuditParams;
|
|
}
|
|
else
|
|
{
|
|
AuthzpFreeNonNull(pMarshalledAuditParams);
|
|
}
|
|
|
|
return b;
|
|
}
|
|
|
|
|