/******************************************************************************* * AUDIT.C * * This module contains the routines for logging audit events * * Copyright (C) 1997-1999 Microsoft Corp. *******************************************************************************/ #include "precomp.h" #pragma hdrstop #include #include #include #include #include #include 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; UNICODE_STRING LuidString; PWSTR StringPointerArray[6]; USHORT StringIndex = 0; TOKEN_STATISTICS TokenInformation; ULONG ReturnLength; LUID LogonId = {0,0}; AUTHZ_AUDIT_EVENT_TYPE_HANDLE hAET = NULL; if (!AuditingEnabled() ) return; // Initialize LuidString. LuidString.Length = 0; LuidString.Buffer = NULL; // //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++; StringPointerArray[StringIndex] = L"Unknown"; if( pWinstation->pLastClientAddress != NULL ) { PSTR szIPV4 = NULL; TCHAR tchWideIP[ 80 ]; int cchWideIP = sizeof( tchWideIP ) / sizeof( TCHAR ); if( pWinstation->pLastClientAddress->length < 16 ) { /* currently only ipv4 apis are in use */ struct in_addr Ipv4; RtlCopyMemory( &Ipv4 , &pWinstation->pLastClientAddress->addr[0] , pWinstation->pLastClientAddress->length ); szIPV4 = inet_ntoa( Ipv4 ); // convert ansi to wide if( MultiByteToWideChar( CP_ACP, // code page 0, // character-type options szIPV4, // string to map -1, // number of bytes in string tchWideIP, // wide-character buffer cchWideIP // size of buffer ) != 0 ) { StringPointerArray[StringIndex] = tchWideIP; } } } 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" )); badAuthzInit: if( hAET != NULL ) AuthziFreeAuditEventType( hAET ); if (LuidString.Buffer != NULL) { RtlFreeHeap(RtlProcessHeap(), 0, LuidString.Buffer); } } /***************************************************************************\ * 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; BOOLEAN ReturnValue; // // 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; } ReturnValue = (AuditInfo->AuditingMode && ((AuditInfo->EventAuditingOptions)[AuditCategoryLogon] & POLICY_AUDIT_EVENT_SUCCESS)); LsaFreeMemory(AuditInfo); return ReturnValue; } 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; BOOLEAN WasEnabled = FALSE; NTSTATUS Status; if( NULL == phAuditEventType ) goto badAuthzInit; Status = RtlAdjustPrivilege( SE_SECURITY_PRIVILEGE | SE_AUDIT_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 FALSE; } *phAuditEventType = NULL; // //only one thread can create hRM // RtlEnterCriticalSection( &g_AuthzCritSection ); if( NULL == hRM ) { fAuthzInit = AuthzInitializeResourceManager( 0, NULL, NULL, NULL, L"Terminal Server", &hRM ); } RtlLeaveCriticalSection( &g_AuthzCritSection ); if ( !fAuthzInit ) { DBGPRINT(("TERMSRV: AuditEvent: AuthzInitializeResourceManager failed with %d\n", GetLastError())); goto badAuthzInit; } 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( !WasEnabled ) { /* * Principle of least rights says to not go around with privileges * held you do not need. So we must disable the privilege. */ RtlAdjustPrivilege( SE_SECURITY_PRIVILEGE | SE_AUDIT_PRIVILEGE, FALSE, // Disable the PRIVILEGE FALSE, // Don't Use Thread token &WasEnabled ); } // 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; } }