WindowsXP-SP1/termsrv/winsta/server/audit.c
2020-09-30 16:53:49 +02:00

687 lines
21 KiB
C

/*******************************************************************************
* AUDIT.C
*
* This module contains the routines for logging audit events
*
* Copyright (C) 1997-1999 Microsoft Corp.
*******************************************************************************/
#include "precomp.h"
#pragma hdrstop
#include <rpc.h>
#include <msaudite.h>
#include <ntlsa.h>
#include <authz.h>
#include <authzi.h>
HANDLE AuditLogHandle = NULL;
HANDLE SystemLogHandle = NULL;
#define MAX_INSTANCE_MEMORYERR 20
/*
* Global data
*/
//Authz Changes
AUTHZ_RESOURCE_MANAGER_HANDLE hRM = NULL;
extern RTL_CRITICAL_SECTION g_AuthzCritSection;
//END Authz Changes
/*
* External procedures defined
*/
VOID
AuditEvent( PWINSTATION pWinstation, ULONG EventId );
NTSTATUS
AuthzReportEventW( IN PAUTHZ_AUDIT_EVENT_TYPE_HANDLE pHAET,
IN DWORD Flags,
IN ULONG EventId,
IN PSID pUserID,
IN USHORT NumStrings,
IN ULONG DataSize OPTIONAL, //Future - DO NOT USE
IN PWSTR* Strings,
IN PVOID Data OPTIONAL //Future - DO NOT USE
);
BOOL AuthzInit( IN DWORD Flags,
IN USHORT CategoryID,
IN USHORT AuditID,
IN USHORT ParameterCount,
OUT PAUTHZ_AUDIT_EVENT_TYPE_HANDLE phAuditEventType
);
BOOLEAN
AuditingEnabled ();
VOID
AuditEnd();
/*
* Internal procedures defined
*/
NTSTATUS
AdtBuildLuidString(
IN PLUID Value,
OUT PUNICODE_STRING ResultantString
);
BOOLEAN
IsAuditLogFull(
HANDLE LogHandle
)
{
BOOLEAN retval = TRUE;
EVENTLOG_FULL_INFORMATION EventLogFullInformation;
DWORD dwBytesNeeded;
if (GetEventLogInformation(LogHandle,
EVENTLOG_FULL_INFO,
&EventLogFullInformation,
sizeof(EventLogFullInformation),
&dwBytesNeeded ) ) {
if (EventLogFullInformation.dwFull == FALSE) {
retval = FALSE;
}
}
return retval;
}
NTSTATUS
AdtBuildLuidString(
IN PLUID Value,
OUT PUNICODE_STRING ResultantString
)
/*++
Routine Description:
This function builds a unicode string representing the passed LUID.
The resultant string will be formatted as follows:
(0x00005678,0x12340000)
Arguments:
Value - The value to be transformed to printable format (Unicode string).
ResultantString - Points to the unicode string header. The body of this
unicode string will be set to point to the resultant output value
if successful. Otherwise, the Buffer field of this parameter
will be set to NULL.
FreeWhenDone - If TRUE, indicates that the body of the ResultantString
must be freed to process heap when no longer needed.
Return Values:
STATUS_NO_MEMORY - indicates memory could not be allocated
for the string body.
All other Result Codes are generated by called routines.
--*/
{
NTSTATUS Status;
UNICODE_STRING IntegerString;
ULONG Buffer[(16*sizeof(WCHAR))/sizeof(ULONG)];
IntegerString.Buffer = (PWCHAR)&Buffer[0];
IntegerString.MaximumLength = 16*sizeof(WCHAR);
//
// Length (in WCHARS) is 3 for (0x
// 10 for 1st hex number
// 3 for ,0x
// 10 for 2nd hex number
// 1 for )
// 1 for null termination
//
ResultantString->Length = 0;
ResultantString->MaximumLength = 28 * sizeof(WCHAR);
ResultantString->Buffer = RtlAllocateHeap( RtlProcessHeap(), 0,
ResultantString->MaximumLength);
if (ResultantString->Buffer == NULL) {
return(STATUS_NO_MEMORY);
}
Status = RtlAppendUnicodeToString( ResultantString, L"(0x" );
Status = RtlIntegerToUnicodeString( Value->HighPart, 16, &IntegerString );
Status = RtlAppendUnicodeToString( ResultantString, IntegerString.Buffer );
Status = RtlAppendUnicodeToString( ResultantString, L",0x" );
Status = RtlIntegerToUnicodeString( Value->LowPart, 16, &IntegerString );
Status = RtlAppendUnicodeToString( ResultantString, IntegerString.Buffer );
Status = RtlAppendUnicodeToString( ResultantString, L")" );
return(STATUS_SUCCESS);
}
VOID
AuditEvent( PWINSTATION pWinstation, ULONG EventId )
{
NTSTATUS Status, Status2;
UNICODE_STRING LuidString;
PWSTR StringPointerArray[6];
USHORT StringIndex = 0;
TOKEN_STATISTICS TokenInformation;
ULONG ReturnLength;
BOOLEAN WasEnabled;
LUID LogonId = {0,0};
AUTHZ_AUDIT_EVENT_TYPE_HANDLE hAET = NULL;
if (!AuditingEnabled() )
return;
Status = RtlAdjustPrivilege(
SE_SECURITY_PRIVILEGE,
TRUE, // Enable the PRIVILEGE
FALSE, // Don't Use Thread token (under impersonation)
&WasEnabled
);
if ( Status == STATUS_NO_TOKEN ) {
DBGPRINT(("TERMSRV: AuditEvent: RtlAdjustPrivilege failure 0x%x\n",Status));
return;
}
//
//AUTHZ Changes
//
if( !AuthzInit( 0, SE_CATEGID_LOGON, (USHORT)EventId, 6, &hAET ))
goto badAuthzInit;
if (pWinstation->UserName && (wcslen(pWinstation->UserName) > 0)) {
StringPointerArray[StringIndex] = pWinstation->UserName;
} else {
StringPointerArray[StringIndex] = L"Unknown";
}
StringIndex++;
if (pWinstation->Domain && (wcslen(pWinstation->Domain) > 0)) {
StringPointerArray[StringIndex] = pWinstation->Domain;
} else {
StringPointerArray [StringIndex] = L"Unknown";
}
StringIndex++;
if (pWinstation->UserToken != NULL) {
Status = NtQueryInformationToken (
pWinstation->UserToken,
TokenStatistics,
&TokenInformation,
sizeof(TokenInformation),
&ReturnLength
);
if (NT_SUCCESS(Status)) {
Status = AdtBuildLuidString( &(TokenInformation.AuthenticationId), &LuidString );
} else {
Status = AdtBuildLuidString( &LogonId, &LuidString );
}
} else {
Status = AdtBuildLuidString( &LogonId, &LuidString );
}
StringPointerArray[StringIndex] = LuidString.Buffer;
StringIndex++;
if (pWinstation->WinStationName && (wcslen(pWinstation->WinStationName) > 0)) {
StringPointerArray[StringIndex] = pWinstation->WinStationName;
} else {
StringPointerArray[StringIndex] = L"Unknown" ;
}
StringIndex++;
if (pWinstation->Client.ClientName && (wcslen(pWinstation->Client.ClientName) > 0)) {
StringPointerArray[StringIndex] = pWinstation->Client.ClientName;
} else {
StringPointerArray[StringIndex] = L"Unknown";
}
StringIndex++;
if (pWinstation->Client.ClientAddress && (wcslen(pWinstation->Client.ClientAddress) > 0)) {
StringPointerArray[StringIndex] = pWinstation->Client.ClientAddress;
} else {
StringPointerArray[StringIndex] = L"Unknown";
}
StringIndex++;
//Authz Changes
Status = AuthzReportEventW( &hAET,
APF_AuditSuccess,
EventId,
pWinstation->pUserSid,
StringIndex,
0,
StringPointerArray,
NULL
);
//end authz changes
if ( !NT_SUCCESS(Status))
DBGPRINT(("Termsrv - failed to report event \n" ));
if( !WasEnabled ) {
/*
* Principle of least rights says to not go around with privileges
* held you do not need. So we must disable the shutdown privilege
* if it was just a logoff force.
*/
Status2 = RtlAdjustPrivilege(
SE_SECURITY_PRIVILEGE,
FALSE, // Disable the PRIVILEGE
FALSE, // Don't Use Thread token
&WasEnabled
);
}
badAuthzInit:
if( hAET != NULL )
AuthziFreeAuditEventType( hAET );
}
/***************************************************************************\
* AuditingEnabled
*
* Purpose : Check auditing via LSA.
*
* Returns: TRUE on success, FALSE on failure
*
* History:
* 5-6-92 DaveHart Created.
\***************************************************************************/
BOOLEAN
AuditingEnabled()
{
NTSTATUS Status, IgnoreStatus;
PPOLICY_AUDIT_EVENTS_INFO AuditInfo;
OBJECT_ATTRIBUTES ObjectAttributes;
SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
LSA_HANDLE PolicyHandle;
//
// Set up the Security Quality Of Service for connecting to the
// LSA policy object.
//
SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
SecurityQualityOfService.EffectiveOnly = FALSE;
//
// Set up the object attributes to open the Lsa policy object
//
InitializeObjectAttributes(
&ObjectAttributes,
NULL,
0L,
NULL,
NULL
);
ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
//
// Open the local LSA policy object
//
Status = LsaOpenPolicy(
NULL,
&ObjectAttributes,
POLICY_VIEW_AUDIT_INFORMATION | POLICY_SET_AUDIT_REQUIREMENTS,
&PolicyHandle
);
if (!NT_SUCCESS(Status)) {
DBGPRINT(("Termsrv: Failed to open LsaPolicyObject Status = 0x%lx", Status));
return FALSE;
}
Status = LsaQueryInformationPolicy(
PolicyHandle,
PolicyAuditEventsInformation,
(PVOID *)&AuditInfo
);
IgnoreStatus = LsaClose(PolicyHandle);
ASSERT(NT_SUCCESS(IgnoreStatus));
if (!NT_SUCCESS(Status)) {
DBGPRINT(("Termsrv: Failed to query audit event info Status = 0x%lx", Status));
return FALSE;
}
return (AuditInfo->AuditingMode &&
((AuditInfo->EventAuditingOptions)[AuditCategoryLogon] &
POLICY_AUDIT_EVENT_SUCCESS));
}
VOID WriteErrorLogEntry(
IN NTSTATUS NtStatusCode,
IN PVOID pRawData,
IN ULONG RawDataLength
)
{
NTSTATUS Status;
ULONG Length;
if ( !SystemLogHandle ) {
UNICODE_STRING ModuleName;
RtlInitUnicodeString( &ModuleName, L"TermService");
Status = ElfRegisterEventSourceW( NULL, &ModuleName, &SystemLogHandle );
if (!NT_SUCCESS(Status)) {
DBGPRINT(("Termsrv - failed to open System log file\n"));
return;
}
}
if (IsAuditLogFull(SystemLogHandle))
return;
Status = ElfReportEventW( SystemLogHandle,
EVENTLOG_ERROR_TYPE,
0,
NtStatusCode,
NULL,
0,
RawDataLength,
NULL,
pRawData,
0,
NULL,
NULL );
if ( !NT_SUCCESS(Status))
DBGPRINT(("Termsrv - failed to report event \n" ));
}
// This function is duplicated in \nt\termsrv\sessdir\dis\tssdis.cpp.
/****************************************************************************/
// PostErrorValueEvent
//
// Utility function used to create a system log error event containing one
// hex DWORD error code value.
/****************************************************************************/
void PostErrorValueEvent(unsigned EventCode, DWORD ErrVal)
{
HANDLE hLog;
WCHAR hrString[128];
PWSTR String = NULL;
extern WCHAR gpszServiceName[];
static DWORD numInstances = 0;
//
//count the numinstances of out of memory error, if this is more than
//a specified number, we just won't log them
//
if( STATUS_COMMITMENT_LIMIT == ErrVal )
{
if( numInstances > MAX_INSTANCE_MEMORYERR )
return;
//
//if applicable, tell the user that we won't log any more of the out of memory errors
//
if( numInstances >= MAX_INSTANCE_MEMORYERR - 1 ) {
wsprintfW(hrString, L"0x%X. This type of error will not be logged again to avoid clutter.", ErrVal);
String = hrString;
}
numInstances++;
}
hLog = RegisterEventSource(NULL, gpszServiceName);
if (hLog != NULL) {
if( NULL == String ) {
wsprintfW(hrString, L"0x%X", ErrVal);
String = hrString;
}
ReportEvent(hLog, EVENTLOG_ERROR_TYPE, 0, EventCode, NULL, 1, 0,
(const WCHAR **)&String, NULL);
DeregisterEventSource(hLog);
}
}
/*************************************************************
* AuthzInit Purpose : Initialize authz for logging an event to the security log
*Flags - unused
*Category Id - Security Category to which this event belongs
*Audit Id - An id for the event
*PArameter count - Number of parameters that will be passed to the logging function later
****************************************************************/
BOOL AuthzInit( IN DWORD Flags,
IN USHORT CategoryID,
IN USHORT AuditID,
IN USHORT ParameterCount,
OUT PAUTHZ_AUDIT_EVENT_TYPE_HANDLE phAuditEventType
)
{
BOOL fAuthzInit = TRUE;
if( NULL == phAuditEventType )
goto badAuthzInit;
*phAuditEventType = NULL;
//
//only one thread can create hRM
//
RtlEnterCriticalSection( &g_AuthzCritSection );
if( NULL == hRM )
{
fAuthzInit = AuthzInitializeResourceManager( 0,
NULL,
NULL,
NULL,
L"Terminal Server",
&hRM
);
if ( !fAuthzInit )
{
DBGPRINT(("TERMSRV: AuditEvent: AuthzInitializeResourceManager failed with %d\n", GetLastError()));
goto badAuthzInit;
}
}
RtlLeaveCriticalSection( &g_AuthzCritSection );
fAuthzInit = AuthziInitializeAuditEventType( Flags,
CategoryID,
AuditID,
ParameterCount,
phAuditEventType
);
if ( !fAuthzInit )
{
DBGPRINT(("TERMSRV: AuditEvent: AuthziInitializeAuditEventType failed with %d\n", GetLastError()));
goto badAuthzInit;
}
badAuthzInit:
if( !fAuthzInit )
{
if( NULL != *phAuditEventType )
{
if( !AuthziFreeAuditEventType( *phAuditEventType ))
DBGPRINT(("TERMSRV: AuditEvent: AuthziFreeAuditEventType failed with %d\n", GetLastError()));
*phAuditEventType = NULL;
}
}
// if( fAuthzInit )
// DBGPRINT(("TERMSRV: Successfully initialized authz = %d\n", AuditID));
return fAuthzInit;
}
/*********************************************************
* Purpose : Log an Event to the security log
* In pHAET
* Audit Event type obtained from a call to AuthzInit() above
* In Flags
* APF_AuditSuccess or others as listed in the header file
* pUserSID - Unused
* NumStrings - Number of strings contained within "Strings"
* DataSize - unused
* Strings- Pointer to a sequence of unicode strings
* Data - unused
*
**********************************************************/
NTSTATUS
AuthzReportEventW( IN PAUTHZ_AUDIT_EVENT_TYPE_HANDLE pHAET,
IN DWORD Flags,
IN ULONG EventId,
IN PSID pUserSID,
IN USHORT NumStrings,
IN ULONG DataSize OPTIONAL, //Future - DO NOT USE
IN PWSTR* Strings,
IN PVOID Data OPTIONAL //Future - DO NOT USE
)
{
NTSTATUS status = STATUS_ACCESS_DENIED;
AUTHZ_AUDIT_EVENT_HANDLE hAE = NULL;
BOOL fSuccess = FALSE;
PAUDIT_PARAMS pParams = NULL;
if( NULL == hRM || NULL == pHAET || *pHAET == NULL )
return status;
fSuccess = AuthziAllocateAuditParams( &pParams, NumStrings );
if ( !fSuccess )
{
DBGPRINT(("TERMSRV: AuditEvent: AuthzAllocateAuditParams failed with %d\n", GetLastError()));
goto BadAuditEvent;
}
if( 6 == NumStrings )
{
fSuccess = AuthziInitializeAuditParamsWithRM( Flags,
hRM,
NumStrings,
pParams,
APT_String, Strings[0],
APT_String, Strings[1],
APT_String, Strings[2],
APT_String, Strings[3],
APT_String, Strings[4],
APT_String, Strings[5]
);
}
else if( 0 == NumStrings )
{
fSuccess = AuthziInitializeAuditParamsWithRM( Flags,
hRM,
NumStrings,
pParams
);
}
else
{
//we don't support anything else
fSuccess = FALSE;
DBGPRINT(("TERMSRV: AuditEvent: unsupported audit type \n"));
goto BadAuditEvent;
}
if ( !fSuccess )
{
DBGPRINT(("TERMSRV: AuditEvent: AuthziInitializeAuditParamsWithRM failed with %d\n", GetLastError()));
goto BadAuditEvent;
}
fSuccess = AuthziInitializeAuditEvent( 0,
hRM,
*pHAET,
pParams,
NULL,
INFINITE,
L"",
L"",
L"",
L"",
&hAE
);
if ( !fSuccess )
{
DBGPRINT(("TERMSRV: AuditEvent: AuthziInitializeAuditEvent failed with %d\n", GetLastError()));
goto BadAuditEvent;
}
fSuccess = AuthziLogAuditEvent( 0,
hAE,
NULL
);
if ( !fSuccess )
{
DBGPRINT(("TERMSRV: AuditEvent: AuthziLogAuditEvent failed with %d\n", GetLastError()));
goto BadAuditEvent;
}
BadAuditEvent:
if( hAE )
AuthzFreeAuditEvent( hAE );
if( pParams )
AuthziFreeAuditParams( pParams );
if( fSuccess )
status = STATUS_SUCCESS;
//if( fSuccess )
// DBGPRINT(("TERMSRV: Successfully audited event with authz= %d\n", EventId));
return status;
}
//
//should only be called once per our process
//
VOID AuditEnd()
{
if( NULL != hRM )
{
if( !AuthzFreeResourceManager( hRM ))
DBGPRINT(("TERMSRV: AuditEvent: AuthzFreeResourceManager failed with %d\n", GetLastError()));
hRM = NULL;
}
}