1638 lines
39 KiB
1638 lines
39 KiB
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
adtpup.c
|
|
|
|
Abstract:
|
|
|
|
This file has functions related to per user auditing.
|
|
|
|
Author:
|
|
|
|
20-August-2001 jhamblin
|
|
|
|
--*/
|
|
|
|
#include <lsapch2.h>
|
|
#include "adtp.h"
|
|
#include <sddl.h>
|
|
|
|
#define LsapAdtLuidIndexPerUserAuditing(L) ((L)->LowPart % PER_USER_AUDITING_LUID_TABLE_SIZE)
|
|
|
|
ULONG LsapAdtDebugPup = 0;
|
|
|
|
//
|
|
// Hash table of Per User policies and the LUID table.
|
|
//
|
|
|
|
PPER_USER_AUDITING_ELEMENT LsapAdtPerUserAuditingTable[PER_USER_AUDITING_POLICY_TABLE_SIZE];
|
|
PPER_USER_AUDITING_LUID_QUERY_ELEMENT LsapAdtPerUserAuditingLuidTable[PER_USER_AUDITING_LUID_TABLE_SIZE];
|
|
|
|
//
|
|
// Locks to protect the tables.
|
|
//
|
|
|
|
RTL_RESOURCE LsapAdtPerUserPolicyTableResource;
|
|
RTL_RESOURCE LsapAdtPerUserLuidTableResource;
|
|
|
|
//
|
|
// Counter for the number of users with registered per user audit policies.
|
|
//
|
|
|
|
LONG LsapAdtPerUserAuditUserCount;
|
|
|
|
//
|
|
// Counter for the number of active logon sessions in the system with per user settings
|
|
// active in the token.
|
|
//
|
|
|
|
LONG LsapAdtPerUserAuditLogonCount;
|
|
|
|
//
|
|
// Handle for the registry key
|
|
//
|
|
|
|
HKEY LsapAdtPerUserKey;
|
|
|
|
//
|
|
// Handle to the event which is signalled when the registry key is changed.
|
|
//
|
|
|
|
HANDLE LsapAdtPerUserKeyEvent;
|
|
|
|
//
|
|
// Timer which is set by the NotifyStub routine. When the timer fires
|
|
// then NotifyFire is called and the per user table is rebuilt.
|
|
//
|
|
|
|
HANDLE LsapAdtPerUserKeyTimer;
|
|
|
|
//
|
|
// Hint array - counts the number of tokens that exist with settings for
|
|
// each category.
|
|
//
|
|
|
|
LONG LsapAdtPerUserAuditHint[POLICY_AUDIT_EVENT_TYPE_COUNT];
|
|
|
|
//
|
|
// Array storing the number of users which have each category enabled in
|
|
// their per user settings.
|
|
//
|
|
|
|
LONG LsapAdtPerUserPolicyCategoryCount[POLICY_AUDIT_EVENT_TYPE_COUNT];
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtConstructTablePerUserAuditing(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine creates the Per User policy table from data found under the
|
|
LsapAdtPerUserKey.
|
|
|
|
Arguments
|
|
|
|
None.
|
|
|
|
Return Value
|
|
|
|
Appropriate NTSTATUS value.
|
|
|
|
--*/
|
|
|
|
{
|
|
#define STACK_BUFFER_VALUE_NAME_INFO_SIZE 256
|
|
UCHAR KeyInfo[sizeof(KEY_VALUE_FULL_INFORMATION) + STACK_BUFFER_VALUE_NAME_INFO_SIZE];
|
|
NTSTATUS Status;
|
|
ULONG ResultLength;
|
|
ULONG i;
|
|
ULONG j;
|
|
ULONG HashValue;
|
|
ULONG NewElementSize;
|
|
PPER_USER_AUDITING_ELEMENT pNewElement;
|
|
PPER_USER_AUDITING_ELEMENT pTempElement;
|
|
ULONG TokenPolicyLength;
|
|
PSID pSid = NULL;
|
|
PKEY_VALUE_FULL_INFORMATION pKeyInfo = NULL;
|
|
UCHAR StringBuffer[80];
|
|
PWSTR pSidString = (PWSTR) StringBuffer;
|
|
BOOLEAN b;
|
|
static DWORD dwRetryCount = 0;
|
|
#define RETRY_COUNT_MAX 3
|
|
|
|
//
|
|
// Close and then reopen the key. This remedies the case where key may have
|
|
// been deleted or renamed.
|
|
//
|
|
|
|
if (LsapAdtPerUserKey)
|
|
{
|
|
NtClose(LsapAdtPerUserKey);
|
|
LsapAdtPerUserKey = NULL;
|
|
}
|
|
|
|
Status = LsapAdtOpenPerUserAuditingKey();
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// LsapAdtOpenPerUserAuditingKey can return success and not open the key
|
|
// (the simple case where the key does not exist)
|
|
//
|
|
|
|
if (NULL == LsapAdtPerUserKey)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Zero the table array as we may be rebuilding it and cannot
|
|
// be certain it is already zeroed.
|
|
//
|
|
|
|
RtlZeroMemory(
|
|
LsapAdtPerUserAuditingTable,
|
|
sizeof(LsapAdtPerUserAuditingTable)
|
|
);
|
|
|
|
//
|
|
// Loop through all the values under the key (sids)
|
|
//
|
|
|
|
for (i = 0, Status = STATUS_SUCCESS; NT_SUCCESS(Status); i++)
|
|
{
|
|
pKeyInfo = (PKEY_VALUE_FULL_INFORMATION) KeyInfo;
|
|
|
|
Status = NtEnumerateValueKey(
|
|
LsapAdtPerUserKey,
|
|
i,
|
|
KeyValueFullInformation,
|
|
pKeyInfo,
|
|
sizeof(KeyInfo),
|
|
&ResultLength
|
|
);
|
|
|
|
//
|
|
// If we failed because the buffer was too small...
|
|
//
|
|
|
|
if (STATUS_BUFFER_TOO_SMALL == Status)
|
|
{
|
|
//
|
|
// Include space for the NULL in case we are debugging and want to
|
|
// dbgprint the key name.
|
|
//
|
|
|
|
pKeyInfo = LsapAllocateLsaHeap( ResultLength + sizeof(WCHAR));
|
|
|
|
if (pKeyInfo)
|
|
{
|
|
Status = NtEnumerateValueKey(
|
|
LsapAdtPerUserKey,
|
|
i,
|
|
KeyValueFullInformation,
|
|
pKeyInfo,
|
|
ResultLength,
|
|
&ResultLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// We have the value information, either in the stack buffer or in the heap
|
|
// allocation.
|
|
//
|
|
|
|
//
|
|
// Copy the string into another buffer so we can null terminate it.
|
|
//
|
|
|
|
if (pKeyInfo->NameLength < sizeof(StringBuffer))
|
|
{
|
|
pSidString = (PWSTR) StringBuffer;
|
|
RtlCopyMemory(pSidString, pKeyInfo->Name, pKeyInfo->NameLength);
|
|
}
|
|
else
|
|
{
|
|
pSidString = LsapAllocateLsaHeap(
|
|
pKeyInfo->NameLength + sizeof(WCHAR)
|
|
);
|
|
if (pSidString)
|
|
{
|
|
RtlCopyMemory(pSidString, pKeyInfo->Name, pKeyInfo->NameLength);
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
pSidString[pKeyInfo->NameLength / sizeof(WCHAR)] = L'\0';
|
|
|
|
#if DBG
|
|
if (LsapAdtDebugPup)
|
|
{
|
|
DbgPrint("pKeyInfo (PKEY_VALUE_FULL_INFORMATION) = 0x%x Name = %S Value = 0x%x\n",
|
|
pKeyInfo, pSidString, *((PUCHAR)pKeyInfo + pKeyInfo->DataOffset));
|
|
}
|
|
#endif
|
|
//
|
|
// Convert the string SID to a binary SID.
|
|
//
|
|
|
|
b = (BOOLEAN) ConvertStringSidToSid(
|
|
pSidString,
|
|
&pSid
|
|
);
|
|
|
|
if (pSidString != (PWSTR) StringBuffer)
|
|
{
|
|
LsapFreeLsaHeap(pSidString);
|
|
}
|
|
|
|
if (!b)
|
|
{
|
|
//
|
|
// Ignore failures from ConvertStringSidToSid. If a malformed Sid is
|
|
// present in the registry, we don't want to fail the PUA table
|
|
// construction.
|
|
//
|
|
|
|
ASSERT(L"ConvertStringSidToSid failed" && FALSE);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Hash the SID.
|
|
//
|
|
|
|
HashValue = LsapAdtHashPerUserAuditing(
|
|
pSid
|
|
);
|
|
|
|
//
|
|
// The size of the element is the base structure + RtlLengthSid(pSid).
|
|
//
|
|
|
|
NewElementSize = sizeof(PER_USER_AUDITING_ELEMENT) + RtlLengthSid(pSid);
|
|
pNewElement = LsapAllocateLsaHeap(NewElementSize);
|
|
|
|
//
|
|
// Initialize the element for this SID. Put it in the table.
|
|
//
|
|
|
|
if (pNewElement)
|
|
{
|
|
//
|
|
// copy raw policy.
|
|
// (this will not work on a big-endian machine)
|
|
//
|
|
|
|
RtlCopyMemory(
|
|
&pNewElement->RawPolicy,
|
|
((PUCHAR) pKeyInfo) + pKeyInfo->DataOffset,
|
|
min(pKeyInfo->DataLength, sizeof(pNewElement->RawPolicy))
|
|
);
|
|
|
|
//
|
|
// Assert if the reg key contains too much information to be a valid
|
|
// policy.
|
|
//
|
|
|
|
ASSERT(pKeyInfo->DataLength <= sizeof(pNewElement->RawPolicy));
|
|
|
|
//
|
|
// Copy in the binary SID.
|
|
//
|
|
|
|
pNewElement->pSid = ((PUCHAR)pNewElement) + sizeof(PER_USER_AUDITING_ELEMENT);
|
|
|
|
RtlCopyMemory(
|
|
pNewElement->pSid,
|
|
pSid,
|
|
RtlLengthSid(pSid)
|
|
);
|
|
|
|
//
|
|
// Calculate the amount of space in pNewElement for the TokenAuditPolicy
|
|
//
|
|
|
|
TokenPolicyLength = sizeof(pNewElement->TokenAuditPolicy) + sizeof(pNewElement->PolicyArray);
|
|
|
|
//
|
|
// Build the policy into a form suitable for passing to NtSetTokenInformation.
|
|
//
|
|
|
|
Status = LsapAdtConstructPolicyPerUserAuditing(
|
|
pNewElement->RawPolicy,
|
|
&pNewElement->TokenAuditPolicy,
|
|
&TokenPolicyLength
|
|
);
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
ASSERT("Failed to LsapAdtConstructPolicyPerUserAuditing in LsapAdtInitializePerUserAuditing" && FALSE);
|
|
LsapFreeLsaHeap(pNewElement);
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Place the element into the table, at the head of the correct hash bucket.
|
|
//
|
|
|
|
pNewElement->Next = LsapAdtPerUserAuditingTable[HashValue];
|
|
LsapAdtPerUserAuditingTable[HashValue] = pNewElement;
|
|
|
|
//
|
|
// Increment the counter for the number of elements in the table.
|
|
//
|
|
|
|
InterlockedIncrement(&LsapAdtPerUserAuditUserCount);
|
|
|
|
//
|
|
// Increment the user / category counter if the policy has include audit bits set.
|
|
//
|
|
|
|
for (j = 0; j < pNewElement->TokenAuditPolicy.PolicyCount; j++)
|
|
{
|
|
if (pNewElement->TokenAuditPolicy.Policy[j].PolicyMask & (TOKEN_AUDIT_SUCCESS_INCLUDE | TOKEN_AUDIT_FAILURE_INCLUDE))
|
|
{
|
|
InterlockedIncrement(&LsapAdtPerUserPolicyCategoryCount[pNewElement->TokenAuditPolicy.Policy[j].Category]);
|
|
}
|
|
}
|
|
#if DBG
|
|
if (LsapAdtDebugPup)
|
|
{
|
|
DbgPrint("PUP added element 0x%x for %S\n", pNewElement, pKeyInfo->Name);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
LocalFree(pSid);
|
|
pSid = NULL;
|
|
|
|
}
|
|
|
|
//
|
|
// If we allocated heap for the key information then free it now.
|
|
//
|
|
|
|
if (pKeyInfo != (PKEY_VALUE_FULL_INFORMATION)KeyInfo)
|
|
{
|
|
LsapFreeLsaHeap(pKeyInfo);
|
|
pKeyInfo = NULL;
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (pSid)
|
|
{
|
|
LocalFree(pSid);
|
|
}
|
|
|
|
//
|
|
// If we broke out of the loop because we have read all values then
|
|
// set the status to success.
|
|
//
|
|
|
|
if (Status == STATUS_NO_MORE_ENTRIES) {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
#if DBG
|
|
if (LsapAdtDebugPup)
|
|
{
|
|
DbgPrint("LsapAdtConstructTablePerUserAuditing: Complete with status 0x%x. Count = %d\n", Status, LsapAdtPerUserAuditUserCount);
|
|
}
|
|
#endif
|
|
|
|
if (pKeyInfo != NULL && pKeyInfo != (PKEY_VALUE_FULL_INFORMATION)KeyInfo)
|
|
{
|
|
LsapFreeLsaHeap(pKeyInfo);
|
|
pKeyInfo = NULL;
|
|
}
|
|
|
|
//
|
|
// If failure, call the table free routine, in case some table elements were successfully allocated.
|
|
//
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
(VOID) LsapAdtFreeTablePerUserAuditing();
|
|
|
|
//
|
|
// If one of the registry routines failed because keys were still being
|
|
// modified, then reschedule table creation to occur in 5 seconds.
|
|
// For example, if status is STATUS_INTERNAL_ERROR it often means that a new value
|
|
// was being added under the key at the time NtEnumerateValueKey was called.
|
|
// We do not want to constantly retry if something is wrong, however, so only
|
|
// retry 3 times without success.
|
|
//
|
|
|
|
if (++dwRetryCount < RETRY_COUNT_MAX)
|
|
{
|
|
DWORD dwError;
|
|
dwError = LsapAdtKeyNotifyStubPerUserAuditing(
|
|
NULL
|
|
);
|
|
|
|
//
|
|
// If NotifyStub failed then the table will not be constructed again in 5 seconds.
|
|
// The best we can do here is recursively call ourself immediately. Note that the
|
|
// recursion will stop, as we already incremented dwRetryCount.
|
|
//
|
|
|
|
if (dwError != ERROR_SUCCESS)
|
|
{
|
|
(VOID) LsapAdtKeyNotifyStubPerUserAuditing(
|
|
NULL
|
|
);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LsapAdtAuditPerUserTableCreation(FALSE);
|
|
LsapAuditFailed( Status );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
LsapAdtAuditPerUserTableCreation(TRUE);
|
|
dwRetryCount = 0;
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtOpenPerUserAuditingKey(
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Opens the per user auditing registry key, creating the key if necessary.
|
|
Then we register for notification upon change to the key.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Appropriate NTSTATUS value.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
DWORD dwError;
|
|
DWORD Disposition;
|
|
|
|
#define PER_USER_AUDIT_KEY_COMPLETE L"System\\CurrentControlSet\\Control\\Lsa\\Audit\\PerUserAuditing\\System"
|
|
|
|
//
|
|
// The key should be NULL, otherwise we will leak a handle.
|
|
//
|
|
|
|
ASSERT(LsapAdtPerUserKey == NULL);
|
|
|
|
//
|
|
// These handles should not be NULL, else nothing will work.
|
|
//
|
|
|
|
ASSERT(LsapAdtPerUserKeyEvent != NULL);
|
|
ASSERT(LsapAdtPerUserKeyTimer != NULL);
|
|
|
|
//
|
|
// Get the key for the per user audit settings.
|
|
//
|
|
|
|
dwError = RegCreateKeyEx(
|
|
HKEY_LOCAL_MACHINE,
|
|
PER_USER_AUDIT_KEY_COMPLETE,
|
|
0,
|
|
NULL,
|
|
0,
|
|
KEY_QUERY_VALUE | KEY_NOTIFY,
|
|
NULL,
|
|
&LsapAdtPerUserKey,
|
|
&Disposition
|
|
);
|
|
|
|
if (ERROR_SUCCESS == dwError)
|
|
{
|
|
//
|
|
// Ask to be notified when the key changes.
|
|
//
|
|
|
|
dwError = RegNotifyChangeKeyValue(
|
|
LsapAdtPerUserKey,
|
|
TRUE,
|
|
REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET,
|
|
LsapAdtPerUserKeyEvent,
|
|
TRUE
|
|
);
|
|
|
|
ASSERT(ERROR_SUCCESS == dwError);
|
|
|
|
if (ERROR_SUCCESS != dwError)
|
|
{
|
|
Status = LsapWinerrorToNtStatus(dwError);
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
Status = LsapWinerrorToNtStatus(dwError);
|
|
ASSERT(L"Failed to open per user auditing key." && FALSE);
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapAdtFreeTablePerUserAuditing(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine frees all heap associated with the elements of the Per User Policy table.
|
|
|
|
Arguments
|
|
|
|
None.
|
|
|
|
Return Value
|
|
|
|
Appropriate NTSTATUS value.
|
|
|
|
--*/
|
|
|
|
{
|
|
PPER_USER_AUDITING_ELEMENT pElement;
|
|
PPER_USER_AUDITING_ELEMENT pNextElement;
|
|
LONG i;
|
|
|
|
for (i = 0; i < PER_USER_AUDITING_POLICY_TABLE_SIZE; i++)
|
|
{
|
|
pElement = LsapAdtPerUserAuditingTable[i];
|
|
|
|
while (pElement)
|
|
{
|
|
pNextElement = pElement->Next;
|
|
LsapFreeLsaHeap(pElement);
|
|
pElement = pNextElement;
|
|
InterlockedDecrement(&LsapAdtPerUserAuditUserCount);
|
|
}
|
|
|
|
LsapAdtPerUserAuditingTable[i] = NULL;
|
|
}
|
|
|
|
ASSERT(LsapAdtPerUserAuditUserCount == 0);
|
|
|
|
RtlZeroMemory(
|
|
LsapAdtPerUserPolicyCategoryCount,
|
|
sizeof(LsapAdtPerUserPolicyCategoryCount)
|
|
);
|
|
}
|
|
|
|
|
|
ULONG
|
|
LsapAdtHashPerUserAuditing(
|
|
IN PSID pSid
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This performs a simple hash on the passed in sid.
|
|
|
|
Arguments
|
|
|
|
pSid - The sid to hash.
|
|
|
|
Return Value
|
|
|
|
ULONG hash value.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG HashValue = 0;
|
|
ULONG i;
|
|
ULONG Length = RtlLengthSid(pSid);
|
|
|
|
for (i = 0; i < Length; i++)
|
|
{
|
|
HashValue += ((PUCHAR)pSid)[i];
|
|
}
|
|
|
|
HashValue %= PER_USER_AUDITING_POLICY_TABLE_SIZE;
|
|
return HashValue;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtQueryPerUserAuditing(
|
|
IN PSID pInputSid,
|
|
OUT PTOKEN_AUDIT_POLICY pPolicy,
|
|
IN OUT PULONG pLength,
|
|
OUT PBOOLEAN bFound
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine returns a copy of the current policy active for the
|
|
passed in SID.
|
|
|
|
Arguments
|
|
|
|
pInputSid - the sid to query
|
|
|
|
pPolicy - pointer to memory that will be filled in with the policy setting.
|
|
|
|
pLength - Specifies the size of the passed buffer. Will be filled in with the
|
|
needed length in case of insufficient buffer.
|
|
|
|
bFound - boolean indicating if the InputSid has a policy in the table
|
|
|
|
Return Value
|
|
|
|
Appropriate NTSTATUS value.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG HashValue;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN bSuccess;
|
|
PPER_USER_AUDITING_ELEMENT pTableElement;
|
|
BOOLEAN bLock = FALSE;
|
|
|
|
*bFound = FALSE;
|
|
|
|
bSuccess = LsapAdtAcquirePerUserPolicyTableReadLock();
|
|
|
|
ASSERT(bSuccess);
|
|
|
|
if (!bSuccess)
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
bLock = TRUE;
|
|
|
|
if (0 == LsapAdtPerUserAuditUserCount)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
HashValue = LsapAdtHashPerUserAuditing(pInputSid);
|
|
pTableElement = LsapAdtPerUserAuditingTable[HashValue];
|
|
|
|
while (pTableElement)
|
|
{
|
|
if (RtlEqualSid(
|
|
pInputSid,
|
|
pTableElement->pSid
|
|
))
|
|
{
|
|
//
|
|
// We have found the desired element in the policy table.
|
|
//
|
|
|
|
if (*pLength < PER_USER_AUDITING_POLICY_SIZE(&pTableElement->TokenAuditPolicy))
|
|
{
|
|
*pLength = PER_USER_AUDITING_POLICY_SIZE(&pTableElement->TokenAuditPolicy);
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
*pLength = PER_USER_AUDITING_POLICY_SIZE(&pTableElement->TokenAuditPolicy);
|
|
|
|
RtlCopyMemory(
|
|
pPolicy,
|
|
&pTableElement->TokenAuditPolicy,
|
|
*pLength
|
|
);
|
|
|
|
*bFound = TRUE;
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
pTableElement = pTableElement->Next;
|
|
}
|
|
#if DBG
|
|
//
|
|
// Simple test for a loop in the linked list.
|
|
//
|
|
|
|
if (pTableElement == LsapAdtPerUserAuditingTable[HashValue])
|
|
{
|
|
ASSERT(L"LsapAdtPerUserAuditingTable is messed up." && FALSE);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (bLock)
|
|
{
|
|
LsapAdtReleasePerUserPolicyTableLock();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtFilterAdminPerUserAuditing(
|
|
IN HANDLE hToken,
|
|
IN OUT PTOKEN_AUDIT_POLICY pPolicy
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine decides if the registered policy for the (administrator) user is legitimate.
|
|
An administrator cannot have a policy which excludes him from auditing. This routine
|
|
verifies that the policy does not do this.
|
|
|
|
Arguments
|
|
|
|
hToken - handle to the user's token.
|
|
|
|
pPolicy - a copy of the policy that will be set on the token.
|
|
|
|
Return Value
|
|
|
|
Appropriate NTSTATUS value.
|
|
|
|
--*/
|
|
|
|
{
|
|
BOOL bMember;
|
|
BOOL b;
|
|
ULONG i;
|
|
HANDLE hDupToken = NULL;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
ASSERT(hToken && "hToken should not be NULL here.\n");
|
|
|
|
//
|
|
// hToken is a PrimaryToken; to call CheckTokenMembership we need
|
|
// an impersonation token.
|
|
//
|
|
|
|
b = DuplicateTokenEx(
|
|
hToken,
|
|
TOKEN_QUERY | TOKEN_IMPERSONATE,
|
|
NULL,
|
|
SecurityImpersonation,
|
|
TokenImpersonation,
|
|
&hDupToken
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
ASSERT(L"DuplicateTokenEx failed in LsapAdtFilterAdminPerUserAuditing" && FALSE);
|
|
Status = LsapWinerrorToNtStatus(GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
b = CheckTokenMembership(
|
|
hDupToken,
|
|
WellKnownSids[LsapAliasAdminsSidIndex].Sid,
|
|
&bMember
|
|
);
|
|
|
|
if (!b)
|
|
{
|
|
ASSERT(L"CheckTokenMembership failed in LsapAdtFilterAdminPerUserAuditing" && FALSE);
|
|
Status = LsapWinerrorToNtStatus(GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If the token is an administrator then strip out all exclude bits.
|
|
//
|
|
|
|
if (bMember)
|
|
{
|
|
for (i = 0; i < pPolicy->PolicyCount; i++)
|
|
{
|
|
pPolicy->Policy[i].PolicyMask &= ~(TOKEN_AUDIT_SUCCESS_EXCLUDE | TOKEN_AUDIT_FAILURE_EXCLUDE);
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (hDupToken)
|
|
{
|
|
NtClose(hDupToken);
|
|
}
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtConstructPolicyPerUserAuditing(
|
|
IN ULONGLONG RawPolicy,
|
|
OUT PTOKEN_AUDIT_POLICY pTokenPolicy,
|
|
IN OUT PULONG TokenPolicyLength
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This constructs a policy appropriate for passing to NtSetTokenInformation.
|
|
It converts the raw registry policy into a TOKEN_AUDIT_POLICY.
|
|
|
|
Arguments
|
|
|
|
RawPolicy - a 64 bit quantity describing a user's audit policy settings.
|
|
|
|
pTokenPolicy - points to memory that receives a more presentable form of the RawPolicy.
|
|
|
|
TokenPolicyLength - The length of the pTokenPolicy buffer. Receives the necessary length
|
|
in the case that the buffer is insufficient.
|
|
|
|
Return Value
|
|
|
|
Appropriate NTSTATUS value.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
ULONG j;
|
|
ULONG PolicyBits;
|
|
ULONG CategoryCount;
|
|
ULONG LengthNeeded;
|
|
|
|
//
|
|
// First calculate the number of category settings in the RawPolicy
|
|
// This will reveal if we have enough space to construct the pTokenPolicy.
|
|
//
|
|
|
|
for (CategoryCount = 0, i = 0; i < POLICY_AUDIT_EVENT_TYPE_COUNT; i++)
|
|
{
|
|
if ((RawPolicy >> (4 * i)) & VALID_AUDIT_POLICY_BITS)
|
|
{
|
|
CategoryCount++;
|
|
}
|
|
#if DBG
|
|
if (LsapAdtDebugPup)
|
|
{
|
|
DbgPrint("0x%I64x >> %d & 0x%x == 0x%x\n", RawPolicy, 4*i, VALID_AUDIT_POLICY_BITS,
|
|
(RawPolicy >> (4 * i)) & VALID_AUDIT_POLICY_BITS);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
LengthNeeded = PER_USER_AUDITING_POLICY_SIZE_BY_COUNT(CategoryCount);
|
|
|
|
//
|
|
// Check if the passed buffer is large enough.
|
|
//
|
|
|
|
if (*TokenPolicyLength < LengthNeeded)
|
|
{
|
|
ASSERT(L"The buffer should always be big enough!" && FALSE);
|
|
*TokenPolicyLength = LengthNeeded;
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
*TokenPolicyLength = LengthNeeded;
|
|
|
|
//
|
|
// Build the policy.
|
|
//
|
|
|
|
pTokenPolicy->PolicyCount = CategoryCount;
|
|
|
|
for (j = 0, i = 0; i < POLICY_AUDIT_EVENT_TYPE_COUNT; i++)
|
|
{
|
|
PolicyBits = (ULONG)((RawPolicy >> (4 * i)) & VALID_AUDIT_POLICY_BITS);
|
|
|
|
if (PolicyBits)
|
|
{
|
|
pTokenPolicy->Policy[j].Category = i;
|
|
pTokenPolicy->Policy[j].PolicyMask = PolicyBits;
|
|
j++;
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtStorePolicyByLuidPerUserAuditing(
|
|
IN PLUID pLogonId,
|
|
IN PTOKEN_AUDIT_POLICY pPolicy
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This routine stores a copy of the user's audit policy in a table
|
|
referenced by the LogonId.
|
|
|
|
Arguments
|
|
|
|
pLogonId - the user's logon id. This will be used as the key for
|
|
subsequent lookups of the policy.
|
|
|
|
pPolicy - a pointer to the policy to store.
|
|
|
|
Return Value
|
|
|
|
Appropriate NTSTATUS value.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Index;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN bSuccess;
|
|
BOOLEAN bLock = FALSE;
|
|
PPER_USER_AUDITING_LUID_QUERY_ELEMENT pLuidElement = NULL;
|
|
|
|
bSuccess = LsapAdtAcquirePerUserLuidTableWriteLock();
|
|
|
|
ASSERT(bSuccess);
|
|
|
|
if (!bSuccess)
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
bLock = TRUE;
|
|
Index = LsapAdtLuidIndexPerUserAuditing(pLogonId);
|
|
pLuidElement = LsapAllocateLsaHeap(sizeof(PER_USER_AUDITING_LUID_QUERY_ELEMENT));
|
|
|
|
if (NULL == pLuidElement)
|
|
{
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Initialize the LuidElement.
|
|
//
|
|
|
|
RtlCopyLuid(
|
|
&pLuidElement->Luid,
|
|
pLogonId
|
|
);
|
|
|
|
RtlCopyMemory(
|
|
&pLuidElement->Policy,
|
|
pPolicy,
|
|
PER_USER_AUDITING_POLICY_SIZE(pPolicy)
|
|
);
|
|
|
|
//
|
|
// Place it in the table.
|
|
//
|
|
|
|
pLuidElement->Next = LsapAdtPerUserAuditingLuidTable[Index];
|
|
LsapAdtPerUserAuditingLuidTable[Index] = pLuidElement;
|
|
|
|
Cleanup:
|
|
|
|
if (bLock)
|
|
{
|
|
LsapAdtReleasePerUserLuidTableLock();
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status) && pLuidElement)
|
|
{
|
|
LsapFreeLsaHeap(pLuidElement);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtQueryPolicyByLuidPerUserAuditing(
|
|
IN PLUID pLogonId,
|
|
OUT PTOKEN_AUDIT_POLICY pPolicy,
|
|
IN OUT PULONG pLength,
|
|
OUT PBOOLEAN bFound
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
This finds the policy associated with the passed LogonId.
|
|
|
|
Arguments
|
|
|
|
pLogonId - the key for the policy query.
|
|
|
|
pPolicy - memory that receives the policy.
|
|
|
|
pLength - Specifies the size of the passed buffer. Will be filled in with the
|
|
needed length in case of insufficient buffer.
|
|
|
|
bFound - boolean return indicating if policy is present.
|
|
|
|
Return Value
|
|
|
|
Appropriate NTSTATUS value.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Index;
|
|
ULONG PolicySize;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN bSuccess;
|
|
BOOLEAN bLock = FALSE;
|
|
PPER_USER_AUDITING_LUID_QUERY_ELEMENT pLuidElement;
|
|
|
|
*bFound = FALSE;
|
|
|
|
bSuccess = LsapAdtAcquirePerUserLuidTableReadLock();
|
|
|
|
ASSERT(bSuccess);
|
|
|
|
if (!bSuccess)
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
bLock = TRUE;
|
|
Index = LsapAdtLuidIndexPerUserAuditing(pLogonId);
|
|
pLuidElement = LsapAdtPerUserAuditingLuidTable[Index];
|
|
|
|
while (pLuidElement)
|
|
{
|
|
if (RtlEqualLuid(
|
|
&pLuidElement->Luid,
|
|
pLogonId
|
|
))
|
|
{
|
|
if (*pLength < PER_USER_AUDITING_POLICY_SIZE(&pLuidElement->Policy))
|
|
{
|
|
*pLength = PER_USER_AUDITING_POLICY_SIZE(&pLuidElement->Policy);
|
|
Status = STATUS_BUFFER_TOO_SMALL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
*pLength = PER_USER_AUDITING_POLICY_SIZE(&pLuidElement->Policy);
|
|
|
|
RtlCopyMemory(
|
|
pPolicy,
|
|
&pLuidElement->Policy,
|
|
*pLength
|
|
);
|
|
|
|
*bFound = TRUE;
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
pLuidElement = pLuidElement->Next;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (bLock)
|
|
{
|
|
LsapAdtReleasePerUserLuidTableLock();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtRemoveLuidQueryPerUserAuditing(
|
|
IN PLUID pLogonId
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Remove a LUID query element from the LUID table.
|
|
|
|
Arguments
|
|
|
|
pLogonId - key to the element to remove.
|
|
|
|
Return Value
|
|
|
|
Appropriate NTSTATUS value.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG Index;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN bSuccess;
|
|
PPER_USER_AUDITING_LUID_QUERY_ELEMENT pElement;
|
|
PPER_USER_AUDITING_LUID_QUERY_ELEMENT * pPrevious;
|
|
BOOLEAN bLock = FALSE;
|
|
BOOLEAN bFound = FALSE;
|
|
|
|
bSuccess = LsapAdtAcquirePerUserLuidTableWriteLock();
|
|
|
|
ASSERT(bSuccess);
|
|
|
|
if (!bSuccess)
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Index = LsapAdtLuidIndexPerUserAuditing(pLogonId);
|
|
pPrevious = &LsapAdtPerUserAuditingLuidTable[Index];
|
|
pElement = *pPrevious;
|
|
|
|
|
|
bLock = TRUE;
|
|
|
|
while (pElement)
|
|
{
|
|
if (RtlEqualLuid(
|
|
&pElement->Luid,
|
|
pLogonId
|
|
))
|
|
{
|
|
bFound = TRUE;
|
|
*pPrevious = pElement->Next;
|
|
LsapFreeLsaHeap(pElement);
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
pPrevious = &pElement->Next;
|
|
pElement = *pPrevious;
|
|
}
|
|
|
|
if (!bFound)
|
|
{
|
|
Status = STATUS_NOT_FOUND;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if (bLock)
|
|
{
|
|
LsapAdtReleasePerUserLuidTableLock();
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
DWORD
|
|
LsapAdtKeyNotifyStubPerUserAuditing(
|
|
LPVOID Ignore
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called when the LsapAdtPerUserKeyEvent is signalled. This happens
|
|
when LsapAdtPerUserKey is changed. This routine will set the LsapAdtPerUserKeyTimer
|
|
to signal in 5 seconds.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Appropriate WINERROR value.
|
|
|
|
--*/
|
|
|
|
{
|
|
LARGE_INTEGER Time = {0};
|
|
BOOL b;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
//
|
|
// Set timer for 5 seconds from now.
|
|
//
|
|
|
|
Time.QuadPart = -50000000;
|
|
|
|
b = SetWaitableTimer(
|
|
LsapAdtPerUserKeyTimer,
|
|
&Time,
|
|
0,
|
|
NULL,
|
|
NULL,
|
|
0);
|
|
|
|
if (!b)
|
|
{
|
|
ASSERT("SetWaitableTimer failed" && FALSE);
|
|
dwError = GetLastError();
|
|
}
|
|
|
|
return dwError;
|
|
}
|
|
|
|
|
|
DWORD
|
|
LsapAdtKeyNotifyFirePerUserAuditing(
|
|
LPVOID Ignore
|
|
)
|
|
|
|
/*+
|
|
|
|
Routine Description:
|
|
|
|
This function is called to rebuild the per user table.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
Appropriate DWORD error.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
BOOLEAN bLock = FALSE;
|
|
BOOLEAN bSuccess;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
|
|
UNREFERENCED_PARAMETER(Ignore);
|
|
|
|
#if DBG
|
|
if (LsapAdtDebugPup)
|
|
{
|
|
DbgPrint("LsapAdtPerUserKey modified. LsapAdtKeyNotifyFirePerUserAuditing is rebuilding Table.\n");
|
|
}
|
|
#endif
|
|
|
|
bSuccess = LsapAdtAcquirePerUserPolicyTableWriteLock();
|
|
|
|
ASSERT(bSuccess);
|
|
|
|
if (!bSuccess)
|
|
{
|
|
Status = STATUS_UNSUCCESSFUL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
bLock = TRUE;
|
|
|
|
//
|
|
// Free the pup table, then call Init to reconstruct it.
|
|
//
|
|
|
|
LsapAdtFreeTablePerUserAuditing();
|
|
|
|
Status = LsapAdtConstructTablePerUserAuditing();
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
Cleanup:
|
|
|
|
if (bLock)
|
|
{
|
|
LsapAdtReleasePerUserPolicyTableLock();
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
LsapAuditFailed(Status);
|
|
}
|
|
|
|
return RtlNtStatusToDosError(Status);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
LsapAdtLogonPerUserAuditing(
|
|
PSID pSid,
|
|
PLUID pLogonId,
|
|
HANDLE hToken
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This code should be called when a user is logged on. It sets the per
|
|
user auditing policy onto the newly logged on user's token and stores
|
|
the logon into the LUID table.
|
|
|
|
Arguments:
|
|
|
|
pSid - Sid of user.
|
|
pLogonId - Logon ID of user.
|
|
hToken - handle to token of user.
|
|
|
|
Return Value:
|
|
|
|
Appropriate NTSTATUS value.
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
ULONG i;
|
|
PPER_USER_AUDITING_ELEMENT pPerUserAuditingPolicy = NULL;
|
|
UCHAR PolicyBuffer[PER_USER_AUDITING_MAX_POLICY_SIZE];
|
|
ULONG PolicyLength = sizeof(PolicyBuffer);
|
|
PTOKEN_AUDIT_POLICY pPolicy = (PTOKEN_AUDIT_POLICY) PolicyBuffer;
|
|
BOOLEAN bFound = FALSE;
|
|
NTSTATUS Status;
|
|
TOKEN_AUDIT_POLICY EmptyPolicy = {0};
|
|
|
|
//
|
|
// If it is a local system logon then bail out early. Per user
|
|
// settings cannot exist for local system.
|
|
//
|
|
|
|
if (RtlEqualSid(
|
|
pSid,
|
|
LsapLocalSystemSid
|
|
))
|
|
{
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
Status = LsapAdtQueryPerUserAuditing(
|
|
pSid,
|
|
pPolicy,
|
|
&PolicyLength,
|
|
&bFound
|
|
);
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
if (bFound)
|
|
{
|
|
//
|
|
// Filter out any exclude bits if this user is an administrator.
|
|
//
|
|
|
|
Status = LsapAdtFilterAdminPerUserAuditing(
|
|
hToken,
|
|
pPolicy
|
|
);
|
|
|
|
ASSERT(L"LsapAdtFilterAdminPerUserAuditing failed." && NT_SUCCESS(Status));
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// If there is no policy settings for the user then apply
|
|
// a blank policy. This is required so that no policy may
|
|
// be applied to this token in the future.
|
|
//
|
|
|
|
pPolicy = &EmptyPolicy;
|
|
PolicyLength = sizeof(EmptyPolicy);
|
|
}
|
|
|
|
if (NT_SUCCESS(Status))
|
|
{
|
|
Status = NtSetInformationToken(
|
|
hToken,
|
|
TokenAuditPolicy,
|
|
pPolicy,
|
|
PolicyLength
|
|
);
|
|
|
|
ASSERT(L"NtSetInformationToken failed" && NT_SUCCESS(Status));
|
|
|
|
//
|
|
// Only store the policy in the LUID table if it is the nonempty policy.
|
|
//
|
|
|
|
if (NT_SUCCESS(Status) && bFound)
|
|
{
|
|
LsapAdtLogonCountersPerUserAuditing(pPolicy);
|
|
|
|
Status = LsapAdtStorePolicyByLuidPerUserAuditing(
|
|
pLogonId,
|
|
pPolicy
|
|
);
|
|
|
|
ASSERT(L"LsapAdtStorePolicyByLuidPerUserAuditing failed." && NT_SUCCESS(Status));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!NT_SUCCESS(Status))
|
|
{
|
|
LsapAuditFailed(Status);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapAdtLogonCountersPerUserAuditing(
|
|
PTOKEN_AUDIT_POLICY pPolicy
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This helper routine updates counters when a user is logged on with per user settings.
|
|
|
|
Arguments:
|
|
|
|
pPolicy - the policy applied to the new logon.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
|
|
if (pPolicy->PolicyCount)
|
|
{
|
|
InterlockedIncrement(&LsapAdtPerUserAuditLogonCount);
|
|
}
|
|
|
|
for (i = 0; i < pPolicy->PolicyCount; i++)
|
|
{
|
|
//
|
|
// for the hint array only increment if the policy causes an inclusion of some audit
|
|
// category.
|
|
//
|
|
|
|
if (pPolicy->Policy[i].PolicyMask & (TOKEN_AUDIT_SUCCESS_INCLUDE | TOKEN_AUDIT_FAILURE_INCLUDE))
|
|
{
|
|
InterlockedIncrement(&LsapAdtPerUserAuditHint[pPolicy->Policy[i].Category]);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
LsapAdtLogoffCountersPerUserAuditing(
|
|
PTOKEN_AUDIT_POLICY pPolicy
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This modifies the per user counters to reflect a user logoff.
|
|
|
|
Arguments:
|
|
|
|
pPolicy - the policy that was applied to the logged off user.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
|
|
if (pPolicy->PolicyCount)
|
|
{
|
|
InterlockedDecrement(&LsapAdtPerUserAuditLogonCount);
|
|
}
|
|
|
|
for (i = 0; i < pPolicy->PolicyCount; i++)
|
|
{
|
|
if (pPolicy->Policy[i].PolicyMask & (TOKEN_AUDIT_SUCCESS_INCLUDE | TOKEN_AUDIT_FAILURE_INCLUDE))
|
|
{
|
|
InterlockedDecrement(&LsapAdtPerUserAuditHint[pPolicy->Policy[i].Category]);
|
|
}
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
LsapAdtLogoffPerUserAuditing(
|
|
PLUID pLogonId
|
|
)
|
|
|
|
/**
|
|
|
|
Routine Description:
|
|
|
|
This code frees up any memory and adjusts counters for when a user has
|
|
logged off the machine.
|
|
|
|
Arguments:
|
|
|
|
pLogonId - the logon id of the user
|
|
|
|
Return Value:
|
|
|
|
NTSTATUS.
|
|
|
|
**/
|
|
|
|
{
|
|
UCHAR Buffer[PER_USER_AUDITING_MAX_POLICY_SIZE];
|
|
ULONG Length = sizeof(Buffer);
|
|
PTOKEN_AUDIT_POLICY pPolicy = (PTOKEN_AUDIT_POLICY) Buffer;
|
|
BOOLEAN bFound = FALSE;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// This code is necessary to maintain an accurate count of the sessions with per user audit settings
|
|
// that are active in the system.
|
|
//
|
|
|
|
if (LsapAdtPerUserAuditLogonCount)
|
|
{
|
|
|
|
Status = LsapAdtQueryPolicyByLuidPerUserAuditing(
|
|
pLogonId,
|
|
pPolicy,
|
|
&Length,
|
|
&bFound
|
|
);
|
|
|
|
if (NT_SUCCESS(Status) && bFound)
|
|
{
|
|
|
|
LsapAdtLogoffCountersPerUserAuditing(
|
|
pPolicy
|
|
);
|
|
|
|
Status = LsapAdtRemoveLuidQueryPerUserAuditing(
|
|
pLogonId
|
|
);
|
|
|
|
ASSERT(NT_SUCCESS(Status));
|
|
}
|
|
}
|
|
return Status;
|
|
}
|
|
|