/*++ Copyright (c) 1987-1997 Microsoft Corporation Module Name: changelg.c Abstract: Change Log implementation. This file implements the change log. It is isolated in this file because it has several restrictions. * The globals maintained by this module are initialized during netlogon.dll process attach. They are cleaned up netlogon.dll process detach. * These procedures are used by SAM, LSA, and the netlogon service. The LSA should be the first to load netlogon.dll. It should then immediately call I_NetNotifyRole before allowing SAM or the netlogon service to start. * These procedures cannot use any globals initialized by the netlogon service. Author: Ported from Lan Man 2.0 Environment: User mode only. Contains NT-specific code. Requires ANSI C extensions: slash-slash comments, long external names. Revision History: 22-Jul-1991 (cliffv) Ported to NT. Converted to NT style. 02-Jan-1992 (madana) added support for builtin/multidomain replication. 04-Apr-1992 (madana) Added support for LSA replication. --*/ // // Common include files. // #include "logonsrv.h" // Include files common to entire service #pragma hdrstop // // Include files specific to this .c file // #include // USE_WIN32_CONFIG (if defined), etc. // // Globals defining change log worker thread. // HANDLE NlGlobalChangeLogWorkerThreadHandle; BOOL NlGlobalChangeLogWorkerIsRunning; BOOL NlGlobalChangeLogNotifyBrowser; BOOL NlGlobalChangeLogNotifyBrowserIsRunning; BOOL IsChangeLogWorkerRunning( VOID ); VOID NlChangeLogWorker( IN LPVOID ChangeLogWorkerParam ) /*++ Routine Description: This thread performs any long term operations that: A) must happen even though netlogon isn't up, and B) cannot happen in the context of an LSA or SAM notification. Arguments: None. Return Value: --*/ { NET_API_STATUS NetStatus; NTSTATUS Status; LPWSTR NewDomainName; NlPrint((NL_CHANGELOG, "ChangeLogWorker Thread is starting \n")); // // Loop until there is no more work to do. // LOCK_CHANGELOG(); for (;;) { // // Handle the domain being renamed. // if ( NlGlobalChangeLogNotifyBrowser ) { NlGlobalChangeLogNotifyBrowser = FALSE; NlGlobalChangeLogNotifyBrowserIsRunning = TRUE; UNLOCK_CHANGELOG(); NetStatus = NetpGetDomainName( &NewDomainName ); if ( NetStatus == NO_ERROR ) { // // Tell the bowser about the new domain name // Status = NlBrowserRenameDomain( NULL, NewDomainName ); if ( !NT_SUCCESS(Status) ) { NlPrint(( NL_CRITICAL, "ChangeLogWorker: Browser won't rename domain: %lx\n", Status )); } // // Free the domain name. // NetApiBufferFree( NewDomainName ); } else { NlPrint(( NL_CRITICAL, "ChangeLogWorker cannot get new domain name: %ld\n", NetStatus )); } LOCK_CHANGELOG(); NlGlobalChangeLogNotifyBrowserIsRunning = FALSE; // // If there is nothing more to do, // exit the thread. // } else { NlPrint((NL_CHANGELOG, "ChangeLogWorker Thread is exiting \n")); NlGlobalChangeLogWorkerIsRunning = FALSE; break; } } UNLOCK_CHANGELOG(); return; UNREFERENCED_PARAMETER( ChangeLogWorkerParam ); } BOOL NlStartChangeLogWorkerThread( VOID ) /*++ Routine Description: Start the Change Log Worker thread if it is not already running. Enter with NlGlobalChangeLogCritSect locked. Arguments: None. Return Value: None. --*/ { DWORD ThreadHandle; // // If the worker thread is already running, do nothing. // if ( IsChangeLogWorkerRunning() ) { return FALSE; } NlGlobalChangeLogWorkerThreadHandle = CreateThread( NULL, // No security attributes 0, (LPTHREAD_START_ROUTINE) NlChangeLogWorker, NULL, 0, // No special creation flags &ThreadHandle ); if ( NlGlobalChangeLogWorkerThreadHandle == NULL ) { // // ?? Shouldn't we do something in non-debug case // NlPrint((NL_CRITICAL, "Can't create change log worker thread %lu\n", GetLastError() )); return FALSE; } NlGlobalChangeLogWorkerIsRunning = TRUE; return TRUE; } VOID NlStopChangeLogWorker( VOID ) /*++ Routine Description: Stops the worker thread if it is running and waits for it to stop. Arguments: NONE Return Value: NONE --*/ { // // Determine if the worker thread is already running. // if ( NlGlobalChangeLogWorkerThreadHandle != NULL ) { // // We've asked the worker to stop. It should do so soon. // Wait for it to stop. // NlWaitForSingleObject( "Wait for worker to stop", NlGlobalChangeLogWorkerThreadHandle ); CloseHandle( NlGlobalChangeLogWorkerThreadHandle ); NlGlobalChangeLogWorkerThreadHandle = NULL; } return; } BOOL IsChangeLogWorkerRunning( VOID ) /*++ Routine Description: Test if the change log worker thread is running Enter with NlGlobalChangeLogCritSect locked. Arguments: NONE Return Value: TRUE - if the worker thread is running. FALSE - if the worker thread is not running. --*/ { DWORD WaitStatus; // // Determine if the worker thread is already running. // if ( NlGlobalChangeLogWorkerThreadHandle != NULL ) { // // Time out immediately if the worker is still running. // WaitStatus = WaitForSingleObject( NlGlobalChangeLogWorkerThreadHandle, 0 ); if ( WaitStatus == WAIT_TIMEOUT ) { // // Handle the case that the thread has finished // processing, but is in the process of exitting. // if ( !NlGlobalChangeLogWorkerIsRunning ) { NlStopChangeLogWorker(); return FALSE; } return TRUE; } else if ( WaitStatus == 0 ) { CloseHandle( NlGlobalChangeLogWorkerThreadHandle ); NlGlobalChangeLogWorkerThreadHandle = NULL; return FALSE; } else { NlPrint((NL_CRITICAL, "Cannot WaitFor Change Log Worker thread: %ld\n", WaitStatus )); return TRUE; } } return FALSE; } VOID NlWaitForChangeLogBrowserNotify( VOID ) /*++ Routine Description: Wait for up 20 seconds for the change log worker thread to finish the browser notification on the domain join. Arguments: None Return Value: None --*/ { ULONG WaitCount = 0; // // Wait for 20 seconds max. This is a rare operation, // so just polling periodically is not too bad here. // LOCK_CHANGELOG(); while ( WaitCount < 40 && (NlGlobalChangeLogNotifyBrowser || NlGlobalChangeLogNotifyBrowserIsRunning) ) { if ( WaitCount == 0 ) { NlPrint(( NL_MISC, "NlWaitForChangeLogBrowserNotify: Waiting for change log worker to exit\n" )); } // // Sleep half a second // UNLOCK_CHANGELOG(); Sleep( 500 ); LOCK_CHANGELOG(); WaitCount ++; } UNLOCK_CHANGELOG(); if ( WaitCount == 40 ) { NlPrint(( NL_CRITICAL, "NlWaitForChangeLogBrowserNotify: Couldn't wait for change log worker exit\n" )); } } NTSTATUS NlSendChangeLogNotification( IN enum CHANGELOG_NOTIFICATION_TYPE EntryType, IN PUNICODE_STRING ObjectName, IN PSID ObjectSid, IN ULONG ObjectRid, IN GUID *ObjectGuid, IN GUID *DomainGuid, IN PUNICODE_STRING DomainName ) /*++ Routine Description: Put a ChangeLog Notification entry for netlogon to pick up. Arguments: EntryType - The type of the entry being inserted ObjectName - The name of the account being changed. ObjectSid - Sid of the account be changed. ObjectRid - Rid of the object being changed. ObjectGuid - Guid of the object being changed. DomainGuid - Guid of the domain the object is in DomainName - Name of the domain the object is in Return Value: Status of the operation. --*/ { PCHANGELOG_NOTIFICATION Notification; LPBYTE Where; ULONG SidSize = 0; ULONG NameSize = 0; ULONG DomainNameSize = 0; ULONG Size; // // If the netlogon service isn't running (or at least starting), // don't queue messages to it. // if( NlGlobalChangeLogNetlogonState == NetlogonStopped ) { return STATUS_SUCCESS; } // // Allocate a buffer for the object name. // if ( ObjectSid != NULL ) { SidSize = RtlLengthSid( ObjectSid ); } if ( ObjectName != NULL ) { NameSize = ObjectName->Length + sizeof(WCHAR); } if ( DomainName != NULL ) { DomainNameSize = DomainName->Length + sizeof(WCHAR); } Size = sizeof(*Notification) + SidSize + NameSize + DomainNameSize; Size = ROUND_UP_COUNT( Size, ALIGN_WORST ); Notification = NetpMemoryAllocate( Size ); if ( Notification == NULL ) { return STATUS_NO_MEMORY; } RtlZeroMemory( Notification, Size ); Notification->EntryType = EntryType; Notification->ObjectRid = ObjectRid; Where = (LPBYTE) (Notification + 1); // // Copy the object sid into the buffer. // if ( ObjectSid != NULL ) { RtlCopyMemory( Where, ObjectSid, SidSize ); Notification->ObjectSid = (PSID) Where; Where += SidSize; } else { Notification->ObjectSid = NULL; } // // Copy the object name into the buffer. // if ( ObjectName != NULL ) { Where = ROUND_UP_POINTER( Where, ALIGN_WCHAR ); RtlCopyMemory( Where, ObjectName->Buffer, ObjectName->Length ); ((LPWSTR)Where)[ObjectName->Length/sizeof(WCHAR)] = L'\0'; RtlInitUnicodeString( &Notification->ObjectName, (LPWSTR)Where); Where += NameSize; } else { RtlInitUnicodeString( &Notification->ObjectName, NULL); } // // Copy the domain name into the buffer. // if ( DomainName != NULL ) { Where = ROUND_UP_POINTER( Where, ALIGN_WCHAR ); RtlCopyMemory( Where, DomainName->Buffer, DomainName->Length ); ((LPWSTR)Where)[DomainName->Length/sizeof(WCHAR)] = L'\0'; RtlInitUnicodeString( &Notification->DomainName, (LPWSTR)Where); Where += DomainNameSize; } else { RtlInitUnicodeString( &Notification->DomainName, NULL); } // // Copy the GUIDs into the buffer // if ( ObjectGuid != NULL) { Notification->ObjectGuid = *ObjectGuid; } if ( DomainGuid != NULL) { Notification->DomainGuid = *DomainGuid; } // // Indicate we're about to send the event. // #if NETLOGONDBG EnterCriticalSection( &NlGlobalLogFileCritSect ); NlPrint((NL_CHANGELOG, "NlSendChangeLogNotification: sent %ld for", Notification->EntryType )); if ( ObjectName != NULL ) { NlPrint((NL_CHANGELOG, " %wZ", ObjectName)); } if ( DomainName != NULL ) { NlPrint((NL_CHANGELOG, " Dom:%wZ", DomainName)); } if ( ObjectRid != 0 ) { NlPrint((NL_CHANGELOG, " Rid:0x%lx", ObjectRid )); } if ( ObjectSid != NULL ) { NlPrint((NL_CHANGELOG, " Sid:" )); NlpDumpSid( NL_CHANGELOG, ObjectSid ); } if ( ObjectGuid != NULL ) { NlPrint((NL_CHANGELOG, " Obj Guid:" )); NlpDumpGuid( NL_CHANGELOG, ObjectGuid ); } if ( DomainGuid != NULL ) { NlPrint((NL_CHANGELOG, " Dom Guid:" )); NlpDumpGuid( NL_CHANGELOG, DomainGuid ); } NlPrint((NL_CHANGELOG, "\n" )); LeaveCriticalSection( &NlGlobalLogFileCritSect ); #endif // NETLOGONDBG // // Insert the entry into the list // LOCK_CHANGELOG(); InsertTailList( &NlGlobalChangeLogNotifications, &Notification->Next ); UNLOCK_CHANGELOG(); if ( !SetEvent( NlGlobalChangeLogEvent ) ) { NlPrint((NL_CRITICAL, "Cannot set ChangeLog event: %lu\n", GetLastError() )); } return STATUS_SUCCESS; } NTSTATUS I_NetNotifyDelta ( IN SECURITY_DB_TYPE DbType, IN LARGE_INTEGER SerialNumber, IN SECURITY_DB_DELTA_TYPE DeltaType, IN SECURITY_DB_OBJECT_TYPE ObjectType, IN ULONG ObjectRid, IN PSID ObjectSid, IN PUNICODE_STRING ObjectName, IN DWORD ReplicateImmediately, IN PSAM_DELTA_DATA MemberId ) /*++ Routine Description: This function is called by the SAM and LSA services after each change is made to the SAM and LSA databases. The services describe the type of object that is modified, the type of modification made on the object, the serial number of this modification etc. This information is stored for later retrieval when a BDC or member server wants a copy of this change. See the description of I_NetSamDeltas for a description of how the change log is used. Add a change log entry to circular change log maintained in cache as well as on the disk and update the head and tail pointers It is assumed that Tail points to a block where this new change log entry may be stored. Arguments: DbType - Type of the database that has been modified. SerialNumber - The value of the DomainModifiedCount field for the domain following the modification. DeltaType - The type of modification that has been made on the object. ObjectType - The type of object that has been modified. ObjectRid - The relative ID of the object that has been modified. This parameter is valid only when the object type specified is either SecurityDbObjectSamUser, SecurityDbObjectSamGroup or SecurityDbObjectSamAlias otherwise this parameter is set to zero. ObjectSid - The SID of the object that has been modified. If the object modified is in a SAM database, ObjectSid is the DomainId of the Domain containing the object. ObjectName - The name of the secret object when the object type specified is SecurityDbObjectLsaSecret or the old name of the object when the object type specified is either SecurityDbObjectSamUser, SecurityDbObjectSamGroup or SecurityDbObjectSamAlias and the delta type is SecurityDbRename otherwise this parameter is set to NULL. ReplicateImmediately - TRUE if the change should be immediately replicated to all BDCs. A password change should set the flag TRUE. MemberId - This parameter is specified when group/alias membership is modified. This structure will then point to the member's ID that has been updated. Return Value: STATUS_SUCCESS - The Service completed successfully. --*/ { NTSTATUS Status; CHANGELOG_ENTRY ChangeLogEntry; NETLOGON_DELTA_TYPE NetlogonDeltaType; USHORT Flags = 0; // // Ensure the role is right. Otherwise, all the globals used below // aren't initialized. // if ( NlGlobalChangeLogRole != ChangeLogPrimary && NlGlobalChangeLogRole != ChangeLogBackup ) { return STATUS_INVALID_DOMAIN_ROLE; } // // Also make sure that the change log cache is available. // if ( NlGlobalChangeLogDesc.Buffer == NULL ) { return STATUS_INVALID_DOMAIN_ROLE; } // // Determine the database index. // if( DbType == SecurityDbLsa ) { ChangeLogEntry.DBIndex = LSA_DB; } else if( DbType == SecurityDbSam ) { if ( RtlEqualSid( ObjectSid, NlGlobalChangeLogBuiltinDomainSid )) { ChangeLogEntry.DBIndex = BUILTIN_DB; } else { ChangeLogEntry.DBIndex = SAM_DB; } // // For the SAM database, we no longer need the ObjectSid. // Null out the pointer to prevent us from storing it in the // changelog. // ObjectSid = NULL; } else { // // unknown database, do nothing. // NlPrint((NL_CRITICAL, "I_NetNotifyDelta: Unknown database: %ld\n", DbType )); return STATUS_SUCCESS; } // // Map object type and delta type to NetlogonDeltaType // switch( ObjectType ) { case SecurityDbObjectLsaPolicy: switch (DeltaType) { case SecurityDbNew: case SecurityDbChange: NetlogonDeltaType = AddOrChangeLsaPolicy; break; // unknown delta type default: NlPrint((NL_CRITICAL, "I_NetNotifyDelta: Unknown deltatype for policy: %ld\n", DeltaType )); return STATUS_SUCCESS; } break; case SecurityDbObjectLsaTDomain: switch (DeltaType) { case SecurityDbNew: case SecurityDbChange: NetlogonDeltaType = AddOrChangeLsaTDomain; break; case SecurityDbDelete: NetlogonDeltaType = DeleteLsaTDomain; break; // unknown delta type default: NlPrint((NL_CRITICAL, "I_NetNotifyDelta: Unknown deltatype for tdomain: %ld\n", DeltaType )); return STATUS_SUCCESS; } break; case SecurityDbObjectLsaAccount: switch (DeltaType) { case SecurityDbNew: case SecurityDbChange: NetlogonDeltaType = AddOrChangeLsaAccount; break; case SecurityDbDelete: NetlogonDeltaType = DeleteLsaAccount; break; // unknown delta type default: NlPrint((NL_CRITICAL, "I_NetNotifyDelta: Unknown deltatype for lsa account: %ld\n", DeltaType )); return STATUS_SUCCESS; } break; case SecurityDbObjectLsaSecret: switch (DeltaType) { case SecurityDbNew: case SecurityDbChange: NetlogonDeltaType = AddOrChangeLsaSecret; break; case SecurityDbDelete: NetlogonDeltaType = DeleteLsaSecret; break; // unknown delta type default: NlPrint((NL_CRITICAL, "I_NetNotifyDelta: Unknown deltatype for lsa secret: %ld\n", DeltaType )); return STATUS_SUCCESS; } break; case SecurityDbObjectSamDomain: switch (DeltaType) { case SecurityDbNew: case SecurityDbChange: NetlogonDeltaType = AddOrChangeDomain; break; // unknown delta type default: NlPrint((NL_CRITICAL, "I_NetNotifyDelta: Unknown deltatype for sam domain: %ld\n", DeltaType )); return STATUS_SUCCESS; } break; case SecurityDbObjectSamUser: switch (DeltaType) { case SecurityDbChangePassword: case SecurityDbNew: case SecurityDbChange: case SecurityDbRename: NetlogonDeltaType = AddOrChangeUser; break; case SecurityDbDelete: NetlogonDeltaType = DeleteUser; break; // // unknown delta type // default: NlPrint((NL_CRITICAL, "I_NetNotifyDelta: Unknown deltatype for sam user: %ld\n", DeltaType )); return STATUS_SUCCESS; } break; case SecurityDbObjectSamGroup: switch ( DeltaType ) { case SecurityDbNew: case SecurityDbChange: case SecurityDbRename: case SecurityDbChangeMemberAdd: case SecurityDbChangeMemberSet: case SecurityDbChangeMemberDel: NetlogonDeltaType = AddOrChangeGroup; break; case SecurityDbDelete: NetlogonDeltaType = DeleteGroup; break; // // unknown delta type // default: NlPrint((NL_CRITICAL, "I_NetNotifyDelta: Unknown deltatype for sam group: %ld\n", DeltaType )); return STATUS_SUCCESS; } break; case SecurityDbObjectSamAlias: switch (DeltaType) { case SecurityDbNew: case SecurityDbChange: case SecurityDbRename: case SecurityDbChangeMemberAdd: case SecurityDbChangeMemberSet: case SecurityDbChangeMemberDel: NetlogonDeltaType = AddOrChangeAlias; break; case SecurityDbDelete: NetlogonDeltaType = DeleteAlias; break; // unknown delta type default: NlPrint((NL_CRITICAL, "I_NetNotifyDelta: Unknown deltatype for sam alias: %ld\n", DeltaType )); return STATUS_SUCCESS; } break; default: // unknown object type NlPrint((NL_CRITICAL, "I_NetNotifyDelta: Unknown object type: %ld\n", ObjectType )); return STATUS_SUCCESS; } // // Build the changelog entry and write it to the changelog // ChangeLogEntry.DeltaType = (UCHAR)NetlogonDeltaType; ChangeLogEntry.SerialNumber = SerialNumber; ChangeLogEntry.ObjectRid = ObjectRid; ChangeLogEntry.Flags = Flags; Status = NlWriteChangeLogEntry( &NlGlobalChangeLogDesc, &ChangeLogEntry, ObjectSid, ObjectName, TRUE ); if ( !NT_SUCCESS(Status) ) { return Status; } // // If this change requires immediate replication, do so // if( ReplicateImmediately ) { LOCK_CHANGELOG(); NlGlobalChangeLogReplicateImmediately = TRUE; UNLOCK_CHANGELOG(); if ( !SetEvent( NlGlobalChangeLogEvent ) ) { NlPrint((NL_CRITICAL, "Cannot set ChangeLog event: %lu\n", GetLastError() )); } } return STATUS_SUCCESS; UNREFERENCED_PARAMETER( MemberId ); } NTSTATUS I_NetLogonGetSerialNumber ( IN SECURITY_DB_TYPE DbType, IN PSID DomainSid, OUT PLARGE_INTEGER SerialNumber ) /*++ Routine Description: This function is called by the SAM and LSA services when they startup to get the current serial number written to the changelog. Arguments: DbType - Type of the database that has been modified. DomainSid - For the SAM and builtin database, this specifies the DomainId of the domain whose serial number is to be returned. SerialNumber - Returns the latest set value of the DomainModifiedCount field for the domain. Return Value: STATUS_SUCCESS - The Service completed successfully. STATUS_INVALID_DOMAIN_ROLE - This machine is not the PDC. --*/ { NTSTATUS Status; CHANGELOG_ENTRY ChangeLogEntry; NETLOGON_DELTA_TYPE NetlogonDeltaType; USHORT Flags = 0; ULONG DbIndex; // // Ensure the role is right. Otherwise, all the globals used below // aren't initialized. // if ( NlGlobalChangeLogRole != ChangeLogPrimary && NlGlobalChangeLogRole != ChangeLogBackup ) { NlPrint((NL_CHANGELOG, "I_NetLogonGetSerialNumber: failed 1\n" )); return STATUS_INVALID_DOMAIN_ROLE; } // // Also make sure that the change log cache is available. // if ( NlGlobalChangeLogDesc.Buffer == NULL ) { NlPrint((NL_CHANGELOG, "I_NetLogonGetSerialNumber: failed 2\n" )); return STATUS_INVALID_DOMAIN_ROLE; } // // Determine the database index. // if( DbType == SecurityDbLsa ) { DbIndex = LSA_DB; } else if( DbType == SecurityDbSam ) { if ( RtlEqualSid( DomainSid, NlGlobalChangeLogBuiltinDomainSid )) { DbIndex = BUILTIN_DB; } else { DbIndex = SAM_DB; } } else { NlPrint((NL_CHANGELOG, "I_NetLogonGetSerialNumber: failed 3\n" )); return STATUS_INVALID_DOMAIN_ROLE; } // // Return the current serial number. // SerialNumber->QuadPart = NlGlobalChangeLogDesc.SerialNumber[DbIndex].QuadPart; NlPrint((NL_CHANGELOG, "I_NetLogonGetSerialNumber: returns 0x%lx 0x%lx\n", SerialNumber->HighPart, SerialNumber->LowPart )); return STATUS_SUCCESS; } NTSTATUS NlInitChangeLogBuffer( VOID ) /*++ Routine Description: Open the change log file (netlogon.chg) for reading or writing one or more records. Create this file if it does not exist or is out of sync with the SAM database (see note below). This file must be opened for R/W (deny-none share mode) at the time the cache is initialized. If the file already exists when NETLOGON service started, its contents will be cached in its entirety provided the last change log record bears the same serial number as the serial number field in SAM database else this file will be removed and a new one created. If the change log file did not exist then it will be created. Arguments: NONE Return Value: NT Status code --*/ { NTSTATUS Status; NET_API_STATUS NetStatus; UINT WindowsDirectoryLength; WCHAR ChangeLogFile[MAX_PATH+1]; LPNET_CONFIG_HANDLE SectionHandle = NULL; DWORD NewChangeLogSize; // // Initialize // LOCK_CHANGELOG(); // // Get the size of the changelog. // // Open the NetLogon configuration section. // NewChangeLogSize = DEFAULT_CHANGELOGSIZE; NetStatus = NetpOpenConfigData( &SectionHandle, NULL, // no server name. SERVICE_NETLOGON, TRUE ); // we only want readonly access if ( NetStatus == NO_ERROR ) { (VOID) NlParseOne( SectionHandle, FALSE, // not a GP section NETLOGON_KEYWORD_CHANGELOGSIZE, DEFAULT_CHANGELOGSIZE, MIN_CHANGELOGSIZE, MAX_CHANGELOGSIZE, &NewChangeLogSize ); (VOID) NetpCloseConfigData( SectionHandle ); } NewChangeLogSize = ROUND_UP_COUNT( NewChangeLogSize, ALIGN_WORST); #ifdef notdef NlPrint((NL_INIT, "ChangeLogSize: 0x%lx\n", NewChangeLogSize )); #endif // notdef // // Build the change log file name // WindowsDirectoryLength = GetSystemWindowsDirectoryW( NlGlobalChangeLogFilePrefix, sizeof(NlGlobalChangeLogFilePrefix)/sizeof(WCHAR) ); if ( WindowsDirectoryLength == 0 ) { NlPrint((NL_CRITICAL,"Unable to get changelog file directory name, " "WinError = %ld \n", GetLastError() )); NlGlobalChangeLogFilePrefix[0] = L'\0'; goto CleanChangeLogFile; } if ( WindowsDirectoryLength * sizeof(WCHAR) + sizeof(CHANGELOG_FILE_PREFIX) + CHANGELOG_FILE_POSTFIX_LENGTH * sizeof(WCHAR) > sizeof(NlGlobalChangeLogFilePrefix) ) { NlPrint((NL_CRITICAL,"Changelog file directory name length is " "too long \n" )); NlGlobalChangeLogFilePrefix[0] = L'\0'; goto CleanChangeLogFile; } wcscat( NlGlobalChangeLogFilePrefix, CHANGELOG_FILE_PREFIX ); // // Read in the existing changelog file. // wcscpy( ChangeLogFile, NlGlobalChangeLogFilePrefix ); wcscat( ChangeLogFile, CHANGELOG_FILE_POSTFIX ); InitChangeLogDesc( &NlGlobalChangeLogDesc ); Status = NlOpenChangeLogFile( ChangeLogFile, &NlGlobalChangeLogDesc, FALSE ); if ( !NT_SUCCESS(Status) ) { goto CleanChangeLogFile; } // // Convert the changelog file to the right size/version. // Status = NlResizeChangeLogFile( &NlGlobalChangeLogDesc, NewChangeLogSize ); if ( !NT_SUCCESS(Status) ) { goto CleanChangeLogFile; } goto Cleanup; // // CleanChangeLogFile // CleanChangeLogFile: // // If we just need to start with a newly initialized file, // do it. // Status = NlResetChangeLog( &NlGlobalChangeLogDesc, NewChangeLogSize ); Cleanup: // // Free any resources on error. // if ( !NT_SUCCESS(Status) ) { NlCloseChangeLogFile( &NlGlobalChangeLogDesc ); } UNLOCK_CHANGELOG(); return Status; } NTSTATUS I_NetNotifyRole ( IN POLICY_LSA_SERVER_ROLE Role ) /*++ Routine Description: This function is called by the LSA service upon LSA initialization and when LSA changes domain role. This routine will initialize the change log cache if the role specified is PDC or delete the change log cache if the role specified is other than PDC. When this function initializing the change log if the change log currently exists on disk, the cache will be initialized from disk. LSA should treat errors from this routine as non-fatal. LSA should log the errors so they may be corrected then continue initialization. However, LSA should treat the system databases as read-only in this case. Arguments: Role - Current role of the server. Return Value: STATUS_SUCCESS - The Service completed successfully. --*/ { NTSTATUS Status = STATUS_SUCCESS; CHANGELOG_ROLE PreviousChangeLogRole; // // Change the role of the changelog itself. // Status = NetpNotifyRole ( Role ); // // Tell the netlogon service about the role change. // if ( NT_SUCCESS(Status) ) { Status = NlSendChangeLogNotification( ChangeLogRoleChanged, NULL, NULL, 0, NULL, // Object GUID, NULL, // Domain GUID, NULL ); // Domain Name } return Status; } NTSTATUS NetpNotifyRole ( IN POLICY_LSA_SERVER_ROLE Role ) /*++ Routine Description: This function is called by the LSA service upon LSA initialization and when LSA changes domain role. This routine will initialize the change log cache if the role specified is PDC or delete the change log cache if the role specified is other than PDC. When this function initializing the change log if the change log currently exists on disk, the cache will be initialized from disk. LSA should treat errors from this routine as non-fatal. LSA should log the errors so they may be corrected then continue initialization. However, LSA should treat the system databases as read-only in this case. Arguments: Role - Current role of the server. Return Value: STATUS_SUCCESS - The Service completed successfully. --*/ { NTSTATUS Status = STATUS_SUCCESS; CHANGELOG_ROLE PreviousChangeLogRole; // // If this is a workstation, simply return. // if ( NlGlobalChangeLogRole == ChangeLogMemberWorkstation ) { return STATUS_SUCCESS; } // // Set our role to the new value. // LOCK_CHANGELOG(); PreviousChangeLogRole = NlGlobalChangeLogRole; if( Role == PolicyServerRolePrimary) { NlGlobalChangeLogRole = ChangeLogPrimary; NlPrint(( NL_DOMAIN, "NetpNotifyRole: LSA setting our role to Primary.\n")); } else { NlGlobalChangeLogRole = ChangeLogBackup; NlPrint(( NL_DOMAIN, "NetpNotifyRole: LSA setting our role to Backup.\n")); } // // If the role has changed, // Delete any previous change log buffer. // (Reopen it now to resize it upon role change.) // if ( NlGlobalChangeLogRole != PreviousChangeLogRole ) { NlCloseChangeLogFile( &NlGlobalChangeLogDesc ); Status = NlInitChangeLogBuffer(); } UNLOCK_CHANGELOG(); return Status; } // // Defines a set of handles to Netlogon.dll // #if NETLOGONDBG #define MAX_NETLOGON_DLL_HANDLES 8 PHANDLE NlGlobalNetlogonDllHandles[MAX_NETLOGON_DLL_HANDLES]; ULONG NlGlobalNetlogonDllHandleCount = 0; #endif // NETLOGONDBG NTSTATUS I_NetNotifyNetlogonDllHandle ( IN PHANDLE NetlogonDllHandle ) /*++ Routine Description: Registers the fact that a service has an open handle to the NetlogonDll. This function is called by the LSA service, SAM service, and MSV1_0 authentication package when the load netlogon.dll into the lsass.exe process. Netlogon will close these handles (and NULL the handle) when it wants to unload the DLL from the lsass.exe process. The DLL is only unloaded for debugging purposes. Arguments: NetlogonDllHandle - Specifies a pointer to a handle to netlogon.dll Return Value: STATUS_SUCCESS - The Service completed successfully. --*/ { #if NETLOGONDBG LOCK_CHANGELOG(); if ( NlGlobalNetlogonDllHandleCount >= MAX_NETLOGON_DLL_HANDLES ) { NlPrint((NL_CRITICAL, "Too many Netlogon Dll handles registered.\n" )); } else { #ifdef notdef NlPrint(( NL_MISC, "I_NetNotifyNetlogonDllHandle loading 0x%lx %lx (%ld)\n", NetlogonDllHandle, *NetlogonDllHandle, NlGlobalNetlogonDllHandleCount )); #endif // notdef NlGlobalNetlogonDllHandles[NlGlobalNetlogonDllHandleCount] = NetlogonDllHandle; NlGlobalNetlogonDllHandleCount++; } UNLOCK_CHANGELOG(); #endif // NETLOGONDBG return STATUS_SUCCESS; } NET_API_STATUS NlpFreeNetlogonDllHandles ( VOID ) /*++ Routine Description: Free any curretly register NetlogonDll handles. Arguments: None. Return Value: None. --*/ { NET_API_STATUS NetStatus = NO_ERROR; #if NETLOGONDBG ULONG i; ULONG NewHandleCount = 0; LOCK_CHANGELOG(); for ( i=0; i