Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2208 lines
69 KiB

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
adtobjs.c
Abstract:
Local Security Authority - Auditing object parameter file services.
Author:
Jim Kelly (JimK) 20-Oct-1992
Environment:
Revision History:
--*/
#include <lsapch2.h>
#include <msaudite.h>
#include <msobjs.h>
#include "adtp.h"
//
// This is the maximum length of standard access type names.
// This is used to build an array.
//
#define ADTP_MAX_ACC_NAME_LENGTH (12)
//
//
// This module builds a list of event source module descriptors.
// The source modules are identified by name (kept in the descriptor).
//
//
// For each source module a list of objects exported by that module is
// linked to the source module's descriptor. Each entry in this list
// is an object descriptor containing a name and a base event offset
// for specific access types.
//
//
// The chicken-wire data structure for source module and object descriptors
// looks like:
//
// LsapAdtSourceModules --+
// |
// +------------------+
// |
// |
// | +-----------+ +-----------+
// +--->| Next ----|---------------------------->| Next ----|--->...
// | | | |
// |-----------| |-----------|
// | Name | | Name |
// | | | |
// |-----------| |-----------|
// | Objects | | Objects |
// | o | | o |
// +-----o-----+ +-----o-----+
// o +-------+ +-------+ o
// o | Next--|->| Next--|->... o
// ooo>|-------| |-------| oooooo> ...
// | Name | | Name |
// |-------| |-------|
// | Base | | Base |
// | Offset| | Offset|
// +-------+ +-------+
//
// The specific access type names are expected to have contiguous message IDs
// starting at the base offset value. For example, the access type name for
// specific access bit 0 for the framitz object might have message ID 2132
// (and bit 0 serves as the base offset). So, specific access bit 4 would be
// message ID (2132+4).
//
// The valid mask defines the set of specific accesses defined by each object
// type. If there are gaps in the valid mask, the arithmetic above must still
// be ensured. That is, the message ID of the specific access related to
// bit n is message ID (BaseOffset + bit position). So, for example, if
// bits 0, 1, 4 and 5 are valid (and 2 & 3 are not), be sure to leave unused
// message IDs where bits 2 and 3 would normally be.
//
////////////////////////////////////////////////////////////////////////
// //
// Data types used within this module //
// //
////////////////////////////////////////////////////////////////////////
#define LSAP_ADT_ACCESS_NAME_FORMATTING L"\r\n\t\t\t"
#define LSAP_ADT_ACCESS_NAME_FORMATTING_TAB L"\t"
#define LSAP_ADT_ACCESS_NAME_FORMATTING_NL L"\r\n"
#define LsapAdtSourceModuleLock() (RtlEnterCriticalSection(&LsapAdtSourceModuleLock))
#define LsapAdtSourceModuleUnlock() (RtlLeaveCriticalSection(&LsapAdtSourceModuleLock))
//
// Each event source is represented by a source module descriptor.
// These are kept on a linked list (LsapAdtSourceModules).
//
typedef struct _LSAP_ADT_OBJECT {
//
// Pointer to next source module descriptor
// This is assumed to be the first field in the structure.
//
struct _LSAP_ADT_OBJECT *Next;
//
// Name of object
//
UNICODE_STRING Name;
//
// Base offset of specific access types
//
ULONG BaseOffset;
} LSAP_ADT_OBJECT, *PLSAP_ADT_OBJECT;
//
// Each event source is represented by a source module descriptor.
// These are kept on a linked list (LsapAdtSourceModules).
//
typedef struct _LSAP_ADT_SOURCE {
//
// Pointer to next source module descriptor
// This is assumed to be the first field in the structure.
//
struct _LSAP_ADT_SOURCE *Next;
//
// Name of source module
//
UNICODE_STRING Name;
//
// list of objects
//
PLSAP_ADT_OBJECT Objects;
} LSAP_ADT_SOURCE, *PLSAP_ADT_SOURCE;
////////////////////////////////////////////////////////////////////////
// //
// Variables global within this module //
// //
////////////////////////////////////////////////////////////////////////
//
// List head for source modules, and lock protecting references
// or modifications of the links in that list.
//
// Once a module's or object's name and value are established, they
// are never changed. So, this lock only needs to be held while
// links are being referenced or changed. You don't need to retain
// it just so you can reference, for example, the name or BaseOffset
// of an object.
//
PLSAP_ADT_SOURCE LsapAdtSourceModules;
RTL_CRITICAL_SECTION LsapAdtSourceModuleLock;
//
// This is used to house well-known access ID strings.
// Each string name may be up to ADTP_MAX_ACC_NAME_LENGTH WCHARs long.
// There are 16 specific names, and 7 well known event ID strings.
//
WCHAR LsapAdtAccessIdsStringBuffer[ADTP_MAX_ACC_NAME_LENGTH * 23]; // max wchars in each of 23 strings
UNICODE_STRING LsapAdtEventIdStringDelete,
LsapAdtEventIdStringReadControl,
LsapAdtEventIdStringWriteDac,
LsapAdtEventIdStringWriteOwner,
LsapAdtEventIdStringSynchronize,
LsapAdtEventIdStringAccessSysSec,
LsapAdtEventIdStringMaxAllowed,
LsapAdtEventIdStringSpecific[16];
////////////////////////////////////////////////////////////////////////
// //
// Services exported by this module. //
// //
////////////////////////////////////////////////////////////////////////
NTSTATUS
LsapAdtObjsInitialize(
)
/*++
Routine Description:
This function reads the object parameter file information from the
registry.
This service should be called in pass 1.
Arguments:
None.
Return Value:
STATUS_NO_MEMORY - indicates memory could not be allocated
to store the object information.
All other Result Codes are generated by called routines.
--*/
{
NTSTATUS Status,
IgnoreStatus;
OBJECT_ATTRIBUTES ObjectAttributes;
HANDLE AuditKey,
ModuleKey,
ObjectNamesKey = NULL ;
ULONG i,
ModuleIndex,
ObjectIndex,
RequiredLength;
UNICODE_STRING AuditKeyName,
TmpString;
PLSAP_ADT_SOURCE NextModule = NULL;
PKEY_BASIC_INFORMATION KeyInformation;
PLSAP_ADT_OBJECT NextObject;
PKEY_VALUE_FULL_INFORMATION KeyValueInformation;
PULONG ObjectData;
BOOLEAN ModuleHasObjects = TRUE;
//
// Initialize module-global variables, including strings we will need
//
//
// List of source modules and objects. These lists are constantly
// being adjusted to try to improve performance. Access to these
// lists is protected by a critical section.
//
LsapAdtSourceModules = NULL;
Status = RtlInitializeCriticalSection(&LsapAdtSourceModuleLock);
if (!NT_SUCCESS(Status))
{
return Status;
}
//
// we need a number of strings.
//
i = 0;
LsapAdtEventIdStringDelete.Length = 0;
LsapAdtEventIdStringDelete.MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringDelete.Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_DELETE,
10, //Base
&LsapAdtEventIdStringDelete
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i += ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringReadControl.Length = 0;
LsapAdtEventIdStringReadControl.MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringReadControl.Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_READ_CONTROL,
10, //Base
&LsapAdtEventIdStringReadControl
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i += ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringWriteDac.Length = 0;
LsapAdtEventIdStringWriteDac.MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringWriteDac.Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_WRITE_DAC,
10, //Base
&LsapAdtEventIdStringWriteDac
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i += ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringWriteOwner.Length = 0;
LsapAdtEventIdStringWriteOwner.MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringWriteOwner.Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_WRITE_OWNER,
10, //Base
&LsapAdtEventIdStringWriteOwner
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i += ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringSynchronize.Length = 0;
LsapAdtEventIdStringSynchronize.MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringSynchronize.Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SYNCHRONIZE,
10, //Base
&LsapAdtEventIdStringSynchronize
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i += ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringAccessSysSec.Length = 0;
LsapAdtEventIdStringAccessSysSec.MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringAccessSysSec.Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_ACCESS_SYS_SEC,
10, //Base
&LsapAdtEventIdStringAccessSysSec
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i+= ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringMaxAllowed.Length = 0;
LsapAdtEventIdStringMaxAllowed.MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringMaxAllowed.Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_MAXIMUM_ALLOWED,
10, //Base
&LsapAdtEventIdStringMaxAllowed
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i+= ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringSpecific[0].Length = 0;
LsapAdtEventIdStringSpecific[0].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringSpecific[0].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_0,
10, //Base
&LsapAdtEventIdStringSpecific[0]
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i+= ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringSpecific[1].Length = 0;
LsapAdtEventIdStringSpecific[1].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringSpecific[1].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_1,
10, //Base
&LsapAdtEventIdStringSpecific[1]
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i+= ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringSpecific[2].Length = 0;
LsapAdtEventIdStringSpecific[2].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringSpecific[2].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_2,
10, //Base
&LsapAdtEventIdStringSpecific[2]
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i+= ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringSpecific[3].Length = 0;
LsapAdtEventIdStringSpecific[3].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringSpecific[3].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_3,
10, //Base
&LsapAdtEventIdStringSpecific[3]
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i+= ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringSpecific[4].Length = 0;
LsapAdtEventIdStringSpecific[4].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringSpecific[4].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_4,
10, //Base
&LsapAdtEventIdStringSpecific[4]
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i+= ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringSpecific[5].Length = 0;
LsapAdtEventIdStringSpecific[5].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringSpecific[5].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_5,
10, //Base
&LsapAdtEventIdStringSpecific[5]
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i+= ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringSpecific[6].Length = 0;
LsapAdtEventIdStringSpecific[6].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringSpecific[6].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_6,
10, //Base
&LsapAdtEventIdStringSpecific[6]
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i+= ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringSpecific[7].Length = 0;
LsapAdtEventIdStringSpecific[7].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringSpecific[7].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_7,
10, //Base
&LsapAdtEventIdStringSpecific[7]
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i+= ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringSpecific[8].Length = 0;
LsapAdtEventIdStringSpecific[8].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringSpecific[8].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_8,
10, //Base
&LsapAdtEventIdStringSpecific[8]
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i+= ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringSpecific[9].Length = 0;
LsapAdtEventIdStringSpecific[9].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringSpecific[9].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_9,
10, //Base
&LsapAdtEventIdStringSpecific[9]
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i+= ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringSpecific[10].Length = 0;
LsapAdtEventIdStringSpecific[10].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringSpecific[10].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_10,
10, //Base
&LsapAdtEventIdStringSpecific[10]
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i+= ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringSpecific[11].Length = 0;
LsapAdtEventIdStringSpecific[11].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringSpecific[11].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_11,
10, //Base
&LsapAdtEventIdStringSpecific[11]
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i+= ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringSpecific[12].Length = 0;
LsapAdtEventIdStringSpecific[12].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringSpecific[12].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_12,
10, //Base
&LsapAdtEventIdStringSpecific[12]
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i+= ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringSpecific[13].Length = 0;
LsapAdtEventIdStringSpecific[13].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringSpecific[13].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_13,
10, //Base
&LsapAdtEventIdStringSpecific[13]
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i+= ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringSpecific[14].Length = 0;
LsapAdtEventIdStringSpecific[14].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringSpecific[14].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_14,
10, //Base
&LsapAdtEventIdStringSpecific[14]
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
i+= ADTP_MAX_ACC_NAME_LENGTH; //Skip to the beginning of the next string
LsapAdtEventIdStringSpecific[15].Length = 0;
LsapAdtEventIdStringSpecific[15].MaximumLength = (ADTP_MAX_ACC_NAME_LENGTH * sizeof(WCHAR));
LsapAdtEventIdStringSpecific[15].Buffer = (PWSTR)&LsapAdtAccessIdsStringBuffer[i];
Status = RtlIntegerToUnicodeString ( SE_ACCESS_NAME_SPECIFIC_15,
10, //Base
&LsapAdtEventIdStringSpecific[15]
);
if (!NT_SUCCESS(Status)) {
return(Status);
}
//
// The modules and their objects are listed in the registry
// under the key called LSAP_ADT_AUDIT_MODULES_KEY_NAME.
// Open that key.
//
RtlInitUnicodeString( &AuditKeyName, LSAP_ADT_AUDIT_MODULES_KEY_NAME );
InitializeObjectAttributes( &ObjectAttributes, &AuditKeyName, OBJ_CASE_INSENSITIVE, 0, NULL );
Status = NtOpenKey( &AuditKey, KEY_READ, &ObjectAttributes ); // AuditKey is open handle to top of security modules registry
for (ModuleIndex = 0; NT_SUCCESS(Status); ModuleIndex ++)
{
//
// Enumerate the subkeys under AuditKey, storing their names in KeyInformation. First calculate the buffer size needed to
// store the key name.
//
KeyInformation = NULL;
Status = NtEnumerateKey( AuditKey, ModuleIndex, KeyBasicInformation, (PVOID)KeyInformation, 0, &RequiredLength );
if (Status == STATUS_BUFFER_TOO_SMALL) // must test this, in case the NtEnumerateKey fails for some other reason
{
KeyInformation = RtlAllocateHeap( RtlProcessHeap(), 0, RequiredLength );
if (KeyInformation == NULL)
{
return(STATUS_NO_MEMORY);
}
Status = NtEnumerateKey( AuditKey, ModuleIndex, KeyBasicInformation, (PVOID) KeyInformation, RequiredLength, &RequiredLength );
if (NT_SUCCESS(Status))
{
//
// Build a source module descriptor (LSAP_ADT_SOURCE) for the subkey of AuditKey (aka KeyInformation)
//
NextModule = RtlAllocateHeap( RtlProcessHeap(), 0, sizeof(LSAP_ADT_SOURCE) );
if (NextModule == NULL) {
return(STATUS_NO_MEMORY);
}
NextModule->Next = LsapAdtSourceModules;
LsapAdtSourceModules = NextModule;
NextModule->Objects = NULL;
NextModule->Name.Length = (USHORT)KeyInformation->NameLength;
NextModule->Name.MaximumLength = NextModule->Name.Length + 2;
NextModule->Name.Buffer = RtlAllocateHeap( RtlProcessHeap(), 0, NextModule->Name.MaximumLength );
if (NextModule->Name.Buffer == NULL)
{
return(STATUS_NO_MEMORY);
}
TmpString.Length = (USHORT)KeyInformation->NameLength;
TmpString.MaximumLength = TmpString.Length;
TmpString.Buffer = &KeyInformation->Name[0];
RtlCopyUnicodeString( &NextModule->Name, &TmpString );
RtlFreeHeap( RtlProcessHeap(), 0, KeyInformation );
//
// open the module subkey to which KeyInformation refers. call it "ModuleKey".
//
InitializeObjectAttributes( &ObjectAttributes, &NextModule->Name, OBJ_CASE_INSENSITIVE, AuditKey, NULL );
Status = NtOpenKey( &ModuleKey, KEY_READ, &ObjectAttributes );
DebugLog((DEB_TRACE_AUDIT, "LsapAdtObjsInitialize() :: opening ModuleKey %S returned 0x%x\n",
NextModule->Name.Buffer, Status));
if (!NT_SUCCESS(Status))
{
return(Status);
}
//
// Open the source module's "\ObjectNames" subkey as the handle "ObjectNamesKey";
//
RtlInitUnicodeString( &TmpString, LSAP_ADT_OBJECT_NAMES_KEY_NAME );
InitializeObjectAttributes( &ObjectAttributes, &TmpString, OBJ_CASE_INSENSITIVE, ModuleKey, NULL );
Status = NtOpenKey( &ObjectNamesKey, KEY_READ, &ObjectAttributes );
IgnoreStatus = NtClose( ModuleKey );
ASSERT(NT_SUCCESS(IgnoreStatus));
// DbgPrint("LsapAdtObjsInitialize() :: opening ObjectNamesKey returned 0x%x\n", Status);
ModuleHasObjects = TRUE;
if (Status == STATUS_OBJECT_NAME_NOT_FOUND)
{
ModuleHasObjects = FALSE;
Status = STATUS_SUCCESS;
}
}
}
//
// At this point we have either:
//
// 1) Found a source module with objects under it
// that need to be retrieved.
// This is indicated by successful status value and
// (ModuleHasObjects == TRUE).
//
// 2) found a source module with no objects under it,
// This is indicated by (ModuleHasObjects == FALSE)
//
// 3) exhausted our source modules enumeration,
//
// 4) hit another type of error, or
//
// (3) and (4) are indicatd by non-successful status values.
//
// In the case of (1) or (2) , NextModule points to the module we
// are working on. For case (1), ObjectNamesKey is the handle to
// the \ObjectNames registry key for the source module.
//
for (ObjectIndex = 0; (NT_SUCCESS(Status)) && (ModuleHasObjects == TRUE); ObjectIndex ++)
{
//
// Now enumerate the objects (i.e. values under \...\ObjectNames\ ) of this
// source module.
//
// first calculate size of the ObjectIndex'th key. Store in KeyValueInformation.
KeyValueInformation = NULL;
Status = NtEnumerateValueKey( ObjectNamesKey, ObjectIndex, KeyValueFullInformation, KeyValueInformation, 0, &RequiredLength );
if (Status == STATUS_BUFFER_TOO_SMALL)
{
KeyValueInformation = RtlAllocateHeap( RtlProcessHeap(), 0, RequiredLength );
if (KeyValueInformation == NULL)
{
return(STATUS_NO_MEMORY);
}
Status = NtEnumerateValueKey( ObjectNamesKey, ObjectIndex, KeyValueFullInformation, KeyValueInformation, RequiredLength, &RequiredLength );
if (NT_SUCCESS(Status))
{
//
// Build an object descriptor for the object represented
// by this object.
//
NextObject = RtlAllocateHeap( RtlProcessHeap(), 0, sizeof(LSAP_ADT_OBJECT) );
if (NextObject == NULL)
{
return(STATUS_NO_MEMORY);
}
NextObject->Next = NextModule->Objects;
NextModule->Objects = NextObject;
NextObject->Name.Length = (USHORT)KeyValueInformation->NameLength;
NextObject->Name.MaximumLength = NextObject->Name.Length + 2;
NextObject->Name.Buffer = RtlAllocateHeap( RtlProcessHeap(), 0, NextObject->Name.MaximumLength );
if (NextObject->Name.Buffer == NULL)
{
return(STATUS_NO_MEMORY);
}
TmpString.Length = (USHORT)KeyValueInformation->NameLength;
TmpString.MaximumLength = TmpString.Length;
TmpString.Buffer = &KeyValueInformation->Name[0];
RtlCopyUnicodeString( &NextObject->Name, &TmpString );
if (KeyValueInformation->DataLength < sizeof(ULONG))
{
NextObject->BaseOffset = SE_ACCESS_NAME_SPECIFIC_0;
}
else
{
ObjectData = (PVOID)(((PUCHAR)KeyValueInformation) + KeyValueInformation->DataOffset);
NextObject->BaseOffset = (*ObjectData);
}
// DbgPrint("LsapAdtObjsInitialize() :: opening key %S with BaseOffset %d\n", NextObject->Name.Buffer, NextObject->BaseOffset);
} //end_if (NT_SUCCESS on enumeration)
RtlFreeHeap( RtlProcessHeap(), 0, KeyValueInformation );
} // end if buffer_too_small
//
// if we run out of values in the enumeration of this module, then we want to break
// into the enumeration of the next
//
if (Status == STATUS_NO_MORE_ENTRIES)
{
Status = STATUS_SUCCESS;
ModuleHasObjects = FALSE;
}
} // end for (ObjectIndex ... ) {} (enumerating values)
if ( (Status == STATUS_SUCCESS) && (ModuleHasObjects == FALSE) )
{
IgnoreStatus = NtClose( ObjectNamesKey );
}
} // end for (Module... ){} (enumerating modules)
IgnoreStatus = NtClose( AuditKey );
ASSERT(NT_SUCCESS(IgnoreStatus));
//
// If we were successful, then we will probably have a
// current completion status of STATUS_NO_MORE_ENTRIES
// (indicating our enumerations above were run). Change
// this to success.
//
if (Status == STATUS_NO_MORE_ENTRIES)
{
Status = STATUS_SUCCESS;
}
return(Status);
}
NTSTATUS
LsapGuidToString(
IN GUID *ObjectType,
IN PUNICODE_STRING UnicodeString
)
/*++
Routine Description:
This routine converts a GUID to its text form.
Arguments:
ObjectType - Specifies the GUID to translate.
UnicodeString - Returns the text string.
Return Values:
STATUS_SUCCESS - Operation was successful.
STATUS_NO_MEMORY - Not enough memory to allocate string.
--*/
{
NTSTATUS Status;
RPC_STATUS RpcStatus;
LPWSTR GuidString = NULL;
ULONG GuidStringSize;
LPWSTR LocalGuidString;
//
// Convert the GUID to text
//
RpcStatus = UuidToStringW( ObjectType,
&GuidString );
if ( RpcStatus != RPC_S_OK ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
GuidStringSize = (ULONG) ((wcslen( GuidString ) + 1) * sizeof(WCHAR));
LocalGuidString = LsapAllocateLsaHeap( GuidStringSize );
if ( LocalGuidString == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
RtlCopyMemory( LocalGuidString, GuidString, GuidStringSize );
RtlInitUnicodeString( UnicodeString, LocalGuidString );
Status = STATUS_SUCCESS;
Cleanup:
if ( GuidString != NULL ) {
RpcStringFreeW( &GuidString );
}
return Status;
}
NTSTATUS
LsapDsGuidToString(
IN GUID *ObjectType,
IN PUNICODE_STRING UnicodeString
)
/*++
Routine Description:
This routine converts a GUID to a string. The GUID is one of the following:
Class Guid indicating the class of an object.
Property Set Guid identifying a property set.
Property Guid identifying a property.
In each case, the routine returns a text string naming the object/property
set or property.
If the passed in GUID is cannot be found in the schema,
the GUID will simply be converted to a text string.
Arguments:
ObjectType - Specifies the GUID to translate.
UnicodeString - Returns the text string.
Return Values:
STATUS_NO_MEMORY - Not enough memory to allocate string.
--*/
{
NTSTATUS Status;
RPC_STATUS RpcStatus;
LPWSTR GuidString = NULL;
ULONG GuidStringSize;
ULONG GuidStringLen;
LPWSTR LocalGuidString;
//
// Convert the GUID to text
//
RpcStatus = UuidToStringW( ObjectType,
&GuidString );
if ( RpcStatus != RPC_S_OK ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
GuidStringLen = (ULONG) wcslen( GuidString );
GuidStringSize = (GuidStringLen + 4) * sizeof(WCHAR);
LocalGuidString = LsapAllocateLsaHeap( GuidStringSize );
if ( LocalGuidString == NULL ) {
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
LocalGuidString[0] = L'%';
LocalGuidString[1] = L'{';
RtlCopyMemory( &LocalGuidString[2], GuidString, GuidStringLen*sizeof(WCHAR) );
LocalGuidString[GuidStringLen+2] = L'}';
LocalGuidString[GuidStringLen+3] = L'\0';
RtlInitUnicodeString( UnicodeString, LocalGuidString );
Status = STATUS_SUCCESS;
Cleanup:
if ( GuidString != NULL ) {
RpcStringFreeW( &GuidString );
}
return Status;
}
NTSTATUS
LsapAdtAppendString(
OUT PUNICODE_STRING ResultantString,
OUT PBOOLEAN FreeWhenDone,
IN PUNICODE_STRING StringToAppend,
IN PULONG StringIndex
)
/*++
Routine Description:
This function appends a string to the next available of the LSAP_ADT_OBJECT_TYPE_STRINGS unicode
output strings.
Arguments:
ResultantString - Points to an array of LSAP_ADT_OBJECT_TYPE_STRINGS unicode string headers. 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.
StringToAppend - String to be appended to ResultantString.
StringIndex - Index to the current ResultantString to be used.
Passes in an index to the resultant string to use.
Passes out the index to the resultant string being used.
Return Values:
STATUS_NO_MEMORY - indicates memory could not be allocated
to store the object information.
All other Result Codes are generated by called routines.
--*/
{
NTSTATUS Status;
UNICODE_STRING SourceString;
ULONG Index;
// Must be multiple of sizeof(WCHAR)
#define ADT_MAX_STRING 0xFFFE
//
// Initialization.
//
SourceString = *StringToAppend;
Index = *StringIndex;
//
// If all of the strings are already full,
// early out.
//
if ( Index >= LSAP_ADT_OBJECT_TYPE_STRINGS ) {
return STATUS_SUCCESS;
}
//
// Loop until the source string is completely appended.
//
while ( SourceString.Length ) {
//
// If the destination string has room,
// append to it.
//
if ( FreeWhenDone[Index] && ResultantString[Index].Length != ADT_MAX_STRING ){
UNICODE_STRING SubString;
USHORT RoomLeft;
//
// If the Source String is a replacement string,
// make sure we don't split it across a ResultantString boundary
//
RoomLeft = ResultantString[Index].MaximumLength -
ResultantString[Index].Length;
if ( SourceString.Buffer[0] != L'%' ||
RoomLeft >= SourceString.Length ) {
//
// Compute the substring that fits.
//
SubString.Length = min( RoomLeft, SourceString.Length );
SubString.Buffer = SourceString.Buffer;
SourceString.Length = SourceString.Length - SubString.Length;
SourceString.Buffer = (LPWSTR)(((LPBYTE)SourceString.Buffer) + SubString.Length);
//
// Append the substring onto the destination.
//
Status = RtlAppendUnicodeStringToString(
&ResultantString[Index],
&SubString );
ASSERT(NT_SUCCESS(Status));
}
}
//
// If there's more to copy,
// grow the buffer.
//
if ( SourceString.Length ) {
ULONG NewSize;
LPWSTR NewBuffer;
//
// If the current buffer is full,
// move to the next buffer.
//
if ( ResultantString[Index].Length >= ADT_MAX_STRING ) {
//
// If the buffer is full,
// silently return to the caller.
//
*StringIndex = Index;
return STATUS_SUCCESS;
}
//
// Allocate a buffer suitable for both the old string and the new one.
//
// Allocate the buffer at least large enough for the new string.
// Always grow the buffer in 1Kb chunks.
// Don't allocate larger than the maximum allowed size.
//
NewSize = max( ResultantString[Index].MaximumLength + 1024,
SourceString.Length );
NewSize = min( NewSize, ADT_MAX_STRING );
NewBuffer = LsapAllocateLsaHeap( NewSize );
if ( NewBuffer == NULL ) {
*StringIndex = Index;
return STATUS_NO_MEMORY;
}
//
// Copy the old buffer into the new buffer.
//
if ( ResultantString[Index].Buffer != NULL ) {
RtlCopyMemory( NewBuffer,
ResultantString[Index].Buffer,
ResultantString[Index].Length );
if ( FreeWhenDone[Index] ) {
LsapFreeLsaHeap( ResultantString[Index].Buffer );
}
}
ResultantString[Index].Buffer = NewBuffer;
ResultantString[Index].MaximumLength = (USHORT) NewSize;
FreeWhenDone[Index] = TRUE;
}
}
*StringIndex = Index;
return STATUS_SUCCESS;
}
NTSTATUS
LsapAdtAppendZString(
OUT PUNICODE_STRING ResultantString,
OUT PBOOLEAN FreeWhenDone,
IN LPWSTR StringToAppend,
IN PULONG StringIndex
)
/*++
Routine Description:
Same as LsapAdpAppendString but takes a zero terminated string.
Arguments:
Same as LsapAdpAppendString but takes a zero terminated string.
Return Values:
STATUS_NO_MEMORY - indicates memory could not be allocated
to store the object information.
All other Result Codes are generated by called routines.
--*/
{
UNICODE_STRING UnicodeString;
RtlInitUnicodeString( &UnicodeString, StringToAppend );
return LsapAdtAppendString( ResultantString,
FreeWhenDone,
&UnicodeString,
StringIndex );
}
int
__cdecl
CompareObjectTypes(
const void * Param1,
const void * Param2
)
/*++
Routine Description:
Qsort comparison routine for sorting an object type array by access mask.
--*/
{
const SE_ADT_OBJECT_TYPE *ObjectType1 = Param1;
const SE_ADT_OBJECT_TYPE *ObjectType2 = Param2;
return ObjectType1->AccessMask - ObjectType2->AccessMask;
}
NTSTATUS
LsapAdtBuildObjectTypeStrings(
IN PUNICODE_STRING SourceModule,
IN PUNICODE_STRING ObjectTypeName,
IN PSE_ADT_OBJECT_TYPE ObjectTypeList,
IN ULONG ObjectTypeCount,
OUT PUNICODE_STRING ResultantString,
OUT PBOOLEAN FreeWhenDone,
OUT PUNICODE_STRING NewObjectTypeName
)
/*++
Routine Description:
This function builds a LSAP_ADT_OBJECT_TYPE_STRINGS unicode strings containing parameter
file replacement parameters (e.g. %%1043) and Object GUIDs separated by carriage
return and tab characters suitable for display via the event viewer.
The buffers returned by this routine must be deallocated when no
longer needed if FreeWhenDone is true.
Arguments:
SourceModule - The module (ala event viewer modules) defining the
object type.
ObjectTypeName - The type of object to which the access mask applies.
ObjectTypeList - List of objects being granted access.
ObjectTypeCount - Number of objects in ObjectTypeList.
ResultantString - Points to an array of LSAP_ADT_OBJECT_TYPE_STRINGS unicode string headers. 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.
NewObjectTypeName - Returns a new name for the object type if one is
available.
Return Values:
STATUS_NO_MEMORY - indicates memory could not be allocated
to store the object information.
All other Result Codes are generated by called routines.
--*/
{
NTSTATUS Status;
UNICODE_STRING LocalString;
BOOLEAN LocalFreeWhenDone;
ULONG ResultantStringIndex = 0;
ULONG i;
ACCESS_MASK PreviousAccessMask;
ULONG Index;
USHORT IndentLevel;
static LPWSTR Tabs[] =
{
L"\t",
L"\t\t",
L"\t\t\t",
L"\t\t\t\t"
};
USHORT cTabs = sizeof(Tabs) / sizeof(LPWSTR);
//
// Initialize all LSAP_ADT_OBJECT_TYPE_STRINGS buffers to empty strings
//
for ( i=0; i<LSAP_ADT_OBJECT_TYPE_STRINGS; i++ ) {
RtlInitUnicodeString( &ResultantString[i], L"" );
FreeWhenDone[i] = FALSE;
}
//
// If there are no objects,
// we're done.
//
if ( ObjectTypeCount == 0 ) {
return STATUS_SUCCESS;
}
//
// Group the objects with like access masks together.
// (Simply sort them).
//
qsort( ObjectTypeList,
ObjectTypeCount,
sizeof(SE_ADT_OBJECT_TYPE),
CompareObjectTypes );
//
// Loop through the objects outputting a line for each one.
//
PreviousAccessMask = ObjectTypeList[0].AccessMask -1;
for ( Index=0; Index<ObjectTypeCount; Index++ ) {
//
// If this access mask is different than the one for the previous
// object,
// output a new copy of the access mask.
//
if ( ObjectTypeList[Index].AccessMask != PreviousAccessMask ) {
PreviousAccessMask = ObjectTypeList[Index].AccessMask;
if ( ObjectTypeList[Index].AccessMask == 0 ) {
RtlInitUnicodeString( &LocalString,
L"---" LSAP_ADT_ACCESS_NAME_FORMATTING_NL );
LocalFreeWhenDone = FALSE;
} else {
//
// Build a string with the access mask in it.
//
Status = LsapAdtBuildAccessesString(
SourceModule,
ObjectTypeName,
ObjectTypeList[Index].AccessMask,
FALSE,
&LocalString,
&LocalFreeWhenDone );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
}
//
// Append it to the output string.
//
Status = LsapAdtAppendString(
ResultantString,
FreeWhenDone,
&LocalString,
&ResultantStringIndex );
if ( LocalFreeWhenDone ) {
LsapFreeLsaHeap( LocalString.Buffer );
}
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
}
IndentLevel = ObjectTypeList[Index].Level;
if (IndentLevel >= cTabs) {
IndentLevel = cTabs-1;
}
//
// Indent the GUID.
//
Status = LsapAdtAppendZString(
ResultantString,
FreeWhenDone,
Tabs[IndentLevel],
&ResultantStringIndex );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
//
// If this is the DS,
// convert the GUID to a name from the schema.
//
Status = LsapDsGuidToString( &ObjectTypeList[Index].ObjectType,
&LocalString );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
//
// Append the GUID string to the output strings.
//
Status = LsapAdtAppendString(
ResultantString,
FreeWhenDone,
&LocalString,
&ResultantStringIndex );
LsapFreeLsaHeap( LocalString.Buffer );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
//
// Put the GUID on a line by itself.
//
Status = LsapAdtAppendZString(
ResultantString,
FreeWhenDone,
LSAP_ADT_ACCESS_NAME_FORMATTING_NL,
&ResultantStringIndex );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
}
Status = STATUS_SUCCESS;
Cleanup:
return Status;
}
#define LSAP_ADT_OBJECT_TYPE_NAME_LENGTH (39*sizeof(WCHAR))
NTSTATUS
LsapAdtBuildAccessesString(
IN PUNICODE_STRING SourceModule,
IN PUNICODE_STRING ObjectTypeName,
IN ACCESS_MASK Accesses,
IN BOOLEAN Indent,
OUT PUNICODE_STRING ResultantString,
OUT PBOOLEAN FreeWhenDone
)
/*++
Routine Description:
This function builds a unicode string containing parameter
file replacement parameters (e.g. %%1043) separated by carriage
return and tab characters suitable for display via the event viewer.
The buffer returned by this routine must be deallocated when no
longer needed if FreeWhenDone is true.
NOTE: To enhance performance, each time a target source module
descriptor is found, it is moved to the beginning of the
source module list. This ensures frequently accessed source
modules are always near the front of the list.
Similarly, target object descriptors are moved to the front
of their lists when found. This further ensures high performance
by quicly locating
Arguments:
SourceModule - The module (ala event viewer modules) defining the
object type.
ObjectTypeName - The type of object to which the access mask applies.
Accesses - The access mask to be used in building the display string.
Indent - Access Mask should be indented.
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
to store the object information.
All other Result Codes are generated by called routines.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG AccessCount = 0;
ULONG BaseOffset;
ULONG i;
ACCESS_MASK Mask;
PLSAP_ADT_SOURCE Source;
PLSAP_ADT_SOURCE FoundSource = NULL;
PLSAP_ADT_OBJECT Object;
PLSAP_ADT_OBJECT FoundObject = NULL;
BOOLEAN Found;
BOOLEAN IsDs = FALSE;
UNICODE_STRING DsSourceName;
UNICODE_STRING DsObjectTypeName;
#ifdef LSAP_ADT_TEST_DUMP_SOURCES
printf("Module:\t%wS\n", SourceModule);
printf("\t Object:\t%wS\n", ObjectTypeName);
printf("\t Accesses:\t0x%lx\n", Accesses);
#endif
//
// If we have no accesses, return "-"
//
if (Accesses == 0) {
RtlInitUnicodeString( ResultantString, L"-" );
(*FreeWhenDone) = FALSE;
return(STATUS_SUCCESS);
}
//
// First figure out how large a buffer we need
//
Mask = Accesses;
//
// Count the number of set bits in the
// passed access mask.
//
while ( Mask != 0 ) {
Mask = Mask & (Mask - 1);
AccessCount++;
}
#ifdef LSAP_ADT_TEST_DUMP_SOURCES
printf("\t \t%d bits set in mask.\n", AccessCount);
#endif
//
// We have accesses, allocate a string large enough to deal
// with them all. Strings will be of the format:
//
// %%nnnnnnnnnn\n\r\t\t%%nnnnnnnnnn\n\r\t\t ... %nnnnnnnnnn\n\r\t\t
//
// where nnnnnnnnnn - is a decimal number 10 digits long or less.
//
// So, a typical string will look like:
//
// %%601\n\r\t\t%%1604\n\r\t\t%%1608\n
//
// Since each such access may use at most:
//
// 10 (for the nnnnnnnnnn digit)
// + 2 (for %%)
// + 8 (for \n\t\t)
// --------------------------------
// 20 wide characters
//
// The total length of the output string will be:
//
// AccessCount (number of accesses)
// x 20 (size of each entry)
// -------------------------------------
// wchars
//
// Throw in 1 more WCHAR for null termination, and we are all set.
//
ResultantString->Length = 0;
ResultantString->MaximumLength = (USHORT)AccessCount * (20 * sizeof(WCHAR)) +
sizeof(WCHAR); //for the null termination
#ifdef LSAP_ADT_TEST_DUMP_SOURCES
printf("\t \t%d byte buffer allocated.\n", ResultantString->MaximumLength);
#endif
ResultantString->Buffer = LsapAllocateLsaHeap( ResultantString->MaximumLength );
if (ResultantString->Buffer == NULL) {
return(STATUS_NO_MEMORY);
}
(*FreeWhenDone) = TRUE;
//
// Special case standard and special access types.
// Walk the lists for specific access types.
//
if (Accesses & STANDARD_RIGHTS_ALL) {
if (Accesses & DELETE) {
Status = RtlAppendUnicodeToString( ResultantString, L"%%" );
ASSERT( NT_SUCCESS( Status ));
Status = RtlAppendUnicodeStringToString( ResultantString, &LsapAdtEventIdStringDelete);
ASSERT( NT_SUCCESS( Status ));
if ( Indent ) {
Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING );
} else {
Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING_NL );
}
ASSERT( NT_SUCCESS( Status ));
}
if (Accesses & READ_CONTROL) {
Status = RtlAppendUnicodeToString( ResultantString, L"%%" );
ASSERT( NT_SUCCESS( Status ));
Status = RtlAppendUnicodeStringToString( ResultantString, &LsapAdtEventIdStringReadControl);
ASSERT( NT_SUCCESS( Status ));
if ( Indent ) {
Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING );
} else {
Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING_NL );
}
ASSERT( NT_SUCCESS( Status ));
}
if (Accesses & WRITE_DAC) {
Status = RtlAppendUnicodeToString( ResultantString, L"%%" );
ASSERT( NT_SUCCESS( Status ));
Status = RtlAppendUnicodeStringToString( ResultantString, &LsapAdtEventIdStringWriteDac);
ASSERT( NT_SUCCESS( Status ));
if ( Indent ) {
Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING );
} else {
Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING_NL );
}
ASSERT( NT_SUCCESS( Status ));
}
if (Accesses & WRITE_OWNER) {
Status = RtlAppendUnicodeToString( ResultantString, L"%%" );
ASSERT( NT_SUCCESS( Status ));
Status = RtlAppendUnicodeStringToString( ResultantString, &LsapAdtEventIdStringWriteOwner);
ASSERT( NT_SUCCESS( Status ));
if ( Indent ) {
Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING );
} else {
Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING_NL );
}
ASSERT( NT_SUCCESS( Status ));
}
if (Accesses & SYNCHRONIZE) {
Status = RtlAppendUnicodeToString( ResultantString, L"%%" );
ASSERT( NT_SUCCESS( Status ));
Status = RtlAppendUnicodeStringToString( ResultantString, &LsapAdtEventIdStringSynchronize);
ASSERT( NT_SUCCESS( Status ));
if ( Indent ) {
Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING );
} else {
Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING_NL );
}
ASSERT( NT_SUCCESS( Status ));
}
}
if (Accesses & ACCESS_SYSTEM_SECURITY) {
Status = RtlAppendUnicodeToString( ResultantString, L"%%" );
ASSERT( NT_SUCCESS( Status ));
Status = RtlAppendUnicodeStringToString( ResultantString, &LsapAdtEventIdStringAccessSysSec);
ASSERT( NT_SUCCESS( Status ));
if ( Indent ) {
Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING );
} else {
Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING_NL );
}
ASSERT( NT_SUCCESS( Status ));
}
if (Accesses & MAXIMUM_ALLOWED) {
Status = RtlAppendUnicodeToString( ResultantString, L"%%" );
ASSERT( NT_SUCCESS( Status ));
Status = RtlAppendUnicodeStringToString( ResultantString, &LsapAdtEventIdStringMaxAllowed);
ASSERT( NT_SUCCESS( Status ));
if ( Indent ) {
Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING );
} else {
Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING_NL );
}
ASSERT( NT_SUCCESS( Status ));
}
//
// If there are any specific access bits set, then get
// the appropriate source module and object type base
// message ID offset. If there is no module-specific
// object definition, then use SE_ACCESS_NAME_SPECIFIC_0
// as the base.
//
if ((Accesses & SPECIFIC_RIGHTS_ALL) == 0) {
return(Status);
}
LsapAdtSourceModuleLock();
Source = (PLSAP_ADT_SOURCE)&LsapAdtSourceModules;
Found = FALSE;
while ((Source->Next != NULL) && !Found) {
if (RtlEqualUnicodeString(&Source->Next->Name, SourceModule, TRUE)) {
Found = TRUE;
FoundSource = Source->Next;
//
// Move to front of list of source modules.
//
Source->Next = FoundSource->Next; // Remove from list
FoundSource->Next = LsapAdtSourceModules; // point to first element
LsapAdtSourceModules = FoundSource; // Make it the first element
#ifdef LSAP_ADT_TEST_DUMP_SOURCES
printf("\t \tModule Found.\n");
#endif
} else {
Source = Source->Next;
}
}
if (Found == TRUE) {
//
// Find the object
//
Object = (PLSAP_ADT_OBJECT)&(FoundSource->Objects);
Found = FALSE;
//
// Initialize Ds string
//
RtlInitUnicodeString( &DsSourceName, ACCESS_DS_SOURCE_W );
//
// Determine if this is DS Object
// ObjectTypeName GUID follows this format: %{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
//
IsDs = RtlEqualUnicodeString( SourceModule, &DsSourceName, TRUE ) &&
LSAP_ADT_OBJECT_TYPE_NAME_LENGTH == ObjectTypeName->Length &&
L'%' == ObjectTypeName->Buffer[0] &&
L'{' == ObjectTypeName->Buffer[1] &&
L'}' == ObjectTypeName->Buffer[(LSAP_ADT_OBJECT_TYPE_NAME_LENGTH / sizeof(WCHAR)) - 1];
//
// Initialize DS Object string
//
RtlInitUnicodeString( &DsObjectTypeName, ACCESS_DS_OBJECT_TYPE_NAME_W );
while ((Object->Next != NULL) && !Found) {
if ( (IsDs && RtlEqualUnicodeString(&Object->Next->Name, &DsObjectTypeName, TRUE)) ||
RtlEqualUnicodeString(&Object->Next->Name, ObjectTypeName, TRUE) ) {
Found = TRUE;
FoundObject = Object->Next;
//
// Move to front of list of soure modules.
//
Object->Next = FoundObject->Next; // Remove from list
FoundObject->Next = FoundSource->Objects; // point to first element
FoundSource->Objects = FoundObject; // Make it the first element
} else {
Object = Object->Next;
}
}
}
//
// We are done playing with link fields of the source modules
// and objects. Free the lock.
//
LsapAdtSourceModuleUnlock();
//
// If we have found an object, use it as our base message
// ID. Otherwise, use SE_ACCESS_NAME_SPECIFIC_0.
//
if (Found) {
BaseOffset = FoundObject->BaseOffset;
#ifdef LSAP_ADT_TEST_DUMP_SOURCES
printf("\t \tObject Found. Base Offset: 0x%lx\n", BaseOffset);
#endif
} else {
BaseOffset = SE_ACCESS_NAME_SPECIFIC_0;
#ifdef LSAP_ADT_TEST_DUMP_SOURCES
printf("\t \tObject NOT Found. Base Offset: 0x%lx\n", BaseOffset);
#endif
}
//
// At this point, we have a base offset (even if we had to use our
// default).
//
// Now cycle through the specific access bits and see which ones need
// to be added to ResultantString.
//
{
UNICODE_STRING IntegerString;
WCHAR IntegerStringBuffer[10]; //must be 10 wchar bytes long
ULONG NextBit;
IntegerString.Buffer = (PWSTR)IntegerStringBuffer;
IntegerString.MaximumLength = 10*sizeof(WCHAR);
IntegerString.Length = 0;
for ( i=0, NextBit=1 ; i<16 ; i++, NextBit <<= 1 ) {
//
// specific access flags are in the low-order bits of the mask
//
if ((NextBit & Accesses) != 0) {
//
// Found one - add it to ResultantString
//
Status = RtlIntegerToUnicodeString (
(BaseOffset + i),
10, //Base
&IntegerString
);
if (NT_SUCCESS(Status)) {
Status = RtlAppendUnicodeToString( ResultantString, L"%%" );
ASSERT( NT_SUCCESS( Status ));
Status = RtlAppendUnicodeStringToString( ResultantString, &IntegerString);
ASSERT( NT_SUCCESS( Status ));
if ( Indent ) {
Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING );
} else {
Status = RtlAppendUnicodeToString( ResultantString, LSAP_ADT_ACCESS_NAME_FORMATTING_NL );
}
ASSERT( NT_SUCCESS( Status ));
}
}
}
}
return(Status);
//ErrorAfterAlloc:
//
// LsapFreeLsaHeap( ResultantString->Buffer );
// ResultantString->Buffer = NULL;
// (*FreeWhenDone) = FALSE;
// return(Status);
}
NTSTATUS
LsapAdtBuildUserAccountControlString(
IN ULONG UserAccountControlOld,
IN ULONG UserAccountControlNew,
OUT PUNICODE_STRING ResultantString1,
OUT PBOOLEAN FreeWhenDone1,
OUT PUNICODE_STRING ResultantString2,
OUT PBOOLEAN FreeWhenDone2,
OUT PUNICODE_STRING ResultantString3,
OUT PBOOLEAN FreeWhenDone3
)
/*++
Routine Description:
This function builds a unicode string containing parameter
file replacement parameters (e.g. %%1043) separated by carriage
return and tab characters suitable for display via the event viewer.
The buffer returned by this routine must be deallocated when no
longer needed if FreeWhenDone is true.
Arguments:
UserAccountControl -
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 buffer 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.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
UNICODE_STRING IntegerString;
WCHAR IntegerStringBuffer[10]; //must be 10 wchar bytes long
ULONG ChangedMask;
ULONG Mask;
ULONG BitCount = 0;
ULONG i;
ULONG BaseOffset;
const ULONG BaseOffsetOff = SE_ADT_FLAG_UAC_ACCOUNT_DISABLED_OFF;
const ULONG BaseOffsetOn = SE_ADT_FLAG_UAC_ACCOUNT_DISABLED_ON;
*FreeWhenDone1 = FALSE;
*FreeWhenDone2 = FALSE;
*FreeWhenDone3 = FALSE;
IntegerString.Buffer = IntegerStringBuffer;
IntegerString.MaximumLength = 10 * sizeof(WCHAR);
IntegerString.Length = 0;
//
// Allocate memory for the first two strings (old and new
// UserAccountControl in hex).
// Both strings look like '0xBADCAB1E'
//
ResultantString1->Length = 0;
ResultantString1->MaximumLength = (10 + 1) * sizeof(WCHAR);
ResultantString1->Buffer = LsapAllocateLsaHeap(
ResultantString1->MaximumLength);
if (ResultantString1->Buffer == NULL)
{
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
*FreeWhenDone1 = TRUE;
ResultantString2->Length = 0;
ResultantString2->MaximumLength = (10 + 1) * sizeof(WCHAR);
ResultantString2->Buffer = LsapAllocateLsaHeap(
ResultantString2->MaximumLength);
if (ResultantString2->Buffer == NULL)
{
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
*FreeWhenDone2 = TRUE;
//
// First string: the old value.
//
Status = RtlAppendUnicodeToString(ResultantString1, L"0x");
ASSERT(NT_SUCCESS(Status));
Status = RtlIntegerToUnicodeString(
UserAccountControlOld,
16, // Base
&IntegerString
);
ASSERT(NT_SUCCESS(Status));
Status = RtlAppendUnicodeStringToString(ResultantString1, &IntegerString);
ASSERT(NT_SUCCESS(Status));
//
// Second string: the new value.
//
Status = RtlAppendUnicodeToString(ResultantString2, L"0x");
ASSERT(NT_SUCCESS(Status));
Status = RtlIntegerToUnicodeString(
UserAccountControlNew,
16, // Base
&IntegerString
);
ASSERT(NT_SUCCESS(Status));
Status = RtlAppendUnicodeStringToString(ResultantString2, &IntegerString);
ASSERT(NT_SUCCESS(Status));
//
// Compute the bits that have changed by xor'ing old and new.
//
ChangedMask = UserAccountControlOld ^ UserAccountControlNew;
//
// If no bits have changed, return "-" for the third string.
//
if (ChangedMask == 0)
{
RtlInitUnicodeString(ResultantString3, L"-");
Status = STATUS_SUCCESS;
goto Cleanup;
}
//
// Figure out how large a buffer we need.
// Count the number of 1 - bits in ChangedMask.
//
for (Mask = 1; Mask; Mask <<= 1)
{
if (ChangedMask & Mask)
{
BitCount++;
}
}
//
// Allocate a string large enough to deal with all bits set.
// Strings will be of the format:
//
// \n\r\t\t%%nnnnnnnnnn\n\r\t\t%%nnnnnnnnnn\n\r\t\t ... %nnnnnnnnnn
//
// where nnnnnnnnnn - is a decimal number 10 digits long or less.
// The first two lines in the string display the old and new hex value of UserAccountControl.
//
// So, a typical string will look like:
//
// \n\r\t\t%%601\n\r\t\t%%1604\n\r\t\t%%1608
//
// Since each such bit may use at most:
//
// 6 (for \n\r\t\t%%)
// + 10 (for the nnnnnnnnnn digits)
// --------------------------------
// 16 wide characters
//
// The total length of the output string for the bits will be:
//
// BitCount (number of bits set)
// x 16 (size of each entry)
// -------------------------------------
// BitCount x 16 wchars
//
// Throw in 1 more WCHAR for null termination, and we are all set.
//
ResultantString3->Length = 0;
ResultantString3->MaximumLength = ((USHORT)BitCount * 16 + 1) * sizeof(WCHAR);
ResultantString3->Buffer = LsapAllocateLsaHeap(
ResultantString3->MaximumLength);
if (ResultantString3->Buffer == NULL)
{
Status = STATUS_NO_MEMORY;
goto Cleanup;
}
*FreeWhenDone3 = TRUE;
//
// Third string: Build a line for each bit that has changed.
//
for (i = 0, Mask = 1; i < 32 && Mask; i++, Mask <<= 1)
{
if (Mask & ChangedMask)
{
//
// Found one - add it to ResultantString3
//
BaseOffset = (Mask & UserAccountControlNew) ? BaseOffsetOn : BaseOffsetOff;
Status = RtlIntegerToUnicodeString(
(BaseOffset + i),
10, // Base
&IntegerString
);
ASSERT(NT_SUCCESS(Status));
if (NT_SUCCESS(Status))
{
Status = RtlAppendUnicodeToString(ResultantString3, L"\r\n\t\t%%");
ASSERT(NT_SUCCESS(Status));
Status = RtlAppendUnicodeStringToString(ResultantString3, &IntegerString);
ASSERT(NT_SUCCESS(Status));
}
}
}
Status = STATUS_SUCCESS;
Cleanup:
return Status;
}