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.
 
 
 
 
 
 

5021 lines
138 KiB

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
dbinit.c
Abstract:
Local Security Authority - Database Server Initialization
This module contains functions which perform initialization of
the Database Server. Certain information is obtained from the
LSA database and is set up in global data for easy retrieval.
Author:
Scott Birrell (ScottBi) July 25, 1991
Environment:
User Mode
Revision History:
12-Nov-1997 MikeSw
Added lsa policy handle cache for interdomain lookups
--*/
//
// Define this to allocate all globals in this module
//
#include <lsapch2.h>
#include "lsasrvp.h"
#include "dbp.h"
#include <bndcache.h>
#include <wincrypt.h>
#include <lsapmsgs.h>
#include <ntddnfs.h>
#include <remboot.h>
#ifdef DS_LOOKUP
#include <dslookup.h>
#endif
#include <sertlp.h>
#include <cryptdll.h>
#include <md5.h>
#include <rc4.h>
#include <wxlpc.h>
#include <dnsapi.h>
//
// Well known Sids for security packages include a subauthority equal to
// the associated package's RPC Id. We'll cause a compile time error if
// these numbers ever change.
//
#if (SECURITY_PACKAGE_NTLM_RID != RPC_C_AUTHN_WINNT)
#error SECURITY_PACKAGE_NTLM_RID definition must be equal to \
RPC_C_AUTHN_WINNT
#endif
#if (SECURITY_PACKAGE_DIGEST_RID != RPC_C_AUTHN_DIGEST)
#error SECURITY_PACKAGE_DIGEST_RID definition must equal RPC_C_AUTHN_DIGEST
#endif
#if (SECURITY_PACKAGE_SCHANNEL_RID != RPC_C_AUTHN_GSS_SCHANNEL)
#error SECURITY_PACKAGE_SCHANNEL_RID definition must equal \
RPC_C_AUTHN_GSS_SCHANNEL
#endif
extern LSAP_DB_TRUSTED_DOMAIN_LIST LsapDbTrustedDomainList;
NTSTATUS
LsapDbBuildObjectCaches(
);
NTSTATUS
LsapAssignInitialHiveProtection(
HANDLE HiveRoot
);
NTSTATUS
LsapCreateDatabaseProtection(
PISECURITY_DESCRIPTOR Sd
);
NTSTATUS
LsapGenerateRandomDomainSid(
OUT PSID NewDomainSid
);
NTSTATUS
LsapSetupInitialize(
VOID
);
NTSTATUS
LsapUpdateDatabaseProtection(
IN ULONG Revision
);
NTSTATUS
LsapDsInitFixupQueue(
VOID
);
static UCHAR SyskeyBuffer[LSAP_SYSKEY_SIZE];
static UCHAR OldSyskeyBuffer[LSAP_SYSKEY_SIZE];
NTSTATUS
LsapDbInitializeServer(
IN ULONG Pass
)
/*++
Routine Description:
This function initializes the LSA Database Server. The following
steps are performed:
o Initialize the LSA Database Lock
o Acquire the LSA Database Lock
o Initialize the Unicode Strings for the fixed names within the
LSA Database, e.g. LSA Database object attributes and well-known
object names.
o Initialize the Unicode Strings for the LSA Database Object constant
and well known names, e.g SubKeys, fixed object names.
o Initialize the Unicode Strings for LSA Object Containing Dirs
o Initialize the Generic Mappings for Database Object Types
o Initialize the Lsa Database Handle Table
o Install the LSA Database if necessary - Creates the Lsa Database
o and Manager account objects, and initializes the transaction
subtree
o Initialize the abs min, abs max and installation default quota limits
o Release the LSA Database Lock
Arguments:
None.
Return Value:
NTSTATUS - Standard Nt Result Code
All Result Codes are generated by called routines.
--*/
{
NTSTATUS Status = STATUS_SUCCESS, IgnoreStatus;
BOOL BooleanStatus = TRUE;
BOOLEAN AcquiredLock = FALSE;
BOOLEAN FreeComputerName = FALSE;
LSAP_DB_OBJECT_INFORMATION ObjectInformation;
PLSAPR_POLICY_ACCOUNT_DOM_INFO PolicyAccountDomainInfo = NULL;
PLSAPR_POLICY_DNS_DOMAIN_INFO PolicyDnsDomainInfo = NULL;
UNICODE_STRING ComputerName, CipherKey;
ULONG Length;
ULONG Revision, RevisionLength = sizeof( ULONG );
DWORD WinStatus;
//
// Initialize the LSA Database Lock and set it into the locked state
//
if (Pass == 1 ) {
LsapDsDebugInitialize();
RtlZeroMemory( &LsaDsStateInfo, sizeof( LsaDsStateInfo ) );
LsapDbState.DbServerInitialized = FALSE;
#if DBG
LsapDbState.RegistryTransactionOpen = FALSE;
#endif
//
// Disable Replicator Notifications.
//
LsapDbDisableReplicatorNotification();
//
// This function call will initialize all of the global or well known locks used by
// the Lsa
//
Status = LsapDbInitializeLock();
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
Status = LsapDbInitHandleTables();
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
//
// Initialize the forest trust cache.
// The cache is not usable until populated.
//
Status = LsapForestTrustCacheInitialize();
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
//
// Initialize the policy change notification mechanism
//
Status = LsapInitializeNotifiyList();
if ( !NT_SUCCESS( Status ) ) {
goto InitializeServerError ;
}
//
// Initialize the Fixup queue
//
Status = LsapDsInitFixupQueue();
if ( !NT_SUCCESS( Status ))
{
goto InitializeServerError ;
}
//
// Initialize the binding handle cache
//
Status = LsapInitBindingCache();
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
}
//
// Acquire the LSA Database Lock. This allows subroutines to
// assert that the LSA Database is locked. Otherwise, it is
// not actually necessary, given that no other thread can access the
// LSA until initialization is complete.
//
if (Pass == 1) {
//
// Initialize the Unicode Strings for the fixed names within the
// LSA Database, e.g. LSA Database object attributes and well-known
// object names.
//
Status = LsapDbInitializeUnicodeNames();
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
//
// Initialize the Unicode Strings for the Containing Directories for
// each LSA Database Object Type.
//
Status = LsapDbInitializeContainingDirs();
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
//
// Initialize the LSA Subsystem name string. This is needed for
// NtAccessCheckAuditAlarm calls
//
RtlInitUnicodeString(&LsapState.SubsystemName, L"LSA");
//
// Initialize the Shutdown Pending state.
//
LsapState.SystemShutdownPending = FALSE;
//
// Initialize the Database Object Types. Information stored
// includes the Generic mappings and Object Counts.
//
Status = LsapDbInitializeObjectTypes();
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
//
// Open the LSA Database root Registry subkey. This stays
// open for use in adding transactions.
//
Status = LsapDbOpenRootRegistryKey();
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
//
// Initialize the Lsa Database Cipher Key
// This hard-coded key is used for retrieving old pre-NT4 SP4
// encrypted keys only
//
RtlInitUnicodeString( &CipherKey, L"823543" );
Status = LsapDbInitializeCipherKey( &CipherKey,
&LsapDbCipherKey );
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
//
// Initialize the LSA Database Transaction Subtree, creating it if
// one does not already exist. If the Transaction Subtree exists,
// commit any partially committed transaction if appropriate.
//
Status = RtlInitializeRXact(
LsapDbState.DbRootRegKeyHandle,
TRUE,
(PRTL_RXACT_CONTEXT *) &LsapDbState.RXactContext
);
if (!NT_SUCCESS(Status)) {
if (Status != STATUS_RXACT_STATE_CREATED) {
LsapLogError(
"LsapDbInitializeServer: Registry Transaction Init returned 0x%lx\n",
Status
);
goto InitializeServerError;
}
LsapLogError(
"LsapDbInitializeServer: Registry Transaction State Did Not Exist\n",
Status
);
goto InitializeServerError;
}
//
// Setup attributes for opening the Policy object.
//
ObjectInformation.ObjectTypeId = PolicyObject;
ObjectInformation.ContainerTypeId = 0;
ObjectInformation.Sid = NULL;
ObjectInformation.ObjectAttributeNameOnly = FALSE;
ObjectInformation.DesiredObjectAccess = 0;
InitializeObjectAttributes(
&ObjectInformation.ObjectAttributes,
&LsapDbNames[Policy],
0L,
NULL,
NULL
);
//
// Now try to open the root LSA Database object (Policy). This is a
// trusted call, so no access checking or impersonation will be done.
// Note that the handle obtained will remain open indefinitely. It is
// used after initialization for all internally generated accesses to
// the Policy object
//
Status = LsapDbOpenObject(
&ObjectInformation,
0L,
LSAP_DB_TRUSTED,
&LsapDbHandle
);
if (!NT_SUCCESS(Status)) {
//
// Open of LSA Database object failed. If any error other than
// object not found, there is a serious error which prevents the
// LSA from functioning, so abort.
//
if (Status != STATUS_OBJECT_NAME_NOT_FOUND) {
LsapLogError(
"LsapDbInitializeServer: Open failed 0x%lx\n"
"The Lsa Database must be reinstalled or manually\n"
"erased before using the system\n",
Status
);
goto InitializeServerError;
}
//
// The Lsa Database object was not found. Run the database installation
// routine so that people can boot without having to run the
// installation applet first.
//
LsapDatabaseSetupPerformed = TRUE;
Status = LsapDbInstallLsaDatabase(1);
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
}
//
// The Lsa Database object was successfully opened, possibly after
// having just been created. Proceed with the rest of server
// initialization. First, setup in-memory copies of the Installation
// Default, Absolute Min and Absolute Max system quota limits.
//
//
// Make the policy handle available throughout LSA
//
LsapPolicyHandle = LsapDbHandle;
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
//
// Bring the database up to the current revision level,
// if necessary. This is not a syskey upgrade.
//
Status = LsapDbUpgradeRevision(FALSE,FALSE);
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
//
// Read the revision attribute. If the revision is greater than
// LSAP_DB_REVSION_1_5 then obtain the syskey from winlogon. In previous
// revisions SAM would have obtained the syskey from winlogon if the machine
// had been syskey'd
//
Status = LsapDbReadAttributeObject(
LsapDbHandle,
&LsapDbNames[PolRevision],
(PVOID) &Revision,
&RevisionLength
);
if ( !NT_SUCCESS(Status) ) {
goto InitializeServerError;
}
//
// Query the syskey from winlogon. Do so only if the revision is greater than 1_5.
// This is because in previous builds SAM used to manage the syskey. From this
// release onwards. The below routine also intializes the LSA encryption key
//
if (Revision >= LSAP_DB_REVISION_1_5)
{
Status = LsapDbGetSyskeyFromWinlogon();
if (!NT_SUCCESS(Status))
{
goto InitializeServerError;
}
}
//
// Initialize privilege object related code
//
Status = LsapDbInitializePrivilegeObject();
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
//
// Perform initialization for the Replicator. Replications
// are still disabled at this point.
//
Status = LsapDbInitializeReplication();
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
//
// Initialize the data for the new APIs (user rights)
//
Status = LsapDbInitializeRights();
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
} else if (Pass == 2) {
BOOLEAN ExpectTrue;
OSVERSIONINFOEX OsVersionInfoEx = { 0 };
//
// Perform the second stage of database initialization.
// This is the initialization that depends on the product type.
// First, get the product type. Note that the Product Type may
// have already been retrieved from a number of routines that
// may be called during early installation, including
// LsarSetInformationPolicy() and LsarCreateTrustedDomain().
//
ExpectTrue = RtlGetNtProductType(&LsapProductType);
ASSERT( ExpectTrue == TRUE );
//
// find out the product suite mask.
// this is used later to determine if we are running
// on a specific product suite such the small business server
//
OsVersionInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
ExpectTrue = (BOOLEAN) GetVersionEx((OSVERSIONINFO*) &OsVersionInfoEx);
ASSERT( ExpectTrue == TRUE );
LsapProductSuiteMask = OsVersionInfoEx.wSuiteMask;
//
// If necessary, install the rest of our database.
//
if (LsapDatabaseSetupPerformed == TRUE) {
Status = LsapDbInstallLsaDatabase(2);
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
}
//
// If this is a Win Nt product, set the SAM Accounts Domain
// Name equal to the Computer Name, which may have been changed
// since the last boot.
//
//
// If this is setup, do nothing, since we've set it elsewhere..
// If this is safe mode mode, don't set it either
//
if ( !LsaISetupWasRun() ) {
if ( ((LsapProductType == NtProductWinNt) ||
(LsapProductType == NtProductServer)) &&
!LsaISafeMode() ) {
Status = LsarQueryInformationPolicy(
LsapPolicyHandle,
PolicyAccountDomainInformation,
(PLSAPR_POLICY_INFORMATION *) &PolicyAccountDomainInfo
);
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
Length = (ULONG) 0;
ComputerName.Buffer = UNICODE_NULL;
FreeComputerName = FALSE;
BooleanStatus = GetComputerNameW(
(LPWSTR) ComputerName.Buffer,
(LPDWORD) &Length
);
WinStatus = GetLastError();
if (WinStatus != ERROR_BUFFER_OVERFLOW) {
KdPrint(("LsapDbInitializeServer: Failed to get Computer Name Length\n"
"Using default MACHINENAME instead\n"));
RtlInitUnicodeString( &ComputerName, LSAP_DB_DEFAULT_COMPUTER_NAME );
Length = (ULONG) ComputerName.Length;
} else if (Length <= 1) {
KdPrint(("LsapDbInitializeServer: Null Computer Name\n"
"Using default MACHINENAME instead\n"));
RtlInitUnicodeString( &ComputerName, LSAP_DB_DEFAULT_COMPUTER_NAME );
Length = (ULONG) ComputerName.Length;
} else {
ComputerName.Length = (USHORT) ((Length - 1) * sizeof (WCHAR));
ComputerName.MaximumLength = (USHORT) (Length * sizeof(WCHAR));
ComputerName.Buffer = MIDL_user_allocate( ComputerName.MaximumLength );
if ( ComputerName.Buffer == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto InitializeServerError;
}
FreeComputerName = TRUE;
}
if (!GetComputerNameW(
(LPWSTR) ComputerName.Buffer,
(LPDWORD) &Length
)) {
KdPrint(("LsapDbInitializeServer: Failed to get Computer Name\n"
"Using default MACHINENAME instead\n"));
RtlInitUnicodeString( &ComputerName, LSAP_DB_DEFAULT_COMPUTER_NAME );
}
PolicyAccountDomainInfo->DomainName = *((PLSAPR_UNICODE_STRING) &ComputerName);
Status = LsarSetInformationPolicy(
LsapPolicyHandle,
PolicyAccountDomainInformation,
(PLSAPR_POLICY_INFORMATION) PolicyAccountDomainInfo
);
if ( FreeComputerName ) {
MIDL_user_free( ComputerName.Buffer );
}
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
}
Status = RpcMgmtEnableIdleCleanup();
if ( !NT_SUCCESS( Status )) {
goto InitializeServerError;
}
} else {
Status = LsapSetupInitialize();
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
}
// Perform initialization for Lookup Sids and Names, including
// initialization of the Trusted Domain List.
//
Status = LsapDbLookupInitialize();
if (!NT_SUCCESS(Status)) {
goto InitializeServerError;
}
//
// Load the object caches. Any that fail to load have caching
// permanently turned off.
//
IgnoreStatus = LsapDbBuildObjectCaches();
//
// Find out if this machine is a DC in a root domain
//
Status = LsapDbQueryInformationPolicy(
LsapPolicyHandle,
PolicyDnsDomainInformation,
( PLSAPR_POLICY_INFORMATION * )&PolicyDnsDomainInfo
);
if ( !NT_SUCCESS( Status )) {
goto InitializeServerError;
}
if ( DnsNameCompareEqual == DnsNameCompareEx_W(
PolicyDnsDomainInfo->DnsDomainName.Buffer,
PolicyDnsDomainInfo->DnsForestName.Buffer,
0 )) {
DcInRootDomain = TRUE;
} else {
DcInRootDomain = FALSE;
}
LsaIFree_LSAPR_POLICY_INFORMATION(
PolicyDnsDomainInformation,
( PLSAPR_POLICY_INFORMATION )PolicyDnsDomainInfo
);
//
// Mark the Server as being completely initialized.
//
LsapDbState.DbServerInitialized = TRUE;
LsapDbEnableReplicatorNotification();
}
InitializeServerFinish:
return(Status);
InitializeServerError:
goto InitializeServerFinish;
}
NTSTATUS
LsapDbUpgradeRevision(
IN BOOLEAN SyskeyUpgrade,
IN BOOLEAN GenerateNewSyskey
)
/*++
Routine Description:
This function brings the LSA policy database up to date if necessary.
Arguments:
SyskeyUpgrade -- This paramter is set to true when this function is called a second
time around when upgrading NT4 or Win2K B3 machines from LsaIHealthCheck
GenerateNewSyskey -- This parameter is set to true when this function is called a second
time around when upgrading NT4 or Win2k B3 machines from LsaIHealthCheck
and the system is not already syskey'd
Return Value:
NTSTATUS - Standard Nt Result Code
All Result Codes are generated by called routines.
--*/
{
NTSTATUS Status;
ULONG Revision = LSAP_DB_REVISION_1_0, RevisionLength = sizeof( ULONG );
LSAP_DB_ATTRIBUTE Attributes[20];
PLSAP_DB_ATTRIBUTE NextAttribute;
ULONG AttributeCount = 0;
BOOLEAN PolRevisionWritten = FALSE;
NextAttribute = Attributes;
//
// Read the Revision Info from the PolRevision attribute
// of the Policy object in the LSA Database.
//
Status = LsapDbReadAttributeObject(
LsapDbHandle,
&LsapDbNames[PolRevision],
(PVOID) &Revision,
&RevisionLength
);
if ( !NT_SUCCESS(Status) ) {
Revision = LSAP_DB_REVISION_1_0;
if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
//
// attribute doesn't exist.
// This means the database is an NT1.0 format.
// Upgrade it to the current revision.
//
Status = STATUS_SUCCESS;
}
}
//
// Revison 1_1 created the ModifiedIdAtLastPromotion attribute on the policy object.
// This attribute is no longer used for anything so don't create it.
//
//
// Revision 1_2 corresponded to an encryption of secrets that shipped with NT4 that was
// incompatible with NT5. Therefore skip this revision level. The incompatible encryption
// of secrets would be handled by revision number update
//
//
// Update the security descriptor to revision 1.3
//
if ( NT_SUCCESS( Status ) && (Revision < LSAP_DB_REVISION_1_3) ) {
Status = LsapUpdateDatabaseProtection( LSAP_DB_REVISION_1_3 );
if ( NT_SUCCESS( Status ) ) {
Revision = LSAP_DB_REVISION_1_3;
if ( !PolRevisionWritten ) {
LsapDbInitializeAttribute(
NextAttribute,
&LsapDbNames[PolRevision],
&Revision,
sizeof (ULONG),
FALSE
);
NextAttribute++;
AttributeCount++;
PolRevisionWritten = TRUE;
ASSERT( AttributeCount < ( sizeof( Attributes ) / sizeof( LSAP_DB_ATTRIBUTE ) ) );
}
} else {
Status = STATUS_SUCCESS;
}
}
if ( NT_SUCCESS( Status ) && (Revision < LSAP_DB_REVISION_1_5) && (SyskeyUpgrade)) {
BOOLEAN IsUpgrade = FALSE;
PVOID Syskey = NULL;
ULONG SyskeyLength = 0;
LSAP_DB_ENCRYPTION_KEY NewEncryptionKey;
//
// NT4 SP4 shipped with an encryption of secret's that is incompatible with
// NT5 if the syskey'd setting was turned on.
// Therefore we walk over all secrets and patch them back. It is not necessary
// that we have all the key's setup at this point. The way this works is we attempt,
// to patch back all the secrets, and if we did not have the right key to decrypt them
// we will error out. In Syskey'd machines , SAM will have the right key and will call back
// into LSA when it has obtained the key. During SAM's callback this routine will be executed
// again and we will call upgrade all secrets.
//
// This upgrade involves a secret upgrade only. We simply have to read them and
// write them back out and everything will be kosher...
// Note if the secret upgrade fails for any reason, we will not update the database revision.
//
//
// First obtain the syskey from the SAM hive. Admittedly and ahsamedly this does read the SAM
// hive directly
//
//
// If we are a syskey'd machine the syskey should have been passed to us by now.
//
ASSERT((NULL!=LsapDbSysKey) || (GenerateNewSyskey));
//
// Initialize the key for secret encryption
//
Status = LsapDbGenerateNewKey(
&NewEncryptionKey
);
if (NT_SUCCESS(Status))
{
if (GenerateNewSyskey)
{
ULONG SyskeyLength = sizeof(SyskeyBuffer);
// Boot option is WxStored.
NewEncryptionKey.BootType = WxStored;
//
// A new syskey is being generated
//
Status = LsapDbSetupInitialSyskey(
&SyskeyLength,
&LsapDbSysKey
);
}
else
{
//
// Since we are upgrading from a syskey'd machine, get the boot option from SAM
// SAM is initialized enough at this point as it makes the callout to LSA supplying it with
// the syskey. It is O.K to pass in a value of 0, because then the default account domain
// is used.
//
Status = SamIGetBootKeyInformation(
(SAMPR_HANDLE) 0,
(SAMPR_BOOT_TYPE*)&NewEncryptionKey.BootType
);
}
}
if (NT_SUCCESS(Status))
{
//
// Setup the secret cipher key
// Ordinarily, the key used for reading equals the key used for writing
//
LsapDbInitializeSecretCipherKeyRead( &NewEncryptionKey );
LsapDbInitializeSecretCipherKeyWrite( &NewEncryptionKey );
//
// Encrypt the key with syskey
//
LsapDbEncryptKeyWithSyskey(
&NewEncryptionKey,
LsapDbSysKey,
LSAP_SYSKEY_SIZE
);
LsapDbInitializeAttribute(
NextAttribute,
&LsapDbNames[PolSecretEncryptionKey],
&NewEncryptionKey,
sizeof (NewEncryptionKey),
FALSE
);
NextAttribute++;
AttributeCount++;
}
//
// The secret upgrade is executed only during GUI setup upgrade of a workstation, or a DC being upgraded
// from NT4. In all other cases we simply patch the revision number up to the correct value.
//
if ((NT_SUCCESS(Status)) && ( (SamIIsDownlevelDcUpgrade()) ||
((LsapProductType != NtProductLanManNt) && (SamIIsSetupInProgress(&IsUpgrade)) && (IsUpgrade))))
{
//
// Ignore the return code below, we still want to move the revision
// level to 1.5, and this code will not be retried anyways
//
LsapDbUpgradeSecretForKeyChange();
}
if ( NT_SUCCESS( Status ) ) {
Revision = LSAP_DB_REVISION_1_5;
if ( !PolRevisionWritten ) {
LsapDbInitializeAttribute(
NextAttribute,
&LsapDbNames[PolRevision],
&Revision,
sizeof (ULONG),
FALSE
);
NextAttribute++;
AttributeCount++;
PolRevisionWritten = TRUE;
//
// Revision is now 1.5
//
ASSERT( AttributeCount < ( sizeof( Attributes ) / sizeof( LSAP_DB_ATTRIBUTE ) ) );
}
}
}
//
// Only upgrade past revision 1.5 if we've upgraded to revison 1.5.
//
// We don't upgrade to revision 1.5 in the mainline LSA initialization code.
// Rather, we upgrade to revision 1.5 in a callback from SAM. If then we moved on
// to revisions greater than 1.5 during LSA initialization, we'd never have a chance to
// do the revision 1.5 upgrade code.
//
if ( Revision >= LSAP_DB_REVISION_1_5 ) {
//
// Update the security descriptor to revision 1.6
//
if ( NT_SUCCESS( Status ) && (Revision < LSAP_DB_REVISION_1_6) ) {
Status = LsapUpdateDatabaseProtection( LSAP_DB_REVISION_1_6 );
if ( NT_SUCCESS( Status ) ) {
Revision = LSAP_DB_REVISION_1_6;
if ( !PolRevisionWritten ) {
LsapDbInitializeAttribute(
NextAttribute,
&LsapDbNames[PolRevision],
&Revision,
sizeof (ULONG),
FALSE
);
NextAttribute++;
AttributeCount++;
PolRevisionWritten = TRUE;
//
// Revision is now 1.6
//
ASSERT( AttributeCount < ( sizeof( Attributes ) / sizeof( LSAP_DB_ATTRIBUTE ) ) );
}
} else {
Status = STATUS_SUCCESS;
}
}
//
// Update the security descriptor to revision 1.7
//
if ( NT_SUCCESS( Status ) && (Revision < LSAP_DB_REVISION_1_7) ) {
Status = LsapUpdateDatabaseProtection( LSAP_DB_REVISION_1_7 );
if ( NT_SUCCESS( Status ) ) {
Revision = LSAP_DB_REVISION_1_7;
if ( !PolRevisionWritten ) {
LsapDbInitializeAttribute(
NextAttribute,
&LsapDbNames[PolRevision],
&Revision,
sizeof (ULONG),
FALSE
);
NextAttribute++;
AttributeCount++;
PolRevisionWritten = TRUE;
//
// Revision is now 1.7
//
ASSERT( AttributeCount < ( sizeof( Attributes ) / sizeof( LSAP_DB_ATTRIBUTE ) ) );
}
} else {
Status = STATUS_SUCCESS;
}
}
//
// In the future, revision updates can be made
// by adding "if" blocks similar to the one above.
//
// Remember, however, that the attributes are pointing
// to values in local variables. Any local variable
// value changed before the attribute is written out
// will cause that attribute value to be changed.
//
//
}
//
// Now write out all attributes that have been added (if any)
//
if (AttributeCount > 0) {
Status = LsapDbReferenceObject(
LsapDbHandle,
0,
PolicyObject,
PolicyObject,
LSAP_DB_LOCK | LSAP_DB_START_TRANSACTION
);
if (NT_SUCCESS(Status)) {
ASSERT( AttributeCount < ( sizeof( Attributes ) / sizeof( LSAP_DB_ATTRIBUTE ) ) );
Status = LsapDbWriteAttributesObject(
LsapDbHandle,
Attributes,
AttributeCount
);
//
// No attributes are replicatable.
// (That's good, too, since SAM hasn't told Netlogon our role yet.)
Status = LsapDbDereferenceObject(
&LsapDbHandle,
PolicyObject,
PolicyObject,
(LSAP_DB_LOCK |
LSAP_DB_FINISH_TRANSACTION |
LSAP_DB_OMIT_REPLICATOR_NOTIFICATION ),
SecurityDbChange,
Status
);
}
}
return( Status );
}
NTSTATUS
LsapDbBuildObjectCaches(
)
/*++
Routine Description:
This function builds caches for Lsa objects. These caches contain a
subset of the information for some object types.
Arguments:
None
Return Values:
NTSTATUS - Standard Nt Result Code.
--*/
{
NTSTATUS IgnoreStatus;
LSAP_DB_OBJECT_TYPE_ID ObjectTypeId;
//
// Initialize all the caches.
//
for (ObjectTypeId = PolicyObject;
ObjectTypeId <= SecretObject;
ObjectTypeId++) {
IgnoreStatus = LsapDbRebuildCache( ObjectTypeId );
}
return(STATUS_SUCCESS);
}
NTSTATUS
LsapDbInitializeObjectTypes(
)
/*++
Routine Description:
This function initializes the information pertinent to each object
type in the LSA Database. This information includes the following:
o Generic Mapping Arrays
The Generic Mapping array for each object defines the list of
object-type-specific access types that correspond to the generic
access types GENERIC_READ, GENERIC_WRITE, GENERIC_EXECUTE and
GENERIC_ALL for the object type.
o Object Count Information
The Object Count Information includes a count of the number of objects
that exist for each type, the upper limit on this number (if any) for
each object type, and the error code to return when that limit is
reached.
o Write Operation Masks
These specify which access types are update operations
o Default accesses granted to World and Admin aliases
o Invalid access masks for each object type
These masks specify the bits in an access mask that are invalid for
a given object type.
o Initial owners of each object type
o Object caching supported for each object type.
Arguments:
None. The Generic Mapping arrays are held the LsapDbState structure.
Return Value:
NTSTATUS - Standard Nt Result Code. Currently, there are no error
situations in this code, so STATUS_SUCCESS is always returned.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PGENERIC_MAPPING GenericMapping;
PLSAP_DB_OBJECT_TYPE ObjectType;
LSAP_DB_OBJECT_TYPE_ID ObjectTypeId;
//
// Initialize the Generic Mapping Array for the PolicyObject Object Type
// Note that there is only one object of this type and objects of this
// type can neither be created nor destroyed.
//
GenericMapping =
&LsapDbState.DbObjectTypes[PolicyObject].GenericMapping;
GenericMapping->GenericRead =
STANDARD_RIGHTS_READ |
POLICY_VIEW_AUDIT_INFORMATION |
POLICY_GET_PRIVATE_INFORMATION;
GenericMapping->GenericWrite =
STANDARD_RIGHTS_WRITE |
POLICY_TRUST_ADMIN |
POLICY_CREATE_ACCOUNT |
POLICY_CREATE_SECRET |
POLICY_CREATE_PRIVILEGE |
POLICY_SET_DEFAULT_QUOTA_LIMITS |
POLICY_SET_AUDIT_REQUIREMENTS |
POLICY_AUDIT_LOG_ADMIN |
POLICY_SERVER_ADMIN;
GenericMapping->GenericExecute =
STANDARD_RIGHTS_EXECUTE |
POLICY_VIEW_LOCAL_INFORMATION |
POLICY_LOOKUP_NAMES;
GenericMapping->GenericAll = POLICY_ALL_ACCESS | POLICY_NOTIFICATION;
//
// Initialize the Generic Mapping Array for the Account Object Type
// Note that Account Objects can be created and destroyed.
//
GenericMapping =
&LsapDbState.DbObjectTypes[AccountObject].GenericMapping;
GenericMapping->GenericRead =
STANDARD_RIGHTS_READ |
ACCOUNT_VIEW;
GenericMapping->GenericWrite =
STANDARD_RIGHTS_WRITE |
ACCOUNT_ADJUST_PRIVILEGES |
ACCOUNT_ADJUST_QUOTAS |
ACCOUNT_ADJUST_SYSTEM_ACCESS;
GenericMapping->GenericExecute =
STANDARD_RIGHTS_EXECUTE;
GenericMapping->GenericAll = ACCOUNT_ALL_ACCESS;
//
// Initialize the Generic Mapping Array for the TrustedDomain Object
// Type.
//
GenericMapping =
&LsapDbState.DbObjectTypes[TrustedDomainObject].GenericMapping;
GenericMapping->GenericRead =
STANDARD_RIGHTS_READ |
TRUSTED_QUERY_DOMAIN_NAME;
GenericMapping->GenericWrite =
STANDARD_RIGHTS_WRITE |
TRUSTED_SET_CONTROLLERS |
TRUSTED_SET_POSIX;
GenericMapping->GenericExecute =
STANDARD_RIGHTS_EXECUTE |
TRUSTED_QUERY_CONTROLLERS |
TRUSTED_QUERY_POSIX;
GenericMapping->GenericAll = TRUSTED_ALL_ACCESS;
//
// Initialize the Generic Mapping Array for the Secret Object
// Type.
//
GenericMapping =
&LsapDbState.DbObjectTypes[SecretObject].GenericMapping;
GenericMapping->GenericRead =
STANDARD_RIGHTS_READ |
SECRET_QUERY_VALUE;
GenericMapping->GenericWrite =
STANDARD_RIGHTS_WRITE |
SECRET_SET_VALUE;
GenericMapping->GenericExecute =
STANDARD_RIGHTS_EXECUTE;
GenericMapping->GenericAll = SECRET_ALL_ACCESS;
//
// Initialize the Object Count Information to defaults
//
ObjectType = &(LsapDbState.DbObjectTypes[PolicyObject]);
for (ObjectTypeId = PolicyObject;
ObjectTypeId < DummyLastObject;
ObjectTypeId++) {
ObjectType->ObjectCount = 0;
ObjectType->ObjectCountLimited = FALSE;
ObjectType->ObjectCountError = STATUS_SUCCESS;
ObjectType->MaximumObjectCount = 0;
}
//
// Set specific limits for Secret Object Type. This is the only
// object type so far to have limits.
//
ObjectType = &(LsapDbState.DbObjectTypes[SecretObject]);
ObjectType->ObjectCountLimited = TRUE;
ObjectType->ObjectCountError = STATUS_TOO_MANY_SECRETS;
ObjectType->MaximumObjectCount = LSA_SECRET_MAXIMUM_COUNT;
//
// Initialize the write operations for each object type
//
LsapDbState.DbObjectTypes[PolicyObject].WriteOperations = LSAP_POLICY_WRITE_OPS;
LsapDbState.DbObjectTypes[TrustedDomainObject].WriteOperations = LSAP_TRUSTED_WRITE_OPS;
LsapDbState.DbObjectTypes[AccountObject].WriteOperations = LSAP_ACCOUNT_WRITE_OPS;
LsapDbState.DbObjectTypes[SecretObject].WriteOperations = LSAP_SECRET_WRITE_OPS;
//
// Initialize the default accesses granted to Domain Admins alias
//
LsapDbState.DbObjectTypes[PolicyObject].AliasAdminsAccess = GENERIC_ALL;
LsapDbState.DbObjectTypes[TrustedDomainObject].AliasAdminsAccess = GENERIC_ALL | DELETE;
LsapDbState.DbObjectTypes[AccountObject].AliasAdminsAccess = GENERIC_ALL | DELETE;
LsapDbState.DbObjectTypes[SecretObject].AliasAdminsAccess = GENERIC_ALL | DELETE;
//
// Initialize the default accesses granted to World alias
//
LsapDbState.DbObjectTypes[PolicyObject].WorldAccess = GENERIC_EXECUTE;
LsapDbState.DbObjectTypes[TrustedDomainObject].WorldAccess = GENERIC_EXECUTE;
LsapDbState.DbObjectTypes[AccountObject].WorldAccess = GENERIC_EXECUTE;
LsapDbState.DbObjectTypes[SecretObject].WorldAccess = GENERIC_EXECUTE;
//
// Initialize the default accesses granted to AnonymousLogon alias
//
LsapDbState.DbObjectTypes[PolicyObject].AnonymousLogonAccess = POLICY_VIEW_LOCAL_INFORMATION | POLICY_LOOKUP_NAMES;
LsapDbState.DbObjectTypes[TrustedDomainObject].AnonymousLogonAccess = 0;
LsapDbState.DbObjectTypes[AccountObject].AnonymousLogonAccess = 0;
LsapDbState.DbObjectTypes[SecretObject].AnonymousLogonAccess = 0;
//
// Initialize the default accesses granted to LocalService and NetworkService
//
LsapDbState.DbObjectTypes[PolicyObject].LocalServiceAccess = POLICY_NOTIFICATION;
LsapDbState.DbObjectTypes[TrustedDomainObject].LocalServiceAccess = 0;
LsapDbState.DbObjectTypes[AccountObject].LocalServiceAccess = 0;
LsapDbState.DbObjectTypes[SecretObject].LocalServiceAccess = 0;
LsapDbState.DbObjectTypes[PolicyObject].NetworkServiceAccess = POLICY_NOTIFICATION;
LsapDbState.DbObjectTypes[TrustedDomainObject].NetworkServiceAccess = 0;
LsapDbState.DbObjectTypes[AccountObject].NetworkServiceAccess = 0;
LsapDbState.DbObjectTypes[SecretObject].NetworkServiceAccess = 0;
//
// Initialize the Invalid Access masks for each object type
//
LsapDbState.DbObjectTypes[PolicyObject].InvalidMappedAccess =
((ACCESS_MASK)(~(POLICY_ALL_ACCESS | POLICY_NOTIFICATION | ACCESS_SYSTEM_SECURITY | MAXIMUM_ALLOWED)));
LsapDbState.DbObjectTypes[TrustedDomainObject].InvalidMappedAccess =
((ACCESS_MASK)(~(TRUSTED_ALL_ACCESS | ACCESS_SYSTEM_SECURITY | MAXIMUM_ALLOWED)));
LsapDbState.DbObjectTypes[AccountObject].InvalidMappedAccess =
((ACCESS_MASK)(~(ACCOUNT_ALL_ACCESS | ACCESS_SYSTEM_SECURITY | MAXIMUM_ALLOWED)));
LsapDbState.DbObjectTypes[SecretObject].InvalidMappedAccess =
((ACCESS_MASK)(~(SECRET_ALL_ACCESS | ACCESS_SYSTEM_SECURITY | MAXIMUM_ALLOWED)));
//
// Initialize the Initial Owners for new objects of each type
//
LsapDbState.DbObjectTypes[PolicyObject].InitialOwnerSid = LsapAliasAdminsSid;
LsapDbState.DbObjectTypes[TrustedDomainObject].InitialOwnerSid = LsapAliasAdminsSid;
LsapDbState.DbObjectTypes[AccountObject].InitialOwnerSid = LsapAliasAdminsSid;
LsapDbState.DbObjectTypes[SecretObject].InitialOwnerSid = LsapAliasAdminsSid;
//
// Specify method of access to objects of the type. Currently, all objects
// of a given type are accessed in the same way, either by Sid or by Name
// but not both.
//
LsapDbState.DbObjectTypes[PolicyObject].AccessedByName = TRUE;
LsapDbState.DbObjectTypes[TrustedDomainObject].AccessedByName = FALSE;
LsapDbState.DbObjectTypes[AccountObject].AccessedByName = FALSE;
LsapDbState.DbObjectTypes[SecretObject].AccessedByName = TRUE;
LsapDbState.DbObjectTypes[PolicyObject].AccessedBySid = FALSE;
LsapDbState.DbObjectTypes[TrustedDomainObject].AccessedBySid = TRUE;
LsapDbState.DbObjectTypes[AccountObject].AccessedBySid = TRUE;
LsapDbState.DbObjectTypes[SecretObject].AccessedBySid = FALSE;
//
// Specify the object types for which caching is supported (in full
// or in part) and turn caching off initially for all object types.
// Object types for which caching is supported have ther caches set
// to the "Invalid" state. Automatic restore is allowed for caches
// in this state. Object types for which caching is not supported
// are set to the "Not supported" state. Note that a cache is
// also placed in the "not supported" state if an attempt to restore
// it fails.
//
LsapDbMakeCacheInvalid( PolicyObject );
LsapDbMakeCacheInvalid( TrustedDomainObject );
LsapDbMakeCacheInvalid( AccountObject );
LsapDbMakeCacheUnsupported( SecretObject );
return(Status);
}
NTSTATUS
LsapDbInitializeUnicodeNames()
/*++
Routine Description:
This function initializes two arrays of Unicode Strings. The
LsapDbNames array contains Unicode Strings for all of the constant
names in the Lsa Database. The LsapDbObjectTypeNames is indexed
by Object Type Id and contains the Unicode Strings for all of the
LSA Database object types.
Arguments:
None.
Return Value:
NTSTATUS - Standard Nt Result Code
All Result Codes are generated by called routines.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
LSAP_DB_NAMES Index;
LSAP_DB_OBJECT_TYPE_ID ObjectTypeId;
PCWSTR UnicodeNames[DummyLastName + 1] = {
L"SecDesc",
L"Privilgs",
L"Sid",
L"Name",
L"AdminMod",
L"OperMode",
L"QuotaLim",
L"DefQuota",
L"PrDomain",
L"Policy",
L"Accounts",
L"Domains",
L"Secrets",
L"CurrVal",
L"OldVal",
L"CupdTime",
L"OupdTime",
L"PolAdtLg",
L"PolAdtEv",
L"PolAcDmN",
L"PolAcDmS",
L"PolDnDDN",
L"PolDnTrN",
L"PolDnDmG",
L"PolEfDat",
L"PolPrDmN",
L"PolPrDmS",
L"PolPdAcN",
L"PolRepSc",
L"PolRepAc",
L"PolRevision",
L"PolMod",
L"PolState",
L"ActSysAc",
L"TrDmName",
L"TrDmTrPN", // Netbios name of trust partner
L"TrDmSid",
L"TrDmAcN",
L"TrDmCtN",
L"TrDmPxOf",
L"TrDmCtEn",
NULL, // TrDmTrTy
NULL, // TrDmTrDi
L"TrDmTrLA", // TrDmTrLA
L"TrDmTrPr", // Trust partner
L"TrDmTrRt", // Root partner
L"TrDmSAI", // Incoming auth. info
L"TrDmSAO", // Outgoing auth. info
L"TrDmForT", // Forest trust info
L"TrDmCrSid", // Creator Sid
L"KerOpts", // Kerberos authentication options
L"KerMinT", // Kerberos Minimum ticket age
L"KerMaxT", // Kerberos maximum ticket age
L"KerMaxR", // Kerberos maximum renewal age
L"KerProxy", // Kerberos proxy lifetime
L"KerLogoff", // Kerberos force logoff duration
L"BhvrVers", // Behavior-Version
L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\Eventlog\\Security",
L"MaxSize",
L"Retention",
L"PseudoSystemCritical",
L"PolSecretEncryptionKey",
L"XRefDnsRoot",
L"XRefNetbiosName",
L"DummyLastName"
};
PCWSTR UnicodeObjectTypeNames[DummyLastObject] = {
L"NullObject",
L"PolicyObject",
L"TrustedDomainObject",
L"UserAccountObject",
L"SecretObject",
L"AllObject",
L"NewTrustedDomainObject"
};
//
// Referenced by LsapDbDsAttInfo
//
static LSAP_DB_DS_INFO StaticLsapDbDsAttInfo[DummyLastName + 1] = {
{ATT_NT_SECURITY_DESCRIPTOR, LsapDbAttribSecDesc, LsapDsLocDs}, // Security Descriptor
{0, LsapDbAttribUnknown, LsapDsLocUnknown }, // Privileges
{ATT_SECURITY_IDENTIFIER, LsapDbAttribSid, LsapDsLocDs}, // Sid
{0, LsapDbAttribUnknown, LsapDsLocDs}, // Name
{0, LsapDbAttribUnknown, LsapDsLocUnknown}, // AdminMod
{0, LsapDbAttribUnknown, LsapDsLocUnknown}, // OperMode
{0, LsapDbAttribUnknown, LsapDsLocUnknown}, // QuotaLim
{0, LsapDbAttribUnknown, LsapDsLocUnknown}, // DefQuota
{0, LsapDbAttribUnknown, LsapDsLocUnknown}, // PrDomain
{0, LsapDbAttribUnknown, LsapDsLocUnknown}, // Policy
{0, LsapDbAttribUnknown, LsapDsLocUnknown}, // Accounts
{0, LsapDbAttribUnknown, LsapDsLocUnknown}, // Domains
{0, LsapDbAttribUnknown, LsapDsLocUnknown}, // Secrets
{ATT_CURRENT_VALUE, LsapDbAttribPByte, LsapDsLocDs}, // CurrVal
{ATT_PRIOR_VALUE, LsapDbAttribPByte, LsapDsLocDs}, // OldVal
{ATT_LAST_SET_TIME, LsapDbAttribPByte, LsapDsLocDs}, // CupdTime
{ATT_PRIOR_SET_TIME, LsapDbAttribPByte, LsapDsLocDs}, // OupdTime
{0, LsapDbAttribUnknown, LsapDsLocUnknown}, // PolAdtLg
{ATT_AUDITING_POLICY, LsapDbAttribPByte, LsapDsLocRegistry}, // PolAdtEv
{0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolAcDmN
{0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolAcDmS
{0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolDnDDN
{0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolDnTrN
{0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolDnDmG
{ATT_EFSPOLICY, LsapDbAttribPByte, LsapDsLocRegistry}, // PolEfDat
{0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolPrDmN
{0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolPrDmS
{0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolPdAcN
{0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolRepSc
{0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolRepAc
{0, LsapDbAttribUnknown, LsapDsLocUnknown}, // PolRevision
{0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolMod
{0, LsapDbAttribUnknown, LsapDsLocRegistry}, // PolState
{0, LsapDbAttribUnknown, LsapDsLocUnknown}, // ActSysAc
{ATT_TRUST_PARTNER, LsapDbAttribUnicode, LsapDsLocDs}, // TrDmName
{ATT_FLAT_NAME, LsapDbAttribUnicode, LsapDsLocDs}, // TrDmTrPN
{ATT_SECURITY_IDENTIFIER, LsapDbAttribSid, LsapDsLocDs}, // TrDmSid
{0, LsapDbAttribUnknown, LsapDsLocUnknown}, // TrDmAcN
{0, LsapDbAttribUnicode, LsapDsLocRegistry}, // TrDmCtN
{ATT_TRUST_POSIX_OFFSET, LsapDbAttribULong, LsapDsLocDs}, // TrDmPxOf
{0, LsapDbAttribUnicode, LsapDsLocRegistry}, // TrDmCtEn
{ATT_TRUST_TYPE, LsapDbAttribULong, LsapDsLocDs}, // TrDmTrTy
{ATT_TRUST_DIRECTION, LsapDbAttribULong, LsapDsLocDs}, // TrDmTrDi
{ATT_TRUST_ATTRIBUTES, LsapDbAttribULong, LsapDsLocDs}, // TrDmTrLA
{ATT_DOMAIN_CROSS_REF, LsapDbAttribDsName, LsapDsLocDs}, // TrDmTrPr
{ATT_ROOT_TRUST, LsapDbAttribDsName, LsapDsLocDs}, // TrDmTrRt
{ATT_TRUST_AUTH_INCOMING, LsapDbAttribPByte, LsapDsLocDs}, // TrDmSAI
{ATT_TRUST_AUTH_OUTGOING, LsapDbAttribPByte, LsapDsLocDs}, // TrDmSAO
{ATT_MS_DS_TRUST_FOREST_TRUST_INFO, LsapDbAttribPByte, LsapDsLocDs}, // TrDmForT
{ATT_MS_DS_CREATOR_SID, LsapDbAttribSid, LsapDsLocDs}, // TrDmCrSid
{ATT_AUTHENTICATION_OPTIONS, LsapDbAttribULong, LsapDsLocRegistry}, // KerOpts,
{ATT_MIN_TICKET_AGE, LsapDbAttribPByte, LsapDsLocRegistry}, // KerMinT,
{ATT_MAX_TICKET_AGE, LsapDbAttribPByte, LsapDsLocRegistry}, // KerMaxT,
{ATT_MAX_RENEW_AGE, LsapDbAttribPByte, LsapDsLocRegistry}, // KerMaxR,
{ATT_PROXY_LIFETIME, LsapDbAttribPByte, LsapDsLocRegistry}, // KerProxy,
{ATT_FORCE_LOGOFF, LsapDbAttribPByte, LsapDsLocRegistry}, // KerLogoff
{ATT_MS_DS_BEHAVIOR_VERSION, LsapDbAttribULong, LsapDsLocDs}, // BhvrVers
{0, LsapDbAttribUnknown, LsapDsLocUnknown}, // Audit log
{0, LsapDbAttribUnknown, LsapDsLocUnknown}, // Audit log size
{0, LsapDbAttribUnknown, LsapDsLocUnknown}, // Audit Recored Retention Period
{ATT_IS_CRITICAL_SYSTEM_OBJECT, LsapDbAttribULong, LsapDsLocDs}, // system critical component.
{0, LsapDbAttribPByte, LsapDsLocRegistry},//PolSecretEncryptionKey
{ATT_DNS_ROOT, LsapDbAttribUnicode, LsapDsLocDs}, // DNS name of cross-ref
{ATT_NETBIOS_NAME, LsapDbAttribUnicode, LsapDsLocDs}, // Netbios domain of cross-ref
{0, LsapDbAttribUnknown, LsapDsLocUnknown} // Dummy Last Name
};
//
// Initialize general array of Unicode Names
//
for (Index = SecDesc; Index < DummyLastName; Index++) {
RtlInitUnicodeString( &LsapDbNames[Index], UnicodeNames[Index] );
}
//
// Initialize array of Unicode Names for Lsa Database Object Types
//
for (ObjectTypeId = NullObject;
ObjectTypeId < DummyLastObject;
ObjectTypeId++) {
RtlInitUnicodeString(
&LsapDbObjectTypeNames[ObjectTypeId],
UnicodeObjectTypeNames[ObjectTypeId]
);
}
LsapDbDsAttInfo = StaticLsapDbDsAttInfo;
return(Status);
}
NTSTATUS
LsapDbInitializeContainingDirs()
/*++
Routine Description:
This function initializes Unicode strings for the names of the Containing
directories for each object type. The Containing Directory is the
Registry Key under which all objects of the given type are created and is
relative to the LSA Database root. Note that objects of a given type all
exist under a single Registry node, that is, the type of an object
uniquely determines the name of its containing directory.
NOTE: Containing Directories are used to produce Physical Object Names
from Logical Object Names. The Physical Object Name is simply
the Logical Object Name prepended with the Containing Directory
Name and a "\".
Arguments:
None.
Return Value:
NTSTATUS - Standard Nt Result Code
All Result Codes are generated by called routines.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
LSAP_DB_OBJECT_TYPE_ID ObjectTypeId;
PWSTR ContainingDirectories[DummyLastObject] = {
L"",
L"",
L"Domains",
L"Accounts",
L"Secrets"
};
//
// Initialize the array of Unicode Strings indexed by object type setting
// the Containing Directory name for each object type.
//
for (ObjectTypeId = PolicyObject;
ObjectTypeId < DummyLastObject;
ObjectTypeId++) {
RtlInitUnicodeString(
&LsapDbContDirs[ObjectTypeId],
ContainingDirectories[ ObjectTypeId ]
);
}
return(Status);
}
NTSTATUS
LsapDbInitializeReplication(
)
/*++
Routine Description:
This function performes LSA initialization for replication and turns
on notification of LSA Database updates to the LSA Database Replicator.
Arguments:
None.
Return Value:
NTSTATUS - Standard Nt Result Code
All Result Codes are generated by called routines.
--*/
{
NTSTATUS Status;
ULONG PolicyModificationInfoLength = sizeof (POLICY_MODIFICATION_INFO);
ULONG PolicyLsaServerRoleInfoLength = sizeof(POLICY_LSA_SERVER_ROLE_INFO);
ULONG LargeIntegerLength = sizeof( LARGE_INTEGER );
//
// Read the Policy Modification Info from the PolMod attribute
// of the Policy object in the LSA Database.
//
Status = LsapDbReadAttributeObject(
LsapDbHandle,
&LsapDbNames[PolMod],
(PVOID) &LsapDbState.PolicyModificationInfo,
&PolicyModificationInfoLength
);
if (!NT_SUCCESS(Status)) {
goto InitializeReplicationError;
}
InitializeReplicationFinish:
return(Status);
InitializeReplicationError:
goto InitializeReplicationFinish;
}
NTSTATUS
LsapDbInitializeCipherKey(
IN PUNICODE_STRING CipherSeed,
IN PLSAP_CR_CIPHER_KEY *CipherKey
)
/*++
Routine Description:
This function initializes the LSA Database Cipher Key.
Arguments:
None.
Return Value:
NTSTATUS - Standard Nt Result Code
All Result Codes are generated by called routines.
--*/
{
NTSTATUS Status;
LSAP_CR_CLEAR_VALUE ClearCipherKey;
PLSAP_CR_CIPHER_VALUE CipherCipherKey;
*CipherKey = NULL;
//
// Initialize the Cipher key to a hardwired constant
// encrypted with itself.
//
LsapCrUnicodeToClearValue( CipherSeed, &ClearCipherKey);
Status = LsapCrEncryptValue(
&ClearCipherKey,
(PLSAP_CR_CIPHER_KEY) &ClearCipherKey,
&CipherCipherKey
);
if (!NT_SUCCESS(Status)) {
LsapLogError( "LsapDbInitializeReplication: NtQuerySystemTime returned 0x%lx\n",
Status );
} else {
*CipherKey = ( PLSAP_CR_CIPHER_KEY )CipherCipherKey;
}
return(Status);
}
NTSTATUS
LsapDbOpenRootRegistryKey(
)
/*++
Routine Description:
This function opens the LSA Database Root Registry Key. This has
the fixed name \Registry\Machine\Security.
Arguments:
None.
Return Value:
NTSTATUS - Standard Nt Result Code
All Result Codes are generated by called routines.
--*/
{
NTSTATUS Status;
UNICODE_STRING DbRootRegKeyNameU;
OBJECT_ATTRIBUTES DbAttributes;
RtlInitUnicodeString( &DbRootRegKeyNameU, LSAP_DB_ROOT_REG_KEY_NAME );
InitializeObjectAttributes(
&DbAttributes,
&DbRootRegKeyNameU,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
Status = RtlpNtOpenKey(
(PHANDLE) &LsapDbState.DbRootRegKeyHandle,
(KEY_READ | KEY_CREATE_SUB_KEY | KEY_ENUMERATE_SUB_KEYS | WRITE_DAC),
&DbAttributes,
0
);
if (!NT_SUCCESS(Status)) {
LsapLogError(
"LsapDbOpenRootRegistryKey: Open Root Key for LSA Policy Database returned 0x%lx\n",
Status
);
goto OpenRootRegistryKeyError;
}
//
// If there are no sub-keys, then we are in system-install.
// Assign the initial protection of this hive.
//
Status = LsapAssignInitialHiveProtection( LsapDbState.DbRootRegKeyHandle );
if (!NT_SUCCESS(Status)) {
LsapLogError(
"LsapDbOpenRootRegistryKey: Couldn't assign initial hive protection 0x%lx\n",
Status
);
goto OpenRootRegistryKeyError;
}
OpenRootRegistryKeyFinish:
return(Status);
OpenRootRegistryKeyError:
goto OpenRootRegistryKeyFinish;
}
NTSTATUS
LsapAssignInitialHiveProtection(
HANDLE HiveRoot
)
/*++
Routine Description:
This function assigns inheritable protection to the hive root key.
It will only do this if the hive root has no sub-keys.
This condition will only exist during system installation.
WARNING -
THIS ROUTINE IS TAILORED TO OPERATE ON THE \REGISTRY\SECURITY HIVE.
As such, it expects the Root key to have exactly one sub-key (a
link to the SAM hive) if the the database has NOT been initialized.
Otherwise, it expects the LSA policy database keys to be present.
Arguments:
None.
Return Value:
STATUS_SUCCESS - Everything went fine. No indication of whether
protection was necessarily assigned or not.
All other status values are generated by called routines.
--*/
{
NTSTATUS
Status;
KEY_BASIC_INFORMATION
DummyBuffer;
ULONG
IgnoreRequiredLength;
SECURITY_DESCRIPTOR
Sd;
//
// See if the hive has more than 1 sub-keys.
//
//
Status = NtEnumerateKey(
HiveRoot,
1, // Index - 0 is the SAM link, 1 is LSA policy database stuff
KeyBasicInformation, // Name of key
&DummyBuffer,
sizeof(DummyBuffer),
&IgnoreRequiredLength
);
if (Status == STATUS_NO_MORE_ENTRIES) {
//
// We are initializing the system...
// Apply a reasonable ACL to the hive root.
//
Status = LsapCreateDatabaseProtection( &Sd );
if (NT_SUCCESS(Status)) {
Status = NtSetSecurityObject(
HiveRoot, // Object to apply to
DACL_SECURITY_INFORMATION, // Information to set
(PSECURITY_DESCRIPTOR)&Sd // Descriptor
);
}
} else {
Status = STATUS_SUCCESS;
}
return(Status);
}
NTSTATUS
LsapCreateDatabaseProtection(
PISECURITY_DESCRIPTOR Sd
)
/*++
Routine Description:
This function allocates and initializes protection to assign to
the SAM database.
Upon return, any non-zero pointers in the security descriptors
point to memory allocated from process heap. It is the caller's
responsibility to free this memory.
Protection is:
System: All Access
Admin: ReadControl | WriteDac
Arguments:
Sd - Address of a security descriptor to initialize.
Return Value:
STATUS_SUCCESS - The Security descriptor has been initialize.
STATUS_NO_MEMORY - couldn't allocate memory for the protection info.
--*/
{
NTSTATUS
Status;
ULONG
Length;
USHORT
i;
PACL
Dacl;
PACE_HEADER
Ace;
//
// Initialize the security descriptor.
// This call should not fail.
//
Status = RtlCreateSecurityDescriptor( Sd, SECURITY_DESCRIPTOR_REVISION1 );
ASSERT(NT_SUCCESS(Status));
Length = (ULONG)sizeof(ACL) +
(2*((ULONG)sizeof(ACCESS_ALLOWED_ACE))) +
RtlLengthSid( LsapLocalSystemSid ) +
RtlLengthSid( LsapAliasAdminsSid ) +
8; // The 8 is just for good measure
Dacl = RtlAllocateHeap( RtlProcessHeap(), 0, Length );
if (Dacl == NULL) {
return(STATUS_NO_MEMORY);
}
Status = RtlCreateAcl (Dacl, Length, ACL_REVISION2 );
ASSERT(NT_SUCCESS(Status));
//
// Add ACEs to the ACL...
// These calls should not be able to fail.
//
Status = RtlAddAccessAllowedAce(
Dacl,
ACL_REVISION2,
(GENERIC_ALL),
LsapLocalSystemSid
);
ASSERT(NT_SUCCESS(Status));
Status = RtlAddAccessAllowedAce(
Dacl,
ACL_REVISION2,
(READ_CONTROL | WRITE_DAC),
LsapAliasAdminsSid
);
ASSERT(NT_SUCCESS(Status));
//
// Now mark the ACEs as inheritable...
//
for ( i=0; i<Dacl->AceCount; i++) {
//
// Get the address of the next ACE
// (Shouldn't fail)
//
Status = RtlGetAce( Dacl, (ULONG)i, &Ace );
ASSERT(NT_SUCCESS(Status));
Ace->AceFlags |= (CONTAINER_INHERIT_ACE);
}
//
// And add the ACL to the security descriptor.
// This call should not fail.
//
Status = RtlSetDaclSecurityDescriptor(
Sd,
TRUE, // DaclPresent
Dacl, // Dacl OPTIONAL
FALSE // DaclDefaulted OPTIONAL
);
ASSERT(NT_SUCCESS(Status));
return(STATUS_SUCCESS);
}
NTSTATUS
LsapUpdateDatabaseProtection(
IN ULONG Revision
)
/*++
Routine Description:
This function allocates and updates protection to assign to
the LSA database.
Arguments:
Revision - New database revision level
LSAP_DB_REVISION_1_3 -- grant POLICY_NOTIFICATION access to Administrators
LSAP_DB_REVISION_1_6 -- grant POLICY_VIEW_LOCAL_INFORMATION | POLICY_LOOKUP_NAMES to AnonymousLogonSid
LSAP_DB_REVISION_1_7 -- grant POLICY_NOTIFICATION to LocalService/NetworkService
Return Value:
STATUS_SUCCESS - The Security descriptor has been initialize.
STATUS_NO_MEMORY - couldn't allocate memory for the protection info.
--*/
{
NTSTATUS Status;
NTSTATUS TempStatus;
PSECURITY_DESCRIPTOR CurrentSd = NULL;
PSECURITY_DESCRIPTOR RelativeSd = NULL;
ULONG RelativeSdLength;
PSECURITY_DESCRIPTOR NewSd;
SECURITY_DESCRIPTOR NewSdBuffer;
USHORT i;
PACL Dacl, NewDacl = NULL, TempAcl;
PACE_HEADER Ace;
PSID AceSid;
BOOLEAN AdminAceFound = FALSE;
BOOLEAN UpdatedSd = FALSE;
//
// First, read the initial security descriptor...
//
Status = LsapRegReadObjectSD( LsapPolicyHandle,
&CurrentSd );
if ( !NT_SUCCESS( Status ) ) {
goto Cleanup;
}
ASSERT( Revision == LSAP_DB_REVISION_1_3
||
Revision == LSAP_DB_REVISION_1_6
||
Revision == LSAP_DB_REVISION_1_7 );
NewSd = CurrentSd;
if ( Revision == LSAP_DB_REVISION_1_3
||
Revision == LSAP_DB_REVISION_1_6
||
Revision == LSAP_DB_REVISION_1_7 )
{
Dacl = RtlpDaclAddrSecurityDescriptor( ( PISECURITY_DESCRIPTOR )CurrentSd );
if ( Dacl ) {
//
// We'll have to find the ace for local system
//
Ace = ( PACE_HEADER )FirstAce( Dacl );
for(i = 0; i < Dacl->AceCount; i++, Ace = ( PACE_HEADER )NextAce( Ace ) ) {
if ( IsObjectAceType( Ace ) ) {
AceSid = RtlObjectAceSid( Ace );
} else {
AceSid = &( ( PKNOWN_ACE )Ace )->SidStart;
}
//
// When upgrading to revision 1_3, update the administrators ACE
//
if ( Revision == LSAP_DB_REVISION_1_3 &&
RtlEqualSid( AceSid, LsapAliasAdminsSid ) ) {
//
// Get the access mask and or in our new bit
//
if ( IsObjectAceType( Ace ) ) {
if (( ((PKNOWN_OBJECT_ACE)Ace)->Mask & POLICY_NOTIFICATION ) == 0 ) {
((PKNOWN_OBJECT_ACE)Ace)->Mask |= POLICY_NOTIFICATION;
UpdatedSd = TRUE;
}
} else {
if (( ((PKNOWN_ACE)Ace)->Mask & POLICY_NOTIFICATION ) == 0 ) {
((PKNOWN_ACE)Ace)->Mask |= POLICY_NOTIFICATION;
UpdatedSd = TRUE;
}
}
AdminAceFound = TRUE;
}
}
}
//
// If we didn't find an ACE to update, make sure to add it...
//
if ( !AdminAceFound )
{
ULONG NewDaclLength;
UpdatedSd = TRUE;
NewDaclLength = Dacl ? Dacl->AclSize : 0;
if ( Revision == LSAP_DB_REVISION_1_3 )
{
NewDaclLength += ( ULONG )sizeof( ACCESS_ALLOWED_ACE ) +
RtlLengthSid( LsapAliasAdminsSid );
}
else if ( Revision == LSAP_DB_REVISION_1_6 )
{
NewDaclLength += ( ULONG )sizeof( ACCESS_ALLOWED_ACE ) +
RtlLengthSid( LsapAnonymousSid );
}
else
{
ASSERT( Revision == LSAP_DB_REVISION_1_7 );
NewDaclLength += 2 * ( ULONG )sizeof( ACCESS_ALLOWED_ACE ) +
RtlLengthSid( LsapLocalServiceSid ) +
RtlLengthSid( LsapNetworkServiceSid );
}
NewDacl = LsapAllocateLsaHeap( NewDaclLength );
if ( NewDacl == NULL ) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
if ( Dacl ) {
RtlCopyMemory( NewDacl, Dacl, Dacl->AclSize );
NewDacl->AclSize = ( USHORT )NewDaclLength;
} else {
Status = RtlCreateAcl ( NewDacl, NewDaclLength, ACL_REVISION2 );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
}
//
// For version 1.3, grant administrators All access and Policy notification access
//
if ( Revision == LSAP_DB_REVISION_1_3 ) {
Status = RtlAddAccessAllowedAce( NewDacl,
ACL_REVISION2,
POLICY_ALL_ACCESS |
POLICY_NOTIFICATION,
LsapAliasAdminsSid );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
//
// For version 1.6, grant anonymous logon View Local and Lookup names access.
//
} else if ( Revision == LSAP_DB_REVISION_1_6) {
Status = RtlAddAccessAllowedAce( NewDacl,
ACL_REVISION2,
POLICY_VIEW_LOCAL_INFORMATION |
POLICY_LOOKUP_NAMES,
LsapAnonymousSid );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
//
// For version 1.7, grant Policy notification access to LocalService
// and NetworkService
//
} else {
ASSERT( Revision == LSAP_DB_REVISION_1_7 );
Status = RtlAddAccessAllowedAce( NewDacl,
ACL_REVISION2,
POLICY_NOTIFICATION,
LsapLocalServiceSid );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
Status = RtlAddAccessAllowedAce( NewDacl,
ACL_REVISION2,
POLICY_NOTIFICATION,
LsapNetworkServiceSid );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
}
//
// If the current security descriptor is self relative,
// convert it to absolute so I can set the DACL on it.
//
if ( RtlpAreControlBitsSet( ( PISECURITY_DESCRIPTOR )CurrentSd,
SE_SELF_RELATIVE ) ) {
NewSd = &NewSdBuffer;
Status = RtlCreateSecurityDescriptor( NewSd,
SECURITY_DESCRIPTOR_REVISION );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
((PISECURITY_DESCRIPTOR)NewSd)->Control =
((PISECURITY_DESCRIPTOR)CurrentSd)->Control;
AceSid = RtlpOwnerAddrSecurityDescriptor(
( PISECURITY_DESCRIPTOR )CurrentSd );
if ( AceSid ) {
( (PISECURITY_DESCRIPTOR)NewSd )->Owner = AceSid;
}
AceSid = RtlpGroupAddrSecurityDescriptor(
( PISECURITY_DESCRIPTOR )CurrentSd );
if ( AceSid ) {
( ( PISECURITY_DESCRIPTOR )NewSd )->Group = AceSid;
}
TempAcl = RtlpSaclAddrSecurityDescriptor(
( PISECURITY_DESCRIPTOR )CurrentSd );
if ( TempAcl ) {
( ( PISECURITY_DESCRIPTOR )NewSd )->Sacl = TempAcl;
}
RtlpClearControlBits( ( PISECURITY_DESCRIPTOR )NewSd,
SE_SELF_RELATIVE );
}
//
// Put the computed DACL onto the SD.
//
Status = RtlSetDaclSecurityDescriptor( NewSd,
TRUE,
NewDacl,
FALSE );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
//
// Convert the SD to self relative before writing it to the database.
//
RelativeSdLength = 0;
Status = RtlMakeSelfRelativeSD( NewSd,
NULL,
&RelativeSdLength );
if (Status != STATUS_BUFFER_TOO_SMALL) { // This is the expected case
if ( NT_SUCCESS(Status) ) {
Status = STATUS_INTERNAL_ERROR;
}
goto Cleanup;
}
RelativeSd = LsapAllocateLsaHeap( RelativeSdLength );
if ( RelativeSd == NULL) {
Status = STATUS_INSUFFICIENT_RESOURCES;
goto Cleanup;
}
Status = RtlMakeSelfRelativeSD( NewSd,
RelativeSd,
&RelativeSdLength );
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
NewSd = RelativeSd;
}
}
if ( UpdatedSd ) {
ULONG NewSdLength;
//
// Set the security descriptor back on the object
//
NewSdLength = RtlLengthSecurityDescriptor( NewSd );
//
// Add a Registry transaction to write the Security Descriptor as the
// value of the new object's SecDesc subkey.
//
Status = LsapDbReferenceObject(
LsapDbHandle,
0,
PolicyObject,
PolicyObject,
LSAP_DB_LOCK | LSAP_DB_START_TRANSACTION
);
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
Status = LsapDbWriteAttributeObject( LsapPolicyHandle,
&LsapDbNames[ SecDesc ],
NewSd,
NewSdLength );
TempStatus = LsapDbDereferenceObject(
&LsapDbHandle,
PolicyObject,
PolicyObject,
(LSAP_DB_LOCK |
LSAP_DB_OMIT_REPLICATOR_NOTIFICATION | // Each BDC should upgrade its own database
LSAP_DB_FINISH_TRANSACTION),
(SECURITY_DB_DELTA_TYPE) 0,
Status );
if ( !NT_SUCCESS(TempStatus) ) {
if (NT_SUCCESS(Status) ) {
Status = TempStatus;
}
}
}
Cleanup:
//
// Free the security descriptor
//
LsapFreeLsaHeap( CurrentSd );
LsapFreeLsaHeap( RelativeSd );
LsapFreeLsaHeap( NewDacl );
return( Status );
}
NTSTATUS
LsapDbInitializeLock(
)
/*++
Routine Description:
This function initializes the LSA Database Lock. It is called once
only, during LSA Database initialization.
Arguments:
None.
Return Value:
NTSTATUS - Standard Nt Result Code
All Result Codes are generated by called routines.
--*/
{
NTSTATUS Status;
Status = SafeInitializeCriticalSection( &LsapDbState.AccountLock, ( DWORD )ACCOUNT_LOCK_ENUM );
if (!NT_SUCCESS(Status) ) {
goto Cleanup;
}
Status = SafeInitializeCriticalSection( &LsapDbState.PolicyLock, ( DWORD )POLICY_LOCK_ENUM );
if (!NT_SUCCESS(Status) ) {
goto Cleanup;
}
Status = SafeInitializeCriticalSection( &LsapDbState.SecretLock, ( DWORD )SECRET_LOCK_ENUM );
if (!NT_SUCCESS(Status) ) {
goto Cleanup;
}
Status = SafeInitializeCriticalSection( &LsapDbState.RegistryLock, ( DWORD )REGISTRY_LOCK_ENUM );
if (!NT_SUCCESS(Status) ) {
goto Cleanup;
}
Status = SafeInitializeCriticalSection( &LsapDbState.HandleTableLock, ( DWORD )HANDLE_TABLE_LOCK_ENUM );
if (!NT_SUCCESS(Status) ) {
goto Cleanup;
}
try
{
SafeInitializeResource( &LsapDbState.PolicyCacheLock, ( DWORD )POLICY_CACHE_LOCK_ENUM );
Status = STATUS_SUCCESS;
}
except( EXCEPTION_EXECUTE_HANDLER )
{
Status = GetExceptionCode();
}
if (!NT_SUCCESS(Status) ) {
goto Cleanup;
}
//
// Initialize the Resource for the Trusted Domain List.
//
InitializeListHead( &LsapDbTrustedDomainList.ListHead );
LsapDbTrustedDomainList.TrustedDomainCount = 0;
LsapDbTrustedDomainList.CurrentSequenceNumber = 0;
LsapDbMakeCacheInvalid( TrustedDomainObject );
try
{
SafeInitializeResource( &LsapDbTrustedDomainList.Resource, ( DWORD )TRUST_LOCK_ENUM );
Status = STATUS_SUCCESS ;
}
except (EXCEPTION_EXECUTE_HANDLER )
{
Status = GetExceptionCode();
}
if ( !NT_SUCCESS( Status ) ) {
goto Cleanup;
}
try
{
//
// do not use the safelock library for ScePolicyLock
// because it is usually acquired on one thread and
// released on another
//
RtlInitializeResource( &LsapDbState.ScePolicyLock );
Status = STATUS_SUCCESS ;
}
except ( EXCEPTION_EXECUTE_HANDLER )
{
Status = GetExceptionCode();
}
if (!NT_SUCCESS( Status )) {
goto Cleanup;
}
LsapDbState.SceSyncEvent = CreateEvent( NULL, TRUE, TRUE, NULL );
if ( LsapDbState.SceSyncEvent == NULL ) {
Status = GetLastError();
goto Cleanup;
}
#if DBG
try
{
SafeInitializeResource( &LsapDsThreadInfoListResource, ( DWORD )THREAD_INFO_LIST_LOCK_ENUM );
Status = STATUS_SUCCESS ;
}
except ( EXCEPTION_EXECUTE_HANDLER )
{
Status = GetExceptionCode();
}
if ( !NT_SUCCESS( Status ) ) {
goto Cleanup;
}
RtlZeroMemory( &LsapDsThreadInfoList, sizeof( LSADS_THREAD_INFO_NODE ) * LSAP_THREAD_INFO_LIST_MAX );
#endif
Status = STATUS_SUCCESS;
Cleanup:
return( Status );
}
NTSTATUS
LsapDbInitializeWellKnownValues(
)
/*++
Routine Description:
This function initializes the well-known values used by LSA.
Arguments:
None.
Return Value:
NTSTATUS - Standard Nt Result Code
All Result Codes are generated by called routines.
--*/
{
NTSTATUS Status;
BOOLEAN b;
//
// Initialize the Well Known Sids
//
b = LsapDbInitializeWellKnownSids( &WellKnownSids );
if (!b ) {
Status = STATUS_UNSUCCESSFUL;
goto InitializeWellKnownValuesError;
}
//
// Initialize the well known privilege values
//
Status = LsapDbInitializeWellKnownPrivs();
if (!NT_SUCCESS(Status)) {
goto InitializeWellKnownValuesError;
}
InitializeWellKnownValuesFinish:
return(Status);
InitializeWellKnownValuesError:
goto InitializeWellKnownValuesFinish;
}
NTSTATUS
LsapDbInitializeWellKnownPrivs(
)
/*++
Routine Description:
This function initializes the well-known privilege values.
Arguments:
None.
Return Value:
NTSTATUS - Standard Nt Result Code
Currently, only STATUS_SUCCESS is returned.
--*/
{
LsapTcbPrivilege = RtlConvertLongToLuid(SE_TCB_PRIVILEGE);
return STATUS_SUCCESS;
}
BOOLEAN
LsapDbInitializeWellKnownSids(
OUT PLSAP_WELL_KNOWN_SID_ENTRY *WellKnownSids
)
/*++
Routine Description:
This function initializes the Well-Known Sids
Arguments:
WellKnownSids - Receives a pointer to a newly created table of
the Well Known Sids.
Return Value:
BOOLEAN - TRUE if successful, else FALSE.
--*/
{
NTSTATUS Status;
BOOLEAN BooleanStatus = TRUE;
LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex;
ULONG SubAuthorities[LSAP_WELL_KNOWN_MAX_SUBAUTH_LEVEL];
ULONG OutputWellKnownSidsLength;
PLSAP_WELL_KNOWN_SID_ENTRY OutputWellKnownSids = NULL;
UNICODE_STRING SidName, NtAuthorityName, UsersName;
HMODULE StringsResource;
SID_IDENTIFIER_AUTHORITY InternetSiteAuthority
= SECURITY_INTERNETSITE_AUTHORITY;
//
// Get the message resource we need to get the SID names from
//
StringsResource = (HMODULE) LoadLibrary( L"LSASRV.DLL" );
if (StringsResource == NULL) {
return(FALSE);
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_NT_AUTHORITY,
&NtAuthorityName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
//
// Allocate memory for the table of Sids.
//
OutputWellKnownSidsLength =
LsapDummyLastSidIndex * sizeof(LSAP_WELL_KNOWN_SID_ENTRY);
OutputWellKnownSids = RtlAllocateHeap(
RtlProcessHeap(),
0,
OutputWellKnownSidsLength
);
if (OutputWellKnownSids == NULL) {
goto InitializeWellKnownSidsError;
}
//
// Allocate and initialize the universal SIDs
//
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_NULL,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_NULL_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapNullSidIndex,
&LsapNullSidAuthority,
1,
SubAuthorities,
SidName.Buffer,
L"",
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_WORLD,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_WORLD_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapWorldSidIndex,
&LsapWorldSidAuthority,
1,
SubAuthorities,
SidName.Buffer,
L"",
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_LOCAL,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_LOCAL_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapLocalSidIndex,
&LsapLocalSidAuthority,
1,
SubAuthorities,
SidName.Buffer,
L"",
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_CREATOR_OWNER,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_CREATOR_OWNER_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapCreatorOwnerSidIndex,
&LsapCreatorSidAuthority,
1,
SubAuthorities,
SidName.Buffer,
L"",
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_CREATOR_GROUP,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_CREATOR_GROUP_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapCreatorGroupSidIndex,
&LsapCreatorSidAuthority,
1,
SubAuthorities,
SidName.Buffer,
L"",
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_CREATOR_OWNER_SERVER,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_CREATOR_OWNER_SERVER_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapCreatorOwnerServerSidIndex,
&LsapCreatorSidAuthority,
1,
SubAuthorities,
SidName.Buffer,
L"",
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_CREATOR_GROUP_SERVER,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_CREATOR_GROUP_SERVER_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapCreatorGroupServerSidIndex,
&LsapCreatorSidAuthority,
1,
SubAuthorities,
SidName.Buffer,
L"",
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
//
// Initialize the Nt well-known Sids
//
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_NT_DOMAIN,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapNtAuthoritySidIndex,
&LsapNtAuthority,
0,
NULL,
L"",
SidName.Buffer,
SidTypeDomain
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_DIALUP,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_DIALUP_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapDialupSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_NETWORK,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_NETWORK_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapNetworkSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_BATCH,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_BATCH_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapBatchSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_INTERACTIVE,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_INTERACTIVE_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapInteractiveSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_REMOTE_INTERACTIVE,
&SidName,
0,
NULL); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_REMOTE_LOGON_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapRemoteInteractiveSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_THIS_ORGANIZATION,
&SidName,
0,
NULL); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_THIS_ORGANIZATION_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapThisOrganizationSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_OTHER_ORGANIZATION,
&SidName,
0,
NULL); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_OTHER_ORGANIZATION_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapOtherOrganizationSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_TERMINAL_SERVER,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_TERMINAL_SERVER_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapTerminalServerSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_SERVICE,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_SERVICE_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapServiceSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_ANONYMOUS,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_ANONYMOUS_LOGON_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapAnonymousSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_PROXY,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_PROXY_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapProxySidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_SERVER,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_SERVER_LOGON_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapServerSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_SELF,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_PRINCIPAL_SELF_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapSelfSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_AUTHENTICATED_USER,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_AUTHENTICATED_USER_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapAuthenticatedUserSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
//
// Add any Logon Id well known sids here.
//
SubAuthorities[0] = SECURITY_LOGON_IDS_RID;
SubAuthorities[1] = 0;
SubAuthorities[2] = 0;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapLogonSidIndex,
&LsapNtAuthority,
3,
SubAuthorities,
L"",
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_SYSTEM,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_LOCAL_SYSTEM_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapLocalSystemSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_LOCALSERVICE,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_LOCAL_SERVICE_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapLocalServiceSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_NETWORKSERVICE,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_NETWORK_SERVICE_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapNetworkServiceSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_RESTRICTED,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_RESTRICTED_CODE_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapRestrictedSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_INTERNET,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapInternetDomainIndex,
&InternetSiteAuthority,
0,
SubAuthorities,
L"",
SidName.Buffer,
SidTypeDomain
)) {
goto InitializeWellKnownSidsError;
}
//
// Add any security package well known sids
//
SubAuthorities[0] = SECURITY_PACKAGE_BASE_RID;
SubAuthorities[1] = RPC_C_AUTHN_WINNT;
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_NTLM_AUTH,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapNTLMAuthenticationSidIndex,
&LsapNtAuthority,
SECURITY_PACKAGE_RID_COUNT,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
SubAuthorities[1] = RPC_C_AUTHN_DIGEST;
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_DIGEST_AUTH,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapDigestAuthenticationSidIndex,
&LsapNtAuthority,
SECURITY_PACKAGE_RID_COUNT,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
SubAuthorities[1] = RPC_C_AUTHN_GSS_SCHANNEL;
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_SCHANNEL_AUTH,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapSChannelAuthenticationSidIndex,
&LsapNtAuthority,
SECURITY_PACKAGE_RID_COUNT,
SubAuthorities,
SidName.Buffer,
NtAuthorityName.Buffer,
SidTypeWellKnownGroup
)) {
goto InitializeWellKnownSidsError;
}
//
// Initialize SIDs from the BUILTIN domain. Leave the Name
// field blank for the aliases as they can be renamed while
// the system is running (and the lookup code will fall
// through to SAM for these).
//
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_BUILTIN,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
SubAuthorities[0] = SECURITY_BUILTIN_DOMAIN_RID;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapBuiltInDomainSidIndex,
&LsapNtAuthority,
1,
SubAuthorities,
L"",
SidName.Buffer,
SidTypeDomain
)) {
goto InitializeWellKnownSidsError;
}
SubAuthorities[1] = DOMAIN_ALIAS_RID_USERS;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapAliasUsersSidIndex,
&LsapNtAuthority,
2,
SubAuthorities,
L"",
SidName.Buffer,
SidTypeAlias
)) {
goto InitializeWellKnownSidsError;
}
SubAuthorities[1] = DOMAIN_ALIAS_RID_ADMINS;
if (!LsapDbInitializeWellKnownSid(
OutputWellKnownSids,
LsapAliasAdminsSidIndex,
&LsapNtAuthority,
2,
SubAuthorities,
L"",
SidName.Buffer,
SidTypeAlias
)) {
goto InitializeWellKnownSidsError;
}
Status = LsapGetMessageStrings(
StringsResource,
LSAP_SID_NAME_USERS,
&SidName,
0,
NULL
); ASSERT(NT_SUCCESS(Status));
//
// Check if all Sids initialized.
//
#ifdef LSAP_DEBUG_MESSAGE_STRINGS
DbgPrint("\nLSA (dbinit): Displaying all well known sids...\n\n");
#endif //LSAP_DEBUG_MESSAGE_STRINGS
for (WellKnownSidIndex = LsapNullSidIndex;
WellKnownSidIndex < LsapDummyLastSidIndex;
WellKnownSidIndex++) {
#ifdef LSAP_DEBUG_MESSAGE_STRINGS
DbgPrint(" *%wZ* : *%wZ*\n",
&OutputWellKnownSids[WellKnownSidIndex].DomainName,
&OutputWellKnownSids[WellKnownSidIndex].Name);
#endif //LSAP_DEBUG_MESSAGE_STRINGS
if (OutputWellKnownSids[WellKnownSidIndex].Sid == NULL) {
#if DBG
DbgPrint(
"Well Known Sid Index %d not initialized\n",
WellKnownSidIndex
);
#endif //DBG
}
}
*WellKnownSids = OutputWellKnownSids;
return(TRUE);
InitializeWellKnownSidsError:
return(FALSE);
}
BOOLEAN
LsapDbInitializeWellKnownSid(
OUT PLSAP_WELL_KNOWN_SID_ENTRY WellKnownSids,
IN LSAP_WELL_KNOWN_SID_INDEX WellKnownSidIndex,
IN PSID_IDENTIFIER_AUTHORITY IdentifierAuthority,
IN UCHAR SubAuthorityCount,
IN PULONG SubAuthorities,
IN PWSTR Name,
IN PWSTR DomainName,
IN SID_NAME_USE Use
)
/*++
Routine Description:
This function initializes an entry in the specified well-known Sid table.
The entry contains the well known Sid and its name.
Arguments:
WellKnownSids - Pointer to the first entry in the Well Known Sid table.
WellKnownSidIndex - Index into table of Well Known Sids.
Sid - Receives a pointer to a Sid with the correct size for the
number of subauthorities specified.
IdentifierAuthority - Pointer to Identifier authority.
SubAuthorityCount - Count of SubAuthorities
SubAuthorities - Array of SubAuthorities.
Name - Pointer to Unicode Name buffer containing the Sid's Name
DomainName - Pointer to Unicode Name buffer containing the
Sids Domain Name (if any) or descriptive text, such as
"Well Known Group" for Sids of Well Known Groups
SidNameUse - Specifies code for Sid's Use. The following values
may be specified:
SidTypeUser
SidTypeGroup
SidTypeDomain
SidTypeAlias
SidTypeWellKnownGroup
SidTypeDeletedAccount
SidTypeInvalid
SidTypeUnknown
Return Value:
BOOLEAN - TRUE if Sid initialized, else FALSE.
--*/
{
PLSAP_WELL_KNOWN_SID_ENTRY
WellKnownSidEntry = &WellKnownSids[WellKnownSidIndex];
PSID OutputSid = NULL;
OutputSid = RtlAllocateHeap(
RtlProcessHeap(),
0,
RtlLengthRequiredSid(SubAuthorityCount)
);
if (OutputSid == NULL) {
goto InitializeWellKnownSidError;
}
RtlInitializeSid( OutputSid, IdentifierAuthority, SubAuthorityCount);
if (SubAuthorityCount != 0) {
RtlCopyMemory(
RtlSubAuthoritySid( OutputSid, 0 ),
SubAuthorities,
SubAuthorityCount * sizeof(ULONG)
);
}
WellKnownSidEntry->Sid = OutputSid;
//
// Fill in the Domain Name
//
RtlInitUnicodeString(
&WellKnownSidEntry->DomainName,
DomainName
);
//
// Fill in the Use and Name.
//
WellKnownSidEntry->Use = Use;
RtlInitUnicodeString(
&WellKnownSidEntry->Name,
Name
);
return(TRUE);
InitializeWellKnownSidError:
#if DBG
DbgPrint("LSA Initialization of Well Known Sids Failed\n");
DbgPrint("Insufficient memory resources\n");
#endif // DBG
return(FALSE);
}
NTSTATUS
LsapGetMessageStrings(
LPVOID Resource,
DWORD Index1,
PUNICODE_STRING String1,
DWORD Index2,
PUNICODE_STRING String2 OPTIONAL
)
/*++
Routine Description:
This gets 1 or 2 message strings values from a resource message table.
The string buffers are allocated and the strings initialized properly.
The strings will be NULL terminated.
The string buffers must be freed using LocalFree() when no longer needed.
Arguments:
Resource - points to the resource table.
Index1 - Index of first message to retrieve.
String1 - Points to a UNICODE_STRING structure to receive the first
message string. The string will be null terminated.
Index2 - Index of second message to retrieve.
String2 - Points to a UNICODE_STRING structure to receive the first
message string. If this parameter is NULL, then only one message
string is retrieved. The string will be null terminated.
Return Value:
None.
--*/
{
#ifdef LSAP_DEBUG_MESSAGE_STRINGS
DbgPrint("LSA (dbinit): String 1 -\n");
DbgPrint(" Index: 0x%lx\n", Index1);
#endif //LSAP_DEBUG_MESSAGE_STRINGS
String1->Buffer = NULL;
String1->MaximumLength = (USHORT) FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_ALLOCATE_BUFFER,
Resource,
Index1,
0, // Use caller's language
(LPWSTR)&(String1->Buffer),
0,
NULL
);
if (String1->Buffer == NULL) {
return(STATUS_RESOURCE_DATA_NOT_FOUND);
} else {
//
// Note that we are retrieving a message from a message file.
// This message will have a cr/lf tacked on the end of it
// (0x0d 0x0a) that we don't want to be part of our returned
// strings. However, we do need to null terminate our string
// so we will convert the 0x0d into a null terminator.
//
// Also note that FormatMessage() returns a character count,
// not a byte count. So, we have to do some adjusting to make
// the string lengths correct.
//
ASSERT(String1->MaximumLength >= 2); // We always expect cr/lf on our strings
//
// Adjust character count
//
String1->MaximumLength -= 1; // For the lf - we'll convert the cr.
//
// Set null terminator
//
String1->Buffer[String1->MaximumLength - 1] = 0;
//
// Change lengths to byte count instead of character count
//
String1->MaximumLength *= sizeof(WCHAR); // to make it a byte count
String1->Length = String1->MaximumLength - sizeof(WCHAR);
#ifdef LSAP_DEBUG_MESSAGE_STRINGS
DbgPrint(" String: %wZ\n", String1);
DbgPrint(" Max: (0x%lx)\n", String1->MaximumLength);
DbgPrint(" Cur: (0x%lx)\n", String1->Length);
DbgPrint(" ");
{
ULONG i;
for (i=0; i<String1->MaximumLength; i++) {
DbgPrint("%2x ", (*((PUCHAR)String1->Buffer)+i));
}
DbgPrint("\n");
}
#endif //LSAP_DEBUG_MESSAGE_STRINGS
}
if (!ARGUMENT_PRESENT(String2)) {
return(STATUS_SUCCESS);
}
String2->Buffer = NULL;
String2->MaximumLength = (USHORT) FormatMessage(FORMAT_MESSAGE_FROM_HMODULE |
FORMAT_MESSAGE_ALLOCATE_BUFFER,
Resource,
Index2,
0, // Use caller's language
(LPWSTR)&(String2->Buffer),
0,
NULL
);
if (String2->Buffer == NULL) {
LocalFree( String1->Buffer );
return(STATUS_RESOURCE_DATA_NOT_FOUND);
} else {
//
// Note that we are retrieving a message from a message file.
// This message will have a cr/lf tacked on the end of it
// (0x0d 0x0a) that we don't want to be part of our returned
// strings. However, we do need to null terminate our string
// so we will convert the 0x0d into a null terminator.
//
// Also note that FormatMessage() returns a character count,
// not a byte count. So, we have to do some adjusting to make
// the string lengths correct.
//
ASSERT(String2->MaximumLength >= 2); // We always expect cr/lf on our strings
//
// Adjust character count
//
String2->MaximumLength -= 1; // For the lf - we'll convert the cr.
//
// Set null terminator
//
String2->Buffer[String2->MaximumLength - 1] = 0;
//
// Change lengths to byte count instead of character count
//
String2->MaximumLength *= sizeof(WCHAR); // to make it a byte count
String2->Length = String2->MaximumLength - sizeof(WCHAR);
#ifdef LSAP_DEBUG_MESSAGE_STRINGS
DbgPrint(" String: %wZ\n", String2);
DbgPrint(" Max: (0x%lx)\n", String2->MaximumLength);
DbgPrint(" Cur: (0x%lx)\n", String2->Length);
DbgPrint(" ");
{
ULONG i;
for (i=0; i<String2->MaximumLength; i++) {
DbgPrint("%2x ", (*((PUCHAR)String2->Buffer)+i));
}
DbgPrint("\n");
}
#endif //LSAP_DEBUG_MESSAGE_STRINGS
}
return(STATUS_SUCCESS);
}
#if defined(REMOTE_BOOT)
VOID
LsapDbInitializeRemoteBootState(
)
/*++
Routine Description:
This function initializes the remote boot state used by LSA.
Arguments:
None.
Return Value:
NTSTATUS - Standard Nt Result Code
All Result Codes are generated by called routines.
--*/
{
NTSTATUS Status ;
HANDLE RdrDevice ;
UNICODE_STRING String ;
OBJECT_ATTRIBUTES ObjA ;
IO_STATUS_BLOCK IoStatus ;
//
// This is the default if anything goes wrong.
//
LsapDbState.RemoteBootState = LSAP_DB_REMOTE_BOOT_NO_NOTIFICATION;
//
// Open the redirector device.
//
RtlInitUnicodeString( &String, DD_NFS_DEVICE_NAME_U );
InitializeObjectAttributes( &ObjA,
&String,
0,
0,
0);
Status = NtOpenFile( &RdrDevice,
GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE,
&ObjA,
&IoStatus,
FILE_SHARE_READ | FILE_SHARE_WRITE,
0 );
if ( !NT_SUCCESS( Status ) )
{
DebugLog(( DEB_TRACE, "FAILED to open %ws, status %x\n",
String.Buffer, Status ));
return;
}
Status = NtFsControlFile(
RdrDevice,
NULL,
NULL,
NULL,
&IoStatus,
FSCTL_LMMR_RI_IS_PASSWORD_SETTABLE,
NULL,
0,
NULL,
0 );
if ( Status == STATUS_SUCCESS )
{
LsapDbState.RemoteBootState = LSAP_DB_REMOTE_BOOT_NOTIFY;
}
else if ( Status == STATUS_UNSUCCESSFUL )
{
LsapDbState.RemoteBootState = LSAP_DB_REMOTE_BOOT_CANT_NOTIFY;
}
NtClose(RdrDevice);
}
#endif // defined(REMOTE_BOOT)
NTSTATUS
LsapGenerateRandomDomainSid(
OUT PSID NewDomainSid
)
/*++
Routine Description:
This function will generate a random sid to be used for the new account domain sid during
setup.
Arguments:
NewDomainSid - Where the new domain sid is returned. Freed via RtlFreeSid()
Return Values:
STATUS_SUCCESS -- Success.
STATUS_INSUFFICIENT_RESOURCES -- A memory allocation failed
--*/
{
NTSTATUS Status = STATUS_UNSUCCESSFUL;
ULONG SubAuth1, SubAuth2, SubAuth3;
SID_IDENTIFIER_AUTHORITY IdentifierAuthority = SECURITY_NT_AUTHORITY;
if(!RtlGenRandom( &SubAuth1, sizeof(SubAuth1) ))
{
ASSERT( NT_SUCCESS(Status) );
return Status;
}
if(!RtlGenRandom( &SubAuth2, sizeof(SubAuth2) ))
{
ASSERT( NT_SUCCESS(Status) );
return Status;
}
if(!RtlGenRandom( &SubAuth3, sizeof(SubAuth3) ))
{
ASSERT( NT_SUCCESS(Status) );
return Status;
}
Status = RtlAllocateAndInitializeSid( &IdentifierAuthority,
4,
0x15,
SubAuth1,
SubAuth2,
SubAuth3,
0,
0,
0,
0,
NewDomainSid );
return( Status );
}
NTSTATUS
LsapSetupInitialize(
VOID
)
/*++
Routine Description:
This function will generate a random sid to be used for the new account domain sid during
setup.
Arguments:
NewDomainSid - Where the new domain sid is returned. Freed via RtlFreeSid()
Return Values:
STATUS_SUCCESS -- Success.
STATUS_INSUFFICIENT_RESOURCES -- A memory allocation failed
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PSID NewDomainSid = NULL;
HMODULE StringsResource;
LSAPR_POLICY_ACCOUNT_DOM_INFO AccountDomainInfo;
Status = LsapGenerateRandomDomainSid( &NewDomainSid );
if ( NT_SUCCESS( Status ) ) {
//
// We can use GetModuleHandle, since we are getting it on ourselves.
//
StringsResource = (HMODULE) GetModuleHandle( L"LSASRV.DLL" );
ASSERT( StringsResource );
Status = LsapGetMessageStrings( StringsResource,
LSAP_DEFAULT_DOMAIN_NAME,
( PUNICODE_STRING )&AccountDomainInfo.DomainName,
0,
NULL );
}
//
// Ok, if we got this far, then we can initialize the account domain
//
if ( NT_SUCCESS( Status ) ) {
AccountDomainInfo.DomainSid = NewDomainSid;
Status = LsarSetInformationPolicy( LsapPolicyHandle,
PolicyAccountDomainInformation,
( PLSAPR_POLICY_INFORMATION )&AccountDomainInfo );
LocalFree( AccountDomainInfo.DomainName.Buffer );
}
if ( NewDomainSid ) {
RtlFreeSid( NewDomainSid );
}
return( Status );
}
static GUID LsapDbPasswordAuthenticator = {0xf0ce3a80,0x155f,0x11d3,0xb7,0xe6,0x00,0x80,0x5f,0x48,0xca,0xeb};
NTSTATUS
LsapDbGenerateNewKey(
IN LSAP_DB_ENCRYPTION_KEY * NewEncryptionKey
)
/*++
Routine Description
This routine generates a new Encryption key that can be used for
encrypting secrets.
Parameters
NewEncryptionKey -- Pointer to a structure that contains the new key
Return Values
STATUS_SUCCESS
STATUS_UNSUCCESSFUL
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
//
// generate a random number for the key
//
if (!RtlGenRandom(NewEncryptionKey->Key,sizeof(NewEncryptionKey->Key)))
{
return(STATUS_UNSUCCESSFUL);
}
//
// Copy in the GUID for the authenticator
//
NewEncryptionKey->Authenticator = LsapDbPasswordAuthenticator;
//
// Set the version #
//
NewEncryptionKey->Revision = LSAP_DB_ENCRYPTION_KEY_VERSION ;
NewEncryptionKey->Flags = 0;
//
// Generate a Salt
//
if (!RtlGenRandom(NewEncryptionKey->Salt,sizeof(NewEncryptionKey->Salt)))
{
return(STATUS_UNSUCCESSFUL);
}
//
// Generate a random value for the old syskey
//
if (!RtlGenRandom(NewEncryptionKey->OldSyskey,sizeof(NewEncryptionKey->OldSyskey)))
{
return(STATUS_UNSUCCESSFUL);
}
//
// Set the boot type
//
NewEncryptionKey->BootType = WxStored;
return(STATUS_SUCCESS);
}
VOID
LsapDbEncryptKeyWithSyskey(
OUT LSAP_DB_ENCRYPTION_KEY * KeyToEncrypt,
IN PVOID Syskey,
IN ULONG SyskeyLength
)
/*++
This routine encrypts the KeyToEncrypt parameter with
the syskey passed in
Arguments
KeyToEncrypt -- the key to encrypt
Syskey -- The syskey passed in
SyskeyLength -- The length of the syskey
Return Values
None, void function
--*/
{
MD5_CTX Md5Context;
struct RC4_KEYSTRUCT Rc4Key;
ULONG i;
//
// Create an MD5 hash of the key and salt
//
MD5Init(&Md5Context);
MD5Update(
&Md5Context,
Syskey,
SyskeyLength
);
//
// Hash in the salt many many times. This slows down
// attackers employing a brute force approach to attack
//
for (i=0;i<1000;i++)
{
MD5Update(
&Md5Context,
KeyToEncrypt->Salt,
sizeof(KeyToEncrypt->Salt)
);
}
MD5Final(
&Md5Context
);
//
// Initialize the RC4 key sequence.
//
rc4_key(
&Rc4Key,
MD5DIGESTLEN,
Md5Context.digest
);
rc4(
&Rc4Key,
sizeof(KeyToEncrypt->Key)+ sizeof(KeyToEncrypt->Authenticator)+sizeof(KeyToEncrypt->OldSyskey),
(PUCHAR) &KeyToEncrypt->Authenticator
);
}
NTSTATUS
LsapDbDecryptKeyWithSyskey(
IN LSAP_DB_ENCRYPTION_KEY * KeyToDecrypt,
IN PVOID Syskey,
IN ULONG SyskeyLength
)
/*++
This function provides a decryption using the syskey. Since RC4 is symmetric
encryption algorithm, this function simply calls the previous encrypt routine
Arguments
KeyToDecrypt -- the key to dencrypt
Syskey -- The syskey passed in
SyskeyLength -- The length of the syskey
Return Values
STATUS_SUCCESS , on successful decryption
STATUS_WRONG_PASSWORD on unsuccessful decryption
--*/
{
LsapDbEncryptKeyWithSyskey(KeyToDecrypt,Syskey,SyskeyLength);
if (!RtlEqualMemory(&KeyToDecrypt->Authenticator,&LsapDbPasswordAuthenticator,sizeof(GUID)))
{
return(STATUS_WRONG_PASSWORD);
}
return(STATUS_SUCCESS);
}
NTSTATUS
LsapDbSetupInitialSyskey(
OUT PULONG SyskeyLength,
OUT PVOID *Syskey
)
/*++
This generates a new syskey and changes the winlogon state such that
winlogon recognizes the new syskey and the boot option of system saves
syskey.
Arguments
SyskeyLength -- The length of the syskey is returned in here
Syskey -- The syskey itself is returned in here
Return Values
STATUS_SUCCESS
Other resource error codes
--*/
{
NTSTATUS NtStatus = STATUS_SUCCESS;
*Syskey = LsapAllocateLsaHeap(LSAP_SYSKEY_SIZE );
if (NULL==*Syskey)
{
NtStatus = STATUS_INSUFFICIENT_RESOURCES;
goto Error;
}
*SyskeyLength = LSAP_SYSKEY_SIZE;
//
// Generate the syskey
//
if (!RtlGenRandom( *Syskey, *SyskeyLength))
{
NtStatus = STATUS_UNSUCCESSFUL;
goto Error;
}
//
// Save the syskey in the registry
// If this operation fails, then no state is changed, machine remains
// un syskey-d till next boot
//
NtStatus = WxSaveSysKey(*SyskeyLength, *Syskey);
if (!NT_SUCCESS(NtStatus))
{
goto Error;
}
//
// Set the boot option in the registry
// If this operation fails, still no problem. Machine remains unsyskey'd
// and the boot key that has been saved will be reset on next boot.
//
NtStatus = WxSaveBootOption(WxStored);
if (!NT_SUCCESS(NtStatus))
{
goto Error;
}
Error:
if (!NT_SUCCESS(NtStatus))
{
*SyskeyLength = 0;
if (NULL!=*Syskey)
{
MIDL_user_free(*Syskey);
}
}
return(NtStatus);
}
NTSTATUS
LsapDbGetSyskeyFromWinlogon()
/*++
Routine Description
This routine obtains the syskey from winlogon and decrypts the
Password encryption key in the LSA policy database. The global variable
LsapDbSecretCipherKey is set with the password encryption key and the
the global variable LsapDbSyskey is set with the syskey. This value is
then queried by SAM/DS to decrypt their respective password encryption keys
and then cleared before the end of SamIInitialize.
The special case handled inside this routine is of the case of a fresh install.
In this particular case winlogon is not yet setup to expect a query of syskey
from the lsass process. However since the LSA install code has been called just
moments, before, that code sets the winlogon state and also fills the global
LsapDbSyskey. Therefore if LsapDbSyskey is not NULL then this would be the
fresh install case.
Arguments
None
Return Values
STATUS_SUCCESS
STATUS_UNSUCCESSFUL
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
NTSTATUS DecryptStatus = STATUS_SUCCESS;
ULONG DecryptionKeyLength = 0;
HANDLE WinlogonHandle=NULL;
ULONG Tries = 0;
LSAP_DB_ENCRYPTION_KEY StoredEncryptionKeyData;
ULONG StoredEncryptionKeyDataLength = sizeof( LSAP_DB_ENCRYPTION_KEY );
ULONG SyskeyLen=LSAP_SYSKEY_SIZE;
BOOLEAN FreshInstall=FALSE;
//
// Read the attribute information in the LSA policy database
//
Status = LsapDbReadAttributeObject(
LsapDbHandle,
&LsapDbNames[PolSecretEncryptionKey],
(PVOID) &StoredEncryptionKeyData,
&StoredEncryptionKeyDataLength
);
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
if (NULL!=LsapDbSysKey)
{
//
// In the fresh install case , code in dbinstal.c already
// sets LsapDbSyskey to the syskey value.
//
FreshInstall = TRUE;
Status = LsapDbDecryptKeyWithSyskey(
&StoredEncryptionKeyData ,
LsapDbSysKey,
LSAP_SYSKEY_SIZE
);
}
else
{
//
// because LsapDbDecryptKeyWithSyskey() is an in-place
// operation, so we'd better save the Encrypted Syskey first
//
LSAP_DB_ENCRYPTION_KEY TempStoredEncryptionKeyData;
TempStoredEncryptionKeyData = StoredEncryptionKeyData;
//
// Call Winlogon to obtain the key information.
//
Status = WxConnect(
&WinlogonHandle
);
if (!NT_SUCCESS(Status))
{
//
// Winlogon may fail if secret encryption is not enabled. In those
// cases continue. Else Fail the boot
//
if (WxNone==StoredEncryptionKeyData.BootType)
{
Status = STATUS_SUCCESS;
}
goto Cleanup;
}
for (Tries = 0; Tries < LSAP_BOOT_KEY_RETRY_COUNT ; Tries++ )
{
//
// restore the data which need to be decrypted.
//
StoredEncryptionKeyData = TempStoredEncryptionKeyData;
//
// Retry this RETRY_COUNT_TIMES, this allows the user a chance
// to correct himself, in case he entered a wrong boot password
//
if (WxNone!=StoredEncryptionKeyData.BootType)
{
//
// Get the key to be used to decrypt the PEK list
//
Status = WxGetKeyData(
WinlogonHandle,
StoredEncryptionKeyData.BootType,
LSAP_SYSKEY_SIZE,
SyskeyBuffer,
&SyskeyLen
);
if (!NT_SUCCESS(Status)) {
goto Cleanup;
}
ASSERT(SyskeyLen==LSAP_SYSKEY_SIZE);
//
// Decrypt the Blob passed in with the key supplied by winlogon
//
Status = LsapDbDecryptKeyWithSyskey(
&StoredEncryptionKeyData ,
SyskeyBuffer,
LSAP_SYSKEY_SIZE
);
if (!NT_SUCCESS(Status))
{
DecryptStatus = STATUS_WRONG_PASSWORD;
}
else
{
//
// We successfully decrypted, break out of the loop
//
DecryptStatus = STATUS_SUCCESS;
break;
}
}
else
{
break;
}
}
//
// Tell winlogon regarding success or failure of the scheme
//
Status = WxReportResults(
WinlogonHandle,
DecryptStatus
);
if (!NT_SUCCESS(Status)) {
goto Cleanup;
}
Status = DecryptStatus;
}
if (NT_SUCCESS(Status))
{
//
// Initialize the new secret cipher key
// The keys used for reading and writing secrets are ordinarily the same
//
LsapDbInitializeSecretCipherKeyRead( &StoredEncryptionKeyData );
LsapDbInitializeSecretCipherKeyWrite( &StoredEncryptionKeyData );
if (!FreshInstall)
{
//
// Set the global variable LsapDBSysKey
//
LsapDbSysKey = SyskeyBuffer;
//
// Set up the old syskey for recovery cases
// by SAM and LSA
//
RtlCopyMemory(
OldSyskeyBuffer,
StoredEncryptionKeyData.OldSyskey,
sizeof(StoredEncryptionKeyData.OldSyskey)
);
LsapDbOldSysKey = OldSyskeyBuffer;
}
}
Cleanup:
if (WinlogonHandle != NULL) {
NtClose(WinlogonHandle);
}
return(Status);
}
VOID
LsapDbInitializeSecretCipherKeyRead(
PLSAP_DB_ENCRYPTION_KEY PassedInEncryptionKeyData
)
/*++
Routine Description
Given a pointer to the encryption key ( as in the struct ), this routine
initializes a CipherKey structure that can be used by the LSA's secret encryption
and decryption routines.
Arguments
PassedInEncryptionKeyData -- The struct representing the key
Return Values
Void function
--*/
{
static LSAP_DB_ENCRYPTION_KEY StaticEncryptionKeyData;
static LSAP_CR_CIPHER_KEY DecryptedSecretCipherKey;
RtlCopyMemory(&StaticEncryptionKeyData,PassedInEncryptionKeyData,sizeof(LSAP_DB_ENCRYPTION_KEY));
DecryptedSecretCipherKey.Buffer = StaticEncryptionKeyData.Key;
DecryptedSecretCipherKey.Length = DecryptedSecretCipherKey.MaximumLength
= sizeof(StaticEncryptionKeyData.Key);
LsapDbSecretCipherKeyRead = &DecryptedSecretCipherKey;
}
VOID
LsapDbInitializeSecretCipherKeyWrite(
PLSAP_DB_ENCRYPTION_KEY PassedInEncryptionKeyData
)
/*++
Routine Description
Given a pointer to the encryption key ( as in the struct ), this routine
initializes a CipherKey structure that can be used by the LSA's secret encryption
and decryption routines.
Arguments
PassedInEncryptionKeyData -- The struct representing the key
CipherKey -- The structure that needs to be initialzed with the
key for LSA's secret encryption
Return Values
Void function
--*/
{
static LSAP_DB_ENCRYPTION_KEY StaticEncryptionKeyData;
static LSAP_CR_CIPHER_KEY DecryptedSecretCipherKey;
RtlCopyMemory(&StaticEncryptionKeyData,PassedInEncryptionKeyData,sizeof(LSAP_DB_ENCRYPTION_KEY));
DecryptedSecretCipherKey.Buffer = StaticEncryptionKeyData.Key;
DecryptedSecretCipherKey.Length = DecryptedSecretCipherKey.MaximumLength
= sizeof(StaticEncryptionKeyData.Key);
LsapDbSecretCipherKeyWrite = &DecryptedSecretCipherKey;
}
VOID
LsapDbSetSyskey(
PVOID Syskey,
ULONG SyskeyLength
)
/*++
This function sets the syskey in the global syskey buffer
--*/
{
ASSERT(LSAP_SYSKEY_SIZE==SyskeyLength);
RtlCopyMemory(SyskeyBuffer,Syskey,SyskeyLength);
LsapDbSysKey = SyskeyBuffer;
}
NTSTATUS
LsaISetBootOption(
IN ULONG BootOption,
IN PVOID OldKey,
IN ULONG OldKeyLength,
IN PVOID NewKey,
IN ULONG NewKeyLength
)
/*++
This function is used to change the syskey value in the LSA, or change
the boot option type.
Arguments
BootOption -- New Boot Option
OldKey -- Old key, used to verify value
OldKeyLength -- Length of old key
NewKey -- New key, LSA's password encryption key is encrypted using
this value
NewKeyLength -- Length of new key
Return Values
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG StoredEncryptionKeyDataLength = sizeof( LSAP_DB_ENCRYPTION_KEY );
LSAP_DB_ENCRYPTION_KEY StoredEncryptionKeyData;
ULONG SyskeyLen=0;
LSAP_DB_ATTRIBUTE Attributes[20];
PLSAP_DB_ATTRIBUTE NextAttribute;
ULONG AttributeCount = 0;
NextAttribute = Attributes;
//
// Validate Some parameters
//
if ((NewKeyLength != LSAP_SYSKEY_SIZE ) || (OldKeyLength != LSAP_SYSKEY_SIZE))
{
return (STATUS_INVALID_PARAMETER);
}
if ((NULL==NewKey) || (NULL==OldKey))
{
return(STATUS_INVALID_PARAMETER);
}
//
// Read the attribute information in the LSA policy database
//
Status = LsapDbReadAttributeObject(
LsapDbHandle,
&LsapDbNames[PolSecretEncryptionKey],
(PVOID) &StoredEncryptionKeyData,
&StoredEncryptionKeyDataLength
);
if (!NT_SUCCESS(Status) ) {
goto Cleanup;
}
//
// Decrypt the data
//
Status = LsapDbDecryptKeyWithSyskey(
&StoredEncryptionKeyData,
OldKey,
OldKeyLength
);
if (!NT_SUCCESS(Status))
{
goto Cleanup;
}
//
// Change the boot option
//
StoredEncryptionKeyData.BootType = BootOption;
//
// Save the old key ( for recovery )
//
ASSERT(sizeof(StoredEncryptionKeyData.OldSyskey) == OldKeyLength);
RtlCopyMemory(
&StoredEncryptionKeyData.OldSyskey,
OldKey,
OldKeyLength
);
//
// Re-encrypt the data using the new key
//
LsapDbEncryptKeyWithSyskey(
&StoredEncryptionKeyData,
NewKey,
NewKeyLength
);
LsapDbInitializeAttribute(
NextAttribute,
&LsapDbNames[PolSecretEncryptionKey],
&StoredEncryptionKeyData,
sizeof (StoredEncryptionKeyData),
FALSE
);
NextAttribute++;
AttributeCount++;
//
// Now write out all attributes that have been added (if any)
//
if (AttributeCount > 0) {
Status = LsapDbReferenceObject(
LsapDbHandle,
0,
PolicyObject,
PolicyObject,
LSAP_DB_LOCK | LSAP_DB_START_TRANSACTION
);
if (NT_SUCCESS(Status)) {
ASSERT( AttributeCount < ( sizeof( Attributes ) / sizeof( LSAP_DB_ATTRIBUTE ) ) );
Status = LsapDbWriteAttributesObject(
LsapDbHandle,
Attributes,
AttributeCount
);
//
// No attributes are replicatable.
// (That's good, too, since SAM hasn't told Netlogon our role yet.)
Status = LsapDbDereferenceObject(
&LsapDbHandle,
PolicyObject,
PolicyObject,
(LSAP_DB_LOCK |
LSAP_DB_FINISH_TRANSACTION |
LSAP_DB_OMIT_REPLICATOR_NOTIFICATION ),
SecurityDbChange,
Status
);
}
}
Cleanup:
return(Status);
}
NTSTATUS
LsaIGetBootOption(
OUT PULONG BootOption
)
/*++
This function is used to obtain the boot option from LSA
Arguments
BootOption -- New Boot is passed in here
Return Values
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG StoredEncryptionKeyDataLength = sizeof( LSAP_DB_ENCRYPTION_KEY );
LSAP_DB_ENCRYPTION_KEY StoredEncryptionKeyData;
//
// Read the attribute information in the LSA policy database
//
Status = LsapDbReadAttributeObject(
LsapDbHandle,
&LsapDbNames[PolSecretEncryptionKey],
(PVOID) &StoredEncryptionKeyData,
&StoredEncryptionKeyDataLength
);
if (!NT_SUCCESS(Status) ) {
goto Cleanup;
}
*BootOption = StoredEncryptionKeyData.BootType;
Cleanup:
return(Status);
}