mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1818 lines
44 KiB
1818 lines
44 KiB
/*++
|
|
|
|
Copyright (c) 1987-1991 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 <nt.h> // LARGE_INTEGER definition
|
|
#include <ntrtl.h> // LARGE_INTEGER definition
|
|
#include <nturtl.h> // LARGE_INTEGER definition
|
|
#include <ntlsa.h> // needed by changelg.h
|
|
|
|
#define NOMINMAX // Avoid redefinition of min and max in stdlib.h
|
|
#include <rpc.h> // Needed by logon.h
|
|
#include <logon_s.h>// includes lmcons.h, lmaccess.h, netlogon.h,
|
|
// ssi.h, windef.h
|
|
#include <winbase.h>
|
|
#include <stdio.h> // sprintf ...
|
|
|
|
//
|
|
// Include files specific to this .c file
|
|
//
|
|
#include <config.h> // net config helpers.
|
|
#include <configp.h> // USE_WIN32_CONFIG (if defined), etc.
|
|
#include <confname.h> // SECTION_ equates, NETLOGON_KEYWORD_ equates.
|
|
#include "iniparm.h" // defaults
|
|
|
|
//
|
|
// BEWARE: Be careful about adding netlogon.dll specific include files here.
|
|
// This module is call by SAM and LSA. The netlogon service may not yet
|
|
// be running. Therefore, guard against referencing netlogon.dll globals
|
|
// other than those defined in changelg.h.
|
|
//
|
|
|
|
#include <samrpc.h> // Needed by samisrv.h
|
|
#include <samisrv.h> // Needed by changelg.h
|
|
#include <lsarpc.h> // Needed by lsrvdata.h and logonsrv.h
|
|
#define CHANGELOG_ALLOCATE
|
|
#include <changelg.h> // Local procedure definitions
|
|
#undef CHANGELOG_ALLOCATE
|
|
|
|
#include <lmerrlog.h> // NELOG_* defined here ..
|
|
#include <netlib.h> // NetpMemoryAllocate
|
|
#include <netlibnt.h> // NetpNtStatusToApiStatus
|
|
|
|
#define DEBUG_ALLOCATE
|
|
#include <nldebug.h> // Netlogon debugging
|
|
#undef DEBUG_ALLOCATE
|
|
#include <align.h>
|
|
#include <nlp.h> // NlpWriteEventlog defined here.
|
|
|
|
#include <nlrepl.h> // I_Net* definitions
|
|
#include <chworker.h> // worker functions
|
|
#include "chutil.h" // utility functions
|
|
|
|
|
|
enum {
|
|
ChangeLogPrimary,
|
|
ChangeLogBackup,
|
|
ChangeLogMemberWorkstation,
|
|
ChangeLogUnknown
|
|
} NlGlobalChangeLogRole;
|
|
|
|
//
|
|
// from parse.c
|
|
//
|
|
|
|
NET_API_STATUS
|
|
NlParseOne(
|
|
IN LPNET_CONFIG_HANDLE SectionHandle,
|
|
IN LPWSTR Keyword,
|
|
IN ULONG DefaultValue,
|
|
IN ULONG MinimumValue,
|
|
IN ULONG MaximumValue,
|
|
OUT PULONG Value
|
|
);
|
|
|
|
|
|
|
|
NTSTATUS
|
|
NlSendChangeLogNotification(
|
|
IN enum CHANGELOG_NOTIFICATION_TYPE EntryType,
|
|
IN PUNICODE_STRING ObjectName,
|
|
IN PSID ObjectSid,
|
|
IN ULONG ObjectRid
|
|
)
|
|
/*++
|
|
|
|
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.
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
{
|
|
PCHANGELOG_NOTIFICATION Notification;
|
|
LPBYTE Where;
|
|
ULONG SidSize = 0;
|
|
ULONG NameSize = 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);
|
|
}
|
|
|
|
Size = sizeof(*Notification) + SidSize + NameSize;
|
|
Size = ROUND_UP_COUNT( Size, ALIGN_WORST );
|
|
|
|
Notification = NetpMemoryAllocate( Size );
|
|
|
|
if ( Notification == NULL ) {
|
|
return STATUS_NO_MEMORY;
|
|
}
|
|
|
|
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 new server 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);
|
|
}
|
|
|
|
//
|
|
// Indicate we're about to send the event.
|
|
//
|
|
|
|
NlPrint((NL_CHANGELOG,
|
|
"NlSendChangeLogNotification: sent %ld for %wZ Rid: 0x%lx Sid: ",
|
|
Notification->EntryType,
|
|
&Notification->ObjectName,
|
|
Notification->ObjectRid ));
|
|
NlpDumpSid( NL_CHANGELOG, Notification->ObjectSid );
|
|
|
|
|
|
|
|
//
|
|
// 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;
|
|
}
|
|
|
|
|
|
VOID
|
|
NlLmBdcListSet(
|
|
IN ULONG LmBdcCount,
|
|
IN PULONG LmBdcRidArray
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the list of LM BDCs to the specified list.
|
|
|
|
Arguments:
|
|
|
|
LmBdcCount - Number of BDCs in the list
|
|
|
|
LmBdcRidArray - Array of Rids of Lanman BDC accounts.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// If a previous array exists,
|
|
// delete it.
|
|
//
|
|
LOCK_CHANGELOG();
|
|
if ( NlGlobalLmBdcRidArray != NULL ) {
|
|
NetpMemoryFree( NlGlobalLmBdcRidArray );
|
|
NlGlobalLmBdcRidArray = NULL;
|
|
NlGlobalLmBdcCount = 0;
|
|
}
|
|
|
|
//
|
|
// Allocate the new array.
|
|
//
|
|
|
|
NlGlobalLmBdcRidArray = NetpMemoryAllocate( LmBdcCount * sizeof(ULONG) );
|
|
|
|
if ( NlGlobalLmBdcRidArray != NULL ) {
|
|
RtlCopyMemory( NlGlobalLmBdcRidArray,
|
|
LmBdcRidArray,
|
|
LmBdcCount * sizeof(ULONG) );
|
|
NlGlobalLmBdcCount = LmBdcCount;
|
|
}
|
|
UNLOCK_CHANGELOG();
|
|
}
|
|
|
|
|
|
PULONG
|
|
NlLmBdcListFind(
|
|
IN ULONG Rid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns a pointer to the specified RID in the LM BDC list.
|
|
|
|
Enter with the change log crit sect locked.
|
|
|
|
Arguments:
|
|
|
|
Rid - Rid of the Lanman BDC being found
|
|
|
|
Return Value:
|
|
|
|
NULL, if the entry can't be found
|
|
|
|
--*/
|
|
{
|
|
ULONG i;
|
|
|
|
//
|
|
// Simply loop through the array entries.
|
|
//
|
|
|
|
for ( i=0; i<NlGlobalLmBdcCount; i++ ) {
|
|
if ( NlGlobalLmBdcRidArray[i] == Rid ) {
|
|
return &NlGlobalLmBdcRidArray[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
NlLmBdcListAdd(
|
|
IN ULONG Rid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Add the specified RID to the LM BDC list.
|
|
|
|
Notify the changelog worker thread and the netlogon service of the new BDC.
|
|
|
|
Arguments:
|
|
|
|
Rid - Rid of the Lanman BDC being added
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Ensure the RID doesn't already exist in the array.
|
|
//
|
|
|
|
LOCK_CHANGELOG();
|
|
|
|
if ( NlLmBdcListFind( Rid ) == NULL ) {
|
|
|
|
//
|
|
// Allocate a larger array.
|
|
// (NetpMemoryReallocate properly handles the case where the
|
|
// array didn't previously exist.)
|
|
//
|
|
|
|
NlGlobalLmBdcRidArray = NetpMemoryReallocate(
|
|
NlGlobalLmBdcRidArray,
|
|
(NlGlobalLmBdcCount+1) * sizeof(ULONG) );
|
|
|
|
if ( NlGlobalLmBdcRidArray == NULL ) {
|
|
NlGlobalLmBdcCount = 0;
|
|
UNLOCK_CHANGELOG();
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Set the RID into the array entry.
|
|
//
|
|
|
|
NlGlobalLmBdcRidArray[NlGlobalLmBdcCount] = Rid;
|
|
NlGlobalLmBdcCount ++;
|
|
NlPrint((NL_CHANGELOG,
|
|
"NlLmBdcListAdd: Lm Bdc 0x%lx added (%ld)\n",
|
|
Rid,
|
|
NlGlobalLmBdcCount ));
|
|
|
|
//
|
|
// Start the changelog worker thread if it's not running.
|
|
//
|
|
|
|
(VOID) NlStartChangeLogWorkerThread();
|
|
|
|
//
|
|
// Tell netlogon that a downlevel BDC has been added.
|
|
//
|
|
|
|
(VOID) NlSendChangeLogNotification(
|
|
ChangeLogLmServerAdded,
|
|
NULL,
|
|
NULL,
|
|
Rid );
|
|
|
|
}
|
|
|
|
UNLOCK_CHANGELOG();
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
NlLmBdcListDel(
|
|
IN ULONG Rid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete the specified RID from the LM BDC list.
|
|
|
|
This routine is specifically designed to be called on ALL user account
|
|
deletions. Since the user account might have been a member of the servers
|
|
group, all user account deletions are checked to see if they represent
|
|
an LM BDC.
|
|
|
|
Notify the changelog worker thread and the netlogon service of the deleted BDC.
|
|
|
|
Arguments:
|
|
|
|
Rid - Rid of the Lanman BDC being deleted.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PULONG RidEntry;
|
|
|
|
//
|
|
// If the entry exists,
|
|
// delete it by copying the last entry of the array on top of this one
|
|
// and making the array one entry smaller.
|
|
//
|
|
|
|
LOCK_CHANGELOG();
|
|
|
|
RidEntry = NlLmBdcListFind( Rid );
|
|
|
|
if ( RidEntry != NULL ) {
|
|
*RidEntry = NlGlobalLmBdcRidArray[ NlGlobalLmBdcCount-1 ];
|
|
NlGlobalLmBdcCount --;
|
|
NlPrint((NL_CHANGELOG,
|
|
"NlLmBdcListDel: Lm Bdc 0x%lx deleted (%ld)\n",
|
|
Rid,
|
|
NlGlobalLmBdcCount ));
|
|
|
|
if ( NlGlobalLmBdcCount == 0 ) {
|
|
NetpMemoryFree( NlGlobalLmBdcRidArray );
|
|
NlGlobalLmBdcRidArray = NULL;
|
|
}
|
|
|
|
//
|
|
// worker thread must be running now
|
|
//
|
|
|
|
if( IsChangeLogWorkerRunning() ) {
|
|
|
|
(VOID) NlAddWorkerQueueEntry( ServersGroupDel, 0 );
|
|
|
|
} else {
|
|
NlAssert( FALSE );
|
|
}
|
|
|
|
//
|
|
// Tell netlogon that a downlevel BDC has been removed.
|
|
//
|
|
|
|
(VOID) NlSendChangeLogNotification(
|
|
ChangeLogLmServerDeleted,
|
|
NULL,
|
|
NULL,
|
|
Rid );
|
|
}
|
|
|
|
UNLOCK_CHANGELOG();
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
BOOL LanmanReplicateImmediately = FALSE;
|
|
|
|
//
|
|
// Ensure the role is right. Otherwise, all the globals used below
|
|
// aren't initialized.
|
|
//
|
|
|
|
if ( NlGlobalChangeLogRole != ChangeLogPrimary ) {
|
|
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, NlGlobalChWorkerBuiltinDomainSid )) {
|
|
|
|
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.
|
|
//
|
|
|
|
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:
|
|
return STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
|
|
case SecurityDbObjectLsaTDomain:
|
|
|
|
switch (DeltaType) {
|
|
case SecurityDbNew:
|
|
case SecurityDbChange:
|
|
NetlogonDeltaType = AddOrChangeLsaTDomain;
|
|
|
|
//
|
|
// Tell the netlogon service to update its in-memory list now.
|
|
//
|
|
(VOID) NlSendChangeLogNotification( ChangeLogTrustAdded,
|
|
NULL,
|
|
ObjectSid,
|
|
0 );
|
|
break;
|
|
|
|
case SecurityDbDelete:
|
|
NetlogonDeltaType = DeleteLsaTDomain;
|
|
|
|
//
|
|
// Tell the netlogon service to update its in-memory list now.
|
|
//
|
|
(VOID) NlSendChangeLogNotification( ChangeLogTrustDeleted,
|
|
NULL,
|
|
ObjectSid,
|
|
0 );
|
|
break;
|
|
|
|
// unknown delta type
|
|
default:
|
|
return STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
|
|
case SecurityDbObjectLsaAccount:
|
|
|
|
switch (DeltaType) {
|
|
case SecurityDbNew:
|
|
case SecurityDbChange:
|
|
NetlogonDeltaType = AddOrChangeLsaAccount;
|
|
break;
|
|
|
|
case SecurityDbDelete:
|
|
NetlogonDeltaType = DeleteLsaAccount;
|
|
break;
|
|
|
|
// unknown delta type
|
|
default:
|
|
return STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
|
|
case SecurityDbObjectLsaSecret:
|
|
|
|
switch (DeltaType) {
|
|
case SecurityDbNew:
|
|
case SecurityDbChange:
|
|
NetlogonDeltaType = AddOrChangeLsaSecret;
|
|
break;
|
|
|
|
case SecurityDbDelete:
|
|
NetlogonDeltaType = DeleteLsaSecret;
|
|
break;
|
|
|
|
// unknown delta type
|
|
default:
|
|
return STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
|
|
case SecurityDbObjectSamDomain:
|
|
|
|
switch (DeltaType) {
|
|
case SecurityDbNew:
|
|
case SecurityDbChange:
|
|
NetlogonDeltaType = AddOrChangeDomain;
|
|
break;
|
|
|
|
// unknown delta type
|
|
default:
|
|
return STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case SecurityDbObjectSamUser:
|
|
|
|
switch (DeltaType) {
|
|
case SecurityDbChangePassword:
|
|
Flags |= CHANGELOG_PASSWORD_CHANGE;
|
|
LanmanReplicateImmediately = TRUE;
|
|
NetlogonDeltaType = AddOrChangeUser;
|
|
break;
|
|
|
|
case SecurityDbNew:
|
|
|
|
//
|
|
// For down-level system, a newly added user needs to
|
|
// have it's membership in "Domain Users" updated, too.
|
|
// The following worker entry will add the additional
|
|
// delta entry and increment the serial number
|
|
// accordingly.
|
|
//
|
|
|
|
LOCK_CHANGELOG();
|
|
if( IsChangeLogWorkerRunning() ) {
|
|
(VOID) NlAddWorkerQueueEntry( ChangeLogAddUser, ObjectRid );
|
|
}
|
|
UNLOCK_CHANGELOG();
|
|
|
|
NetlogonDeltaType = AddOrChangeUser;
|
|
break;
|
|
|
|
case SecurityDbChange:
|
|
NetlogonDeltaType = AddOrChangeUser;
|
|
break;
|
|
|
|
//
|
|
// This is a dummy delta sent by chworker to indicate that "Domain Users"
|
|
// was added as a member of this user.
|
|
//
|
|
case SecurityDbChangeMemberAdd:
|
|
Flags |= CHANGELOG_DOMAINUSERS_CHANGED;
|
|
NetlogonDeltaType = AddOrChangeUser;
|
|
break;
|
|
|
|
case SecurityDbDelete:
|
|
|
|
//
|
|
// This might be a Lanman BDC so check to be sure.
|
|
//
|
|
|
|
NlLmBdcListDel( ObjectRid );
|
|
|
|
NetlogonDeltaType = DeleteUser;
|
|
break;
|
|
|
|
|
|
case SecurityDbRename:
|
|
NetlogonDeltaType = RenameUser;
|
|
|
|
//
|
|
// For down-level system, Rename user is handled as two
|
|
// deltas, viz. 1) Delete old user and 2) Add new user.
|
|
// The following worker entry will add the additional
|
|
// delta entry and increment the serial number
|
|
// accordingly.
|
|
//
|
|
|
|
LOCK_CHANGELOG();
|
|
|
|
if( IsChangeLogWorkerRunning() ) {
|
|
(VOID) NlAddWorkerQueueEntry( ChangeLogRenameUser, ObjectRid );
|
|
}
|
|
|
|
UNLOCK_CHANGELOG();
|
|
|
|
break;
|
|
|
|
//
|
|
// unknown delta type
|
|
//
|
|
|
|
default:
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
break;
|
|
|
|
case SecurityDbObjectSamGroup:
|
|
|
|
switch ( DeltaType ) {
|
|
case SecurityDbNew:
|
|
case SecurityDbChange:
|
|
NetlogonDeltaType = AddOrChangeGroup;
|
|
break;
|
|
|
|
case SecurityDbDelete:
|
|
NetlogonDeltaType = DeleteGroup;
|
|
|
|
//
|
|
// when a global group is deleted, we also delete it
|
|
// from the special group list, if it is included
|
|
// in the list.
|
|
//
|
|
|
|
LOCK_CHANGELOG();
|
|
|
|
if( IsChangeLogWorkerRunning() ) {
|
|
|
|
PGLOBAL_GROUP_ENTRY GroupEntry;
|
|
|
|
GroupEntry = NlGetGroupEntry (
|
|
&NlGlobalSpecialServerGroupList,
|
|
ObjectRid );
|
|
|
|
if( GroupEntry != NULL ) {
|
|
|
|
RemoveEntryList( &GroupEntry->Next );
|
|
NetpMemoryFree( GroupEntry );
|
|
}
|
|
|
|
}
|
|
|
|
UNLOCK_CHANGELOG();
|
|
break;
|
|
|
|
case SecurityDbRename:
|
|
NetlogonDeltaType = RenameGroup;
|
|
|
|
//
|
|
// For down-level system, Rename group is handled as
|
|
// three deltas, viz. 1) Delete old group, 2) Add new
|
|
// group and 3. Changemembership of new group. The
|
|
// following worker entry will add the additional
|
|
// two delta entries and increment the serial number
|
|
// accordingly.
|
|
//
|
|
|
|
LOCK_CHANGELOG();
|
|
|
|
if( IsChangeLogWorkerRunning() ) {
|
|
(VOID) NlAddWorkerQueueEntry( ChangeLogRenameGroup, ObjectRid );
|
|
|
|
}
|
|
|
|
UNLOCK_CHANGELOG();
|
|
|
|
break;
|
|
|
|
case SecurityDbChangeMemberAdd:
|
|
case SecurityDbChangeMemberSet:
|
|
case SecurityDbChangeMemberDel: {
|
|
|
|
UNICODE_STRING ServersGroup;
|
|
|
|
NetlogonDeltaType = ChangeGroupMembership;
|
|
|
|
//
|
|
// without object name we can't do much here.
|
|
//
|
|
if( ObjectName == NULL ) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// do something for down level
|
|
//
|
|
|
|
RtlInitUnicodeString( &ServersGroup, SSI_SERVER_GROUP_W );
|
|
|
|
LOCK_CHANGELOG();
|
|
|
|
if( RtlEqualUnicodeString(
|
|
&ServersGroup, ObjectName, (BOOLEAN)TRUE ) ) {
|
|
|
|
//
|
|
// Handle a new LM BDC.
|
|
//
|
|
|
|
if( DeltaType == SecurityDbChangeMemberAdd ) {
|
|
|
|
NlLmBdcListAdd( MemberId->GroupMemberId.MemberRid );
|
|
|
|
|
|
//
|
|
// Handle an LM BDC being deleted.
|
|
//
|
|
|
|
} else if( DeltaType == SecurityDbChangeMemberDel ) {
|
|
|
|
NlLmBdcListDel( MemberId->GroupMemberId.MemberRid );
|
|
}
|
|
|
|
} else {
|
|
|
|
if( IsChangeLogWorkerRunning() ) {
|
|
|
|
//
|
|
// Change log work is running. If the global groups
|
|
// list watched is empty, add this delta in the
|
|
// queue anyway, otherwise add this delta to entry
|
|
// only if this group is monitored.
|
|
//
|
|
|
|
if( IsListEmpty( &NlGlobalSpecialServerGroupList ) ||
|
|
|
|
( NlGetGroupEntry(
|
|
&NlGlobalSpecialServerGroupList,
|
|
ObjectRid ) != NULL ) ) {
|
|
|
|
|
|
(VOID) NlAddWorkerQueueEntry(
|
|
ChangeLogGroupMembership,
|
|
MemberId->GroupMemberId.MemberRid );
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
UNLOCK_CHANGELOG();
|
|
|
|
break;
|
|
}
|
|
|
|
//
|
|
// unknown delta type
|
|
//
|
|
default:
|
|
return STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case SecurityDbObjectSamAlias:
|
|
|
|
switch (DeltaType) {
|
|
case SecurityDbNew:
|
|
case SecurityDbChange:
|
|
NetlogonDeltaType = AddOrChangeAlias;
|
|
break;
|
|
|
|
case SecurityDbDelete:
|
|
NetlogonDeltaType = DeleteAlias;
|
|
break;
|
|
|
|
case SecurityDbRename:
|
|
NetlogonDeltaType = RenameAlias;
|
|
break;
|
|
|
|
case SecurityDbChangeMemberAdd:
|
|
case SecurityDbChangeMemberSet:
|
|
case SecurityDbChangeMemberDel:
|
|
|
|
NetlogonDeltaType = ChangeAliasMembership;
|
|
|
|
LOCK_CHANGELOG();
|
|
|
|
//
|
|
// if this delta is BUILTIN domain delta and the group
|
|
// modified is special group then add this delta to
|
|
// workers queue if it is running.
|
|
//
|
|
|
|
if ( (ChangeLogEntry.DBIndex == BUILTIN_DB) &&
|
|
( IsChangeLogWorkerRunning() ) &&
|
|
( IsSpecialLocalGroup( ObjectRid ) ) ) {
|
|
|
|
ULONG Rid;
|
|
PUCHAR SubAuthorityCount;
|
|
BOOLEAN EqualSid;
|
|
|
|
//
|
|
// if the member modified belongs to the local SAM
|
|
// database.
|
|
//
|
|
|
|
SubAuthorityCount =
|
|
RtlSubAuthorityCountSid(
|
|
MemberId->AliasMemberId.MemberSid);
|
|
|
|
(*SubAuthorityCount)--;
|
|
|
|
if( NlGlobalChWorkerSamDomainSid != NULL ) {
|
|
|
|
EqualSid = RtlEqualSid(
|
|
NlGlobalChWorkerSamDomainSid,
|
|
MemberId->AliasMemberId.MemberSid);
|
|
} else {
|
|
EqualSid = FALSE;
|
|
}
|
|
|
|
(*SubAuthorityCount)++;
|
|
|
|
if( EqualSid ) {
|
|
|
|
Rid = *RtlSubAuthoritySid(
|
|
MemberId->AliasMemberId.MemberSid,
|
|
(*SubAuthorityCount) -1 );
|
|
|
|
(VOID) NlAddWorkerQueueEntry(
|
|
ChangeLogAliasMembership,
|
|
Rid );
|
|
|
|
|
|
//
|
|
// add this member in the global group list,
|
|
// since this member may be a global group and we
|
|
// don't want to miss any delta made on this group.
|
|
// Worker thread will adjust the list and remove
|
|
// unwanted user entries from the list.
|
|
//
|
|
|
|
Status = NlAddGroupEntry(
|
|
&NlGlobalSpecialServerGroupList,
|
|
Rid );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
|
|
NlPrint((NL_CRITICAL,
|
|
"NlAddGroupEntry failed %lx\n",
|
|
Status ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
UNLOCK_CHANGELOG();
|
|
|
|
break;
|
|
|
|
// unknown delta type
|
|
default:
|
|
return STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
|
|
// unknown object type
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Build the changelog entry and write it to the changelog
|
|
//
|
|
|
|
ChangeLogEntry.DeltaType = NetlogonDeltaType;
|
|
ChangeLogEntry.SerialNumber = SerialNumber;
|
|
ChangeLogEntry.ObjectRid = ObjectRid;
|
|
ChangeLogEntry.Flags = ReplicateImmediately ? CHANGELOG_REPLICATE_IMMEDIATELY : 0;
|
|
ChangeLogEntry.Flags |= Flags;
|
|
|
|
(VOID) NlWriteChangeLogEntry( &NlGlobalChangeLogDesc, &ChangeLogEntry, ObjectSid, ObjectName, TRUE );
|
|
|
|
|
|
//
|
|
// 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() ));
|
|
}
|
|
|
|
|
|
//
|
|
// If this change requires immediate replication to Lanman BDCs, do so
|
|
//
|
|
|
|
} else if( LanmanReplicateImmediately ) {
|
|
|
|
LOCK_CHANGELOG();
|
|
NlGlobalChangeLogLanmanReplicateImmediately = TRUE;
|
|
UNLOCK_CHANGELOG();
|
|
|
|
if ( !SetEvent( NlGlobalChangeLogEvent ) ) {
|
|
NlPrint((NL_CRITICAL,
|
|
"Cannot set ChangeLog event: %lu\n",
|
|
GetLastError() ));
|
|
}
|
|
|
|
}
|
|
|
|
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[PATHLEN+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.
|
|
#if defined(USE_WIN32_CONFIG)
|
|
SERVICE_NETLOGON,
|
|
#else
|
|
SECT_NT_NETLOGON, // section name
|
|
#endif
|
|
TRUE ); // we only want readonly access
|
|
|
|
if ( NetStatus == NO_ERROR ) {
|
|
|
|
(VOID) NlParseOne( SectionHandle,
|
|
NETLOGON_KEYWORD_CHANGELOGSIZE,
|
|
DEFAULT_CHANGELOGSIZE,
|
|
MIN_CHANGELOGSIZE,
|
|
MAX_CHANGELOGSIZE,
|
|
&NewChangeLogSize );
|
|
|
|
(VOID) NetpCloseConfigData( SectionHandle );
|
|
}
|
|
|
|
NewChangeLogSize = ROUND_UP_COUNT( NewChangeLogSize, ALIGN_WORST);
|
|
|
|
NlPrint((NL_INIT, "ChangeLogSize: 0x%lx\n", NewChangeLogSize ));
|
|
|
|
|
|
//
|
|
// Build the change log file name
|
|
//
|
|
|
|
WindowsDirectoryLength = GetWindowsDirectoryW(
|
|
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:
|
|
|
|
//
|
|
// start changelog worker thread
|
|
//
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
|
|
if ( NlGlobalChangeLogRole == ChangeLogPrimary ) {
|
|
(VOID)NlStartChangeLogWorkerThread();
|
|
}
|
|
|
|
//
|
|
// Free any resources on error.
|
|
//
|
|
|
|
} else {
|
|
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;
|
|
|
|
//
|
|
// If the netlogon service is running,
|
|
// then we can't change role so simply return.
|
|
//
|
|
|
|
if( NlGlobalChangeLogNetlogonState != NetlogonStopped ) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// If this is a workstation, simply return.
|
|
//
|
|
|
|
if ( NlGlobalChangeLogRole == ChangeLogMemberWorkstation ) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Set our role to the new value.
|
|
//
|
|
|
|
if( Role == PolicyServerRolePrimary) {
|
|
|
|
NlGlobalChangeLogRole = ChangeLogPrimary;
|
|
|
|
} else {
|
|
|
|
NlGlobalChangeLogRole = ChangeLogBackup;
|
|
}
|
|
|
|
//
|
|
// Delete any previous change log buffer and initialize it again.
|
|
// (This allows the size to be changed on every role change.)
|
|
//
|
|
|
|
NlCloseChangeLogFile( &NlGlobalChangeLogDesc );
|
|
|
|
Status = NlInitChangeLogBuffer();
|
|
|
|
return Status;
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
//
|
|
// 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;
|
|
NlAssert( OldUserAccountControl == 0 || NewUserAccountControl == 0 );
|
|
NlAssert( OldUserAccountControl != 0 || NewUserAccountControl != 0 );
|
|
|
|
|
|
//
|
|
// Handle deletion of a Server Trust Account
|
|
//
|
|
|
|
if ( OldUserAccountControl == USER_SERVER_TRUST_ACCOUNT ) {
|
|
|
|
Status = NlSendChangeLogNotification( ChangeLogNtServerDeleted,
|
|
ObjectName,
|
|
NULL,
|
|
0 );
|
|
|
|
|
|
//
|
|
// Handle deletion of a Domain Trust Account
|
|
//
|
|
|
|
} else if ( OldUserAccountControl == USER_INTERDOMAIN_TRUST_ACCOUNT ) {
|
|
|
|
Status = NlSendChangeLogNotification( ChangeLogTrustedDomainDeleted,
|
|
ObjectName,
|
|
NULL,
|
|
0 );
|
|
|
|
|
|
//
|
|
// Handle deletion of a Workstation Trust Account
|
|
//
|
|
|
|
} else if ( OldUserAccountControl == USER_WORKSTATION_TRUST_ACCOUNT ) {
|
|
|
|
Status = NlSendChangeLogNotification( ChangeLogWorkstationDeleted,
|
|
ObjectName,
|
|
NULL,
|
|
0 );
|
|
|
|
//
|
|
// Handle creation of a Server Trust Account
|
|
//
|
|
|
|
} else if ( NewUserAccountControl == USER_SERVER_TRUST_ACCOUNT ) {
|
|
|
|
if ( NlGlobalChangeLogRole == ChangeLogPrimary ) {
|
|
Status = NlSendChangeLogNotification( ChangeLogNtServerAdded,
|
|
ObjectName,
|
|
NULL,
|
|
ObjectRid );
|
|
} else {
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Ignore all other changes for now.
|
|
//
|
|
|
|
} else {
|
|
|
|
Status = STATUS_SUCCESS;
|
|
}
|
|
|
|
return Status;
|
|
UNREFERENCED_PARAMETER( DomainSid );
|
|
}
|
|
|
|
|
|
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.
|
|
//
|
|
|
|
InitializeCriticalSection( &NlGlobalChangeLogCritSect );
|
|
#if DBG
|
|
InitializeCriticalSection( &NlGlobalLogFileCritSect );
|
|
NlGlobalTrace = 0xFFFFFFFF;
|
|
NlGlobalLogFile = INVALID_HANDLE_VALUE;
|
|
NlGlobalLogFileMaxSize = DEFAULT_MAXIMUM_LOGFILE_SIZE;
|
|
#endif // DBG
|
|
InitChangeLogDesc( &NlGlobalChangeLogDesc );
|
|
NlGlobalChWorkerBuiltinDomainSid = NULL;
|
|
NlGlobalChWorkerSamDomainSid = NULL;
|
|
|
|
NlGlobalChangeLogNetlogonState = NetlogonStopped;
|
|
NlGlobalChangeLogEvent = NULL;
|
|
NlGlobalChangeLogReplicateImmediately = FALSE;
|
|
NlGlobalChangeLogLanmanReplicateImmediately = FALSE;
|
|
InitializeListHead( &NlGlobalChangeLogNotifications );
|
|
|
|
|
|
NlGlobalChWorkerSamServerHandle = NULL;
|
|
NlGlobalChWorkerPolicyHandle = NULL;
|
|
NlGlobalChWorkerSamDBHandle = NULL;
|
|
NlGlobalChWorkerBuiltinDBHandle = NULL;
|
|
|
|
NlGlobalChangeLogWorkerQueueEvent = NULL;
|
|
InitializeListHead(&NlGlobalChangeLogWorkerQueue);
|
|
InitializeListHead(&NlGlobalSpecialServerGroupList);
|
|
|
|
NlGlobalChangeLogWorkerThreadHandle = NULL;
|
|
NlGlobalChangeLogWorkInit = FALSE;
|
|
|
|
NlGlobalChangeLogWorkerTerminate = FALSE;
|
|
NlGlobalChangeLogFilePrefix[0] = L'\0';
|
|
NlGlobalChangeLogPromotionIncrement = DomainPromotionIncrement;
|
|
NlGlobalChangeLogPromotionMask = DomainPromotionMask.HighPart;
|
|
|
|
NlGlobalLmBdcRidArray = NULL;
|
|
NlGlobalLmBdcCount = 0;
|
|
|
|
//
|
|
// 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 the events that are used by the LanmanNt PDC.
|
|
//
|
|
|
|
if ( NtProductType == NtProductLanManNt ) {
|
|
|
|
//
|
|
// 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 worker queue notify event.
|
|
//
|
|
|
|
NlGlobalChangeLogWorkerQueueEvent =
|
|
CreateEvent( NULL, // No security attributes
|
|
FALSE, // Is automatically reset
|
|
FALSE, // Initially not signaled
|
|
NULL ); // No name
|
|
|
|
if ( NlGlobalChangeLogWorkerQueueEvent == NULL ) {
|
|
NET_API_STATUS NetStatus;
|
|
|
|
NetStatus = GetLastError();
|
|
NlPrint((NL_CRITICAL,
|
|
"Cannot create Worker Queue Event %lu\n",
|
|
NetStatus ));
|
|
return (int) NetpApiStatusToNtStatus(NetStatus);
|
|
}
|
|
|
|
//
|
|
// 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
|
|
&NlGlobalChWorkerBuiltinDomainSid);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Success...
|
|
//
|
|
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Cleanup
|
|
//
|
|
|
|
Cleanup:
|
|
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// netlogon.dll never detaches
|
|
//
|
|
#ifdef NETLOGON_PROCESS_DETACH
|
|
|
|
NTSTATUS
|
|
NlCloseChangeLog(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees any resources consumed by NlInitChangeLog.
|
|
|
|
Arguments:
|
|
|
|
NONE
|
|
|
|
Return Value:
|
|
|
|
NT Status code
|
|
|
|
--*/
|
|
{
|
|
|
|
if ( (NlGlobalChangeLogDesc.FileHandle == INVALID_HANDLE_VALUE) &&
|
|
(NlGlobalChangeLogRole == ChangeLogPrimary) ) {
|
|
|
|
//
|
|
// try to save change log cache one last time.
|
|
//
|
|
|
|
(VOID)NlCreateChangeLogFile( &NlGlobalChangeLogDesc );
|
|
}
|
|
|
|
if ( NlGlobalChangeLogDesc.FileHandle != INVALID_HANDLE_VALUE ) {
|
|
CloseHandle( NlGlobalChangeLogDesc.FileHandle );
|
|
NlGlobalChangeLogDesc.FileHandle = INVALID_HANDLE_VALUE;
|
|
}
|
|
NlGlobalChangeLogFilePrefix[0] = L'\0';
|
|
|
|
if ( NlGlobalChangeLogDesc.Buffer != NULL ) {
|
|
NetpMemoryFree( NlGlobalChangeLogDesc.Buffer );
|
|
NlGlobalChangeLogDesc.Buffer = NULL;
|
|
}
|
|
|
|
if ( NlGlobalChWorkerBuiltinDomainSid != NULL ) {
|
|
RtlFreeSid( NlGlobalChWorkerBuiltinDomainSid );
|
|
NlGlobalChWorkerBuiltinDomainSid = NULL;
|
|
}
|
|
|
|
if ( NlGlobalChWorkerSamDomainSid != NULL ) {
|
|
NetpMemoryFree( NlGlobalChWorkerSamDomainSid );
|
|
NlGlobalChWorkerSamDomainSid = NULL;
|
|
}
|
|
|
|
if ( NlGlobalChangeLogEvent != NULL ) {
|
|
(VOID) CloseHandle(NlGlobalChangeLogEvent);
|
|
NlGlobalChangeLogEvent = NULL;
|
|
}
|
|
|
|
if ( NlGlobalChangeLogWorkerQueueEvent != NULL ) {
|
|
(VOID) CloseHandle(NlGlobalChangeLogWorkerQueueEvent);
|
|
NlGlobalChangeLogWorkerQueueEvent = NULL;
|
|
}
|
|
|
|
//
|
|
// if worker thread running, stop it.
|
|
//
|
|
|
|
NlStopChangeLogWorker();
|
|
|
|
LOCK_CHANGELOG();
|
|
|
|
NlAssert( IsListEmpty( &NlGlobalChangeLogNotifications ) );
|
|
NlAssert( IsListEmpty( &NlGlobalChangeLogWorkerQueue ) );
|
|
|
|
UNLOCK_CHANGELOG();
|
|
|
|
NlGlobalChangeLogWorkInit = FALSE;
|
|
|
|
//
|
|
// close all handles
|
|
//
|
|
|
|
if ( NlGlobalChWorkerSamServerHandle != NULL ) {
|
|
|
|
(VOID)SamrCloseHandle( &NlGlobalChWorkerSamServerHandle);
|
|
}
|
|
|
|
if ( NlGlobalChWorkerPolicyHandle != NULL ) {
|
|
|
|
(VOID)LsarClose( &NlGlobalChWorkerPolicyHandle);
|
|
}
|
|
|
|
if ( NlGlobalChWorkerSamDBHandle != NULL ) {
|
|
|
|
(VOID)SamrCloseHandle( &NlGlobalChWorkerSamDBHandle);
|
|
}
|
|
|
|
if ( NlGlobalChWorkerBuiltinDBHandle != NULL ) {
|
|
|
|
(VOID)SamrCloseHandle( &NlGlobalChWorkerBuiltinDBHandle);
|
|
}
|
|
|
|
DeleteCriticalSection( &NlGlobalChangeLogCritSect );
|
|
#if DBG
|
|
DeleteCriticalSection( &NlGlobalLogFileCritSect );
|
|
#endif // DBG
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
#endif // NETLOGON_PROCESS_DETACH
|