1674 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			1674 lines
		
	
	
		
			41 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| 
 | ||
| /*++
 | ||
| 
 | ||
| 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;
 | ||
|     ULONG StatesResetAttempted = 0;
 | ||
|     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 ) ) {
 | ||
| 
 | ||
|         StatesResetAttempted |= LSAP_DB_FINISH_TRANSACTION;
 | ||
| 
 | ||
|         if (NT_SUCCESS(PreliminaryStatus)) {
 | ||
| 
 | ||
|             Status = LsapDbApplyTransaction(
 | ||
|                          ObjectHandle,
 | ||
|                          Options,
 | ||
|                          SecurityDbDeltaType
 | ||
|                          );
 | ||
| 
 | ||
|         } else {
 | ||
| 
 | ||
|             Status = LsapDbAbortTransaction( Options );
 | ||
|         }
 | ||
| 
 | ||
|         if (!NT_SUCCESS(Status)) {
 | ||
| 
 | ||
|             goto ResetStatesError;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     //
 | ||
|     // If unlocking requested, unlock the Lsa Database.
 | ||
|     //
 | ||
| 
 | ||
|     if (Options & LSAP_DB_LOCK) {
 | ||
| 
 | ||
|         StatesResetAttempted |= LSAP_DB_LOCK;
 | ||
|         LsapDbReleaseLockEx( ObjectTypeId,
 | ||
|                              Options );
 | ||
|     }
 | ||
| 
 | ||
|     //
 | ||
|     // If unlocking if the Audit Log Queue requested, unlock the queue.
 | ||
|     //
 | ||
| 
 | ||
|     if (Options & LSAP_DB_LOG_QUEUE_LOCK) {
 | ||
| 
 | ||
|         StatesResetAttempted |= LSAP_DB_LOG_QUEUE_LOCK;
 | ||
|         LsapAdtReleaseLogFullLock();
 | ||
|     }
 | ||
| 
 | ||
|     //
 | ||
|     // The requested reset operations were performed successfully.
 | ||
|     // Propagate the preliminary status back to the caller.
 | ||
|     //
 | ||
| 
 | ||
|     Status = PreliminaryStatus;
 | ||
| 
 | ||
| ResetStatesFinish:
 | ||
| 
 | ||
|     LsapDsDebugOut(( DEB_FTRACE, "LsapDbResetStates: 0x%lx\n", Status ));
 | ||
|     return( Status );
 | ||
| 
 | ||
| ResetStatesError:
 | ||
| 
 | ||
|     //
 | ||
|     // One or more of the requested reset operations could not be performed.
 | ||
|     // Attempt to restore the database to a usable state.
 | ||
|     //
 | ||
| 
 | ||
|     LsapDbResetStatesError(
 | ||
|         ObjectHandle,
 | ||
|         PreliminaryStatus,
 | ||
|         Options,
 | ||
|         SecurityDbDeltaType,
 | ||
|         StatesResetAttempted
 | ||
|         );
 | ||
| 
 | ||
|     goto ResetStatesFinish;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| VOID
 | ||
| LsapDbResetStatesError(
 | ||
|     IN LSAPR_HANDLE ObjectHandle,
 | ||
|     IN NTSTATUS PreliminaryStatus,
 | ||
|     IN ULONG Options,
 | ||
|     IN SECURITY_DB_DELTA_TYPE SecurityDbDeltaType,
 | ||
|     IN ULONG StatesResetAttempted
 | ||
|     )
 | ||
| 
 | ||
| /*++
 | ||
| 
 | ||
| Routine Description:
 | ||
| 
 | ||
|     This function attempts to restore the Lsa Database state to a usable
 | ||
|     form after a call to LsapDbResetStates() has failed.  It will attempt
 | ||
|     resets that were not attempted by that function because an error
 | ||
|     occurred.
 | ||
| 
 | ||
| Arguments:
 | ||
| 
 | ||
|     ObjectHandle - Handle to an LSA object.  This is expected to have
 | ||
|         already been validated.
 | ||
| 
 | ||
|     PreliminaryStatus - The preliminary Result Code that the caller of
 | ||
|         LsapDbResetStates had.  This is normally propagated back by that
 | ||
|         caller.
 | ||
| 
 | ||
|     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.
 | ||
| 
 | ||
|         LSAP_DB_REBUILD_CACHE - Rebuild the cache for the object's type.
 | ||
|              Note the the cache is normally rebuilt only if the
 | ||
|              Preliminary Status was success and the Final Status was
 | ||
|              a failure.
 | ||
| 
 | ||
|     StatesResetAttempted - Specifies the state resets that were actually
 | ||
|         attempted.
 | ||
| --*/
 | ||
