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.
 
 
 
 
 
 

1515 lines
37 KiB

/*++
Copyright (c) 1991 Microsoft Corporation
Module Name:
dbadmin.c
Abstract:
Local Security Authority - Database Administration
This file contains routines that perform general Lsa Database
administration functions
Author:
Scott Birrell (ScottBi) August 27, 1991
Environment:
Revision History:
--*/
#include <lsapch2.h>
#include "dbp.h"
#include "adtp.h"
#if DBG
LSADS_THREAD_INFO_NODE LsapDsThreadInfoList[ LSAP_THREAD_INFO_LIST_MAX ];
SAFE_RESOURCE LsapDsThreadInfoListResource;
#endif
LSADS_INIT_STATE LsaDsInitState;
NTSTATUS
LsapDbSetStates(
IN ULONG DesiredStatesSet,
IN LSAPR_HANDLE ObjectHandle,
IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId
)
/*++
Routine Description:
This routine turns on special states in the Lsa Database. These
states can be turned off using LsapDbResetStates.
Arguments:
DesiredStatesSet - Specifies the states to be set.
LSAP_DB_LOCK - Acquire the Lsa Database lock.
LSAP_DB_LOG_QUEUE_LOCK - Acquire the Lsa Audit Log
Queue Lock.
LSAP_DB_START_TRANSACTION - Start an Lsa Database transaction. There
must not already be one in progress.
LSAP_DB_READ_ONLY_TRANSACTION - Open a transaction for read only
LSAP_DB_DS_OP_TRANSACTION - Perform a single Ds operation per transaction
ObjectHandle - Pointer to handle to be validated and referenced.
ObjectTypeId - Specifies the expected object type to which the handle
relates. An error is returned if this type does not match the
type contained in the handle.
Return Value:
NTSTATUS - Standard Nt Result Code
STATUS_INVALID_STATE - The Database is not in the correct state
to allow this state change.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
NTSTATUS SecondaryStatus = STATUS_SUCCESS;
ULONG StatesSetHere = 0;
LSAP_DB_HANDLE InternalHandle = ( LSAP_DB_HANDLE )ObjectHandle;
LsapDsDebugOut(( DEB_FTRACE, "LsapDbSetStates\n" ));
//
// If we have an object type that doesn't write to the Ds, make sure we have
// the options set appropriately
//
if ( ObjectTypeId == PolicyObject ||
ObjectTypeId == AccountObject ) {
DesiredStatesSet |= LSAP_DB_NO_DS_OP_TRANSACTION;
}
if ( ObjectTypeId == TrustedDomainObject ) {
DesiredStatesSet |= LSAP_DB_READ_ONLY_TRANSACTION;
}
//
// If requested, lock the Audit Log Queue
//
if (DesiredStatesSet & LSAP_DB_LOG_QUEUE_LOCK) {
Status = LsapAdtAcquireLogFullLock();
if (!NT_SUCCESS(Status)) {
goto SetStatesError;
}
StatesSetHere |= LSAP_DB_LOG_QUEUE_LOCK;
}
//
// If requested, lock the Lsa database
//
if (DesiredStatesSet & LSAP_DB_LOCK) {
LsapDbAcquireLockEx( ObjectTypeId,
DesiredStatesSet );
StatesSetHere |= LSAP_DB_LOCK;
}
//
// If requested, open a database update transaction.
//
if ( FLAG_ON( DesiredStatesSet, LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_NO_DS_OP_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION |
LSAP_DB_START_TRANSACTION ) ) {
Status = LsapDbOpenTransaction( DesiredStatesSet );
if (!NT_SUCCESS(Status)) {
goto SetStatesError;
}
StatesSetHere |= LSAP_DB_START_TRANSACTION;
}
SetStatesFinish:
LsapDsDebugOut(( DEB_FTRACE, "LsapDbSetStates: 0x%lx\n", Status ));
return( Status );
SetStatesError:
//
// If we started a transaction, abort it.
//
if (StatesSetHere & LSAP_DB_START_TRANSACTION) {
SecondaryStatus = LsapDbAbortTransaction( DesiredStatesSet );
}
//
// If we locked the database, unlock it.
//
if (StatesSetHere & LSAP_DB_LOCK) {
LsapDbReleaseLockEx( ObjectTypeId,
DesiredStatesSet );
}
//
// If we locked the Audit Log Queue, unlock it.
//
if (StatesSetHere & LSAP_DB_LOG_QUEUE_LOCK) {
LsapAdtReleaseLogFullLock();
}
goto SetStatesFinish;
}
NTSTATUS
LsapDbResetStates(
IN LSAPR_HANDLE ObjectHandle,
IN ULONG Options,
IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
IN SECURITY_DB_DELTA_TYPE SecurityDbDeltaType,
IN NTSTATUS PreliminaryStatus
)
/*++
Routine Description:
This function resets the Lsa Database states specified. It is used
to reset states set by LsapDbSetStates.
Arguments:
ObjectHandle - Handle to an LSA object. This is expected to have
already been validated.
Options - Specifies optional actions, including states to be reset
LSAP_DB_LOCK - Lsa Database lock to be released
LSAP_DB_LOG_QUEUE_LOCK - Lsa Audit Log Queue Lock to
be released.
LSAP_DB_FINISH_TRANSACTION - Lsa database transaction open.
LSAP_DB_OMIT_REPLICATOR_NOTIFICATION - Omit notification to
Replicators.
ObjectTypeId - Specifies the expected object type to which the handle
relates.
PreliminaryStatus - Indicates the preliminary result code of the
calling routine. Allows reset action to vary depending on the
result code, for example, apply or abort transaction.
Return Value:
NTSTATUS - Standard Nt Result Code. This is the final status to be used
by the caller and is equal to the Preliminary status except in the
case where that is as success status and this routine fails.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
LSAP_DB_HANDLE InternalHandle = ( LSAP_DB_HANDLE )ObjectHandle;
LsapDsDebugOut(( DEB_FTRACE, "LsapDbResetStates (Prelim: 0x%lx )\n", PreliminaryStatus ));
//
// If we have an object type that doesn't write to the Ds, make sure we have
// the options set appropriately
//
if ( ObjectTypeId == PolicyObject ||
ObjectTypeId == AccountObject ) {
Options |= LSAP_DB_NO_DS_OP_TRANSACTION;
}
if ( ObjectTypeId == TrustedDomainObject ) {
Options |= LSAP_DB_READ_ONLY_TRANSACTION;
}
//
// If requested, finish a database update transaction.
//
if ( !FLAG_ON( Options, LSAP_DB_STANDALONE_REFERENCE ) &&
FLAG_ON( Options, LSAP_DB_READ_ONLY_TRANSACTION |
LSAP_DB_NO_DS_OP_TRANSACTION |
LSAP_DB_DS_OP_TRANSACTION |
LSAP_DB_FINISH_TRANSACTION ) ) {
if (NT_SUCCESS(PreliminaryStatus)) {
Status = LsapDbApplyTransaction(
ObjectHandle,
Options,
SecurityDbDeltaType
);
} else {
Status = LsapDbAbortTransaction( Options );
}
}
//
// If unlocking requested, unlock the Lsa Database.
//
if (Options & LSAP_DB_LOCK) {
LsapDbReleaseLockEx( ObjectTypeId,
Options );
}
//
// If unlocking if the Audit Log Queue requested, unlock the queue.
//
if (Options & LSAP_DB_LOG_QUEUE_LOCK) {
LsapAdtReleaseLogFullLock();
}
//
// The requested reset operations were performed successfully.
// Propagate the preliminary status back to the caller.
//
if ( NT_SUCCESS( Status ))
{
Status = PreliminaryStatus;
}
LsapDsDebugOut(( DEB_FTRACE, "LsapDbResetStates: 0x%lx\n", Status ));
return( Status );
}
NTSTATUS
LsapDbOpenTransaction(
IN ULONG Options
)
/*++
Routine Description:
This function starts a transaction within the LSA Database.
WARNING: The Lsa Database must be in the locked state when this function
is called.
Arguments:
Options - Options to apply when opening the transaction. Valid values are:
LSAP_DB_READ_ONLY_TRANSACTION - Open a transaction for read only
Return Value:
NTSTATUS - Standard Nt Result Code
Result codes are those returned from the Registry Transaction
Package.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
BOOLEAN RegTransactionOpened = FALSE;
if ( !FLAG_ON( Options, LSAP_DB_READ_ONLY_TRANSACTION ) ) {
Status = LsapRegOpenTransaction();
if (NT_SUCCESS(Status))
{
RegTransactionOpened = TRUE;
}
}
if ( NT_SUCCESS( Status ) && LsapDsIsFunctionTableValid() ) {
ASSERT( LsaDsStateInfo.DsFuncTable.pOpenTransaction );
Status = (*LsaDsStateInfo.DsFuncTable.pOpenTransaction) ( Options );
if ((!NT_SUCCESS(Status)) && RegTransactionOpened)
{
NTSTATUS IgnoreStatus;
IgnoreStatus = LsapRegAbortTransaction();
}
}
return Status;
}
NTSTATUS
LsapDbApplyTransaction(
IN LSAPR_HANDLE ObjectHandle,
IN ULONG Options,
IN SECURITY_DB_DELTA_TYPE SecurityDbDeltaType
)
/*++
Routine Description:
This function applies a transaction within the LSA Database.
WARNING: The Lsa Database must be in the locked state when this function
is called.
Arguments:
ObjectHandle - Handle to an LSA object. This is expected to have
already been validated.
Options - Specifies optional actions to be taken. The following
options are recognized, other options relevant to calling routines
are ignored.
LSAP_DB_OMIT_REPLICATOR_NOTIFICATION - Omit notification to
Replicator.
Return Value:
NTSTATUS - Standard Nt Result Code
Result codes are those returned from the Registry Transaction
Package.
--*/
{
NTSTATUS Status;
LSAP_DB_HANDLE InternalHandle = ( LSAP_DB_HANDLE )ObjectHandle;
BOOLEAN RegApplied = FALSE, Notify = FALSE;
BOOLEAN RestoreModifiedId = FALSE;
BOOLEAN RegistryLocked = FALSE;
LARGE_INTEGER Increment = {1,0},
OriginalModifiedId = { 0 };
PLSADS_PER_THREAD_INFO CurrentThreadInfo;
ULONG SavedDsOperationCount = 0;
//
// Reference the thread state so it doesn't disappear in the middle of this
// routine.
//
CurrentThreadInfo = LsapQueryThreadInfo();
if ( CurrentThreadInfo ) {
SavedDsOperationCount = CurrentThreadInfo->DsOperationCount;
LsapCreateThreadInfo();
}
//
// Verify that the LSA Database is locked
// One of many locks is locked
//
//ASSERT (LsapDbIsLocked());
//
// Apply the DS transaction before grabbing any more locks.
//
// Note that this applies the transaction before updating the modified ID.
// If we crash before updateing the modified ID, NT 4 BDCs won't be notified
// of this change.
//
if ( LsapDsIsFunctionTableValid() ) {
ASSERT( LsaDsStateInfo.DsFuncTable.pApplyTransaction );
Status = (*LsaDsStateInfo.DsFuncTable.pApplyTransaction)( Options );
if (!NT_SUCCESS(Status)) {
goto Cleanup;
}
}
//
// Notify the replicator unless:
// We are to omit replicator (e.g. for creation of a local secret), OR
// we are installing the Policy Object,
// notification globally disabled.
//
if ((!(Options & LSAP_DB_OMIT_REPLICATOR_NOTIFICATION)) &&
(LsapDbHandle != NULL) &&
(LsapDbState.ReplicatorNotificationEnabled )) {
BOOLEAN DbChanged = FALSE;
//
// If the object is in the DS,
// determine if the DS changed.
//
if ( LsapDsIsHandleDsHandle( InternalHandle )) {
//
// Netlogon notification of DS object change is *ALWAYS* handled
// in the DS notification callback routine. That's the easiest
// way to handle things like TDO changes result in both TDO notifications
// and the corresponding global secret notification.
//
ASSERT( InternalHandle->ObjectTypeId == TrustedDomainObject ||
InternalHandle->ObjectTypeId == SecretObject );
//
// If the object is a registry object,
// determine if the registry has changed.
//
} else {
//
// Grab the registry lock.
// It serializes access to the global ModifiedId
//
LsapDbLockAcquire( &LsapDbState.RegistryLock );
RegistryLocked = TRUE;
ASSERT( SavedDsOperationCount == 0 ||
InternalHandle->ObjectTypeId == PolicyObject );
if ( LsapDbState.RegistryModificationCount > 0 ) {
DbChanged = TRUE;
//
// No one should change the database on a read only transaction.
//
ASSERT( !FLAG_ON( Options, LSAP_DB_READ_ONLY_TRANSACTION) );
}
}
//
// If the DbChanged,
// increment the NT 4 change serial number.
//
if ( DbChanged ) {
OriginalModifiedId = LsapDbState.PolicyModificationInfo.ModifiedId;
RestoreModifiedId = TRUE;
//
// Increment Modification Count.
//
//
// we want to increment the modification count only if we
// are running on a DC
//
// see bug# 327474
//
if (LsapProductType == NtProductLanManNt)
{
LsapDbState.PolicyModificationInfo.ModifiedId.QuadPart +=
Increment.QuadPart;
}
if ( FLAG_ON( Options, LSAP_DB_READ_ONLY_TRANSACTION ) ) {
Status = LsapRegOpenTransaction();
if ( !NT_SUCCESS( Status ) ) {
goto Cleanup;
}
Options &= ~LSAP_DB_READ_ONLY_TRANSACTION;
}
Status = LsapDbWriteAttributeObject( LsapDbHandle,
&LsapDbNames[ PolMod ],
&LsapDbState.PolicyModificationInfo,
(ULONG) sizeof (POLICY_MODIFICATION_INFO) );
if (!NT_SUCCESS(Status)) {
goto Cleanup;
}
Notify = TRUE;
//
// Invalidate the cache for the Policy Modification Information
//
LsapDbMakeInvalidInformationPolicy( PolicyModificationInformation );
}
} else {
Notify = FALSE;
}
//
// If there is a registry transaction in progress,
// apply it.
//
if ( !FLAG_ON( Options, LSAP_DB_READ_ONLY_TRANSACTION ) ) {
// Either we locked it or our caller did
ASSERT( LsapDbIsLocked( &LsapDbState.RegistryLock ));
//
// Apply the Registry Transaction.
//
Status = LsapRegApplyTransaction( );
if ( !NT_SUCCESS( Status ) ) {
goto Cleanup;
}
RegApplied = TRUE;
}
//
// Notify the Replicator
//
if ( Notify ) {
Status = LsapDbNotifyChangeObject( ObjectHandle, SecurityDbDeltaType );
if (!NT_SUCCESS(Status)) {
goto Cleanup;
}
}
Status = STATUS_SUCCESS;
Cleanup:
if ( !NT_SUCCESS(Status) ) {
//
// Transaction failed. Adjust in-memory copy of the Modification
// Count, noting that backing store copy is unaltered.
//
if ( RestoreModifiedId ) {
LsapDbState.PolicyModificationInfo.ModifiedId = OriginalModifiedId;
}
//
// Abort the registry transaction
// (Unless the isn't one or it has already been applied.)
//
if ( !FLAG_ON( Options, LSAP_DB_READ_ONLY_TRANSACTION ) && !RegApplied ) {
(VOID) LsapRegAbortTransaction( );
}
}
if ( RegistryLocked ) {
LsapDbLockRelease( &LsapDbState.RegistryLock );
}
if ( CurrentThreadInfo ) {
LsapClearThreadInfo();
}
return( Status );
}
NTSTATUS
LsapDbAbortTransaction(
IN ULONG Options
)
/*++
Routine Description:
This function aborts a transaction within the LSA Database.
WARNING: The Lsa Database must be in the locked state when this function
is called.
Arguments:
None.
Return Value:
NTSTATUS - Standard Nt Result Code
Result codes are those returned from the Registry Transaction
Package.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
//
// Verify that the LSA Database is locked
// (One of many locks is locked.)
// ASSERT (LsapDbIsLocked());
//
// Abort the Registry Transaction
//
if ( !FLAG_ON( Options, LSAP_DB_READ_ONLY_TRANSACTION ) ) {
ASSERT( LsapDbIsLocked( &LsapDbState.RegistryLock ));
Status = LsapRegAbortTransaction( );
ASSERT( NT_SUCCESS( Status ) );
}
if ( NT_SUCCESS( Status ) && LsapDsIsFunctionTableValid() ) {
ASSERT( LsaDsStateInfo.DsFuncTable.pAbortTransaction );
Status = (*LsaDsStateInfo.DsFuncTable.pAbortTransaction)( Options );
ASSERT( NT_SUCCESS( Status ) );
}
return ( Status );
}
BOOLEAN
LsapDbIsServerInitialized(
)
/*++
Routine Description:
This function indicates whether the Lsa Database Server is initialized.
Arguments:
None.
Return Value:
BOOLEAN - TRUE if the LSA Database Server is initialized, else FALSE.
--*/
{
if (LsapDbState.DbServerInitialized) {
return TRUE;
} else {
return FALSE;
}
}
VOID
LsapDbEnableReplicatorNotification(
)
/*++
Routine Description:
This function turns on Replicator Notification.
Arguments:
None.
Return Value:
None.
--*/
{
LsapDbState.ReplicatorNotificationEnabled = TRUE;
}
VOID
LsapDbDisableReplicatorNotification(
)
/*++
Routine Description:
This function turns off Replicator Notification.
Arguments:
None.
Return Value:
None.
--*/
{
LsapDbState.ReplicatorNotificationEnabled = FALSE;
}
VOID
LsapDbAcquireLockEx(
IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
IN ULONG Options
)
/*++
Routine Description:
This function manages the lock status of the LSA database for a given operation.
The LSA no longer grabs a global lock for all operations. Instead, access locking only
occurs for operations involving a write. Locks can be obtained for read or write, or
converted between the two.
Arguments:
ObjectTypeId - Specifies the expected object type to which the handle
relates. An error is returned if this type does not match the
type contained in the handle.
Options - Specifies optional additional actions including database state
changes to be made, or actions not to be performed.
LSAP_DB_READ_ONLY_TRANSACTION do not lock the registry lock
Return Value:
None
--*/
{
BOOLEAN RegLock = FALSE;
LsapDsDebugOut(( DEB_FTRACE, "LsapDbAcquireLockEx(%x,%x)\n",
ObjectTypeId, Options ));
ASSERT( ObjectTypeId == PolicyObject ||
ObjectTypeId == TrustedDomainObject ||
ObjectTypeId == AccountObject ||
ObjectTypeId == SecretObject ||
ObjectTypeId == NullObject ||
ObjectTypeId == AllObject );
//
// Determine what lock we're talking about
//
switch ( ObjectTypeId ) {
case PolicyObject:
LsapDbLockAcquire( &LsapDbState.PolicyLock );
RegLock = TRUE;
break;
case TrustedDomainObject:
LsapDbAcquireWriteLockTrustedDomainList();
break;
case AccountObject:
LsapDbLockAcquire( &LsapDbState.AccountLock );
RegLock = TRUE;
break;
case SecretObject:
LsapDbAcquireWriteLockTrustedDomainList();
LsapDbLockAcquire( &LsapDbState.SecretLock );
RegLock = TRUE;
break;
case NullObject:
break;
case AllObject:
LsapDbLockAcquire( &LsapDbState.PolicyLock );
LsapDbAcquireWriteLockTrustedDomainList();
LsapDbLockAcquire( &LsapDbState.AccountLock );
LsapDbLockAcquire( &LsapDbState.SecretLock );
RegLock = TRUE;
break;
default:
goto AcquireLockExExit;
}
//
// See about the registry lock. Only take it after holding an object type lock.
//
if ( RegLock &&
!FLAG_ON( Options, LSAP_DB_READ_ONLY_TRANSACTION ) ) {
LsapDbLockAcquire( &LsapDbState.RegistryLock );
}
AcquireLockExExit:
LsapDsDebugOut(( DEB_FTRACE, "LsapDbAcquireLockEx\n" ));
return;
}
VOID
LsapDbReleaseLockEx(
IN LSAP_DB_OBJECT_TYPE_ID ObjectTypeId,
IN ULONG Options
)
/*++
Routine Description:
This function releases the lock obtained in the previous function. Depending on the
state of the preliminary status, the potentially opened transaction is either aborted or
applied
Arguments:
ObjectTypeId - Specifies the expected object type to which the handle
relates. An error is returned if this type does not match the
type contained in the handle.
Options - Specifies optional additional actions including database state
changes to be made, or actions not to be performed.
LSAP_DB_READ_ONLY_TRANSACTION do not release the registry lock
Return Value:
None
--*/
{
BOOLEAN RegLock = FALSE;
LsapDsDebugOut(( DEB_FTRACE, "LsapDbReleaseLockEx(%x,%x)\n",
ObjectTypeId, Options ));
//
// Special-case check until reference count handling logic is fixed,
// then it should go away.
//
if ( FLAG_ON( Options, LSAP_DB_NO_LOCK ) && !FLAG_ON( Options, LSAP_DB_LOCK ) ) {
goto ReleaseLockExExit;
}
ASSERT( ObjectTypeId == PolicyObject ||
ObjectTypeId == TrustedDomainObject ||
ObjectTypeId == AccountObject ||
ObjectTypeId == SecretObject ||
ObjectTypeId == NullObject ||
ObjectTypeId == AllObject );
//
// Determine what lock we're talking about
//
switch ( ObjectTypeId ) {
case PolicyObject:
LsapDbLockRelease( &LsapDbState.PolicyLock );
RegLock = TRUE;
break;
case TrustedDomainObject:
LsapDbReleaseLockTrustedDomainList();
break;
case AccountObject:
LsapDbLockRelease( &LsapDbState.AccountLock );
RegLock = TRUE;
break;
case SecretObject:
LsapDbReleaseLockTrustedDomainList();
LsapDbLockRelease( &LsapDbState.SecretLock );
RegLock = TRUE;
break;
case NullObject:
break;
case AllObject:
LsapDbLockRelease( &LsapDbState.PolicyLock );
LsapDbReleaseLockTrustedDomainList();
LsapDbLockRelease( &LsapDbState.AccountLock );
LsapDbLockRelease( &LsapDbState.SecretLock );
RegLock = TRUE;
break;
default:
goto ReleaseLockExExit;
}
//
// See about the registry lock
//
if ( !FLAG_ON( Options, LSAP_DB_READ_ONLY_TRANSACTION ) && RegLock ) {
#if DBG
HANDLE CurrentThread =(HANDLE) (NtCurrentTeb())->ClientId.UniqueThread;
ASSERT( LsapDbState.RegistryLock.CriticalSection.OwningThread==CurrentThread);
ASSERT( LsapDbIsLocked(&LsapDbState.RegistryLock));
#endif
ASSERT( LsapDbState.RegistryTransactionOpen == FALSE );
LsapDbLockRelease( &LsapDbState.RegistryLock );
}
ReleaseLockExExit:
LsapDsDebugOut(( DEB_FTRACE, "LsapDbReleaseLockEx\n" ));
return;
}
PLSADS_PER_THREAD_INFO
LsapCreateThreadInfo(
VOID
)
/*++
Routine Description:
This function will create a thread info structure to be used to maintain state on
the current operation while a ds/registry operation is happening
If a thread info is currently active on the thread, it's ref count is incremented
Arguments:
NONE
Return Value:
Created thread info on success
NULL on failure
--*/
{
PLSADS_PER_THREAD_INFO CurrentThreadInfo = NULL;
CurrentThreadInfo = TlsGetValue( LsapDsThreadState );
//
// If we have a current operation state, increment it's use count so we know how many
// times we have been called...
//
if ( CurrentThreadInfo ) {
CurrentThreadInfo->UseCount++;
} else {
//
// Have to allocate one
//
CurrentThreadInfo = LsapAllocateLsaHeap( sizeof( LSADS_PER_THREAD_INFO ) );
if ( CurrentThreadInfo ) {
if ( TlsSetValue( LsapDsThreadState, CurrentThreadInfo ) == FALSE ) {
LsapDsDebugOut(( DEB_ERROR,
"TlsSetValue for %p on %lu failed with %lu\n",
CurrentThreadInfo,
GetCurrentThreadId(),
GetLastError() ));
LsapFreeLsaHeap( CurrentThreadInfo );
CurrentThreadInfo = NULL;
} else {
RtlZeroMemory( CurrentThreadInfo, sizeof( LSADS_PER_THREAD_INFO ) );
CurrentThreadInfo->UseCount++;
#if DBG
//
// Add ourselves to the list
//
SafeAcquireResourceExclusive( &LsapDsThreadInfoListResource, TRUE );
{
ULONG i;
BOOLEAN Inserted = FALSE;
for (i = 0; i < LSAP_THREAD_INFO_LIST_MAX; i++ ) {
ASSERT( LsapDsThreadInfoList[ i ].ThreadId != GetCurrentThreadId( ));
if ( LsapDsThreadInfoList[ i ].ThreadInfo == NULL ) {
LsapDsThreadInfoList[ i ].ThreadInfo = CurrentThreadInfo;
LsapDsThreadInfoList[ i ].ThreadId = GetCurrentThreadId( );
Inserted = TRUE;
break;
}
}
if ( !Inserted ) {
LsapDsDebugOut(( DEB_ERROR,
"Failed to insert THREAD_INFO %p in list for %lu: "
"List full\n",
CurrentThreadInfo,
GetCurrentThreadId() ));
}
}
SafeReleaseResource( &LsapDsThreadInfoListResource );
#endif
}
}
}
return( CurrentThreadInfo );
}
VOID
LsapClearThreadInfo(
VOID
)
/*++
Routine Description:
This function will remove a thread info structure to be used to maintain state on
the current operation while a ds/registry operation is happening
If a thread info's ref count is greater than 1, the ref count is decremented, but the
thread info remains
Arguments:
NONE
Return Value:
VOID
--*/
{
PLSADS_PER_THREAD_INFO CurrentThreadInfo = NULL;
NTSTATUS Status;
CurrentThreadInfo = TlsGetValue( LsapDsThreadState );
//
// No thread info, nothing to do
//
if ( CurrentThreadInfo ) {
if ( CurrentThreadInfo->UseCount > 1 ) {
CurrentThreadInfo->UseCount--;
} else {
ASSERT( CurrentThreadInfo->UseCount == 1 );
if ( CurrentThreadInfo->DsTransUseCount != 0 ) {
ASSERT( CurrentThreadInfo->DsTransUseCount == 0 );
LsapDsDebugOut(( DEB_ERROR,
"Aborting transaction inside cleanup!\n" ));
LsapDsCauseTransactionToCommitOrAbort( FALSE );
}
if ( CurrentThreadInfo->DsThreadStateUseCount != 0 ) {
ASSERT( CurrentThreadInfo->DsThreadStateUseCount == 0 );
LsapDsDebugOut(( DEB_ERROR,
"Clear DS thread state inside cleanup!\n" ));
Status = LsapDsMapDsReturnToStatus( THDestroy( ) );
ASSERT( NT_SUCCESS( Status ) );
THRestore( CurrentThreadInfo->InitialThreadState );
CurrentThreadInfo->InitialThreadState = NULL;
CurrentThreadInfo->DsThreadStateUseCount = 0;
}
#if DBG
//
// Remove ourselves from the list
//
SafeAcquireResourceExclusive( &LsapDsThreadInfoListResource, TRUE );
{
ULONG i;
for (i = 0; i < LSAP_THREAD_INFO_LIST_MAX; i++ ) {
if ( LsapDsThreadInfoList[ i ].ThreadId == GetCurrentThreadId( ) ) {
ASSERT( LsapDsThreadInfoList[ i ].ThreadInfo == CurrentThreadInfo );
LsapDsThreadInfoList[ i ].ThreadInfo = NULL;
LsapDsThreadInfoList[ i ].ThreadId = 0;
break;
}
}
}
SafeReleaseResource( &LsapDsThreadInfoListResource );
#endif
//
// Clear the entry out of the thread local storage
//
if ( TlsSetValue( LsapDsThreadState, NULL ) == FALSE ) {
LsapDsDebugOut(( DEB_ERROR,
"Failed to remove %p for thread %lu: %lu\n",
CurrentThreadInfo,
GetCurrentThreadId(),
GetLastError() ));
}
LsapFreeLsaHeap( CurrentThreadInfo );
}
}
}
VOID
LsapSaveDsThreadState(
VOID
)
/*++
Routine Description:
This function will save off the current DS thread state that may exist at the time
the function is called. It does not distinguish between a thread state created by
an outside caller (say SAM), or one created by Lsa itself
If a thread info block does not exist at the time this function is called, nothing
is done
Calling this function refcounts the thread info
Arguments:
NONE
Return Value:
VOID
--*/
{
PLSADS_PER_THREAD_INFO CurrentThreadInfo = NULL;
CurrentThreadInfo = TlsGetValue( LsapDsThreadState );
//
// No thread info, nothing to do
//
if ( CurrentThreadInfo ) {
ASSERT( CurrentThreadInfo->UseCount > 0 );
CurrentThreadInfo->UseCount++;
ASSERT( !CurrentThreadInfo->SavedTransactionValid );
CurrentThreadInfo->SavedTransactionValid = TRUE;
CurrentThreadInfo->SavedThreadState = THSave();
}
}
VOID
LsapRestoreDsThreadState(
VOID
)
/*++
Routine Description:
This function will restore a previously saved DS thread state
If a thread info block does not exist at the time this function is called or there is
no previously saved state exists, nothing is done
Calling this function refcounts the thread info
Arguments:
NONE
Return Value:
VOID
--*/
{
PLSADS_PER_THREAD_INFO CurrentThreadInfo = NULL;
CurrentThreadInfo = TlsGetValue( LsapDsThreadState );
//
// No thread info, nothing to do
//
if ( CurrentThreadInfo ) {
CurrentThreadInfo->UseCount--;
ASSERT( CurrentThreadInfo->UseCount > 0 );
if ( CurrentThreadInfo->SavedTransactionValid == TRUE ) {
CurrentThreadInfo->SavedTransactionValid = FALSE;
if ( CurrentThreadInfo->SavedThreadState ) {
THRestore( CurrentThreadInfo->SavedThreadState );
}
CurrentThreadInfo->SavedThreadState = NULL;
}
}
}
VOID
LsapServerRpcThreadReturnNotify(
LPWSTR CallingFunction
)
/*++
Routine Description:
This API is called when an RPC thread which has a notify routine specified in the servers
ACF file.
Arguments:
NONE
Return Values:
NONE
--*/
{
#if DBG
static BOOLEAN CleanAsRequired = TRUE;
PLSADS_PER_THREAD_INFO CurrentThreadInfo = NULL;
NTSTATUS Status;
HANDLE ThreadHandle = GetCurrentThread();
if ( ( LsaDsInitState == LsapDsNoDs ) ||
( LsaDsInitState == LsapDsUnknown ) )
{
return ;
}
CurrentThreadInfo = TlsGetValue( LsapDsThreadState );
ASSERT( CurrentThreadInfo == NULL );
if ( CurrentThreadInfo ) {
LsapDsDebugOut(( DEB_ERROR, "ThreadInfo left by %ws\n", CallingFunction ));
LsapClearThreadInfo();
}
ASSERT( !THQuery() );
if ( THQuery() ) {
LsapDsDebugOut(( DEB_ERROR,
"Open threadstate in cleanup. Aborting...\n" ));
if ( SampExistsDsTransaction() ) {
LsapDsDebugOut(( DEB_ERROR, "Ds transaction left by %ws\n", CallingFunction ));
LsapDsCauseTransactionToCommitOrAbort( FALSE );
THDestroy( );
}
}
//
// Make sure we are not holding any of the locks when we exit
//
#if 0
ASSERT( ThreadHandle != LsapDbState.AccountLock.ExclusiveOwnerThread );
ASSERT( ThreadHandle != LsapDbState.PolicyLock.ExclusiveOwnerThread );
ASSERT( ThreadHandle != LsapDbState.SecretLock.ExclusiveOwnerThread );
ASSERT( ThreadHandle != LsapDbState.RegistryLock.ExclusiveOwnerThread );
#endif
#endif
UNREFERENCED_PARAMETER( CallingFunction );
}
NTSTATUS
LsaIHealthCheck(
IN OPTIONAL LSAPR_HANDLE DomainHandle,
IN ULONG StateChange,
IN OUT PVOID StateChangeData,
IN OUT PULONG StateChangeDataLength
)
/*++
Routine Description:
This function is actually invoked by Sam to indicate that state of interest to the Lsa
has changed, and provide that state to the Lsa. Specifically, currently, it is the Sam
SessionKey
This function USED to perform sanity checks within LSA. It was invoked from
SAM on a regular basis. However, it was no longer needed. Instead, we took the
function, leaving the appropriate export from lsasrv.dll, to obsfucate the fact that
we are now using to pass the Sam encryption key back and forth...
Arguments:
DomainHandle - What domain this refers to. Null means the root domain
StateChange - What Sam/other in process client state changed that LSA cares about. Can be:
LSAI_SAM_STATE_SESS_KEY - SAM's session key has changed
StateChangeData - What data has changed. Dependent on the type of the state change. The
data format must be pre-agreed upon by the Lsa and the invoker.
Return Values:
None.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
UNICODE_STRING CipherKey;
LsapEnterFunc( "LsaIHealthCheck" );
UNREFERENCED_PARAMETER( DomainHandle );
switch ( StateChange ) {
case LSAI_SAM_STATE_SESS_KEY:
//
// Copy the syskey into memory
//
ASSERT(LSAP_SYSKEY_SIZE==*StateChangeDataLength);
LsapDbSetSyskey(StateChangeData, LSAP_SYSKEY_SIZE);
//
// Now do a database upgrade if necessary
//
Status = LsapDbUpgradeRevision(TRUE, FALSE);
break;
case LSAI_SAM_STATE_UNROLL_SP4_ENCRYPTION:
CipherKey.Length = CipherKey.MaximumLength = (USHORT)*StateChangeDataLength;
CipherKey.Buffer = StateChangeData;
Status = LsapDbInitializeCipherKey( &CipherKey,
&LsapDbSP4SecretCipherKey );
break;
case LSAI_SAM_STATE_RETRIEVE_SESS_KEY:
//
// Return the syskey as part of state change data
//
if (NULL!=LsapDbSysKey)
{
RtlCopyMemory(StateChangeData, LsapDbSysKey, LSAP_SYSKEY_SIZE);
*StateChangeDataLength = LSAP_SYSKEY_SIZE;
}
else
{
Status = STATUS_UNSUCCESSFUL;
}
break;
case LSAI_SAM_STATE_CLEAR_SESS_KEY:
//
// Clear the syskey in memory
//
RtlZeroMemory(LsapDbSysKey,LSAP_SYSKEY_SIZE);
LsapDbSysKey = NULL;
break;
case LSAI_SAM_GENERATE_SESS_KEY:
//
// Generate a new syskey and perform the database upgrade
//
Status = LsapDbUpgradeRevision(TRUE,TRUE);
break;
case LSAI_SAM_STATE_OLD_SESS_KEY:
//
// Return the old syskey as part of state change data
//
if (NULL!=LsapDbOldSysKey)
{
RtlCopyMemory(StateChangeData, LsapDbOldSysKey, LSAP_SYSKEY_SIZE);
*StateChangeDataLength = LSAP_SYSKEY_SIZE;
}
else
{
Status = STATUS_UNSUCCESSFUL; } break; default: LsapDsDebugOut(( DEB_ERROR, "Unhandled state change %lu\n", StateChange )); break; } LsapExitFunc( "LsaIHealthCheck", Status ); return(Status); }