windows-nt/Source/XPSP1/NT/ds/netapi/svcdlls/logonsrv/server/changelg.c

2408 lines
58 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
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 <configp.h> // 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<NlGlobalNetlogonDllHandleCount; i++ ) {
if ( !FreeLibrary( *(NlGlobalNetlogonDllHandles[i]) ) ) {
NetStatus = GetLastError();
NlPrint(( NL_CRITICAL,
"Cannot Free NetlogonDll handle. %ld\n",
NetStatus ));
NlGlobalNetlogonDllHandles[NewHandleCount] =
NlGlobalNetlogonDllHandles[i];
NewHandleCount++;
} else {
NlPrint(( NL_MISC,
"NlpFreeNetlogonDllHandle freed 0x%lx 0x%lx (%ld)\n",
NlGlobalNetlogonDllHandles[i],
*(NlGlobalNetlogonDllHandles[i]),
i ));
*(NlGlobalNetlogonDllHandles[i]) = NULL;
}
}
NlGlobalNetlogonDllHandleCount = NewHandleCount;
UNLOCK_CHANGELOG();
#endif // NETLOGONDBG
return NetStatus;
}
NTSTATUS
I_NetNotifyMachineAccount (
IN ULONG ObjectRid,
IN PSID DomainSid,
IN ULONG OldUserAccountControl,
IN ULONG NewUserAccountControl,
IN PUNICODE_STRING ObjectName
)
/*++
Routine Description:
This function is called by the SAM to indicate that the account type
of a machine account has changed. Specifically, if
USER_INTERDOMAIN_TRUST_ACCOUNT, USER_WORKSTATION_TRUST_ACCOUNT, or
USER_SERVER_TRUST_ACCOUNT change for a particular account, this
routine is called to let Netlogon know of the account change.
This function is called for both PDC and BDC.
Arguments:
ObjectRid - The relative ID of the object that has been modified.
DomainSid - Specifies the SID of the Domain containing the object.
OldUserAccountControl - Specifies the previous value of the
UserAccountControl field of the user.
NewUserAccountControl - Specifies the new (current) value of the
UserAccountControl field of the user.
ObjectName - The name of the account being changed.
Return Value:
Status of the operation.
--*/
{
NTSTATUS Status;
NTSTATUS SavedStatus = STATUS_SUCCESS;
//
// If the netlogon service isn't running,
// Don't bother with the coming and going of accounts.
//
if( NlGlobalChangeLogNetlogonState == NetlogonStopped ) {
return(STATUS_SUCCESS);
}
//
// If this is windows NT,
// There is nothing to maintain.
//
if ( NlGlobalChangeLogRole == ChangeLogMemberWorkstation ) {
return(STATUS_SUCCESS);
}
//
// Make available just the machine account bits.
//
OldUserAccountControl &= USER_MACHINE_ACCOUNT_MASK;
NewUserAccountControl &= USER_MACHINE_ACCOUNT_MASK;
if ( OldUserAccountControl == NewUserAccountControl ) {
return STATUS_SUCCESS;
}
//
// Handle deletion of a Workstation Trust Account
// Handle deletion of a Server Trust Account
//
if ( OldUserAccountControl == USER_SERVER_TRUST_ACCOUNT ||
OldUserAccountControl == USER_WORKSTATION_TRUST_ACCOUNT ) {
Status = NlSendChangeLogNotification( ChangeLogTrustAccountDeleted,
ObjectName,
NULL,
0,
NULL, // Object GUID,
NULL, // Domain GUID,
NULL ); // Domain Name
if ( NT_SUCCESS(SavedStatus) ) {
SavedStatus = Status;
}
}
//
// Handle creation or change of a Workstation Trust Account
// Handle creation or change of a Server Trust Account
//
// Sam is no longer capable of telling me the "previous" value of
// account control.
//
if ( NewUserAccountControl == USER_SERVER_TRUST_ACCOUNT ||
NewUserAccountControl == USER_WORKSTATION_TRUST_ACCOUNT ) {
NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType;
GUID ObjectGuid;
if ( NewUserAccountControl == USER_SERVER_TRUST_ACCOUNT ) {
SecureChannelType = ServerSecureChannel;
} else if ( NewUserAccountControl == USER_WORKSTATION_TRUST_ACCOUNT ) {
SecureChannelType = WorkstationSecureChannel;
}
RtlZeroMemory( &ObjectGuid, sizeof(ObjectGuid) );
*(PULONG)&ObjectGuid = SecureChannelType;
Status = NlSendChangeLogNotification( ChangeLogTrustAccountAdded,
ObjectName,
NULL,
ObjectRid,
&ObjectGuid, // Object GUID
NULL, // Domain GUID
NULL ); // Domain Name
if ( NT_SUCCESS(SavedStatus) ) {
SavedStatus = Status;
}
}
return SavedStatus;
UNREFERENCED_PARAMETER( DomainSid );
}
NTSTATUS
I_NetNotifyDsChange(
IN NL_DS_CHANGE_TYPE DsChangeType
)
/*++
Routine Description:
This function is called by the LSA to indicate that configuration information
in the DS has changed.
This function is called for both PDC and BDC.
Arguments:
DsChangeType - Indicates the type of information that has changed.
Return Value:
Status of the operation.
--*/
{
NTSTATUS Status;
//
// If the netlogon service isn't running,
// Don't bother with the coming and going of DS information.
//
if( NlGlobalChangeLogNetlogonState == NetlogonStopped ) {
return(STATUS_SUCCESS);
}
//
// If this is windows NT,
// There is nothing to maintain.
//
if ( NlGlobalChangeLogRole == ChangeLogMemberWorkstation ) {
return(STATUS_SUCCESS);
}
//
// If this is a notification about the DC demotion,
// just set the global boolean accordingly.
//
if ( DsChangeType == NlDcDemotionInProgress ) {
NlGlobalDcDemotionInProgress = TRUE;
return(STATUS_SUCCESS);
}
if ( DsChangeType == NlDcDemotionCompleted ) {
NlGlobalDcDemotionInProgress = FALSE;
return(STATUS_SUCCESS);
}
//
// Reset the TrustInfoUpToDate event so that any thread that wants to
// access the trust info list will wait until the info is updated (by
// the NlInitTrustList function).
//
if ( DsChangeType == NlOrgChanged ) {
if ( !ResetEvent( NlGlobalTrustInfoUpToDateEvent ) ) {
NlPrint((NL_CRITICAL,
"Cannot reset NlGlobalTrustInfoUpToDateEvent event: %lu\n",
GetLastError() ));
}
}
//
// Tell the netlogon service about the change.
//
Status = NlSendChangeLogNotification( ChangeLogDsChanged,
NULL,
NULL,
(ULONG) DsChangeType,
NULL, // Object GUID,
NULL, // Domain GUID,
NULL ); // Domain Name
return Status;
}
VOID
I_NetNotifyLsaPolicyChange(
IN POLICY_NOTIFICATION_INFORMATION_CLASS ChangeInfoClass
)
/*++
Routine Description:
This function is called by the LSA to indicate that policy information
in the LSA has changed.
This function is called for both PDC and BDC.
Arguments:
DsChangeType - Indicates the type of information that has changed.
Return Value:
Status of the operation.
--*/
{
NTSTATUS Status;
//
// If the netlogon service is running,
// Tell it about the change.
//
// It will, in turn, tell the bowser.
//
if( NlGlobalChangeLogNetlogonState != NetlogonStopped ) {
//
// Tell the netlogon service about the change.
//
Status = NlSendChangeLogNotification( ChangeLogLsaPolicyChanged,
NULL,
NULL,
(ULONG) ChangeInfoClass,
NULL, // Object GUID,
NULL, // Domain GUID,
NULL ); // Domain Name
//
// If the netlogon service is not running,
// handle operations that need handling here.
//
} else {
//
// Tell the browser about the change.
//
switch ( ChangeInfoClass ) {
case PolicyNotifyDnsDomainInformation:
//
// Tell the worker that it needs to notify the browser.
//
LOCK_CHANGELOG();
NlGlobalChangeLogNotifyBrowser = TRUE;
//
// Start the worker.
//
NlStartChangeLogWorkerThread();
UNLOCK_CHANGELOG();
}
}
return;
}
NTSTATUS
I_NetNotifyTrustedDomain (
IN PSID HostedDomainSid,
IN PSID TrustedDomainSid,
IN BOOLEAN IsDeletion
)
/*++
Routine Description:
This function is called by the LSA to indicate that a trusted domain
object has changed.
This function is called for both PDC and BDC.
Arguments:
HostedDomainSid - Domain SID of the domain the trust is from.
TrustedDomainSid - Domain SID of the domain the trust is to.
IsDeletion - TRUE if the trusted domain object was deleted.
FALSE if the trusted domain object was created or modified.
Return Value:
Status of the operation.
--*/
{
NTSTATUS Status;
//
// If the netlogon service isn't running,
// Don't bother with the coming and going of trusted domains..
//
if( NlGlobalChangeLogNetlogonState == NetlogonStopped ) {
return(STATUS_SUCCESS);
}
//
// If this is windows NT,
// There is nothing to maintain.
//
if ( NlGlobalChangeLogRole == ChangeLogMemberWorkstation ) {
return(STATUS_SUCCESS);
}
//
// Reset the TrustInfoUpToDate event so that any thread that wants to
// access the trust info list will wait until the info is updated (by
// the NlInitTrustList function).
//
if ( !ResetEvent( NlGlobalTrustInfoUpToDateEvent ) ) {
NlPrint((NL_CRITICAL,
"Cannot reset NlGlobalTrustInfoUpToDateEvent event: %lu\n",
GetLastError() ));
}
//
// Notify whether this is a creation/change/deletion.
if ( IsDeletion ) {
//
// Tell the netlogon service to update its in-memory list now.
//
Status = NlSendChangeLogNotification( ChangeLogTrustDeleted,
NULL,
TrustedDomainSid,
0,
NULL, // Object GUID,
NULL, // Domain GUID,
NULL ); // Domain Name
} else {
//
// Tell the netlogon service to update its in-memory list now.
//
Status = NlSendChangeLogNotification( ChangeLogTrustAdded,
NULL,
TrustedDomainSid,
0,
NULL, // Object GUID,
NULL, // Domain GUID,
NULL ); // Domain Name
}
return Status;
UNREFERENCED_PARAMETER( HostedDomainSid );
}
NTSTATUS
I_NetNotifyNtdsDsaDeletion (
IN LPWSTR DnsDomainName OPTIONAL,
IN GUID *DomainGuid OPTIONAL,
IN GUID *DsaGuid OPTIONAL,
IN LPWSTR DnsHostName
)
/*++
Routine Description:
This function is called by the DS to indicate that a NTDS-DSA object
and/or DNS records associated with the DNS host name are being deleted.
This function is called on the DC that the object is originally deleted on.
It is not called when the deletion is replicated to other DCs.
Arguments:
DnsDomainName - DNS domain name of the domain the DC was in.
This need not be a domain hosted by this DC.
If NULL, it is implied to be the DnsHostName with the leftmost label
removed.
DomainGuid - Domain Guid of the domain specified by DnsDomainName
If NULL, GUID specific names will not be removed.
DsaGuid - GUID of the NtdsDsa object that is being deleted.
DnsHostName - DNS host name of the DC whose NTDS-DSA object is being deleted.
Return Value:
Status of the operation.
--*/
{
NTSTATUS Status;
UNICODE_STRING DnsDomainNameString;
PUNICODE_STRING DnsDomainNameStringPtr = NULL;
UNICODE_STRING DnsHostNameString;
//
// If the netlogon service isn't running,
// Don't bother with the coming and going of trusted domains..
//
if( NlGlobalChangeLogNetlogonState == NetlogonStopped ) {
return(STATUS_SUCCESS);
}
//
// If this is windows NT,
// There is nothing to maintain.
//
if ( NlGlobalChangeLogRole == ChangeLogMemberWorkstation ) {
return(STATUS_SUCCESS);
}
//
// Queue this to the netlogon service
//
if ( DnsDomainName != NULL ) {
RtlInitUnicodeString( &DnsDomainNameString, DnsDomainName );
DnsDomainNameStringPtr = &DnsDomainNameString;
}
RtlInitUnicodeString( &DnsHostNameString, DnsHostName );
Status = NlSendChangeLogNotification( ChangeLogNtdsDsaDeleted,
&DnsHostNameString,
NULL,
0,
DsaGuid,
DomainGuid,
DnsDomainNameStringPtr );
return Status;
}
NTSTATUS
I_NetLogonSetServiceBits(
IN DWORD ServiceBitsOfInterest,
IN DWORD ServiceBits
)
/*++
Routine Description:
Inidcates whether this DC is currently running the specified service.
For instance,
I_NetLogonSetServiceBits( DS_KDC_FLAG, DS_KDC_FLAG );
tells Netlogon the KDC is running. And
I_NetLogonSetServiceBits( DS_KDC_FLAG, 0 );
tells Netlogon the KDC is not running.
Arguments:
ServiceBitsOfInterest - A mask of the service bits being changed, set,
or reset by this call. Only the following flags are valid:
DS_KDC_FLAG
DS_DS_FLAG
DS_TIMESERV_FLAG
DS_GOOD_TIMESERV_FLAG
ServiceBits - A mask indicating what the bits specified by ServiceBitsOfInterest
should be set to.
Return Value:
STATUS_SUCCESS - Success.
STATUS_INVALID_PARAMETER - The parameters have extaneous bits set.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
ULONG OldDnsBits;
ULONG NewDnsBits;
//
// Ensure the caller passed valid bits.
//
if ( (ServiceBitsOfInterest & ~DS_VALID_SERVICE_BITS) != 0 ||
(ServiceBits & ~ServiceBitsOfInterest) != 0 ) {
return STATUS_INVALID_PARAMETER;
}
//
// Change the bits.
//
LOCK_CHANGELOG();
OldDnsBits = NlGlobalChangeLogServiceBits & DS_DNS_SERVICE_BITS;
NlGlobalChangeLogServiceBits &= ~ServiceBitsOfInterest;
NlGlobalChangeLogServiceBits |= ServiceBits;
NewDnsBits = NlGlobalChangeLogServiceBits & DS_DNS_SERVICE_BITS;
NlGlobalChangeLogDllUnloaded = FALSE;
UNLOCK_CHANGELOG();
//
// If bits changed that would affect which names we register in DNS,
// change the registration now.
//
if ( OldDnsBits != NewDnsBits ) {
//
// Tell the netlogon service to update now.
//
Status = NlSendChangeLogNotification( ChangeDnsNames,
NULL,
NULL,
0, // Name registration need not be forced
NULL, // Object GUID,
NULL, // Domain GUID,
NULL ); // Domain Name
}
return Status;
}
NTSTATUS
NlInitChangeLog(
VOID
)
/*++
Routine Description:
Do the portion of ChangeLog initialization which happens on process
attach for netlogon.dll.
Specifically, Initialize the NlGlobalChangeLogCritSect and several
other global variables.
Arguments:
NONE
Return Value:
NT Status code
--*/
{
LARGE_INTEGER DomainPromotionIncrement = DOMAIN_PROMOTION_INCREMENT;
LARGE_INTEGER DomainPromotionMask = DOMAIN_PROMOTION_MASK;
NTSTATUS Status;
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
NT_PRODUCT_TYPE NtProductType;
//
// Initialize the critical section and anything process detach depends on.
//
#if NETLOGONDBG
try {
InitializeCriticalSection(&NlGlobalLogFileCritSect);
} except( EXCEPTION_EXECUTE_HANDLER ) {
NlPrint((NL_CRITICAL, "Cannot InitializeCriticalSection for NlGlobalLogFileCritSect\n" ));
return STATUS_NO_MEMORY;
}
#endif // NETLOGONDBG
try {
InitializeCriticalSection( &NlGlobalChangeLogCritSect );
} except( EXCEPTION_EXECUTE_HANDLER ) {
NlPrint((NL_CRITICAL, "Cannot InitializeCriticalSection for NlGlobalChangeLogCritSect\n" ));
return STATUS_NO_MEMORY;
}
try {
InitializeCriticalSection( &NlGlobalSecPkgCritSect );
} except( EXCEPTION_EXECUTE_HANDLER ) {
NlPrint((NL_CRITICAL, "Cannot InitializeCriticalSection for NlGlobalSecPkgCritSect\n" ));
return STATUS_NO_MEMORY;
}
#if NETLOGONDBG
NlGlobalParameters.DbFlag = 0xFFFFFFFF;
NlGlobalLogFile = INVALID_HANDLE_VALUE;
NlGlobalParameters.LogFileMaxSize = DEFAULT_MAXIMUM_LOGFILE_SIZE;
NlGlobalLogFileOutputBuffer = NULL;
#endif // NETLOGONDBG
InitChangeLogDesc( &NlGlobalChangeLogDesc );
InitChangeLogDesc( &NlGlobalTempChangeLogDesc );
NlGlobalChangeLogBuiltinDomainSid = NULL;
NlGlobalChangeLogServiceBits = 0;
NlGlobalChangeLogWorkerThreadHandle = NULL;
NlGlobalChangeLogNotifyBrowser = FALSE;
NlGlobalChangeLogNotifyBrowserIsRunning = FALSE;
NlGlobalChangeLogWorkerIsRunning = FALSE;
NlGlobalChangeLogDllUnloaded = TRUE;
NlGlobalChangeLogNetlogonState = NetlogonStopped;
NlGlobalChangeLogEvent = NULL;
NlGlobalChangeLogReplicateImmediately = FALSE;
InitializeListHead( &NlGlobalChangeLogNotifications );
NlGlobalEventlogHandle = NetpEventlogOpen ( SERVICE_NETLOGON,
0 ); // No timeout for now
if ( NlGlobalEventlogHandle == NULL ) {
NlPrint((NL_CRITICAL, "Cannot NetpEventlogOpen\n" ));
return STATUS_NO_MEMORY;
}
NlGlobalChangeLogFilePrefix[0] = L'\0';
NlGlobalChangeLogPromotionIncrement = DomainPromotionIncrement;
NlGlobalChangeLogPromotionMask = DomainPromotionMask.HighPart;
//
// Create special change log notify event.
//
NlGlobalChangeLogEvent =
CreateEvent( NULL, // No security attributes
FALSE, // Is automatically reset
FALSE, // Initially not signaled
NULL ); // No name
if ( NlGlobalChangeLogEvent == NULL ) {
NET_API_STATUS NetStatus;
NetStatus = GetLastError();
NlPrint((NL_CRITICAL, "Cannot create ChangeLog Event %lu\n",
NetStatus ));
return (int) NetpApiStatusToNtStatus(NetStatus);
}
//
// Create the trust-info-up-to-date event.
//
NlGlobalTrustInfoUpToDateEvent =
CreateEvent( NULL, // No security attributes
TRUE, // Is manually reset
TRUE, // Initially signaled
NULL ); // No name
if ( NlGlobalTrustInfoUpToDateEvent == NULL ) {
NET_API_STATUS NetStatus;
NetStatus = GetLastError();
NlPrint((NL_CRITICAL, "Cannot create TrustInfoUpToDate Event %lu\n",
NetStatus ));
return (int) NetpApiStatusToNtStatus(NetStatus);
}
//
// Initialize the Role.
//
// For Windows-NT, just set the role to member workstation once and for all.
//
// For LanMan-Nt initially set it to "unknown" to prevent the
// changelog from being maintained until LSA calls I_NetNotifyRole.
//
if ( !RtlGetNtProductType( &NtProductType ) ) {
NtProductType = NtProductWinNt;
}
if ( NtProductType == NtProductLanManNt ) {
NlGlobalChangeLogRole = ChangeLogUnknown;
} else {
NlGlobalChangeLogRole = ChangeLogMemberWorkstation;
}
//
// Initialize DC specific globals.
//
if ( NtProductType == NtProductLanManNt ) {
//
// Build a Sid for the SAM Builtin domain
//
Status = RtlAllocateAndInitializeSid(
&NtAuthority,
1, // Sub Authority Count
SECURITY_BUILTIN_DOMAIN_RID,
0, // Unused
0, // Unused
0, // Unused
0, // Unused
0, // Unused
0, // Unused
0, // Unused
&NlGlobalChangeLogBuiltinDomainSid);
if ( !NT_SUCCESS(Status) ) {
goto Cleanup;
}
}
//
// Ask the LSA to notify us of any changes to the LSA database.
//
Status = LsaIRegisterPolicyChangeNotificationCallback(
&I_NetNotifyLsaPolicyChange,
PolicyNotifyDnsDomainInformation );
if ( !NT_SUCCESS(Status) ) {
NlPrint((NL_CRITICAL,
"Failed to LsaIRegisterPolicyChangeNotificationCallback. %lX\n",
Status ));
goto Cleanup;
}
//
// Success...
//
Status = STATUS_SUCCESS;
//
// Cleanup
//
Cleanup:
return Status;
}
//
// netlogon.dll never detaches
//
#if NETLOGONDBG
NTSTATUS
NlCloseChangeLog(
VOID
)
/*++
Routine Description:
Frees any resources consumed by NlInitChangeLog.
Arguments:
NONE
Return Value:
NT Status code
--*/
{
NTSTATUS Status;
//
// Ask the LSA to notify us of any changes to the LSA database.
//
Status = LsaIUnregisterAllPolicyChangeNotificationCallback(
&I_NetNotifyLsaPolicyChange );
if ( !NT_SUCCESS(Status) ) {
NlPrint((NL_CRITICAL,
"Failed to LsaIUnregisterPolicyChangeNotificationCallback. %lX\n",
Status ));
}
if ( (NlGlobalChangeLogDesc.FileHandle == INVALID_HANDLE_VALUE) &&
(NlGlobalChangeLogRole == ChangeLogPrimary) ) {
//
// try to save change log cache one last time.
//
(VOID)NlCreateChangeLogFile( &NlGlobalChangeLogDesc );
}
//
// Close the changelogs
//
NlCloseChangeLogFile( &NlGlobalChangeLogDesc );
NlCloseChangeLogFile( &NlGlobalTempChangeLogDesc );
//
// Initialize the globals.
//
NlGlobalChangeLogFilePrefix[0] = L'\0';
if ( NlGlobalChangeLogBuiltinDomainSid != NULL ) {
RtlFreeSid( NlGlobalChangeLogBuiltinDomainSid );
NlGlobalChangeLogBuiltinDomainSid = NULL;
}
if ( NlGlobalChangeLogEvent != NULL ) {
(VOID) CloseHandle(NlGlobalChangeLogEvent);
NlGlobalChangeLogEvent = NULL;
}
if ( NlGlobalTrustInfoUpToDateEvent != NULL ) {
(VOID) CloseHandle(NlGlobalTrustInfoUpToDateEvent);
NlGlobalTrustInfoUpToDateEvent = NULL;
}
//
// Stop the worker thread if it is running
//
NlStopChangeLogWorker();
LOCK_CHANGELOG();
NlAssert( IsListEmpty( &NlGlobalChangeLogNotifications ) );
UNLOCK_CHANGELOG();
//
// Close the eventlog handle
//
NetpEventlogClose( NlGlobalEventlogHandle );
//
// close all handles
//
DeleteCriticalSection(&NlGlobalSecPkgCritSect);
DeleteCriticalSection( &NlGlobalChangeLogCritSect );
#if NETLOGONDBG
if ( NlGlobalLogFileOutputBuffer != NULL ) {
LocalFree( NlGlobalLogFileOutputBuffer );
NlGlobalLogFileOutputBuffer = NULL;
}
DeleteCriticalSection( &NlGlobalLogFileCritSect );
#endif // NETLOGONDBG
return STATUS_SUCCESS;
}
#endif // NETLOGONDBG