| 
 | ||
| {
 | ||
|     NTSTATUS SecondaryStatus = STATUS_SUCCESS;
 | ||
|     NTSTATUS IgnoreStatus;
 | ||
|     LSAP_DB_OBJECT_TYPE_ID ObjectTypeId = NullObject;
 | ||
| 
 | ||
|     //
 | ||
|     // If finishing of a database update transaction was requested but
 | ||
|     // not attempted, do it now.  SecondaryStatus is intentionally NOT
 | ||
|     // checked afterwards.
 | ||
|     //
 | ||
| 
 | ||
|     if ( FLAG_ON( Options, LSAP_DB_READ_ONLY_TRANSACTION |
 | ||
|                               LSAP_DB_NO_DS_OP_TRANSACTION |
 | ||
|                               LSAP_DB_DS_OP_TRANSACTION |
 | ||
|                               LSAP_DB_FINISH_TRANSACTION )  &&
 | ||
|         !(StatesResetAttempted & LSAP_DB_FINISH_TRANSACTION)) {
 | ||
| 
 | ||
|         if (NT_SUCCESS(PreliminaryStatus)) {
 | ||
| 
 | ||
|             SecondaryStatus = LsapDbApplyTransaction(
 | ||
|                                   ObjectHandle,
 | ||
|                                   Options,
 | ||
|                                   SecurityDbDeltaType
 | ||
|                                   );
 | ||
| 
 | ||
|         } else {
 | ||
| 
 | ||
|             SecondaryStatus = LsapDbAbortTransaction( Options );
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     //
 | ||
|     // If the PreliminaryStatus was successful, attempt to rebuild
 | ||
|     // the cache for this object type.
 | ||
|     //
 | ||
| 
 | ||
|     if (NT_SUCCESS(PreliminaryStatus)) {
 | ||
| 
 | ||
|         ObjectTypeId = ((LSAP_DB_HANDLE) ObjectHandle)->ObjectTypeId;
 | ||
| 
 | ||
|         IgnoreStatus = LsapDbRebuildCache( ObjectTypeId );
 | ||
|     }
 | ||
| 
 | ||
|     //
 | ||
|     // If an unlock of the database was requested but not attempted,
 | ||
|     // do this now.
 | ||
|     //
 | ||
| 
 | ||
|     if ((Options & LSAP_DB_LOCK) &&
 | ||
|         !(StatesResetAttempted & LSAP_DB_LOCK)) {
 | ||
| 
 | ||
|         LsapDbReleaseLockEx( ObjectTypeId,
 | ||
|                              Options );
 | ||
|     }
 | ||
| 
 | ||
| 
 | ||
|     //
 | ||
|     // If an unlock of the Audlt Log Queue was requested but not attempted,
 | ||
|     // do this now.
 | ||
|     //
 | ||
| 
 | ||
|     if ((Options & LSAP_DB_LOG_QUEUE_LOCK) &&
 | ||
|         !(StatesResetAttempted & LSAP_DB_LOG_QUEUE_LOCK)) {
 | ||
| 
 | ||
|         LsapAdtReleaseLogFullLock();
 | ||
|     }
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| 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 LSAPR_HANDLE DomainHandle OPTIONAL,
 | ||
|     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);
 | ||
| 
 | ||
| }
 |