2408 lines
58 KiB
C
2408 lines
58 KiB
C
|
/*++
|
|||
|
|
|||
|
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
|