2025-04-27 07:49:33 -04:00

3242 lines
76 KiB
C

/*--
Copyright (c) 1993 Microsoft Corporation
Module Name:
monutil.c
Abstract:
Trusted Domain monitor program support functions.
Author:
10-May-1993 (madana)
Environment:
User mode only.
Contains NT-specific code.
Requires ANSI C extensions: slash-slash comments, long external names.
Revision History:
--*/
#define GLOBAL_DEF
#include <nlmon.h>
#define NlMonpInitUnicodeString( _dst_, _src_, _buf_) \
(_dst_)->Length = (_src_)->Length; \
(_dst_)->MaximumLength = (_src_)->Length + sizeof(WCHAR); \
(_dst_)->Buffer = (LPWSTR) (_buf_); \
RtlCopyMemory( (LPWSTR) (_buf_), (_src_)->Buffer, (_src_)->Length ); \
((LPWSTR) (_buf_))[ (_src_)->Length / sizeof(WCHAR) ] = '\0';
PLIST_ENTRY
FindNamedEntry(
PLIST_ENTRY List,
PUNICODE_STRING Name
)
/*++
Routine Description:
This function returns the specified named entry pointer if the entry
doesn't exist it returns NULL.
Arguments:
DCList - List to browse.
Name - name of the entry whose pointer will be returned.
Return Value:
Domain entry pointer.
--*/
{
PLIST_ENTRY NextEntry;
PENTRY Entry;
for( NextEntry = List->Flink;
NextEntry != List; NextEntry = NextEntry->Flink ) {
Entry = (PENTRY) NextEntry;
if( RtlCompareUnicodeString( &Entry->Name, Name, TRUE ) == 0 ) {
//
// entry already there in the list. return the entry
// pointer.
//
return( NextEntry );
}
}
//
// entry not found.
//
return NULL;
}
PDOMAIN_ENTRY
AddToDomainList(
PUNICODE_STRING DomainName
)
/*++
Routine Description:
Create a new domain entry and add to GlobalDomains list.
Enter with list locked.
Arguments:
DomainName - name of the new domain added to the list.
Return Value:
Pointer to the new domain structure.
--*/
{
PDOMAIN_ENTRY NewEntry;
//
// if this domain is already in the list, just return the entry
// pointer.
//
NewEntry = (PDOMAIN_ENTRY) FindNamedEntry(
&GlobalDomains,
DomainName );
if( NewEntry != NULL ) {
//
// Domain entry already in there.
//
return(NewEntry);
}
//
// Allocate space for the new entry
//
NewEntry = NetpMemoryAllocate(
sizeof(DOMAIN_ENTRY) +
DomainName->Length + sizeof(WCHAR) );
if( NewEntry == NULL ) {
NlMonDbgPrint(("AddToDomainList: can't allocate memory for "
"new domain entry.\n"));
return NULL;
}
NlMonpInitUnicodeString(
&NewEntry->Name, DomainName, (LPWSTR) (NewEntry + 1) );
InitializeListHead( &NewEntry->DCList );
InitializeListHead( &NewEntry->TrustedDomainList );
NewEntry->DomainState = DomainUnknown;
NewEntry->ReferenceCount = 0;
NewEntry->IsMonitoredDomain = FALSE;
NewEntry->UpdateFlags = 0;
NewEntry->ThreadHandle = NULL;
NewEntry->ThreadTerminateFlag = FALSE;
NewEntry->LastUpdateTime = 0;
//
// add it to domain list.
//
InsertTailList(&GlobalDomains, (PLIST_ENTRY)NewEntry);
return NewEntry;
}
PMONITORED_DOMAIN_ENTRY
AddToMonitoredDomainList(
PUNICODE_STRING DomainName
)
/*++
Routine Description:
Create a new monitored domain entry and add it to the
GlobalDomainsMonitored list.
Enter with list locked.
Arguments:
DomainName - name of the new domain added to the list.
Return Value:
Pointer to the new domain structure.
--*/
{
PDOMAIN_ENTRY DomainEntry;
PMONITORED_DOMAIN_ENTRY NewEntry;
//
// if the domain is already monitored, just return entry pointer.
//
NewEntry = (PMONITORED_DOMAIN_ENTRY)
FindNamedEntry( &GlobalDomainsMonitored, DomainName );
if( NewEntry != NULL ) {
NewEntry->DeleteFlag = FALSE;
return NewEntry;
}
//
// allocate space for the new domain that will be monitored.
//
NewEntry = NetpMemoryAllocate(
sizeof(MONITORED_DOMAIN_ENTRY) +
DomainName->Length + sizeof(WCHAR) );
if( NewEntry == NULL ) {
NlMonDbgPrint(("AddToMonitoredDomainList: "
"can't allocate memory for "
"new domain entry.\n"));
return NULL;
}
NlMonpInitUnicodeString(
&NewEntry->Name, DomainName, (LPWSTR) (NewEntry + 1) );
DomainEntry = AddToDomainList( DomainName );
if( DomainEntry == NULL ) {
//
// can't create new domain entry.
//
NetpMemoryFree( NewEntry );
return(NULL);
}
NewEntry->DomainEntry = DomainEntry;
NewEntry->DeleteFlag = FALSE;
DomainEntry->ReferenceCount++;
DomainEntry->IsMonitoredDomain = TRUE;
//
// add it to GlobalDomainsMonitored list.
//
InsertTailList(&GlobalDomainsMonitored, (PLIST_ENTRY)NewEntry);
return(NewEntry);
}
PTRUSTED_DOMAIN_ENTRY
AddToTrustedDomainList(
PLIST_ENTRY List,
PUNICODE_STRING DomainName
)
/*++
Routine Description:
Create a new monitored domain entry and add it to the
GlobalDomainsTrusted list.
Enter with list locked.
Arguments:
List - List to be modified.
DomainName - name of the new domain added to the list.
Return Value:
Pointer to the new domain structure.
--*/
{
PDOMAIN_ENTRY DomainEntry;
PTRUSTED_DOMAIN_ENTRY NewEntry;
//
// if the domain is already trusted, just return entry pointer.
//
NewEntry = (PTRUSTED_DOMAIN_ENTRY)
FindNamedEntry( List, DomainName );
if( NewEntry != NULL ) {
NewEntry->DeleteFlag = FALSE;
return NewEntry;
}
//
// allocate space for the new domain that will be monitored.
//
NewEntry = NetpMemoryAllocate(
sizeof(TRUSTED_DOMAIN_ENTRY) +
DomainName->Length + sizeof(WCHAR) );
if( NewEntry == NULL ) {
NlMonDbgPrint(("AddToTrustedDomainList: "
"can't allocate memory for "
"new domain entry.\n"));
return NULL;
}
NlMonpInitUnicodeString(
&NewEntry->Name, DomainName, (LPWSTR) (NewEntry + 1) );
DomainEntry = AddToDomainList( DomainName );
if( DomainEntry == NULL ) {
//
// can't create new domain entry.
//
NetpMemoryFree( NewEntry );
return(NULL);
}
NewEntry->DomainEntry = DomainEntry;
NewEntry->DeleteFlag = FALSE;
DomainEntry->ReferenceCount++;
//
// add it to list.
//
InsertTailList(List, (PLIST_ENTRY)NewEntry);
return(NewEntry);
}
BOOL
InitDomainListW(
LPWSTR DomainList
)
/*++
Routine Description:
Parse comma separated domain list.
Arguments:
DomainList - comma separate domain list.
Return Value:
TRUE - if successfully parsed.
FALSE - iff the list is bad.
--*/
{
WCHAR DomainName[DNLEN + 1];
PWCHAR d;
PWCHAR p;
DWORD Len;
p = DomainList;
if( *p == L'\0' ) {
return(FALSE);
}
while (*p != L'\0') {
d = DomainName; // destination to next domain name.
while( (*p != L'\0') && (*p == L' ') ) {
p++; // skip leading blanks.
}
//
// read next domain name.
//
while( (*p != L'\0') && (*p != L',') ) {
if( d < DomainName + DNLEN ) {
*d++ = (WCHAR) (*p++);
}
}
if( *p != L'\0' ) {
p++; // skip comma.
}
//
// delete tail end blanks.
//
while ( (d > DomainName) && (*(d-1) == L' ') ) {
d--;
}
*d = L'\0';
if( Len = wcslen(DomainName) ) {
UNICODE_STRING UnicodeDomainName;
RtlInitUnicodeString( &UnicodeDomainName, DomainName );
LOCK_LISTS();
if( AddToMonitoredDomainList( &UnicodeDomainName ) == NULL ) {
UNLOCK_LISTS();
return(FALSE);
}
UNLOCK_LISTS();
}
}
if( IsListEmpty( &GlobalDomainsMonitored ) ) {
return(FALSE);
}
return(TRUE);
}
VOID
ConvertServerAcctNameToServerName(
PUNICODE_STRING ServerAccountName
)
/*++
Routine Description:
Convert user account type name to server type name. By current convension
the servers account name has a "$" sign at the end. ie.
ServerAccountName = ServerName + "$"
Arguments:
ServerAccountName - server account name.
Return Value:
none.
--*/
{
//
// strip off "$" sign from account name.
//
ServerAccountName->Length -= sizeof(WCHAR);
return;
}
NTSTATUS
QueryLsaInfo(
PUNICODE_STRING ServerName,
ACCESS_MASK DesiredAccess,
POLICY_INFORMATION_CLASS InformationClass,
PVOID *Info,
PLSA_HANDLE ReturnHandle //optional
)
/*++
Routine Description:
Open LSA database on the remote server, query requested info and
return lsa handle if asked otherwise close it.
Arguments:
ServerName - Remote machine name.
DesiredAccess - Required access.
InformationClass - info class to be returned.
Info - pointer to a location where the return info buffer pointer is
placed. Caller should free this buffer.
ReturnHandle - if this is non-NULL pointer, LSA handle is returned
here. caller should close this handle after use.
Return Value:
NTSTATUS.
--*/
{
NTSTATUS Status;
OBJECT_ATTRIBUTES ObjectAttributes;
LSA_HANDLE PolicyHandle;
*Info = NULL;
//
// Open Local policy.
//
INIT_OBJ_ATTR(ObjectAttributes);
Status = LsaOpenPolicy( ServerName,
&ObjectAttributes,
DesiredAccess,
&PolicyHandle );
if( !NT_SUCCESS(Status) ) {
NlMonDbgPrint(("QueryLsaInfo: "
"Cannot open LSA Policy database on server %wZ, %lx.\n",
ServerName, Status ));
return Status;
}
//
// read primary domain info.
//
Status = LsaQueryInformationPolicy(
PolicyHandle,
InformationClass,
Info );
if( !NT_SUCCESS(Status) ) {
NlMonDbgPrint(("QueryLsaInfo: "
"Can't read domain info from %wZ's database, %lx.\n",
ServerName, Status));
LsaClose(PolicyHandle);
return Status;
}
if( ReturnHandle != NULL ) {
*ReturnHandle = PolicyHandle;
}
else {
LsaClose(PolicyHandle);
}
return Status;
}
NET_API_STATUS
IsValidNTDC(
PUNICODE_STRING ServerName,
PUNICODE_STRING DomainName
)
/*++
Routine Description:
This function determines that the given server is valid NT Domain
Controller on the given domain.
Arguments:
ServerName - name of the server which has to be validated.
DomainName - name of the domain on which this DC is member.
Return Value:
NET_API_STATUS code.
--*/
{
NET_API_STATUS NetStatus;
PWKSTA_INFO_100 WkstaInfo100 = NULL;
PSERVER_INFO_101 ServerInfo101 = NULL;
//
// ServerName should be non-null string.
//
if( (ServerName->Length <= 0) ||
(ServerName->Buffer == NULL) ||
(*ServerName->Buffer == L'\0') ) {
NetStatus = NERR_BadServiceName;
goto Cleanup;
}
//
// check to see that the server is still member of specified domain.
//
NetStatus = NetWkstaGetInfo(
ServerName->Buffer,
100,
(LPBYTE *)&WkstaInfo100
);
if( (GlobalTerminateFlag) || (NetStatus != NERR_Success) ) {
goto Cleanup;
}
if ( I_NetNameCompare( NULL,
DomainName->Buffer,
WkstaInfo100->wki100_langroup,
NAMETYPE_DOMAIN,
0L) != 0 ) {
goto Cleanup;
}
//
// check the server type is appropriate.
//
NetStatus = NetServerGetInfo(
ServerName->Buffer,
101,
(LPBYTE *)&ServerInfo101
);
if( (GlobalTerminateFlag) || (NetStatus != NERR_Success) ) {
goto Cleanup;
}
if (!((ServerInfo101->sv101_type &
(SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL)) &&
(ServerInfo101->sv101_type & SV_TYPE_NT)) ) {
//
// this server isn't NT Domain Controller.
//
NetStatus = ERROR_INVALID_DOMAIN_ROLE;
}
Cleanup:
if ( ServerInfo101 != NULL ) {
NetApiBufferFree( ServerInfo101 );
}
if ( WkstaInfo100 != NULL ) {
NetApiBufferFree( WkstaInfo100 );
}
IF_DEBUG( VERBOSE ) {
NlMonDbgPrint(("IsValidNTDC: ServerName = %wZ, "
"DomainName = %wZ, and NetStatus = %ld .\n",
ServerName, DomainName, NetStatus ));
}
return NetStatus;
}
NET_API_STATUS
ValidateDC(
PDC_ENTRY DCEntry,
PUNICODE_STRING DomainName
)
/*++
Routine Description:
This function validates the given DCEntry and sets various fields of
this structures.
Arguments:
DCEntry - pointer to DC entry structure.
DomainName - name of the domain of which this DC is member.
Return Value:
NET_API_STATUS code.
--*/
{
NET_API_STATUS NetStatus = NERR_Success;
PWKSTA_INFO_100 WkstaInfo100 = NULL;
PSERVER_INFO_101 ServerInfo101 = NULL;
PNETLOGON_INFO_1 NetlogonInfo1 = NULL;
LPWSTR InputDataPtr = NULL;
//
// lock this list while we update this entry status.
//
LOCK_LISTS();
//
// retry once in RETRY_COUNT calls.
//
if( ( DCEntry->State == DCOffLine ) ||
( DCEntry->DCStatus != NERR_Success) ) {
if( DCEntry->RetryCount != 0 ) {
DCEntry->RetryCount = (DCEntry->RetryCount + 1) % RETRY_COUNT;
NetStatus = DCEntry->DCStatus;
goto Cleanup;
}
DCEntry->RetryCount = (DCEntry->RetryCount + 1) % RETRY_COUNT;
}
//
// check to see that the server is still on specified domain
//
UNLOCK_LISTS();
NetStatus = NetWkstaGetInfo(
DCEntry->DCName.Buffer,
100,
(LPBYTE * )&WkstaInfo100 );
LOCK_LISTS();
if ( NetStatus != NERR_Success ) {
if( NetStatus == ERROR_BAD_NETPATH ) {
DCEntry->State = DCOffLine;
}
goto Cleanup;
}
if( GlobalTerminateFlag ) {
goto Cleanup;
}
if ( I_NetNameCompare( NULL,
DomainName->Buffer,
WkstaInfo100->wki100_langroup,
NAMETYPE_DOMAIN,
0L) != 0 ) {
NetStatus = ERROR_INVALID_DOMAIN_STATE;
goto Cleanup;
}
//
// check the server type is appropriate.
//
UNLOCK_LISTS();
NetStatus = NetServerGetInfo(
DCEntry->DCName.Buffer,
101,
(LPBYTE *)&ServerInfo101 );
LOCK_LISTS();
if( (GlobalTerminateFlag) || (NetStatus != NERR_Success) ) {
goto Cleanup;
}
if( ServerInfo101->sv101_type & SV_TYPE_NT ) {
if ( ServerInfo101->sv101_type & SV_TYPE_DOMAIN_CTRL ) {
DCEntry->Type = NTPDC;
} else if ( ServerInfo101->sv101_type & SV_TYPE_DOMAIN_BAKCTRL ) {
DCEntry->Type = NTBDC;
} else {
NetStatus = ERROR_INVALID_DOMAIN_ROLE;
goto Cleanup;
}
}
else {
if ( ServerInfo101->sv101_type & SV_TYPE_DOMAIN_BAKCTRL ) {
DCEntry->Type = LMBDC;
}
else {
NetStatus = ERROR_INVALID_DOMAIN_ROLE;
goto Cleanup;
}
}
if( DCEntry->Type != LMBDC ) {
//
// Query netlogon to determine replication status and connection
// status to its PDC.
//
UNLOCK_LISTS();
NetStatus = I_NetLogonControl2(
DCEntry->DCName.Buffer,
NETLOGON_CONTROL_QUERY,
1,
(LPBYTE)&InputDataPtr,
(LPBYTE *)&NetlogonInfo1 );
LOCK_LISTS();
if( (GlobalTerminateFlag) || (NetStatus != NERR_Success) ) {
goto Cleanup;
}
DCEntry->ReplicationStatus = NetlogonInfo1->netlog1_flags;
DCEntry->PDCLinkStatus = NetlogonInfo1->netlog1_pdc_connection_status;
}
DCEntry->State = DCOnLine;
Cleanup:
if ( NetlogonInfo1 != NULL ) {
NetApiBufferFree( NetlogonInfo1 );
}
if ( ServerInfo101 != NULL ) {
NetApiBufferFree( ServerInfo101 );
}
if ( WkstaInfo100 != NULL ) {
NetApiBufferFree( WkstaInfo100 );
}
if ( NetStatus != NERR_Success ) {
//
// set other status to unknown.
//
DCEntry->ReplicationStatus = UNKNOWN_REPLICATION_STATE;
DCEntry->PDCLinkStatus = ERROR_BAD_NETPATH;
}
DCEntry->DCStatus = NetStatus;
UNLOCK_LISTS();
IF_DEBUG( VERBOSE ) {
NlMonDbgPrint(("ValidateDC: ServerName = %wZ, "
"DomainName = %wZ, and NetStatus = %ld.\n",
&DCEntry->DCName, DomainName, NetStatus ));
}
return NetStatus;
}
PDC_ENTRY
CreateDCEntry (
PUNICODE_STRING DCName,
DC_TYPE Type
)
/*++
Routine Description:
Create a DC Entry;
Arguments:
DCName - entry name in unc form.
Type - DC type.
Return Value:
Entry pointer. If it can't allocate memory, it returns NULL pointer.
--*/
{
PDC_ENTRY DCEntry;
DCEntry = NetpMemoryAllocate(
sizeof(DC_ENTRY) +
DCName->Length + sizeof(WCHAR) );
if( DCEntry != NULL ) {
NlMonpInitUnicodeString(
&DCEntry->DCName, DCName, (LPWSTR) (DCEntry + 1) );
DCEntry->State = DCOffLine;
DCEntry->Type = Type;
DCEntry->DCStatus = ERROR_BAD_NETPATH;
DCEntry->ReplicationStatus = UNKNOWN_REPLICATION_STATE; //unknown state.
DCEntry->PDCLinkStatus = ERROR_BAD_NETPATH;
DCEntry->DeleteFlag = FALSE;
DCEntry->RetryCount = 0;
InitializeListHead( &DCEntry->TrustedDCs );
DCEntry->TDCLinkState = FALSE;
}
return( DCEntry );
}
NTSTATUS
UpdateDCListFromNTServerAccounts(
SAM_HANDLE SamHandle,
PLIST_ENTRY DCList
)
/*++
Routine Description:
Merge NT server accounts defined in the database to DCList.
Arguments:
SamHandle : Handle SAM database.
DCList : linked list of DCs.
Return Value:
NTSTATUS code.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
PDOMAIN_DISPLAY_MACHINE MachineInformation = NULL;
DWORD SamIndex = 0;
DWORD EntriesRead;
ULONG TotalBytesAvailable;
ULONG BytesReturned;
DWORD i;
PDC_ENTRY DCEntry;
//
// enumerate NT Server accounts
//
do {
//
// Get the list of machine accounts from SAM
//
Status = SamQueryDisplayInformation(
SamHandle,
DomainDisplayMachine,
SamIndex,
MACHINES_PER_PASS,
0xFFFFFFFF,
&TotalBytesAvailable,
&BytesReturned,
&EntriesRead,
&MachineInformation );
if ( (Status != STATUS_NO_MORE_ENTRIES) &&
(!NT_SUCCESS(Status)) ) {
NlMonDbgPrint(("UpdateDCListFromNTServerAccounts: "
"SamrQueryDisplayInformation returned, %lx.\n",
Status));
goto Cleanup;
}
if( GlobalTerminateFlag ) {
goto Cleanup;
}
//
// Set up for the next call to Sam.
//
if ( Status == STATUS_MORE_ENTRIES ) {
SamIndex = MachineInformation[EntriesRead-1].Index + 1;
}
//
// Loop though the list of machine accounts finding the Server accounts.
//
for ( i = 0; i < EntriesRead; i++ ) {
//
// Ensure the machine account is a server account.
//
if ( MachineInformation[i].AccountControl &
USER_SERVER_TRUST_ACCOUNT ) {
WCHAR UncComputerName[MAX_PATH + 3];
UNICODE_STRING UnicodeComputerName;
ConvertServerAcctNameToServerName(
&MachineInformation[i].Machine ); // in place conversion.
//
// form unicode unc computer name.
//
UncComputerName[0] = UncComputerName[1] = L'\\';
RtlCopyMemory(
UncComputerName + 2,
MachineInformation[i].Machine.Buffer,
MachineInformation[i].Machine.Length );
UncComputerName[
MachineInformation[i].Machine.Length /
sizeof(WCHAR) + 2] = L'\0';
RtlInitUnicodeString( &UnicodeComputerName, UncComputerName);
IF_DEBUG( VERBOSE ) {
NlMonDbgPrint(("UpdateDCListFromNTServerAccounts: "
"NT Server %wZ is found on database.\n",
&UnicodeComputerName ));
}
DCEntry = (PDC_ENTRY) FindNamedEntry(
DCList,
&UnicodeComputerName );
if( DCEntry != NULL ) {
DCEntry->DeleteFlag = FALSE;
}
else {
DCEntry = CreateDCEntry( &UnicodeComputerName, NTBDC );
//
// silently ignore NULL entry.
//
if( DCEntry != NULL ) {
//
// add to list.
//
LOCK_LISTS();
InsertTailList(DCList, (PLIST_ENTRY)DCEntry);
UNLOCK_LISTS();
}
}
}
}
//
// free up the memory used up memory.
//
if( MachineInformation != NULL ) {
(VOID) SamFreeMemory( MachineInformation );
MachineInformation = NULL;
}
} while ( Status == STATUS_MORE_ENTRIES );
Cleanup:
if( MachineInformation != NULL ) {
(VOID) SamFreeMemory( MachineInformation );
}
return Status;
}
NTSTATUS
UpdateDCListFromLMServerAccounts(
SAM_HANDLE SamHandle,
PLIST_ENTRY DCList
)
/*++
Routine Description:
Read LM server accounts from SAM "Servers" global group and update
the DC list using this accounts.
Arguments:
SamHandle : Handle SAM database.
DCList : linked list of DCs.
Return Value:
NTSTATUS code.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
UNICODE_STRING NameString;
PSID_NAME_USE NameUse = NULL;
PULONG RelativeId = NULL;
SAM_HANDLE GroupHandle = NULL;
PULONG MemberAttributes = NULL;
PULONG MemberIds = NULL;
ULONG MemberCount;
PUNICODE_STRING MemberNames = NULL;
PSID_NAME_USE MemberNameUse = NULL;
PDC_ENTRY DCEntry;
DWORD i;
//
// Get RID of SERVERS group.
//
RtlInitUnicodeString( &NameString, SERVERS_GROUP );
Status = SamLookupNamesInDomain(
SamHandle,
1,
&NameString,
&RelativeId,
&NameUse );
if ( !NT_SUCCESS(Status) ) {
NlMonDbgPrint(( "UpdateDCListFromLMServerAccounts: SamLookupNamesInDomain "
" failed to transulate %wZ %lx.\n",
&NameString,
Status ));
goto Cleanup;
}
if ( *NameUse != SidTypeGroup ) {
NlMonDbgPrint(( "UpdateDCListFromLMServerAccounts: %wZ is not "
"SidTypeGroup, %ld.\n",
&NameString,
*NameUse ));
goto Cleanup;
}
//
// open group object.
//
Status = SamOpenGroup(
SamHandle,
GROUP_LIST_MEMBERS,
*RelativeId,
&GroupHandle);
if ( !NT_SUCCESS(Status) ) {
NlMonDbgPrint(( "UpdateDCListFromLMServerAccounts: SamOpenGroup of %wZ "
"failed, %lx.\n",
&NameString,
Status ));
goto Cleanup;
}
//
// get group members.
//
Status = SamGetMembersInGroup(
GroupHandle,
&MemberIds,
&MemberAttributes,
&MemberCount );
if ( !NT_SUCCESS( Status ) ) {
NlMonDbgPrint(("UpdateDCListFromLMServerAccounts: SamGetMembersInGroup "
"returned %lx.\n", Status ));
goto Cleanup;
}
if( GlobalTerminateFlag ) {
goto Cleanup;
}
if( MemberCount == 0) {
//
// nothing more to do.
//
goto Cleanup;
}
//
// trasulate members IDs.
//
Status = SamLookupIdsInDomain(
SamHandle,
MemberCount,
MemberIds,
&MemberNames,
&MemberNameUse );
if ( !NT_SUCCESS( Status ) ) {
NlMonDbgPrint((
"UpdateDCListFromLMServerAccounts: SamLookupIdsInDomain "
"returned %lx.\n", Status ));
goto Cleanup;
}
if( GlobalTerminateFlag ) {
goto Cleanup;
}
//
// add user members to list.
//
for (i = 0 ; i < MemberCount; i++) {
WCHAR UncComputerName[MAX_PATH + 3];
UNICODE_STRING UnicodeComputerName;
if ( MemberNameUse[i] != SidTypeUser ) {
continue;
}
//
// form unicode unc computer name.
//
UncComputerName[0] = UncComputerName[1] = L'\\';
RtlCopyMemory(
UncComputerName + 2,
MemberNames[i].Buffer,
MemberNames[i].Length );
UncComputerName[MemberNames[i].Length / sizeof(WCHAR) + 2] = L'\0';
RtlInitUnicodeString( &UnicodeComputerName, UncComputerName);
IF_DEBUG( VERBOSE ) {
NlMonDbgPrint(("UpdateDCListFromLMServerAccounts: "
"LM Server %wZ is found on database.\n",
&UnicodeComputerName ));
}
DCEntry = (PDC_ENTRY) FindNamedEntry(
DCList,
&UnicodeComputerName );
if( DCEntry != NULL ) {
DCEntry->DeleteFlag = FALSE;
}
else {
//
// create new entry and add to list
//
DCEntry = CreateDCEntry( &UnicodeComputerName, LMBDC );
//
// silently ignore NULL entries.
//
if( DCEntry != NULL ) {
//
// add to list.
//
LOCK_LISTS();
InsertTailList(DCList, (PLIST_ENTRY)DCEntry);
UNLOCK_LISTS();
}
}
}
Cleanup:
//
// Free up local resources.
//
if( NameUse != NULL ) {
(VOID) SamFreeMemory( NameUse );
}
if( RelativeId != NULL ) {
(VOID) SamFreeMemory( RelativeId );
}
if( MemberAttributes != NULL ) {
(VOID) SamFreeMemory( MemberAttributes );
}
if( MemberIds != NULL ) {
(VOID) SamFreeMemory( MemberIds );
}
if( MemberNames != NULL ) {
(VOID) SamFreeMemory( MemberNames );
}
if( MemberNameUse != NULL ) {
(VOID) SamFreeMemory( MemberNameUse );
}
if( GroupHandle != NULL ) {
SamCloseHandle( GroupHandle );
}
return Status;
}
VOID
UpdateDCListFromDatabase(
PUNICODE_STRING DomainName,
PLIST_ENTRY DCList
)
/*++
Routine Description:
Update the List of DCs on a given domain using database entries.
Arguments:
DomainName : name of the domain whose DCList to be updated.
DCList : linked list of DCs. DCList must be non-empty.
Return Value:
None.
--*/
{
NTSTATUS Status;
PUNICODE_STRING ServerName;
PPOLICY_ACCOUNT_DOMAIN_INFO AccountDomainInfo = NULL;
SAM_HANDLE SamConnectHandle = NULL;
SAM_HANDLE SamHandle = NULL;
PLIST_ENTRY NextEntry;
PDC_ENTRY DCEntry;
//
// pick a dc from current list.
//
ServerName = NULL;
for( NextEntry = DCList->Flink;
NextEntry != DCList; NextEntry = NextEntry->Flink ) {
DCEntry = (PDC_ENTRY) NextEntry;
if( GlobalTerminateFlag ) {
break;
}
if( IsValidNTDC( &DCEntry->DCName, DomainName ) == NERR_Success ) {
ServerName = &DCEntry->DCName;
break;
}
}
if( ServerName == NULL ) {
NlMonDbgPrint(( "UpdateDCListFromDatabase: "
"No DC is valid in %wZ domain list.\n", DomainName));
return;
}
IF_DEBUG( UPDATE ) {
NlMonDbgPrint(( "UpdateDCListFromDatabase: "
"picked %wZ to get DCList from its database.\n",
ServerName));
}
//
// Get Domain SID info from policy database.
//
Status = QueryLsaInfo(
ServerName,
POLICY_VIEW_LOCAL_INFORMATION,
PolicyAccountDomainInformation,
(PVOID *)&AccountDomainInfo,
NULL ) ;
if( !NT_SUCCESS( Status ) ) {
NlMonDbgPrint(("UpdateDCListFromDatabase: QueryLsaInfo returned %lx.\n",
Status ));
goto Cleanup;
}
//
// Connect to SAM.
//
Status = SamConnect(
ServerName,
&SamConnectHandle,
SAM_SERVER_LOOKUP_DOMAIN,
NULL); // object attributes.
if( !NT_SUCCESS( Status ) ) {
NlMonDbgPrint(("UpdateDCListFromDatabase: SamConnect returned %lx.\n",
Status ));
SamConnectHandle = NULL;
goto Cleanup;
}
//
// Open Account SAM domain.
//
Status = SamOpenDomain(
SamConnectHandle,
DOMAIN_LIST_ACCOUNTS | DOMAIN_LOOKUP,
AccountDomainInfo->DomainSid,
&SamHandle );
if( !NT_SUCCESS( Status ) ) {
NlMonDbgPrint(("UpdateDCListFromDatabase: SamOpenDomain returned "
"%lx.\n", Status ));
SamHandle = NULL;
goto Cleanup;
}
//
// mark all entries in the current list to be deleted.
//
for( NextEntry = DCList->Flink;
NextEntry != DCList; NextEntry = NextEntry->Flink ) {
DCEntry = (PDC_ENTRY) NextEntry;
DCEntry->DeleteFlag = TRUE;
}
if( GlobalTerminateFlag ) {
goto Cleanup;
}
//
// enumerate NT Server accounts
//
Status = UpdateDCListFromNTServerAccounts( SamHandle, DCList );
if( !NT_SUCCESS( Status ) ) {
NlMonDbgPrint(("UpdateDCListFromDatabase: "
"UpdateDCListFromNTServerAccounts returned "
"%lx.\n", Status ));
goto Cleanup;
}
if( GlobalTerminateFlag ) {
goto Cleanup;
}
//
// Now enumerate down level DCs.
//
Status = UpdateDCListFromLMServerAccounts( SamHandle, DCList );
if( !NT_SUCCESS( Status ) ) {
NlMonDbgPrint(("UpdateDCListFromDatabase: "
"UpdateDCListFromLMServerAccounts returned "
"%lx.\n", Status ));
goto Cleanup;
}
Cleanup :
//
// if we have successfully updated the list then
// delete entries that are marked deleted otherwise
// mark them undelete.
//
LOCK_LISTS();
for( NextEntry = DCList->Flink;
NextEntry != DCList; ) {
DCEntry = (PDC_ENTRY) NextEntry;
NextEntry = NextEntry->Flink;
if( DCEntry->DeleteFlag ) {
if( Status == STATUS_SUCCESS ) {
DCEntry->DeleteFlag = FALSE;
}
else {
RemoveEntryList( (PLIST_ENTRY)DCEntry );
NetpMemoryFree( DCEntry );
}
}
}
UNLOCK_LISTS();
if( AccountDomainInfo != NULL ) {
(VOID) LsaFreeMemory( AccountDomainInfo );
}
if( SamHandle != NULL ) {
(VOID) SamCloseHandle ( SamHandle );
}
if( SamConnectHandle != NULL ) {
(VOID) SamCloseHandle ( SamConnectHandle );
}
return;
}
VOID
UpdateDCListByServerEnum(
PUNICODE_STRING DomainName,
PLIST_ENTRY DCList
)
/*++
Routine Description:
Update the List of DCs on a given domain by calling NetServerEnum.
If NetServerEnum failes, it calls NetGetDCName to atleast determine
PDC of the domain.
Arguments:
DomainName : name of the domain whose DCList to be updated.
DCList : linked list of DCs.
Return Value:
None.
--*/
{
NET_API_STATUS NetStatus;
PSERVER_INFO_101 ServerInfo101 = NULL;
DWORD EntriesRead;
DWORD TotalEntries;
PDC_ENTRY DCEntry;
DWORD i;
NetStatus = NetServerEnum( NULL,
101,
(LPBYTE *) &ServerInfo101,
(ULONG)(-1), // Prefmaxlen
&EntriesRead,
&TotalEntries,
SV_TYPE_DOMAIN_CTRL | SV_TYPE_DOMAIN_BAKCTRL,
DomainName->Buffer,
NULL ); // Resume Handle
if( NetStatus != NERR_Success ) {
if( NetStatus != ERROR_NO_BROWSER_SERVERS_FOUND ) {
NlMonDbgPrint(("UpdateDCListByServerEnum: "
"NetServerEnum called with domain name %wZ Failed, "
"%ld.\n", DomainName, NetStatus));
}
ServerInfo101 = NULL;
EntriesRead = 0;
}
if( GlobalTerminateFlag ) {
goto Cleanup;
}
//
// update DC List using ServerEnumList.
//
for( i = 0; i < EntriesRead; i++ ) {
WCHAR UncComputerName[MAX_PATH + 3];
UNICODE_STRING UnicodeComputerName;
DC_TYPE ServerType;
UncComputerName[0] = UncComputerName[1] = L'\\';
wcscpy( UncComputerName + 2, ServerInfo101[i].sv101_name );
RtlInitUnicodeString(&UnicodeComputerName, UncComputerName);
if ( (ServerInfo101[i].sv101_type & SV_TYPE_NT) ) {
if ( ServerInfo101[i].sv101_type & SV_TYPE_DOMAIN_CTRL ) {
ServerType = NTPDC;
}
else {
ServerType = NTBDC;
}
}
else {
if ( ServerInfo101[i].sv101_type & SV_TYPE_DOMAIN_BAKCTRL ) {
ServerType = LMBDC;
}
else {
NlMonDbgPrint(("UpdateDCListByServerEnum: "
"NetServerEnum called with domain name %wZ "
"returned LM PDC %wZ.\n",
DomainName, &UnicodeComputerName ));
ServerType = LMBDC;
}
}
IF_DEBUG( VERBOSE ) {
NlMonDbgPrint(("UpdateDCListByServerEnum: "
"Server %wZ found in NetServerEnumList.\n",
&UnicodeComputerName ));
}
DCEntry = (PDC_ENTRY) FindNamedEntry( DCList, &UnicodeComputerName );
if( DCEntry != NULL ) {
DCEntry->Type = ServerType;
DCEntry->State = DCOnLine;
}
else {
//
// create new entry and add to list
//
DCEntry = CreateDCEntry( &UnicodeComputerName, ServerType );
//
// silently ignore NULL entries.
//
if( DCEntry != NULL ) {
//
// add to list.
//
DCEntry->State = DCOnLine;
LOCK_LISTS();
InsertTailList(DCList, (PLIST_ENTRY)DCEntry);
UNLOCK_LISTS();
}
}
}
Cleanup:
if( ServerInfo101 != NULL ) {
NetApiBufferFree( ServerInfo101 );
}
return;
}
VOID
UpdateTrustList(
PDOMAIN_ENTRY Domain
)
/*++
Routine Description:
This function creates/updates the trusted domains list.
Arguments:
Domain : pointer domain structure.
Return Value:
None.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
DWORD i;
PUNICODE_STRING ServerName = NULL;
PLIST_ENTRY ListHead;
PLIST_ENTRY NextEntry;
PDC_ENTRY DCEntry;
OBJECT_ATTRIBUTES ObjectAttributes;
LSA_HANDLE PolicyHandle = NULL;
LSA_ENUMERATION_HANDLE EnumContext;
PLSA_TRUST_INFORMATION TrustedDomainList = NULL;
DWORD Entries;
PTRUSTED_DOMAIN_ENTRY TDEntry;
IF_DEBUG( TRUST ) {
NlMonDbgPrint(("UpdateTrustList: called for domain %wZ.\n",
&Domain->Name ));
}
//
// pick up a DC to query the trusted domain list.
//
ListHead = &Domain->DCList;
//
// first try possibly good DCs.
//
for( NextEntry = ListHead->Flink;
NextEntry != ListHead; NextEntry = NextEntry->Flink ) {
DCEntry = (PDC_ENTRY) NextEntry;
if( (DCEntry->State == DCOnLine) &&
( DCEntry->DCStatus == NERR_Success ) ) {
if( IsValidNTDC( &DCEntry->DCName, &Domain->Name ) == NERR_Success ) {
ServerName = &DCEntry->DCName;
break;
}
}
}
if( ServerName == NULL ) {
//
// now try all.
//
for( NextEntry = ListHead->Flink;
NextEntry != ListHead; NextEntry = NextEntry->Flink ) {
DCEntry = (PDC_ENTRY) NextEntry;
if( IsValidNTDC( &DCEntry->DCName, &Domain->Name ) == NERR_Success ) {
ServerName = &DCEntry->DCName;
break;
}
}
}
if( ServerName == NULL ) {
IF_DEBUG( TRUST ) {
NlMonDbgPrint(( "UpdateTrustList: "
"No DC is valid in %wZ domain list.\n",
&Domain->Name));
}
goto Cleanup;
}
IF_DEBUG( TRUST ) {
NlMonDbgPrint(("UpdateTrustList: for domain %wZ picked %wZ to query "
"trusted domains.\n",
&Domain->Name, ServerName ));
}
//
// Open policy database.
//
INIT_OBJ_ATTR(ObjectAttributes);
Status = LsaOpenPolicy( ServerName,
&ObjectAttributes,
POLICY_VIEW_LOCAL_INFORMATION,
&PolicyHandle );
if( !NT_SUCCESS(Status) ) {
NlMonDbgPrint(("UpdateTrustList: "
"Cannot open LSA Policy on server %wZ, %lx.\n",
ServerName, Status ));
goto Cleanup;
}
//
// enum trusted domains.
//
EnumContext = 0;
Status = LsaEnumerateTrustedDomains(
PolicyHandle,
&EnumContext,
&TrustedDomainList,
(ULONG)-1,
&Entries );
if( !NT_SUCCESS(Status) &&
(Status != STATUS_NO_MORE_ENTRIES) ) {
NlMonDbgPrint(("UpdateTrustList: "
"Cannot Enumerate trust list on server %wZ, %lx.\n",
ServerName, Status ));
goto Cleanup;
}
if( GlobalTerminateFlag ) {
goto Cleanup;
}
//
// update trust list.
//
ListHead = &Domain->TrustedDomainList;
for ( i = 0; i < Entries; i++) {
TDEntry = (PTRUSTED_DOMAIN_ENTRY)
FindNamedEntry(
ListHead,
&TrustedDomainList[i].Name );
if( TDEntry == NULL ) {
IF_DEBUG( VERBOSE ) {
NlMonDbgPrint(("UpdateTrustList: "
"%wZ is added to %wZ domain trust list.\n",
&TrustedDomainList[i].Name,
&Domain->Name ));
}
//
// add this entry to list.
//
LOCK_LISTS();
if( AddToTrustedDomainList(
ListHead,
&TrustedDomainList[i].Name ) == NULL ) {
UNLOCK_LISTS();
NlMonDbgPrint(("UpdateTrustList: can't allocate memory for "
"new trusted domain entry.\n" ));
goto Cleanup;
}
UNLOCK_LISTS();
}
}
Cleanup :
if( TrustedDomainList != NULL ) {
LsaFreeMemory( TrustedDomainList );
}
if( PolicyHandle != NULL ) {
LsaClose( PolicyHandle );
}
return;
}
VOID
UpdateTrustConnectionList(
PDOMAIN_ENTRY Domain
)
/*++
Routine Description:
This function creates/updates trust connection entries of all DCs.
Arguments:
Domain : pointer domain structure.
Return Value:
None.
--*/
{
PLIST_ENTRY DCList;
PLIST_ENTRY NextDCEntry;
PDC_ENTRY DCEntry;
PLIST_ENTRY TrustConnectList;
PTD_LINK TrustConnectEntry;
PLIST_ENTRY TrustList;
PLIST_ENTRY NextTrustEntry;
PTRUSTED_DOMAIN_ENTRY TrustEntry;
//
// for each DC on this domain.
//
DCList = &Domain->DCList;
TrustList = &Domain->TrustedDomainList;
for( NextDCEntry = DCList->Flink;
NextDCEntry != DCList; NextDCEntry = NextDCEntry->Flink ) {
DCEntry = (PDC_ENTRY) NextDCEntry;
//
// do this update only for running DC
// and it should be NT DC.
//
if( (DCEntry->State != DCOnLine) ||
(DCEntry->Type == LMBDC) ){
continue;
}
TrustConnectList = &DCEntry->TrustedDCs;
//
// for each trusted domain, get connection status.
//
for( NextTrustEntry = TrustList->Flink;
NextTrustEntry != TrustList;
NextTrustEntry = NextTrustEntry->Flink ) {
TrustEntry = (PTRUSTED_DOMAIN_ENTRY) NextTrustEntry;
//
// search in the current list.
//
TrustConnectEntry = (PTD_LINK) FindNamedEntry(
TrustConnectList,
&TrustEntry->Name );
if( TrustConnectEntry == NULL ) {
PTD_LINK NewTrustConnectEntry;
//
// create new entry.
//
NewTrustConnectEntry =
NetpMemoryAllocate(
sizeof(TD_LINK) +
TrustEntry->Name.Length + sizeof(WCHAR) +
(MAX_PATH + 3) * sizeof(WCHAR)
// max computer name space + '\\' + '\0'
);
if( NewTrustConnectEntry == NULL ) {
NlMonDbgPrint(("UpdateTrustConnectionList: can't allocate "
"memory for new connect entry.\n"));
return;
}
InitializeListHead(&NewTrustConnectEntry->NextEntry);
NlMonpInitUnicodeString(
&NewTrustConnectEntry->TDName,
&TrustEntry->Name,
(LPWSTR)(NewTrustConnectEntry + 1) );
NewTrustConnectEntry->DCName.Length = 0;
NewTrustConnectEntry->DCName.MaximumLength =
(MAX_PATH + 3) * sizeof(WCHAR);
NewTrustConnectEntry->DCName.Buffer = (WCHAR *)
((PCHAR)(NewTrustConnectEntry->TDName.Buffer) +
NewTrustConnectEntry->TDName.MaximumLength);
NewTrustConnectEntry->SecureChannelStatus = ERROR_BAD_NETPATH;
NewTrustConnectEntry->DeleteFlag = FALSE;
IF_DEBUG( VERBOSE ) {
NlMonDbgPrint(("UpdateTrustConnectionList: "
"Trust Connection entry for DC %wZ of "
"domain %wZ added.\n",
&DCEntry->DCName,
&TrustEntry->Name ));
}
//
// add to trust connect list.
//
LOCK_LISTS();
InsertTailList(
TrustConnectList,
(PLIST_ENTRY)NewTrustConnectEntry);
UNLOCK_LISTS();
}
}
}
return;
}
VOID
ValidateTrustConnectionList(
PDC_ENTRY DCEntry,
BOOL ValidateTrustedDCs
)
/*++
Routine Description:
This function determines trust DC for each trusted domain to which
the given DC having connection.
Arguments:
DCEntry : pointer DC entry structure.
Return Value:
None.
--*/
{
PLIST_ENTRY TrustConnectList;
PLIST_ENTRY NextTrustConnectEntry;
PTD_LINK TrustConnectEntry;
BOOL TDCLinkState = TRUE;
//
// do this update only for running DC
// and it should be NT DC.
//
if( (DCEntry->State != DCOnLine) ||
(DCEntry->Type == LMBDC) ) {
return;
}
IF_DEBUG( TRUST ) {
NlMonDbgPrint(("ValidateTrustConnectionList: "
"Trust Connections for the %wZ DC are validated.\n",
&DCEntry->DCName ));
}
TrustConnectList = &DCEntry->TrustedDCs;
for( NextTrustConnectEntry = TrustConnectList->Flink;
NextTrustConnectEntry != TrustConnectList;
NextTrustConnectEntry = NextTrustConnectEntry->Flink ) {
NET_API_STATUS NetStatus;
PNETLOGON_INFO_2 NetlogonInfo2 = NULL;
TrustConnectEntry = (PTD_LINK) NextTrustConnectEntry;
if( GlobalTerminateFlag ) {
break;
}
//
// find trusted DC for this domain and its secure channel
// status.
//
NetStatus = I_NetLogonControl2(
DCEntry->DCName.Buffer,
NETLOGON_CONTROL_TC_QUERY,
2,
(LPBYTE)&TrustConnectEntry->TDName.Buffer,
(LPBYTE *)&NetlogonInfo2 );
if( NetStatus != NERR_Success ) {
IF_DEBUG( TRUST ) {
NlMonDbgPrint(("ValidateTrustConnectionList: "
"I_NetLogonControl2 (%wZ) call to query trust "
"channel status of %wZ domain failed, (%ld).\n",
&DCEntry->DCName,
&TrustConnectEntry->TDName,
NetStatus ));
}
//
// Cleanup the previous DC Name.
//
LOCK_LISTS();
TrustConnectEntry->DCName.Length = 0;
*TrustConnectEntry->DCName.Buffer = L'\0';
TrustConnectEntry->SecureChannelStatus = NetStatus;
UNLOCK_LISTS();
TDCLinkState = FALSE;
continue;
}
IF_DEBUG( VERBOSE ) {
NlMonDbgPrint(("ValidateTrustConnectionList: "
"I_NetLogonControl2 (%wZ) call to query trust "
"channel status of %wZ domain returned : "
"TDCName = %ws, TCStatus = %ld.\n",
&DCEntry->DCName,
&TrustConnectEntry->TDName,
NetlogonInfo2->netlog2_trusted_dc_name,
NetlogonInfo2->netlog2_tc_connection_status ));
}
//
// copy this DC name.
//
LOCK_LISTS();
TrustConnectEntry->DCName.Length =
wcslen( NetlogonInfo2->netlog2_trusted_dc_name ) * sizeof(WCHAR);
RtlCopyMemory(
TrustConnectEntry->DCName.Buffer,
NetlogonInfo2->netlog2_trusted_dc_name,
TrustConnectEntry->DCName.Length + sizeof(WCHAR) );
// copy terminator also
TrustConnectEntry->SecureChannelStatus =
NetlogonInfo2->netlog2_tc_connection_status;
if( TrustConnectEntry->SecureChannelStatus != NERR_Success ) {
TDCLinkState = FALSE;
}
//
// update DC status info.
//
DCEntry->ReplicationStatus = NetlogonInfo2->netlog2_flags;
DCEntry->PDCLinkStatus = NetlogonInfo2->netlog2_pdc_connection_status;
UNLOCK_LISTS();
//
// free up API memory.
//
NetApiBufferFree( NetlogonInfo2 );
NetlogonInfo2 = NULL;
//
// validate the this DC. Only non-null server and successful
// connection.
//
if( (TrustConnectEntry->SecureChannelStatus == NERR_Success) &&
(*TrustConnectEntry->DCName.Buffer != L'\0') ) {
if( ValidateTrustedDCs ) {
NetStatus = IsValidNTDC(
&TrustConnectEntry->DCName,
&TrustConnectEntry->TDName );
if( NetStatus != NERR_Success ) {
IF_DEBUG( TRUST ) {
NlMonDbgPrint(("ValidateTrustConnectionList: "
"%wZ's trust DC %wZ is invalid for "
"domain %wZ.\n",
&DCEntry->DCName,
&TrustConnectEntry->DCName,
&TrustConnectEntry->TDName ));
}
//
// hack, hack, hack ...
//
// For foreign trusted domains, the above check will
// return ERROR_LOGON_FAILURE. Just ignore this
// error for now. When the domain wide credential is
// implemeted this problem will be cured.
//
if( NetStatus != ERROR_LOGON_FAILURE ) {
TrustConnectEntry->SecureChannelStatus = NetStatus;
TDCLinkState = FALSE;
}
}
}
}
}
//
// finally set the state of the trusted dc link for this DC.
//
DCEntry->TDCLinkState = TDCLinkState;
}
VOID
UpdateDomainState(
PDOMAIN_ENTRY DomainEntry
)
/*++
Routine Description:
Update the status of the given domain. The status is determined as
below :
1. if all DCs status and the Trusted channel status are success then
DomainState is set to DomainSuccess.
2. if all online DCs status are success and if any of the secure
channel status is non-success or any of the BDC is offline then the
Domainstate is set to DomainProblem.
3. if any of the domain status is non-success or the PDC is
offline then the DomainState is set to DomainSick.
4. if none of the DC is online, then DomainState is set to
DomainDown.
Arguments:
DomainEntry : pointer to domain structure.
Return Value:
NONE.
--*/
{
PLIST_ENTRY DCList;
PLIST_ENTRY NextEntry;
PDC_ENTRY DCEntry;
BOOL DomainDownFlag = TRUE;
BOOL PDCOnLine = FALSE;
DCList = &(DomainEntry->DCList);
for( NextEntry = DCList->Flink;
NextEntry != DCList; NextEntry = NextEntry->Flink ) {
DCEntry = (PDC_ENTRY) NextEntry;
if( DCEntry->State == DCOnLine ) {
DomainDownFlag = FALSE;
if( DCEntry->DCStatus != ERROR_SUCCESS ) {
DomainEntry->DomainState = DomainSick;
return;
}
//
// if this is PDC, mark PDC is heathy.
//
if( DCEntry->Type == NTPDC ) {
PDCOnLine = TRUE;
}
}
}
if( DomainDownFlag ) {
DomainEntry->DomainState = DomainDown;
return;
}
//
// if PDC is not online ..
//
if( !PDCOnLine ) {
DomainEntry->DomainState = DomainSick;
return;
}
//
// now determine the secure channel status
//
DCList = &(DomainEntry->DCList);
for( NextEntry = DCList->Flink;
NextEntry != DCList; NextEntry = NextEntry->Flink ) {
DCEntry = (PDC_ENTRY) NextEntry;
//
// if a DC is offline ..
//
if( DCEntry->State == DCOffLine ) {
DomainEntry->DomainState = DomainProblem;
return;
}
if( DCEntry->Type == LMBDC ) {
//
// LMBDC does not have trusted secure channel.
//
continue;
}
//
// examine the PDC secure channel status.
//
if( (DCEntry->PDCLinkStatus != ERROR_SUCCESS) ||
(DCEntry->TDCLinkState == FALSE) ) {
DomainEntry->DomainState = DomainProblem;
return;
}
}
DomainEntry->DomainState = DomainSuccess;
return;
}
BOOL
IsDomainUpdateThreadRunning(
HANDLE *ThreadHandle
)
/*++
Routine Description:
Test if the Domain update thread is running
Enter with GlobalDomainUpdateThreadCritSect locked.
Arguments:
ThreadHandle - pointer to thread handle.
Return Value:
TRUE - The domain update thread is running
FALSE - The domain update thread is not running.
--*/
{
DWORD WaitStatus;
//
// Determine if the Update thread is already running.
//
if ( *ThreadHandle != NULL ) {
//
// Time out immediately if the thread is still running.
//
WaitStatus = WaitForSingleObject(
*ThreadHandle,
0 );
if ( WaitStatus == WAIT_TIMEOUT ) {
return TRUE;
} else if ( WaitStatus == 0 ) {
CloseHandle( *ThreadHandle );
*ThreadHandle = NULL;
return FALSE;
} else {
NlMonDbgPrint((
"IsDomainUpdateThreadRunning: "
"Cannot WaitFor Domain Update thread: %ld\n",
WaitStatus ));
return TRUE;
}
}
return FALSE;
}
VOID
StopDomainUpdateThread(
HANDLE *ThreadHandle,
BOOL *ThreadTerminateFlag
)
/*++
Routine Description:
Stops the domain update thread if it is running and waits for it to
stop.
Enter with GlobalDomainUpdateThreadCritSect locked.
Arguments:
ThreadHandle - pointer to thread handle.
ThreadTerminateFlag - pointer to thread terminate flag.
Return Value:
NONE
--*/
{
//
// Ask the domain update thread to stop running.
//
*ThreadTerminateFlag = TRUE;
//
// Determine if the domain update thread is already running.
//
if ( *ThreadHandle != NULL ) {
//
// We've asked the thread to stop. It should do so soon.
// Wait for it to stop.
//
DWORD WaitStatus;
WaitStatus = WaitForSingleObject( *ThreadHandle,
5*60*1000 ); // 5 minutes
if ( WaitStatus != 0 ) {
if ( WaitStatus == WAIT_TIMEOUT ) {
NlMonDbgPrint(("NlStopDomainUpdateThread"
"WaitForSingleObject 5-minute timeout.\n" ));
//
// kill the thread.
//
TerminateThread( *ThreadHandle, (DWORD)-1 );
} else {
NlMonDbgPrint(("NlStopDomainUpdateThread"
"WaitForSingleObject error: %ld\n",
WaitStatus));
}
}
CloseHandle( *ThreadHandle );
*ThreadHandle = NULL;
}
*ThreadTerminateFlag = FALSE;
return;
}
BOOL
StartDomainUpdateThread(
PDOMAIN_ENTRY DomainEntry,
DWORD UpdateFlags
)
/*++
Routine Description:
Start the Domain Update thread if it is not already running.
NOTE: LOCK_LISTS() should be locked when this function is called.
since we do update the domain structure here.
Arguments:
DomainEntry - Pointer to domain structure.
Return Value:
None
--*/
{
DWORD LocalThreadHandle;
//
// If the domain update thread is already running, do nothing.
//
EnterCriticalSection( &GlobalDomainUpdateThreadCritSect );
if ( IsDomainUpdateThreadRunning( &DomainEntry->ThreadHandle ) ) {
LeaveCriticalSection( &GlobalDomainUpdateThreadCritSect );
return FALSE;
}
//
// if the last update was done within the GlobalUpdateTimeMSec
// msecs then ignore
//
// Initialize the domain update parameters
//
DomainEntry->ThreadTerminateFlag = FALSE;
DomainEntry->UpdateFlags = UpdateFlags;
DomainEntry->DomainState = DomainUnknown;
DomainEntry->ThreadHandle = CreateThread(
NULL, // No security attributes
THREAD_STACKSIZE,
(LPTHREAD_START_ROUTINE)
DomainUpdateThread,
(LPVOID)DomainEntry,
0, // No special creation flags
&LocalThreadHandle );
if ( DomainEntry->ThreadHandle == NULL ) {
NlMonDbgPrint(("StartDomainUpdateThread"
"Can't create domain update Thread %lu\n",
GetLastError() ));
LeaveCriticalSection( &GlobalDomainUpdateThreadCritSect );
return FALSE;
}
LeaveCriticalSection( &GlobalDomainUpdateThreadCritSect );
return TRUE;
}
VOID
DomainUpdateThread(
PDOMAIN_ENTRY DomainEntry
)
/*++
Routine Description:
Update and validate status of DCs and Trusted DCs of the specifed
Monitored domain.
Arguments:
DomainEntry : Pointer to the domain entry to be updated.
Return Value:
NONE.
--*/
{
PLIST_ENTRY DCList;
PLIST_ENTRY NextEntry;
PDC_ENTRY DCEntry;
DWORD UpdateFlags = DomainEntry->UpdateFlags;
BOOL ThreadTerminate = DomainEntry->ThreadTerminateFlag;
//
// monitored domain first.
//
if (DomainEntry->IsMonitoredDomain ) {
//
// update lists.
//
if( UpdateFlags & UPDATE_DCS_FROM_SERVER_ENUM ) {
UpdateDCListByServerEnum(
&DomainEntry->Name,
&DomainEntry->DCList );
}
//
// if we are asked to terminate, do so.
//
if( GlobalTerminateFlag || ThreadTerminate ) {
return;
}
if( UpdateFlags & UPDATE_DCS_FROM_DATABASE ) {
UpdateDCListFromDatabase(
&DomainEntry->Name,
&DomainEntry->DCList );
}
//
// if we are asked to terminate, do so.
//
if( GlobalTerminateFlag || ThreadTerminate) {
return;
}
if( UpdateFlags & UPDATE_TRUST_DOMAINS_FROM_DATABASE ) {
UpdateTrustList( DomainEntry );
//
// if we are asked to terminate, do so.
//
if( GlobalTerminateFlag || ThreadTerminate ) {
return;
}
UpdateTrustConnectionList( DomainEntry );
//
// start update threads for new trusted domain introduced
// here.
//
if( GlobalMonitorTrust ) {
UpdateAndValidateLists( UpdateFlags, FALSE );
}
}
//
// validate list content.
//
if( UpdateFlags & VALIDATE_DCS ) {
DCList = &(DomainEntry->DCList);
for( NextEntry = DCList->Flink;
NextEntry != DCList; NextEntry = NextEntry->Flink ) {
DCEntry = (PDC_ENTRY) NextEntry;
//
// if we are asked to terminate, do so.
//
if( GlobalTerminateFlag || ThreadTerminate ) {
return;
}
(VOID) ValidateDC( DCEntry, &DomainEntry->Name );
//
// update trust connection status.
//
ValidateTrustConnectionList(
DCEntry,
(BOOL)(UpdateFlags & VALIDATE_TRUST_CONNECTIONS) );
}
}
}
//
// trusted domain.
//
else {
if( UpdateFlags & UPDATE_TRUST_DCS_FROM_SERVER_ENUM ) {
UpdateDCListByServerEnum(
&DomainEntry->Name,
&DomainEntry->DCList );
}
//
// if we are asked to terminate, do so.
//
if( GlobalTerminateFlag || ThreadTerminate ) {
return;
}
if( UpdateFlags & UPDATE_TRUST_DCS_FROM_DATABASE ) {
UpdateDCListFromDatabase(
&DomainEntry->Name,
&DomainEntry->DCList );
}
//
// if we are asked to terminate, do so.
//
if( GlobalTerminateFlag || ThreadTerminate ) {
return;
}
//
// validate list content.
//
if( UpdateFlags & VALIDATE_TRUST_DCS ) {
DCList = &(DomainEntry->DCList);
for( NextEntry = DCList->Flink;
NextEntry != DCList; NextEntry = NextEntry->Flink ) {
DCEntry = (PDC_ENTRY) NextEntry;
//
// if we are asked to terminate, do so.
//
if( GlobalTerminateFlag || ThreadTerminate ) {
return;
}
(VOID) ValidateDC( DCEntry, &DomainEntry->Name );
}
}
}
//
// update domain status.
//
UpdateDomainState( DomainEntry );
//
// Set GloballUpdateEvent to enable the display thread to
// display the update.
//
if ( !SetEvent( GlobalUpdateEvent ) ) {
DWORD WinError;
WinError = GetLastError();
NlMonDbgPrint(("UpdateAndValidateDomain: Cannot set "
"GlobalUpdateEvent: %lu\n",
WinError ));
}
//
// finally set the last update time.
//
DomainEntry->LastUpdateTime = GetCurrentTime();
}
VOID
UpdateAndValidateLists(
DWORD UpdateFlags,
BOOL ForceFlag
)
/*++
Routine Description:
Update and validate all lists.
Arguments:
UpdateFlags : This bit mapped flags indicate what need to be update
during this pass.
Return Value:
NONE.
--*/
{
PLIST_ENTRY DomainList;
PLIST_ENTRY NextDomainEntry;
PMONITORED_DOMAIN_ENTRY DomainMonEntry;
PDOMAIN_ENTRY DomainEntry;
DWORD CurrentTime;
//
// delete if any GlobalDomainsMonitored entry marked to be deleted.
//
LOCK_LISTS();
DomainList = &GlobalDomainsMonitored;
for( NextDomainEntry = DomainList->Flink;
NextDomainEntry != DomainList; ) {
PLIST_ENTRY TDomainList;
PLIST_ENTRY NextTDomainEntry;
PTRUSTED_DOMAIN_ENTRY TDomainEntry;
DomainMonEntry = (PMONITORED_DOMAIN_ENTRY)NextDomainEntry;
NextDomainEntry = NextDomainEntry->Flink;
if( DomainMonEntry->DeleteFlag ) {
//
// unreference from GlobalDomains.
//
DomainMonEntry->DomainEntry->ReferenceCount--;
NetpAssert(DomainMonEntry->DomainEntry->ReferenceCount >= 0);
//
// unreference the trusted domains from GlobalDomains list.
//
TDomainList = &DomainMonEntry->DomainEntry->TrustedDomainList;
for( NextTDomainEntry = TDomainList->Flink;
NextTDomainEntry != TDomainList;
NextTDomainEntry = NextTDomainEntry->Flink ) {
TDomainEntry = (PTRUSTED_DOMAIN_ENTRY)NextTDomainEntry;
TDomainEntry->DomainEntry->ReferenceCount--;
NetpAssert(TDomainEntry->DomainEntry->ReferenceCount >= 0);
}
RemoveEntryList( (PLIST_ENTRY)DomainMonEntry );
NetpMemoryFree( DomainMonEntry );
}
}
//
// remove GlobalDomains entries that are not referenced anymore.
//
DomainList = &GlobalDomains;
for( NextDomainEntry = DomainList->Flink;
NextDomainEntry != DomainList; ) {
DomainEntry = (PDOMAIN_ENTRY)NextDomainEntry;
NextDomainEntry = NextDomainEntry->Flink;
if( DomainEntry->ReferenceCount == 0 ) {
//
// if the DomainUpdateThread is running, postpone this
// delete.
//
EnterCriticalSection( &GlobalDomainUpdateThreadCritSect );
if ( !IsDomainUpdateThreadRunning( &DomainEntry->ThreadHandle ) ) {
RemoveEntryList( (PLIST_ENTRY)DomainEntry );
CleanupDomainEntry( DomainEntry );
}
LeaveCriticalSection( &GlobalDomainUpdateThreadCritSect );
}
}
CurrentTime = GetCurrentTime();
//
// if we are monitoring trusted domains also then update
// GlobalDomains otherwise update just GlobalDomainsMonitored.
//
if( GlobalMonitorTrust ) {
DomainList = &GlobalDomains;
}
else {
DomainList = &GlobalDomainsMonitored;
}
for( NextDomainEntry = DomainList->Flink;
NextDomainEntry != DomainList;
NextDomainEntry = NextDomainEntry->Flink ) {
if( GlobalMonitorTrust ) {
DomainEntry = (PDOMAIN_ENTRY)NextDomainEntry;
}
else {
DomainEntry =
((PMONITORED_DOMAIN_ENTRY)NextDomainEntry)->DomainEntry;
}
//
// if we are asked to terminate, do so.
//
if( GlobalTerminateFlag ) {
break;
}
//
// if the last update was done within the last
// GlobalUpdateTimeMSec msecs then don't start UpdateThread.
// However start the UpdateThread during startup and when
// forced.
//
// Also takecare of wrap around
//
if( (ForceFlag == TRUE) ||
(DomainEntry->LastUpdateTime == 0) ||
(CurrentTime - DomainEntry->LastUpdateTime > GlobalUpdateTimeMSec ) ) {
StartDomainUpdateThread( DomainEntry, UpdateFlags );
}
}
UNLOCK_LISTS();
}
DWORD
InitGlobals(
VOID
)
/*++
Routine Description:
Initialize all global variables.
Arguments:
None.
Return Value:
None.
--*/
{
GlobalTrace = CONST_GLOBALTRACE;
GlobalMonitorTrust = CONST_GLOBALMONITORTRUST;
GlobalUpdateTimeMSec = CONST_GLOBALUPDATETIME * 60000;
InitializeListHead( &GlobalDomains );
InitializeListHead( &GlobalDomainsMonitored );
//
// This should be in try except.
//
// In general, this routine doesn't clean up on failure.
//
try {
InitializeCriticalSection( &GlobalListCritSect );
InitializeCriticalSection( &GlobalDomainUpdateThreadCritSect );
} except( EXCEPTION_EXECUTE_HANDLER ) {
return ERROR_NOT_ENOUGH_MEMORY;
}
GlobalTerminateFlag = FALSE;
GlobalTerminateEvent = CreateEvent( NULL, // No security attributes
TRUE, // Must be manual reset
FALSE, // Initially not signaled
NULL );// No name
if( GlobalTerminateEvent == NULL ) {
DWORD WinError;
WinError = GetLastError();
NlMonDbgPrint(("Can't create GlobalTerminateEvent %lu.\n", WinError));
return( WinError );
}
GlobalRefreshEvent = CreateEvent( NULL, // No security attributes
FALSE, // Must be auto reset
FALSE, // Initially not signaled
NULL );// No name
if( GlobalRefreshEvent == NULL ) {
DWORD WinError;
WinError = GetLastError();
NlMonDbgPrint(("Can't create GlobalRefreshEvent %lu.\n", WinError));
CloseHandle( GlobalTerminateEvent );
return( WinError );
}
GlobalRefreshDoneEvent = CreateEvent( NULL, // No security attributes
FALSE, // Must be auto reset
FALSE, // Initially not signaled
NULL );// No name
if( GlobalRefreshDoneEvent == NULL ) {
DWORD WinError;
WinError = GetLastError();
NlMonDbgPrint(("Can't create GlobalRefresheDoneEvent %lu.\n", WinError));
CloseHandle( GlobalTerminateEvent );
CloseHandle( GlobalRefreshEvent );
return( WinError );
}
GlobalUpdateEvent = CreateEvent( NULL, // No security attributes
FALSE, // Must be auto reset
FALSE, // Initially not signaled
NULL ); // No name
if( GlobalUpdateEvent == NULL ) {
DWORD WinError;
WinError = GetLastError();
NlMonDbgPrint(("Can't create GlobalUpdateEvent %lu.\n", WinError));
CloseHandle( GlobalTerminateEvent );
CloseHandle( GlobalRefreshEvent );
CloseHandle( GlobalRefreshDoneEvent );
return( WinError );
}
GlobalInitialized = TRUE;
return(ERROR_SUCCESS);
}
VOID
FreeList(
PLIST_ENTRY List
)
/*++
Routine Description:
Freeup entries in a given lists.
Arguments:
List : pointer to a list.
Return Value:
None.
--*/
{
PLIST_ENTRY NextEntry;
while ( !IsListEmpty(List) ) {
NextEntry = RemoveHeadList(List);
NetpMemoryFree( NextEntry );
}
}
VOID
CleanupDomainEntry(
PDOMAIN_ENTRY DomainEntry
)
/*++
Routine Description:
Frees a domain entry and its lists.
Arguments:
DomainEntry : pointer to domain structure.
Return Value:
None.
--*/
{
PLIST_ENTRY DCList;
PLIST_ENTRY NextEntry;
PDC_ENTRY DCEntry;
//
// first free trusted dc lists
//
DCList = &(DomainEntry->DCList);
for( NextEntry = DCList->Flink;
NextEntry != DCList; NextEntry = NextEntry->Flink ) {
DCEntry = (PDC_ENTRY) NextEntry;
FreeList( &DCEntry->TrustedDCs );
}
//
// now free the DC list.
//
FreeList( &DomainEntry->DCList );
//
// now free trusted domain list.
//
FreeList( &DomainEntry->TrustedDomainList );
}
VOID
CleanupLists(
VOID
)
/*++
Routine Description:
Freeup all lists.
Arguments:
none.
Return Value:
None.
--*/
{
PDOMAIN_ENTRY DomainEntry;
PLIST_ENTRY NextDomainEntry;
LOCK_LISTS();
while ( !IsListEmpty(&GlobalDomains) ) {
NextDomainEntry = RemoveHeadList(&GlobalDomains);
DomainEntry = (PDOMAIN_ENTRY)NextDomainEntry;
CleanupDomainEntry( DomainEntry );
}
FreeList(&GlobalDomainsMonitored);
UNLOCK_LISTS();
}
VOID
WorkerThread(
VOID
)
/*++
Routine Description:
This thread updates the lists and validate the list content.
Arguments:
none.
Return Value:
None.
--*/
{
#define COUNT_UPDATE_FROM_DATABASE 50
#define COUNT_VALIDATE_TRUST_CONNECTIONS 5
#define WAIT_COUNT 2
#define REFRESH_EVENT 0
#define TERMINATE_EVENT 1
DWORD LoopCount;
DWORD WaitStatus;
HANDLE WaitHandles[ WAIT_COUNT ];
DWORD UpdateFlags;
//
// perpare wait event array.
//
WaitHandles[REFRESH_EVENT] = GlobalRefreshEvent;
WaitHandles[TERMINATE_EVENT] = GlobalTerminateEvent;
LoopCount = 1;
for( ;; ) {
//
// wait for one of the following event to happen :
//
// 1. GlobalRefreshEvent
// 2. GlobalTerminateEvent.
// 3. Timeout in GlobalUpdateTimeMSec
//
WaitStatus = WaitForMultipleObjects(
WAIT_COUNT,
WaitHandles,
FALSE, // Wait for ANY handle
GlobalUpdateTimeMSec );
switch ( WaitStatus ) {
case WAIT_TIMEOUT: // timeout
//
// determine what to update.
//
if( LoopCount % COUNT_UPDATE_FROM_DATABASE ) {
UpdateFlags = UPDATE_FROM_DATABASE;
}
else if( LoopCount % COUNT_VALIDATE_TRUST_CONNECTIONS) {
UpdateFlags = UPDATE_TRUST_CONNECTIONS_STATUS;
}
else {
UpdateFlags = STANDARD_UPDATE;
}
UpdateAndValidateLists( UpdateFlags, FALSE );
//
// also indicate to the UI that the domain state has been
// changed.
//
if ( !SetEvent( GlobalUpdateEvent ) ) {
DWORD WinError;
WinError = GetLastError();
NlMonDbgPrint(("WokerThread: Cannot set "
"GlobalUpdateEvent: %lu\n",
WinError ));
}
LoopCount++;
break;
case REFRESH_EVENT:
UpdateAndValidateLists( UPDATE_ALL, TRUE );
if ( !SetEvent( GlobalRefreshDoneEvent ) ) {
DWORD WinError;
WinError = GetLastError();
NlMonDbgPrint(("WokerThread: Cannot set "
"GlobalRefreshDoneEvent: %lu\n",
WinError ));
}
//
// also indicate to the UI that the domain state has been
// changed.
//
if ( !SetEvent( GlobalUpdateEvent ) ) {
DWORD WinError;
WinError = GetLastError();
NlMonDbgPrint(("WokerThread: Cannot set "
"GlobalUpdateEvent: %lu\n",
WinError ));
}
break;
case TERMINATE_EVENT:
//
// done.
//
goto Cleanup;
break;
default:
NlMonDbgPrint((
"WorkerThread: WaitForMultipleObjects error: %ld\n",
WaitStatus));
break;
}
}
Cleanup:
return;
}