NT4/private/lsa/server/adtinit.c
2020-09-30 17:12:29 +02:00

879 lines
20 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
adtinit.c
Abstract:
Local Security Authority - Auditing Initialization
Author:
Scott Birrell (ScottBi) November 20, 1991
Environment:
Revision History:
--*/
#include <msaudite.h>
#include "lsasrvp.h"
#include "adtp.h"
NTSTATUS LsapAdtInitializeCrashOnFail( VOID );
BOOL LsapShutdownNotification( IN ULONG ControlType );
BOOLEAN LsapShutdownInProgress = FALSE;
ULONG LsapAdtInitializationPass = 0;
//
// Array of drive letter to device mappings for generating path strings.
//
DRIVE_MAPPING DriveMappingArray[MAX_DRIVE_MAPPING];
//
// Name that will be used as the default subsystem name for LSA generated events
//
UNICODE_STRING LsapSubsystemName;
//
// Special privilege values which are not normally audited,
// but generate audits when assigned to a user. See
// LsapAdtAuditSpecialPrivileges.
//
LUID ChangeNotifyPrivilege;
LUID AuditPrivilege;
LUID CreateTokenPrivilege;
LUID AssignPrimaryTokenPrivilege;
LUID BackupPrivilege;
LUID RestorePrivilege;
LUID DebugPrivilege;
//
// Global variable indicating whether or not we are supposed
// to crash when an audit fails.
//
BOOLEAN LsapCrashOnAuditFail = FALSE;
BOOLEAN LsapAllowAdminLogonsOnly = FALSE;
NTSTATUS
LsapAdtInitialize(
IN ULONG Pass
)
/*++
Routine Description:
This function performs initialization of auditing within the LSA, and
it also issues commands to the Reference Monitor to enable it to
complete any initialization of auditing variables that is dependent
on the content of the LSA Database. At time of call, the main
System Init thread is in the Reference Monitor awaiting completion
of all LSA initialization, and the Reference Monitor Command
Server thread is waiting for commands.
The following steps are performed:
o Read the Audit Event and Audit Log information from the LSA
Database.
o Call the Event Logging function to open the Audit Log
o Issue a Reference Monitor command to write the Audit Event Info
to the Reference-Monitor's in-memory database.
Arguments:
Pass - Specifies the stage of initialization to be performed.
Pass 1 - Initialization required before Audit Records can
be written to the Audit Log. Any Audit Records received
during this time will be "cached" by the LSA and will
be written out at Pass 2.
Pass 2 - Write out Audit Records cached during Pass 1.
Return Value:
NTSTATUS - Standard Nt Result Code.
All Result Codes are generated by called routines.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
NTSTATUS SecondaryStatus = STATUS_SUCCESS;
ULONG AuditLogInfoLength = sizeof (POLICY_AUDIT_LOG_INFO);
ULONG AuditEventInfoLength = sizeof (LSARM_POLICY_AUDIT_EVENTS_INFO);
ULONG AuditFullQueryInfoLength = sizeof (POLICY_AUDIT_FULL_QUERY_INFO);
BOOLEAN AcquiredLock = FALSE;
UNICODE_STRING UnicodeString;
PUNICODE_STRING Strings;
PSID Sid = NULL;
LSARM_POLICY_AUDIT_EVENTS_INFO AuditEventsInfo;
Strings = &UnicodeString;
RtlInitUnicodeString( Strings, L"System Restart");
RtlInitUnicodeString( &LsapSubsystemName, L"Security" );
if (Pass == 1) {
Status = LsapAdtInitializeLogQueue();
if (!NT_SUCCESS(Status)) {
goto AuditInitError;
}
//
// Acquire the LSA Database Lock.
//
Status = LsapDbAcquireLock();
if (!NT_SUCCESS(Status)) {
goto AuditInitError;
}
AcquiredLock = TRUE;
//
// Read the Audit Log Information from the PolAdtLg attribute of the Lsa
// Database object.
//
Status = LsapDbReadAttributeObject(
LsapDbHandle,
&LsapDbNames[PolAdtLg],
&LsapAdtLogInformation,
&AuditLogInfoLength
);
if (!NT_SUCCESS(Status)) {
LsapLogError(
"LsapAdtInitialize: Read Audit Log Info returned 0x%lx\n",
Status
);
goto AuditInitError;
}
//
// Query the Audit Log Full Information in the LSA Database. Note
// that it is too early to update a log full condition, so don't
// try to write to the Audit Log.
//
Status = LsapAdtQueryAuditLogFullInfo(
LsapDbHandle,
(ULONG) 0,
&LsapAdtLogFullInformation
);
if (!NT_SUCCESS(Status)) {
LsapLogError(
"LsapAdtInitialize: Update Audit Log Full Info returned 0x%lx\n",
Status
);
goto AuditInitError;
}
//
// Read the Audit Event Information from the AdtEvent attribute of the Lsa
// Database object. The information consists of the Auditing Mode and
// the Auditing Options for each Audit Event Type.
//
Status = LsapDbReadAttributeObject(
LsapDbHandle,
&LsapDbNames[PolAdtEv],
&AuditEventsInfo,
&AuditEventInfoLength
);
if (!NT_SUCCESS(Status)) {
//
// This section of code is temporary and allows an old
// Policy Database to work with the new Audit Event Categories
// without the need to re-install. The Audit Event Information
// is overwritten with the new format and all auditing is turned
// off.
//
if (Status == STATUS_BUFFER_OVERFLOW) {
KdPrint(("LsapAdtInitialize: Old Audit Event Info detected\n"
"Replacing with new format, all auditing disabled\n"));
//
// Initialize Default Event Auditing Options. No auditing is specified
// for any event type.
//
Status = LsapAdtInitializeDefaultAuditing(
LSAP_DB_UPDATE_POLICY_DATABASE,
&AuditEventsInfo
);
if (!NT_SUCCESS(Status)) {
goto AuditInitError;
}
} else {
LsapLogError(
"LsapAdtInitialize: Read Audit Event Info returned 0x%lx\n",
Status
);
goto AuditInitError;
}
}
//
// Set global flags to tell us if we're supposed to be auditing
// successful logons, failed logons, or both
//
//
LsapAdtAuditingLogon( &AuditEventsInfo );
//
// During system initialization, we are effectively logged on as
// system.
//
LsapAdtSystemRestart( &AuditEventsInfo );
(VOID) LsapAdtInitializeCrashOnFail();
//
// Send a command to the Reference Monitor to write the Auditing
// State to its in-memory data.
//
Status = LsapCallRm(
RmAuditSetCommand,
&AuditEventsInfo,
sizeof (LSARM_POLICY_AUDIT_EVENTS_INFO),
NULL,
0
);
if (!NT_SUCCESS(Status)) {
LsapLogError("LsapAdtInitialize: LsapCallRm returned 0x%lx\n", Status);
goto AuditInitError;
}
RtlCopyMemory(
&LsapAdtEventsInformation,
&AuditEventsInfo,
sizeof(LSARM_POLICY_AUDIT_EVENTS_INFO)
);
LsapAdtInitializeDriveLetters();
//
// Initialize privilege values we need
//
ChangeNotifyPrivilege = RtlConvertLongToLuid( SE_CHANGE_NOTIFY_PRIVILEGE );
AuditPrivilege = RtlConvertLongToLuid( SE_AUDIT_PRIVILEGE );
CreateTokenPrivilege = RtlConvertLongToLuid( SE_CREATE_TOKEN_PRIVILEGE );
AssignPrimaryTokenPrivilege = RtlConvertLongToLuid( SE_ASSIGNPRIMARYTOKEN_PRIVILEGE );
BackupPrivilege = RtlConvertLongToLuid( SE_BACKUP_PRIVILEGE );
RestorePrivilege = RtlConvertLongToLuid( SE_RESTORE_PRIVILEGE );
DebugPrivilege = RtlConvertLongToLuid( SE_DEBUG_PRIVILEGE );
//
// Tell base/wincon how to shut us down.
// First, tell base to shut us down as late in the game as possible.
//
SetProcessShutdownParameters(LSAP_SHUTDOWN_LEVEL, SHUTDOWN_NORETRY);
// And, tell them what function to call when we are being shutdown:
SetConsoleCtrlHandler(LsapShutdownNotification, TRUE);
} else if (Pass == 2) {
//
// Write out any Audit Records that were cached during the
// first stage of initialization. The Audit Log will be opened
// on the first write if necessary.
//
//
// BUGBUG - ScottBi 8/6/92 - This action cannot be taken here
// unless we know that the EventLog service is running. For now,
// an attempt is made to open the log each time an Audit Record
// is generated, and the cache grows until a limit is reached,
// at which point auditing is turned off and subsequent records
// are discarded.
//
/*
Status = LsapAdtWriteLog( NULL, (ULONG) 0);
if (!NT_SUCCESS(Status)) {
goto AuditInitError;
}
*/
}
AuditInitFinish:
if (AcquiredLock) {
LsapDbReleaseLock();
}
return(Status);
AuditInitError:
//
// If the Audit Log is full, signal the Log Full condition
//
if (Status == STATUS_LOG_FILE_FULL) {
SecondaryStatus = LsapAdtSignalLogFull();
}
//
// If auditing failed to initialize, output warning and disable
// auditing.
//
if (Pass == 1) {
LsapLogError(
"LSA: Warning - Audit Initialization Pass 1 Returned 0x%lx\n"
" Auditing has been disabled\n",
Status
);
} else {
LsapLogError(
"LSA: Warning - Audit Initialization Pass 2 Returned 0x%lx\n"
" Auditing has been disabled\n",
Status
);
}
LsapAdtEventsInformation.AuditingMode = FALSE;
Status = LsarSetInformationPolicy(
LsapDbHandle,
PolicyAuditEventsInformation,
(PLSAPR_POLICY_INFORMATION) &LsapAdtEventsInformation
);
goto AuditInitFinish;
}
NTSTATUS
LsapAdtInitializeDefaultAuditing(
IN ULONG Options,
OUT PLSARM_POLICY_AUDIT_EVENTS_INFO AuditEventsInformation
)
/*++
Routine Description:
This routine sets an initial default Auditing State in which auditing
is turned off. It is called only during initialization of the LSA
or during the installation of its Policy Database. The initial
auditing state may also optionally be written to the Lsa Policy
Database provided that the Policy Object has been created and its
internal handle is available.
Arguments:
Options - Specifies optional actions to be taken
LSAP_DB_UPDATE_POLICY_DATABASE - Update the corresponding information
in the Policy Database. This option must only be specified
where it is known that the Policy Object exists.
AuditEventsInformation - Pointer to structure that will receive the Audit Event
Information
Return Values:
None.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
LSAP_DB_ATTRIBUTE AuditEventsAttribute;
BOOLEAN ObjectReferenced = FALSE;
ULONG EventAuditingOptionsLength =
(POLICY_AUDIT_EVENT_TYPE_COUNT * sizeof(POLICY_AUDIT_EVENT_OPTIONS));
//
// Turn off auditing and set the count of Audit Event Types (Categories)
//
AuditEventsInformation->AuditingMode = FALSE;
AuditEventsInformation->MaximumAuditEventCount = POLICY_AUDIT_EVENT_TYPE_COUNT;
//
// Turn off auditing for all events.
//
RtlZeroMemory(AuditEventsInformation->EventAuditingOptions, EventAuditingOptionsLength);
if (Options & LSAP_DB_UPDATE_POLICY_DATABASE) {
ASSERT(LsapPolicyHandle != NULL);
//
// Start a transaction on the Policy Object
//
Status = LsapDbReferenceObject(
LsapPolicyHandle,
(ACCESS_MASK) 0,
PolicyObject,
LSAP_DB_ACQUIRE_LOCK | LSAP_DB_START_TRANSACTION
);
if (!NT_SUCCESS(Status)) {
goto InitializeDefaultAuditingError;
}
ObjectReferenced = TRUE;
LsapDbInitializeAttribute(
&AuditEventsAttribute,
&LsapDbNames[PolAdtEv],
AuditEventsInformation,
sizeof (LSARM_POLICY_AUDIT_EVENTS_INFO),
FALSE
);
Status = LsapDbWriteAttributesObject(
LsapPolicyHandle,
&AuditEventsAttribute,
(ULONG) 1
);
if (!NT_SUCCESS(Status)) {
goto InitializeDefaultAuditingError;
}
}
InitializeDefaultAuditingFinish:
if (ObjectReferenced) {
Status = LsapDbDereferenceObject(
LsapPolicyHandle,
PolicyObject,
LSAP_DB_RELEASE_LOCK | LSAP_DB_FINISH_TRANSACTION,
(SECURITY_DB_DELTA_TYPE) 0,
Status
);
ObjectReferenced = FALSE;
}
return(Status);
InitializeDefaultAuditingError:
goto InitializeDefaultAuditingFinish;
}
NTSTATUS
LsapAdtInitializeLogQueue(
)
/*++
Routine Description:
This function initializes the Audit Log Queue.
Arguments:
None.
Return Values:
NTSTATUS - Standard Nt Result Code
Currently, STATUS_SUCCESS is always returned.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
Status = RtlInitializeCriticalSection(&LsapAdtQueueLock);
if (NT_SUCCESS(Status)) {
Status = RtlInitializeCriticalSection(&LsapAdtLogFullLock);
}
LsapAdtLogQueue.FirstQueuedRecord = NULL;
LsapAdtLogQueue.LastQueuedRecord = NULL;
return(Status);
}
VOID
LsapAdtAuditingLogon(
PLSARM_POLICY_AUDIT_EVENTS_INFO AuditEventsInfo
)
/*++
Routine Description:
Examines auditing data and determines if we are auditing
logon events.
Arguments:
AuditEventsInfo - Auditing data.
Return Value:
TRUE if auditing logon, FALSE otherwise.
--*/
{
if ( !AuditEventsInfo->AuditingMode ) {
LsapAuditSuccessfulLogons = FALSE;
LsapAuditFailedLogons = FALSE;
return;
}
if ( (AuditEventsInfo->EventAuditingOptions)[AuditCategoryLogon] & POLICY_AUDIT_EVENT_SUCCESS ) {
LsapAuditSuccessfulLogons = TRUE;
} else {
LsapAuditSuccessfulLogons = FALSE;
}
if ( (AuditEventsInfo->EventAuditingOptions)[AuditCategoryLogon] & POLICY_AUDIT_EVENT_FAILURE ) {
LsapAuditFailedLogons = TRUE;
} else {
LsapAuditFailedLogons = FALSE;
}
}
VOID
LsapAdtInitializeDriveLetters(
VOID
)
/*++
Routine Description:
Initializes an array of symbolic link to drive letter mappings
for use by auditing code.
Arguments:
None.
Return Value:
None.
--*/
{
UNICODE_STRING LinkName;
PUNICODE_STRING DeviceName;
OBJECT_ATTRIBUTES Obja;
HANDLE LinkHandle;
NTSTATUS Status;
ULONG i;
PWCHAR p;
PWCHAR DeviceNameBuffer;
ULONG MappingIndex = 0;
WCHAR wszDosDevices[sizeof(L"\\DosDevices\\A:") + 1];
wcscpy(wszDosDevices, L"\\DosDevices\\A:");
RtlInitUnicodeString(&LinkName, wszDosDevices);
p = (PWCHAR)LinkName.Buffer;
//
// Make p point to the drive letter in the LinkName string
//
p = p+12;
for( i=0 ; i<26 ; i++ ){
*p = (WCHAR)'A' + (WCHAR)i;
InitializeObjectAttributes(
&Obja,
&LinkName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtOpenSymbolicLinkObject(
&LinkHandle,
SYMBOLIC_LINK_QUERY,
&Obja
);
if (NT_SUCCESS( Status )) {
//
// Open succeeded, Now get the link value
//
DriveMappingArray[MappingIndex].DriveLetter = *p;
DeviceName = &DriveMappingArray[MappingIndex].DeviceName;
DeviceNameBuffer = LsapAllocateLsaHeap( MAXIMUM_FILENAME_LENGTH );
DeviceName->Length = 0;
DeviceName->MaximumLength = MAXIMUM_FILENAME_LENGTH;
DeviceName->Buffer = DeviceNameBuffer;
Status = NtQuerySymbolicLinkObject(
LinkHandle,
DeviceName,
NULL
);
NtClose(LinkHandle);
if ( NT_SUCCESS(Status) ) {
MappingIndex++;
} else {
LsapFreeLsaHeap( DeviceNameBuffer );
RtlInitUnicodeString( DeviceName, NULL );
}
}
}
}
NTSTATUS
LsapAdtInitializeCrashOnFail(
VOID
)
/*++
Routine Description:
Reads the registry to see if the user has told us to crash if an audit fails.
Arguments:
None.
Return Value:
STATUS_SUCCESS
--*/
{
HANDLE KeyHandle;
NTSTATUS Status;
NTSTATUS TmpStatus;
OBJECT_ATTRIBUTES Obja;
ULONG ResultLength;
UNICODE_STRING KeyName;
UNICODE_STRING ValueName;
CHAR KeyInfo[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(BOOLEAN)];
PKEY_VALUE_PARTIAL_INFORMATION pKeyInfo;
//
// Check the value of the CrashOnAudit key.
//
RtlInitUnicodeString( &KeyName, L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Lsa");
InitializeObjectAttributes( &Obja,
&KeyName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = NtOpenKey(
&KeyHandle,
KEY_QUERY_VALUE | KEY_SET_VALUE,
&Obja
);
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
LsapCrashOnAuditFail = FALSE;
return( STATUS_SUCCESS );
}
RtlInitUnicodeString( &ValueName, CRASH_ON_AUDIT_FAIL_VALUE );
Status = NtQueryValueKey(
KeyHandle,
&ValueName,
KeyValuePartialInformation,
KeyInfo,
sizeof(KeyInfo),
&ResultLength
);
TmpStatus = NtClose(KeyHandle);
ASSERT(NT_SUCCESS(TmpStatus));
//
// If it's not found, don't enable CrashOnFail.
//
if (!NT_SUCCESS( Status )) {
LsapCrashOnAuditFail = FALSE;
} else {
//
// Check the value of the CrashOnFail value. If it is 1, we
// crash on audit fail. If it is two, we only allow admins to
// logon.
//
pKeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyInfo;
if (*(pKeyInfo->Data) == LSAP_CRASH_ON_AUDIT_FAIL) {
LsapCrashOnAuditFail = TRUE;
} else if (*(pKeyInfo->Data) == LSAP_ALLOW_ADIMIN_LOGONS_ONLY) {
LsapAllowAdminLogonsOnly = TRUE;
}
}
if ( LsapCrashOnAuditFail ) {
BOOLEAN WasEnabled;
Status = RtlAdjustPrivilege(
SE_SHUTDOWN_PRIVILEGE,
TRUE,
FALSE,
&WasEnabled
);
//
// This had better work.
//
ASSERT(NT_SUCCESS(Status));
return( Status );
}
return( STATUS_SUCCESS );
}
BOOL
LsapShutdownNotification(
IN ULONG ControlType
)
/*++
Routine Description:
This routine sets a global flag indicating that shutdown is in progress.
The flag is used by the auditing subsystem to tell whether to bugcheck
when an audit fails - we don't want to bugcheck during shutdown because
the eventlog stopped.
Arguments:
ControlType - a flag indicating what event occurred.
Return Value:
TRUE
--*/
{
if (ControlType == CTRL_SHUTDOWN_EVENT) {
LsapShutdownInProgress = TRUE;
}
return(TRUE);
}