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.
5231 lines
144 KiB
5231 lines
144 KiB
/*++
|
|
|
|
Copyright (c) 1995-1996 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
domain.c
|
|
|
|
Abstract:
|
|
|
|
Code to manage multiple domains hosted on a DC.
|
|
|
|
Author:
|
|
|
|
Cliff Van Dyke (CliffV) 11-Jan-1995
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
//
|
|
// Common include files.
|
|
//
|
|
|
|
#include "logonsrv.h" // Include files common to entire service
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// Include files specific to this .c file
|
|
//
|
|
|
|
|
|
|
|
|
|
// Serialized by NlGlobalDomainCritSect
|
|
LIST_ENTRY NlGlobalServicedDomains = {0}; // Real domains we service
|
|
LIST_ENTRY NlGlobalServicedNdncs = {0}; // Non-domain NCs we service
|
|
BOOL NlGlobalDomainsInitialized = FALSE;
|
|
|
|
|
|
|
|
|
|
NET_API_STATUS
|
|
NlGetDomainName(
|
|
OUT LPWSTR *DomainName,
|
|
OUT LPWSTR *DnsDomainName,
|
|
OUT PSID *AccountDomainSid,
|
|
OUT PSID *PrimaryDomainSid,
|
|
OUT GUID **PrimaryDomainGuid,
|
|
OUT PBOOLEAN DnsForestNameChanged OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine gets the primary domain name and domain SID from the LSA.
|
|
|
|
Arguments:
|
|
|
|
DomainName - Returns name of the primary domain. Free this buffer using LocalFree.
|
|
|
|
DnsDomainName - Returns the DNS domain name of the primary domain.
|
|
The returned name has a trailing . since the name is an absolute name.
|
|
The allocated buffer must be freed via LocalFree.
|
|
Returns NO_ERROR and a pointer to a NULL buffer if there is no
|
|
domain name configured.
|
|
|
|
AccountDomainSid - Returns Account Domain Sid of this machine. Free this buffer using LocalFree.
|
|
|
|
PrimaryDomainSid - Returns Primary Domain Sid of this machine. Free this buffer using LocalFree.
|
|
Only return on workstations.
|
|
|
|
PrimaryDomainGuid - Returns Primary Domain GUID of this machine. Free this buffer using LocalFree.
|
|
|
|
DnsForestNameChanged: Returns TRUE if the tree name changed.
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
Calls NlExit on failures.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
NET_API_STATUS NetStatus;
|
|
|
|
PLSAPR_POLICY_INFORMATION PrimaryDomainInfo = NULL;
|
|
PLSAPR_POLICY_INFORMATION AccountDomainInfo = NULL;
|
|
LSAPR_HANDLE PolicyHandle = NULL;
|
|
|
|
ULONG DomainSidSize;
|
|
ULONG DnsDomainNameLength;
|
|
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
*DomainName = NULL;
|
|
*DnsDomainName = NULL;
|
|
*AccountDomainSid = NULL;
|
|
*PrimaryDomainSid = NULL;
|
|
*PrimaryDomainGuid = NULL;
|
|
|
|
//
|
|
// Open the LSA policy
|
|
//
|
|
|
|
// ?? I'll need to identify which trusted domain here.
|
|
Status = LsaIOpenPolicyTrusted( &PolicyHandle );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NlPrint((NL_CRITICAL,
|
|
"NlGetDomainName: Can't LsaIOpenPolicyTrusted: 0x%lx.\n",
|
|
Status ));
|
|
NetStatus = NetpNtStatusToApiStatus(Status);
|
|
NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogError, NULL);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Get the Account Domain info from the LSA.
|
|
//
|
|
|
|
Status = LsarQueryInformationPolicy(
|
|
PolicyHandle,
|
|
PolicyAccountDomainInformation,
|
|
&AccountDomainInfo );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NlPrint((NL_CRITICAL,
|
|
"NlGetDomainName: Can't LsarQueryInformationPolicy (AccountDomain): 0x%lx.\n",
|
|
Status ));
|
|
NetStatus = NetpNtStatusToApiStatus(Status);
|
|
NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogError, NULL);
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( AccountDomainInfo->PolicyAccountDomainInfo.DomainName.Length == 0 ||
|
|
AccountDomainInfo->PolicyAccountDomainInfo.DomainName.Length >
|
|
DNLEN * sizeof(WCHAR) ||
|
|
AccountDomainInfo->PolicyAccountDomainInfo.DomainSid == NULL ) {
|
|
|
|
NlPrint((NL_CRITICAL, "Account domain info from LSA invalid.\n"));
|
|
|
|
//
|
|
// Avoid event log error in safe mode where our exit is expected
|
|
//
|
|
NlExit( SERVICE_UIC_M_UAS_INVALID_ROLE,
|
|
NO_ERROR,
|
|
LsaISafeMode() ? DontLogError : LogError,
|
|
NULL );
|
|
|
|
NetStatus = SERVICE_UIC_M_UAS_INVALID_ROLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Copy the Account domain id into a buffer to return to the caller.
|
|
//
|
|
|
|
DomainSidSize =
|
|
RtlLengthSid( (PSID)AccountDomainInfo->PolicyAccountDomainInfo.DomainSid );
|
|
*AccountDomainSid = LocalAlloc( 0, DomainSidSize );
|
|
|
|
if ( *AccountDomainSid == NULL ) {
|
|
NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory( *AccountDomainSid,
|
|
(PSID)AccountDomainInfo->PolicyAccountDomainInfo.DomainSid,
|
|
DomainSidSize );
|
|
|
|
|
|
//
|
|
// Get the Primary Domain info from the LSA.
|
|
//
|
|
|
|
Status = LsarQueryInformationPolicy(
|
|
PolicyHandle,
|
|
PolicyDnsDomainInformation,
|
|
&PrimaryDomainInfo );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NlPrint((NL_CRITICAL,
|
|
"NlGetDomainName: Can't LsarQueryInformationPolicy (DnsDomain): 0x%lx.\n",
|
|
Status ));
|
|
NetStatus = NetpNtStatusToApiStatus(Status);
|
|
NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogError, NULL);
|
|
goto Cleanup;
|
|
}
|
|
|
|
if ( PrimaryDomainInfo->PolicyDnsDomainInfo.Name.Length == 0 ||
|
|
PrimaryDomainInfo->PolicyDnsDomainInfo.Name.Length >
|
|
DNLEN * sizeof(WCHAR) ||
|
|
PrimaryDomainInfo->PolicyDnsDomainInfo.Sid == NULL ) {
|
|
|
|
NlPrint((NL_CRITICAL, "Primary domain info from LSA invalid.\n"));
|
|
|
|
// Ditch the sysvol shares in case this is a repair mode boot
|
|
NlGlobalParameters.SysVolReady = FALSE;
|
|
NlCreateSysvolShares();
|
|
|
|
//
|
|
// Avoid event log error in safe mode where our exit is expected
|
|
//
|
|
NlExit( SERVICE_UIC_M_UAS_INVALID_ROLE,
|
|
NO_ERROR,
|
|
LsaISafeMode() ? DontLogError : LogError,
|
|
NULL );
|
|
|
|
NetStatus = SERVICE_UIC_M_UAS_INVALID_ROLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// On a DC, we must have DNS domain name
|
|
//
|
|
|
|
if ( !NlGlobalMemberWorkstation &&
|
|
(PrimaryDomainInfo->PolicyDnsDomainInfo.DnsDomainName.Length == 0 ||
|
|
PrimaryDomainInfo->PolicyDnsDomainInfo.DnsDomainName.Length >
|
|
NL_MAX_DNS_LENGTH*sizeof(WCHAR)) ) {
|
|
|
|
NlExit( SERVICE_UIC_M_UAS_INVALID_ROLE, NO_ERROR, LogError, NULL );
|
|
NetStatus = SERVICE_UIC_M_UAS_INVALID_ROLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Copy the Primary domain id into a buffer to return to the caller.
|
|
//
|
|
|
|
if ( NlGlobalMemberWorkstation ) {
|
|
DomainSidSize =
|
|
RtlLengthSid( (PSID)PrimaryDomainInfo->PolicyDnsDomainInfo.Sid );
|
|
*PrimaryDomainSid = LocalAlloc( 0, DomainSidSize );
|
|
|
|
if ( *PrimaryDomainSid == NULL ) {
|
|
NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory( *PrimaryDomainSid,
|
|
(PSID)PrimaryDomainInfo->PolicyDnsDomainInfo.Sid,
|
|
DomainSidSize );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Copy the Primary domain name into a buffer to return to the caller.
|
|
//
|
|
|
|
*DomainName = LocalAlloc( 0,
|
|
PrimaryDomainInfo->PolicyDnsDomainInfo.Name.Length + sizeof(WCHAR) );
|
|
|
|
if ( *DomainName == NULL ) {
|
|
NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory( *DomainName,
|
|
PrimaryDomainInfo->PolicyDnsDomainInfo.Name.Buffer,
|
|
PrimaryDomainInfo->PolicyDnsDomainInfo.Name.Length );
|
|
|
|
(*DomainName)[
|
|
PrimaryDomainInfo->PolicyDnsDomainInfo.Name.Length /
|
|
sizeof(WCHAR)] = L'\0';
|
|
|
|
|
|
|
|
//
|
|
// Copy the DNS Primary domain name into a buffer to return to the caller.
|
|
//
|
|
|
|
DnsDomainNameLength = PrimaryDomainInfo->PolicyDnsDomainInfo.DnsDomainName.Length / sizeof(WCHAR);
|
|
|
|
if ( DnsDomainNameLength != 0 ) {
|
|
|
|
*DnsDomainName = LocalAlloc( 0, (DnsDomainNameLength+2) * sizeof(WCHAR));
|
|
|
|
if ( *DnsDomainName == NULL ) {
|
|
NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlCopyMemory( *DnsDomainName,
|
|
PrimaryDomainInfo->PolicyDnsDomainInfo.DnsDomainName.Buffer,
|
|
DnsDomainNameLength*sizeof(WCHAR) );
|
|
|
|
if ( (*DnsDomainName)[DnsDomainNameLength-1] != L'.' ) {
|
|
(*DnsDomainName)[DnsDomainNameLength++] = L'.';
|
|
}
|
|
(*DnsDomainName)[DnsDomainNameLength] = L'\0';
|
|
}
|
|
|
|
|
|
//
|
|
// Get the GUID of the domain we're a member of
|
|
//
|
|
|
|
if ( IsEqualGUID( &PrimaryDomainInfo->PolicyDnsDomainInfo.DomainGuid, &NlGlobalZeroGuid) ) {
|
|
*PrimaryDomainGuid = NULL;
|
|
} else {
|
|
|
|
*PrimaryDomainGuid = LocalAlloc( 0, sizeof(GUID) );
|
|
|
|
if ( *PrimaryDomainGuid == NULL ) {
|
|
NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
**PrimaryDomainGuid = PrimaryDomainInfo->PolicyDnsDomainInfo.DomainGuid;
|
|
}
|
|
|
|
//
|
|
// Set the name of the tree this domain is in.
|
|
//
|
|
|
|
NetStatus = NlSetDnsForestName( (PUNICODE_STRING)&PrimaryDomainInfo->PolicyDnsDomainInfo.DnsForestName,
|
|
DnsForestNameChanged );
|
|
|
|
if ( NetStatus != NO_ERROR ) {
|
|
NlPrint((NL_CRITICAL, "Can't NlSetDnsForestName %ld\n", NetStatus ));
|
|
NlExit( SERVICE_UIC_RESOURCE, NetStatus, LogError, NULL);
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
|
|
NetStatus = NERR_Success;
|
|
//
|
|
// Return
|
|
//
|
|
Cleanup:
|
|
if ( NetStatus != NERR_Success ) {
|
|
if ( *PrimaryDomainSid != NULL ) {
|
|
LocalFree (*PrimaryDomainSid);
|
|
*PrimaryDomainSid = NULL;
|
|
}
|
|
if ( *AccountDomainSid != NULL ) {
|
|
LocalFree (*AccountDomainSid);
|
|
*AccountDomainSid = NULL;
|
|
}
|
|
if ( *DomainName != NULL ) {
|
|
LocalFree (*DomainName);
|
|
*DomainName = NULL;
|
|
}
|
|
if ( *DnsDomainName != NULL ) {
|
|
NetApiBufferFree(*DnsDomainName);
|
|
*DnsDomainName = NULL;
|
|
}
|
|
if ( *PrimaryDomainGuid != NULL ) {
|
|
LocalFree (*PrimaryDomainGuid);
|
|
*PrimaryDomainGuid = NULL;
|
|
}
|
|
|
|
}
|
|
|
|
if ( AccountDomainInfo != NULL ) {
|
|
LsaIFree_LSAPR_POLICY_INFORMATION(
|
|
PolicyAccountDomainInformation,
|
|
AccountDomainInfo );
|
|
}
|
|
|
|
if ( PrimaryDomainInfo != NULL ) {
|
|
LsaIFree_LSAPR_POLICY_INFORMATION(
|
|
PolicyDnsDomainInformation,
|
|
PrimaryDomainInfo );
|
|
}
|
|
|
|
if ( PolicyHandle != NULL ) {
|
|
Status = LsarClose( &PolicyHandle );
|
|
NlAssert( NT_SUCCESS(Status) );
|
|
}
|
|
return NetStatus;
|
|
}
|
|
|
|
NET_API_STATUS
|
|
NlGetDnsHostName(
|
|
OUT LPWSTR *DnsHostName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine gets DnsHostName of this machine.
|
|
|
|
Arguments:
|
|
|
|
DnsHostName - Returns the DNS Host Name of the machine.
|
|
Will return a NULL pointer if this machine has no DNS host name.
|
|
Free this buffer using LocalFree.
|
|
|
|
Return Value:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
|
|
WCHAR LocalDnsUnicodeHostName[NL_MAX_DNS_LENGTH+1];
|
|
ULONG LocalDnsUnicodeHostNameLen;
|
|
|
|
//
|
|
// Get the DNS host name.
|
|
//
|
|
|
|
*DnsHostName = NULL;
|
|
|
|
LocalDnsUnicodeHostNameLen = sizeof( LocalDnsUnicodeHostName ) / sizeof(WCHAR);
|
|
if ( !GetComputerNameExW( ComputerNameDnsFullyQualified,
|
|
LocalDnsUnicodeHostName,
|
|
&LocalDnsUnicodeHostNameLen )) {
|
|
|
|
NetStatus = GetLastError();
|
|
|
|
//
|
|
// If we're not running TCP,
|
|
// simply use the Netbios name.
|
|
//
|
|
|
|
if ( NetStatus == ERROR_FILE_NOT_FOUND ) {
|
|
*DnsHostName = NULL;
|
|
NetStatus = NO_ERROR;
|
|
goto Cleanup;
|
|
|
|
} else {
|
|
NlPrint(( NL_CRITICAL,
|
|
"Cannot GetComputerNameExW() %ld\n",
|
|
NetStatus ));
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the string into an allocated buffer.
|
|
//
|
|
|
|
*DnsHostName = NetpAllocWStrFromWStr( LocalDnsUnicodeHostName );
|
|
|
|
if ( *DnsHostName == NULL ) {
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
NetStatus = NO_ERROR;
|
|
|
|
Cleanup:
|
|
return NetStatus;;
|
|
}
|
|
|
|
NTSTATUS
|
|
NlGetNdncNames(
|
|
OUT PDS_NAME_RESULTW **NdncNames,
|
|
OUT GUID **NdncGuids,
|
|
OUT PULONG NameCount
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Get the names of non-domain NCs we host from the DS
|
|
|
|
Arguments:
|
|
|
|
NdncNames - Returns an array of pointers to DS_NAME_RESULT structures
|
|
describing NDNC names. The number of returned DS_NAME_RESULT structures
|
|
is given by NameCount. Each returned DS_NAME_RESULT structure must be freed
|
|
by calling DsFreeNameResultW after which the NdncNames array itself must be
|
|
freed by calling LocalFree.
|
|
|
|
NameCount - Returns the number of DS_NAME_RESULT structures in the NdncNames array
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
|
|
ULONG LocalReAllocLoopCount = 0;
|
|
PDSNAME *DnList = NULL;
|
|
ULONG DnListSize = 0;
|
|
ULONG DnListEntryCount = 0;
|
|
|
|
HANDLE hDs = NULL;
|
|
LPWSTR NameToCrack;
|
|
PDS_NAME_RESULTW CrackedName = NULL;
|
|
PDS_NAME_RESULTW *LocalNdncNames = NULL;
|
|
GUID *LocalNdncGuids = NULL;
|
|
ULONG LocalNameCount = 0;
|
|
ULONG Index;
|
|
|
|
//
|
|
// Pre-allocate some memory for the list of NDNC DNs.
|
|
// Let's guess we are going to have 4 DNs of maximum
|
|
// DNS name size.
|
|
//
|
|
|
|
DnListSize = 4 * ( sizeof(DSNAME) + DNS_MAX_NAME_LENGTH*sizeof(WCHAR) );
|
|
|
|
DnList = LocalAlloc( 0, DnListSize );
|
|
if ( DnList == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the list of NDNC DNs
|
|
//
|
|
|
|
Status = NlGetConfigurationNamesList(
|
|
DSCONFIGNAMELIST_NCS,
|
|
DSCNL_NCS_NDNCS | DSCNL_NCS_LOCAL_MASTER,
|
|
&DnListSize,
|
|
DnList );
|
|
|
|
//
|
|
// If the buffer was small, keep reallocating it until
|
|
// it's big enough
|
|
//
|
|
|
|
while( Status == STATUS_BUFFER_TOO_SMALL ) {
|
|
PDSNAME *TmpDnList = NULL;
|
|
|
|
//
|
|
// Guard against infinite loop
|
|
//
|
|
NlAssert( LocalReAllocLoopCount < 20 );
|
|
if ( LocalReAllocLoopCount >= 20 ) {
|
|
Status = STATUS_INTERNAL_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Reallocate the memory as much as needed
|
|
//
|
|
TmpDnList = LocalReAlloc( DnList,
|
|
DnListSize,
|
|
LMEM_MOVEABLE );
|
|
|
|
if ( TmpDnList == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
DnList = TmpDnList;
|
|
|
|
//
|
|
// Call it again
|
|
//
|
|
Status = NlGetConfigurationNamesList(
|
|
DSCONFIGNAMELIST_NCS,
|
|
DSCNL_NCS_NDNCS | DSCNL_NCS_LOCAL_MASTER,
|
|
&DnListSize,
|
|
DnList );
|
|
|
|
LocalReAllocLoopCount ++;
|
|
}
|
|
|
|
//
|
|
// Error out on failure
|
|
//
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the number of entries returned
|
|
//
|
|
|
|
for ( Index = 0; DnList[Index] != NULL; Index ++ ) {
|
|
DnListEntryCount ++;
|
|
}
|
|
|
|
//
|
|
// If there are no entries, we are done
|
|
//
|
|
|
|
if ( DnListEntryCount == 0 ) {
|
|
NlPrint(( NL_CRITICAL, "NlGetNdncNames: GetConfigurationNamesList returned 0 entries\n" ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer to store the canonical NDNC names
|
|
//
|
|
|
|
LocalNdncNames = LocalAlloc( LMEM_ZEROINIT, DnListEntryCount * sizeof(PDS_NAME_RESULTW) );
|
|
if ( LocalNdncNames == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate a buffer to store the NDNC GUIDs
|
|
//
|
|
|
|
LocalNdncGuids = LocalAlloc( LMEM_ZEROINIT, DnListEntryCount * sizeof(GUID) );
|
|
if( LocalNdncGuids == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Crack each DN into canonical form
|
|
//
|
|
|
|
for ( Index = 0; DnList[Index] != NULL; Index ++ ) {
|
|
NameToCrack = DnList[Index]->StringName;
|
|
|
|
NetStatus = DsCrackNamesW(
|
|
NULL, // No need to bind to DS for syntactical mapping
|
|
DS_NAME_FLAG_SYNTACTICAL_ONLY, // only syntactical mapping
|
|
DS_FQDN_1779_NAME, // Translate from DN
|
|
DS_CANONICAL_NAME, // Translate to canonical form
|
|
1, // 1 name to translate
|
|
&NameToCrack, // name to translate
|
|
&CrackedName ); // cracked name
|
|
|
|
//
|
|
// Use this name if it was cracked successfully
|
|
//
|
|
if ( NetStatus != NO_ERROR ) {
|
|
NlPrint(( NL_CRITICAL, "NlGetNdncNames: DsCrackNamesW failed for %ws: 0x%lx\n",
|
|
NameToCrack,
|
|
NetStatus ));
|
|
} else if ( CrackedName->rItems[0].status != DS_NAME_NO_ERROR ) {
|
|
NlPrint(( NL_CRITICAL, "NlGetNdncNames: DsCrackNamesW substatus error for %ws: 0x%lx\n",
|
|
NameToCrack,
|
|
CrackedName->rItems[0].status ));
|
|
} else if ( CrackedName->cItems != 1 ) {
|
|
NlPrint(( NL_CRITICAL, "NlGetNdncNames: DsCrackNamesW returned %lu names for %ws\n",
|
|
CrackedName->cItems,
|
|
NameToCrack ));
|
|
} else {
|
|
LocalNdncNames[LocalNameCount] = CrackedName;
|
|
LocalNdncGuids[LocalNameCount] = DnList[Index]->Guid;
|
|
LocalNameCount ++;
|
|
CrackedName = NULL;
|
|
}
|
|
|
|
if ( CrackedName != NULL ) {
|
|
DsFreeNameResultW( CrackedName );
|
|
CrackedName = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Success
|
|
//
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if ( DnList != NULL ) {
|
|
LocalFree( DnList );
|
|
}
|
|
|
|
//
|
|
// Return the data on success
|
|
//
|
|
|
|
if ( NT_SUCCESS(Status) ) {
|
|
*NdncNames = LocalNdncNames;
|
|
*NdncGuids = LocalNdncGuids;
|
|
*NameCount = LocalNameCount;
|
|
} else {
|
|
if ( LocalNdncNames != NULL ) {
|
|
for ( Index = 0; Index < LocalNameCount; Index++ ) {
|
|
DsFreeNameResultW( LocalNdncNames[Index] );
|
|
}
|
|
LocalFree( LocalNdncNames );
|
|
}
|
|
if ( LocalNdncGuids != NULL ) {
|
|
LocalFree( LocalNdncGuids );
|
|
}
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NET_API_STATUS
|
|
NlUpdateServicedNdncs(
|
|
IN LPWSTR ComputerName,
|
|
IN LPWSTR DnsHostName,
|
|
IN BOOLEAN CallNlExitOnFailure,
|
|
OUT PBOOLEAN ServicedNdncChanged OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Update the serviced non-domain NC list.
|
|
|
|
Arguments:
|
|
|
|
ComputerName - Name of this computer.
|
|
|
|
DnsHostName - DNS Host name of this computer in the specified domain.
|
|
|
|
CallNlExitOnFailure - TRUE if NlExit should be called on failure.
|
|
|
|
ServicedNdncChanged - Set to TRUE if the list of NDNCs changed.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus = NO_ERROR;
|
|
NTSTATUS Status;
|
|
|
|
PDS_NAME_RESULTW *NdncNames = NULL;
|
|
GUID *NdncGuids = NULL;
|
|
ULONG NdncCount = 0;
|
|
ULONG CurrentNdncCount = 0;
|
|
ULONG DeletedNdncCount = 0;
|
|
ULONG NdncIndex;
|
|
BOOLEAN LocalServicedNdncChanged = FALSE;
|
|
|
|
PLIST_ENTRY DomainEntry;
|
|
PDOMAIN_INFO DomainInfo;
|
|
PDOMAIN_INFO *DeletedNdncArray = NULL;
|
|
|
|
//
|
|
// Avoid this operation in the setup mode when
|
|
// we may not fully function as a DC as in the
|
|
// case of a NT4 to NT5 DC upgrade.
|
|
//
|
|
|
|
if ( NlDoingSetup() ) {
|
|
NlPrint(( NL_MISC, "NlUpdateServicedNdncs: avoid NDNC update in setup mode\n" ));
|
|
NetStatus = NO_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If for some reason we have no DNS host name,
|
|
// we don't support non-domain NC -- silently
|
|
// ignore this update.
|
|
//
|
|
|
|
if ( DnsHostName == NULL ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlUpdateServicedNdncs: Ignoring update since DnsHostName is NULL\n" ));
|
|
NetStatus = NO_ERROR;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the NDNC names from the DS
|
|
//
|
|
|
|
Status = NlGetNdncNames( &NdncNames,
|
|
&NdncGuids,
|
|
&NdncCount );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|
if ( CallNlExitOnFailure ) {
|
|
NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogErrorAndNetStatus, NULL );
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate an array to store pointers to NDNCs
|
|
// that we may delete
|
|
//
|
|
|
|
EnterCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
for ( DomainEntry = NlGlobalServicedNdncs.Flink ;
|
|
DomainEntry != &NlGlobalServicedNdncs;
|
|
DomainEntry = DomainEntry->Flink ) {
|
|
|
|
CurrentNdncCount ++;
|
|
}
|
|
|
|
if ( CurrentNdncCount > 0 ) {
|
|
DeletedNdncArray = LocalAlloc( LMEM_ZEROINIT, CurrentNdncCount * sizeof(PDOMAIN_INFO) );
|
|
if ( DeletedNdncArray == NULL ) {
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Loop through NDNC entries we have and determine
|
|
// whether the entry should be deleted
|
|
//
|
|
|
|
for ( DomainEntry = NlGlobalServicedNdncs.Flink ;
|
|
DomainEntry != &NlGlobalServicedNdncs;
|
|
DomainEntry = DomainEntry->Flink ) {
|
|
|
|
DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, DomNext);
|
|
|
|
//
|
|
// Skip this entry if it's already marked for deletion
|
|
//
|
|
if ( DomainInfo->DomFlags & DOM_DELETED ) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Loop through the DS suplied NDNC names and see if
|
|
// we already have this NDNC in our list
|
|
//
|
|
for ( NdncIndex = 0; NdncIndex < NdncCount; NdncIndex++ ) {
|
|
if ( NlEqualDnsName( (LPCWSTR) DomainInfo->DomUnicodeDnsDomainName,
|
|
(LPCWSTR) NdncNames[NdncIndex]->rItems[0].pDomain ) ) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If this NDNC that we have no longer exists,
|
|
// mark it for deletion
|
|
//
|
|
if ( NdncIndex == NdncCount ) {
|
|
NlDeleteDomain( DomainInfo );
|
|
|
|
//
|
|
// Remember that this entry should be deleted
|
|
//
|
|
DeletedNdncArray[DeletedNdncCount] = DomainInfo;
|
|
DeletedNdncCount ++;
|
|
|
|
LocalServicedNdncChanged = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Add NDNCs that we don't already have
|
|
//
|
|
|
|
for ( NdncIndex = 0; NdncIndex < NdncCount; NdncIndex++ ) {
|
|
|
|
DomainInfo = NULL;
|
|
for ( DomainEntry = NlGlobalServicedNdncs.Flink ;
|
|
DomainEntry != &NlGlobalServicedNdncs;
|
|
DomainEntry = DomainEntry->Flink ) {
|
|
|
|
DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, DomNext);
|
|
|
|
//
|
|
// If this entry is not to be deleted,
|
|
// check it for match
|
|
//
|
|
if ( (DomainInfo->DomFlags & DOM_DELETED) == 0 &&
|
|
NlEqualDnsName( (LPCWSTR) DomainInfo->DomUnicodeDnsDomainName,
|
|
(LPCWSTR) NdncNames[NdncIndex]->rItems[0].pDomain ) ) {
|
|
break;
|
|
}
|
|
DomainInfo = NULL;
|
|
}
|
|
|
|
//
|
|
// Add this NDNC to our list if we don't have it already
|
|
//
|
|
if ( DomainInfo == NULL ) {
|
|
NetStatus = NlCreateDomainPhase1( NULL, // No Netbios name for NDNC
|
|
NdncNames[NdncIndex]->rItems[0].pDomain,
|
|
NULL, // No SID for NDNC
|
|
&NdncGuids[NdncIndex],
|
|
ComputerName,
|
|
DnsHostName,
|
|
CallNlExitOnFailure,
|
|
DOM_NON_DOMAIN_NC, // This is NDNC
|
|
&DomainInfo );
|
|
|
|
if ( NetStatus == NO_ERROR ) {
|
|
LocalServicedNdncChanged = TRUE;
|
|
NlDereferenceDomain( DomainInfo );
|
|
} else if ( CallNlExitOnFailure ) {
|
|
// NlExit was already called
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
|
|
//
|
|
// Now that the domain crit sect isn't locked
|
|
// we can safely unlink and delete the unneeded NDNCs
|
|
// by removing the last reference
|
|
//
|
|
|
|
for ( NdncIndex = 0; NdncIndex < DeletedNdncCount; NdncIndex++ ) {
|
|
NlDereferenceDomain( DeletedNdncArray[NdncIndex] );
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if ( NdncNames != NULL ) {
|
|
for ( NdncIndex = 0; NdncIndex < NdncCount; NdncIndex++ ) {
|
|
DsFreeNameResultW( NdncNames[NdncIndex] );
|
|
}
|
|
LocalFree( NdncNames );
|
|
}
|
|
|
|
if ( NdncGuids != NULL ) {
|
|
LocalFree( NdncGuids );
|
|
}
|
|
|
|
if ( DeletedNdncArray != NULL ) {
|
|
LocalFree( DeletedNdncArray );
|
|
}
|
|
|
|
if ( NetStatus == NO_ERROR && ServicedNdncChanged != NULL ) {
|
|
*ServicedNdncChanged = LocalServicedNdncChanged;
|
|
}
|
|
|
|
return NetStatus;
|
|
}
|
|
|
|
NTSTATUS
|
|
NlUpdateDnsRootAlias(
|
|
IN PDOMAIN_INFO DomainInfo,
|
|
OUT PBOOL AliasNamesChanged OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Update the aliases for DNS domain and forest names.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Domain whose alias names should be updated.
|
|
|
|
AliasNamesChanged - Set to TRUE if either the domain name
|
|
alias or the forest name alias changed.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status = STATUS_SUCCESS;
|
|
LPWSTR DnsDomainNameAlias = NULL;
|
|
LPWSTR DnsForestNameAlias = NULL;
|
|
LPSTR Utf8DnsDomainNameAlias = NULL;
|
|
LPSTR Utf8DnsForestNameAlias = NULL;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
if ( AliasNamesChanged != NULL ) {
|
|
*AliasNamesChanged = FALSE;
|
|
}
|
|
|
|
//
|
|
// Avoid this operation in the setup mode when
|
|
// we may not fully function as a DC as in the
|
|
// case of a NT4 to NT5 DC upgrade.
|
|
//
|
|
|
|
if ( NlDoingSetup() ) {
|
|
NlPrint(( NL_MISC, "NlUpdateDnsRootAlias: avoid DnsRootAlias update in setup mode\n" ));
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Allocate the buffers
|
|
//
|
|
|
|
DnsDomainNameAlias = LocalAlloc( LMEM_ZEROINIT,
|
|
DNS_MAX_NAME_BUFFER_LENGTH * sizeof(WCHAR) );
|
|
if ( DnsDomainNameAlias == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
DnsForestNameAlias = LocalAlloc( LMEM_ZEROINIT,
|
|
DNS_MAX_NAME_BUFFER_LENGTH * sizeof(WCHAR) );
|
|
if ( DnsForestNameAlias == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Get the name aliases from the DS
|
|
//
|
|
|
|
Status = NlGetDnsRootAlias( DnsDomainNameAlias, DnsForestNameAlias );
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlUpdateDnsRootAlias: NlGetDnsRootAlias failed 0x%lx\n",
|
|
Status ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Convert the names to UTF-8
|
|
//
|
|
|
|
if ( wcslen(DnsDomainNameAlias) > 0 ) {
|
|
Utf8DnsDomainNameAlias = NetpAllocUtf8StrFromWStr( DnsDomainNameAlias );
|
|
if ( Utf8DnsDomainNameAlias == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if ( wcslen(DnsForestNameAlias) > 0 ) {
|
|
Utf8DnsForestNameAlias = NetpAllocUtf8StrFromWStr( DnsForestNameAlias );
|
|
if ( Utf8DnsForestNameAlias == NULL ) {
|
|
Status = STATUS_NO_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Update the DNS domain name alias
|
|
//
|
|
|
|
EnterCriticalSection( &NlGlobalDomainCritSect );
|
|
|
|
//
|
|
// Ignore this update if the name alias is same as the active one
|
|
//
|
|
// Note: NlEqualDnsNameUtf8 checks for NULL on input
|
|
//
|
|
|
|
if ( NlEqualDnsNameUtf8(DomainInfo->DomUtf8DnsDomainName,
|
|
Utf8DnsDomainNameAlias) ) {
|
|
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlUpdateDnsRootAlias: Ignoring DnsDomainNameAlias update for same active name: %s %s\n",
|
|
DomainInfo->DomUtf8DnsDomainName,
|
|
Utf8DnsDomainNameAlias ));
|
|
|
|
//
|
|
// Ignore this update if the name alias is same as the current name alias
|
|
//
|
|
|
|
} else if ( NlEqualDnsNameUtf8(DomainInfo->DomUtf8DnsDomainNameAlias,
|
|
Utf8DnsDomainNameAlias) ) {
|
|
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlUpdateDnsRootAlias: Ignoring DnsDomainNameAlias update for same alias name: %s %s\n",
|
|
DomainInfo->DomUtf8DnsDomainNameAlias,
|
|
Utf8DnsDomainNameAlias ));
|
|
|
|
//
|
|
// Otherwise update the alias
|
|
//
|
|
|
|
} else {
|
|
|
|
if ( AliasNamesChanged != NULL ) {
|
|
*AliasNamesChanged = TRUE;
|
|
}
|
|
|
|
NlPrint(( NL_DOMAIN,
|
|
"NlUpdateDnsRootAlias: Updating DnsDomainNameAlias from %s to %s\n",
|
|
DomainInfo->DomUtf8DnsDomainNameAlias,
|
|
Utf8DnsDomainNameAlias ));
|
|
|
|
if ( DomainInfo->DomUtf8DnsDomainNameAlias != NULL ) {
|
|
NetpMemoryFree( DomainInfo->DomUtf8DnsDomainNameAlias );
|
|
DomainInfo->DomUtf8DnsDomainNameAlias = NULL;
|
|
}
|
|
|
|
DomainInfo->DomUtf8DnsDomainNameAlias = Utf8DnsDomainNameAlias;
|
|
Utf8DnsDomainNameAlias = NULL;
|
|
}
|
|
|
|
LeaveCriticalSection( &NlGlobalDomainCritSect );
|
|
|
|
//
|
|
// Update the DNS forest name alias
|
|
//
|
|
|
|
EnterCriticalSection( &NlGlobalDnsForestNameCritSect );
|
|
|
|
//
|
|
// Ignore this update if the name alias is same as the active one
|
|
//
|
|
// Note: NlEqualDnsNameUtf8 checks for NULL on input
|
|
//
|
|
|
|
if ( NlEqualDnsNameUtf8(NlGlobalUtf8DnsForestName,
|
|
Utf8DnsForestNameAlias) ) {
|
|
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlUpdateDnsRootAlias: Ignoring DnsForestNameAlias update for same active name: %s %s\n",
|
|
NlGlobalUtf8DnsForestName,
|
|
Utf8DnsForestNameAlias));
|
|
|
|
//
|
|
// Ignore this update if the name alias is same as the current name alias
|
|
//
|
|
|
|
} else if ( NlEqualDnsNameUtf8(NlGlobalUtf8DnsForestNameAlias,
|
|
Utf8DnsForestNameAlias) ) {
|
|
|
|
NlPrint(( NL_CRITICAL,
|
|
"NlUpdateDnsRootAlias: Ignoring DnsForestNameAlias update for same alias name: %s %s\n",
|
|
NlGlobalUtf8DnsForestNameAlias,
|
|
Utf8DnsForestNameAlias));
|
|
|
|
} else {
|
|
|
|
if ( AliasNamesChanged != NULL ) {
|
|
*AliasNamesChanged = TRUE;
|
|
}
|
|
|
|
NlPrint(( NL_DOMAIN,
|
|
"NlUpdateDnsRootAlias: Updating DnsForestNameAlias from %s to %s\n",
|
|
NlGlobalUtf8DnsForestNameAlias,
|
|
Utf8DnsForestNameAlias ));
|
|
|
|
if ( NlGlobalUtf8DnsForestNameAlias != NULL ) {
|
|
NetpMemoryFree( NlGlobalUtf8DnsForestNameAlias );
|
|
NlGlobalUtf8DnsForestNameAlias = NULL;
|
|
}
|
|
|
|
NlGlobalUtf8DnsForestNameAlias = Utf8DnsForestNameAlias;
|
|
Utf8DnsForestNameAlias = NULL;
|
|
}
|
|
|
|
LeaveCriticalSection( &NlGlobalDnsForestNameCritSect );
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
Cleanup:
|
|
|
|
if ( DnsDomainNameAlias != NULL ) {
|
|
LocalFree( DnsDomainNameAlias );
|
|
}
|
|
|
|
if ( DnsForestNameAlias != NULL ) {
|
|
LocalFree( DnsForestNameAlias );
|
|
}
|
|
|
|
if ( Utf8DnsDomainNameAlias != NULL ) {
|
|
NetpMemoryFree( Utf8DnsDomainNameAlias );
|
|
}
|
|
|
|
if ( Utf8DnsForestNameAlias != NULL ) {
|
|
NetpMemoryFree( Utf8DnsForestNameAlias );
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
NET_API_STATUS
|
|
NlInitializeDomains(
|
|
VOID
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize brdomain.c and create the primary domain.
|
|
|
|
Arguments:
|
|
|
|
None
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
NTSTATUS Status;
|
|
|
|
PDOMAIN_INFO DomainInfo = NULL;
|
|
LPWSTR ComputerName = NULL;
|
|
LPWSTR DnsHostName = NULL;
|
|
LPWSTR DomainName = NULL;
|
|
LPWSTR DnsDomainName = NULL;
|
|
PSID AccountDomainSid = NULL;
|
|
PSID PrimaryDomainSid = NULL;
|
|
GUID *DomainGuid = NULL;
|
|
|
|
//
|
|
// Initialize globals
|
|
//
|
|
|
|
try {
|
|
InitializeCriticalSection( &NlGlobalDomainCritSect );
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL );
|
|
goto Cleanup;
|
|
}
|
|
|
|
InitializeListHead(&NlGlobalServicedDomains);
|
|
InitializeListHead(&NlGlobalServicedNdncs);
|
|
NlGlobalDomainsInitialized = TRUE;
|
|
|
|
//
|
|
// Get the computername and domain name of this machine
|
|
// (in both the Netbios and DNS forms).
|
|
//
|
|
|
|
NetStatus = NetpGetComputerName( &ComputerName );
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL );
|
|
goto Cleanup;
|
|
}
|
|
|
|
NlGlobalUnicodeComputerName = NetpAllocWStrFromWStr( ComputerName );
|
|
|
|
if ( NlGlobalUnicodeComputerName == NULL ) {
|
|
NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
NetStatus = NlGetDomainName( &DomainName,
|
|
&DnsDomainName,
|
|
&AccountDomainSid,
|
|
&PrimaryDomainSid,
|
|
&DomainGuid,
|
|
NULL );
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
// NlExit was already called
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Be consistent.
|
|
// Avoid getting a DNS Host Name if we have no DNS domain name.
|
|
if ( DnsDomainName != NULL ) {
|
|
NetStatus = NlGetDnsHostName( &DnsHostName );
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL );
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Create the Domain Info struct and initialize it.
|
|
//
|
|
|
|
NetStatus = NlCreateDomainPhase1( DomainName,
|
|
DnsDomainName,
|
|
AccountDomainSid,
|
|
DomainGuid,
|
|
ComputerName,
|
|
DnsHostName,
|
|
TRUE, // Call NlExit on failure
|
|
DOM_REAL_DOMAIN | DOM_PRIMARY_DOMAIN, // Primary domain of this machine
|
|
&DomainInfo );
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
// NlExit was already called
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Finish workstation initialization.
|
|
//
|
|
|
|
if ( NlGlobalMemberWorkstation ) {
|
|
|
|
//
|
|
// Ensure the primary and account domain ID are different.
|
|
//
|
|
|
|
if ( RtlEqualSid( PrimaryDomainSid, AccountDomainSid ) ) {
|
|
|
|
LPWSTR AlertStrings[3];
|
|
|
|
//
|
|
// alert admin.
|
|
//
|
|
|
|
AlertStrings[0] = DomainInfo->DomUnicodeComputerNameString.Buffer;
|
|
AlertStrings[1] = DomainInfo->DomUnicodeDomainName;
|
|
AlertStrings[2] = NULL;
|
|
|
|
//
|
|
// Save the info in the eventlog
|
|
//
|
|
|
|
NlpWriteEventlog(
|
|
ALERT_NetLogonSidConflict,
|
|
EVENTLOG_ERROR_TYPE,
|
|
AccountDomainSid,
|
|
RtlLengthSid( AccountDomainSid ),
|
|
AlertStrings,
|
|
2 );
|
|
|
|
//
|
|
// This isn't fatal. (Just drop through)
|
|
//
|
|
}
|
|
|
|
|
|
LOCK_TRUST_LIST( DomainInfo );
|
|
NlAssert( DomainInfo->DomClientSession == NULL );
|
|
|
|
//
|
|
// Allocate the Client Session structure.
|
|
//
|
|
|
|
DomainInfo->DomClientSession = NlAllocateClientSession(
|
|
DomainInfo,
|
|
&DomainInfo->DomUnicodeDomainNameString,
|
|
&DomainInfo->DomUnicodeDnsDomainNameString,
|
|
PrimaryDomainSid,
|
|
DomainInfo->DomDomainGuid,
|
|
CS_DIRECT_TRUST |
|
|
(DomainInfo->DomUnicodeDnsDomainNameString.Length != 0 ? CS_NT5_DOMAIN_TRUST : 0),
|
|
WorkstationSecureChannel,
|
|
0 ); // No trust attributes
|
|
|
|
if ( DomainInfo->DomClientSession == NULL ) {
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Save a copy of the client session for convenience.
|
|
// A workstation only has one client session.
|
|
//
|
|
NlGlobalClientSession = DomainInfo->DomClientSession;
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
|
|
|
|
//
|
|
// Finish DC initialization.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Do the time intensive portion of creating the domain.
|
|
//
|
|
|
|
NetStatus = NlCreateDomainPhase2( DomainInfo,
|
|
TRUE ); // Call NlExit on failure
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
// NlExit was already called
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Initialize the list of non-domain NCs we host
|
|
//
|
|
|
|
NetStatus = NlUpdateServicedNdncs( ComputerName,
|
|
DnsHostName,
|
|
TRUE, // Call NlExit on failure
|
|
NULL ); // Don't care if NDNC list changed
|
|
|
|
if ( NetStatus != NO_ERROR ) {
|
|
// NlExit was already called
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Update the domain and forest name aliases
|
|
//
|
|
|
|
Status = NlUpdateDnsRootAlias( DomainInfo,
|
|
NULL ); // don't care if name changed
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|
NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogErrorAndNetStatus, NULL );
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
NetStatus = NERR_Success;
|
|
|
|
//
|
|
// Free locally used resources
|
|
//
|
|
Cleanup:
|
|
if ( DomainInfo != NULL ) {
|
|
NlDereferenceDomain( DomainInfo );
|
|
}
|
|
if ( ComputerName != NULL ) {
|
|
(VOID)NetApiBufferFree( ComputerName );
|
|
}
|
|
if ( DnsHostName != NULL ) {
|
|
(VOID)LocalFree( DnsHostName );
|
|
}
|
|
if ( DomainName != NULL ) {
|
|
(VOID)LocalFree( DomainName );
|
|
}
|
|
if ( DnsDomainName != NULL ) {
|
|
(VOID)LocalFree( DnsDomainName );
|
|
}
|
|
if ( AccountDomainSid != NULL ) {
|
|
(VOID)LocalFree( AccountDomainSid );
|
|
}
|
|
if ( PrimaryDomainSid != NULL ) {
|
|
(VOID)LocalFree( PrimaryDomainSid );
|
|
}
|
|
if ( DomainGuid != NULL ) {
|
|
(VOID)LocalFree( DomainGuid );
|
|
}
|
|
|
|
return NetStatus;
|
|
}
|
|
|
|
|
|
VOID
|
|
NlFreeComputerName(
|
|
PDOMAIN_INFO DomainInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Free the ComputerName fields for this domain.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Domain the computername is being defined for.
|
|
|
|
ComputerName - Computername of this machine for the domain.
|
|
|
|
DnsHostName - DNS Hostname of this machine for the domain.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
|
|
--*/
|
|
{
|
|
DomainInfo->DomUncUnicodeComputerName[0] = L'\0';
|
|
|
|
RtlInitUnicodeString( &DomainInfo->DomUnicodeComputerNameString,
|
|
DomainInfo->DomUncUnicodeComputerName );
|
|
if ( DomainInfo->DomUnicodeDnsHostNameString.Buffer != NULL ) {
|
|
RtlFreeUnicodeString( &DomainInfo->DomUnicodeDnsHostNameString );
|
|
RtlInitUnicodeString( &DomainInfo->DomUnicodeDnsHostNameString, NULL );
|
|
}
|
|
if ( DomainInfo->DomUtf8DnsHostName != NULL) {
|
|
NetpMemoryFree( DomainInfo->DomUtf8DnsHostName );
|
|
DomainInfo->DomUtf8DnsHostName = NULL;
|
|
}
|
|
|
|
DomainInfo->DomOemComputerName[0] = '\0';
|
|
DomainInfo->DomOemComputerNameLength = 0;
|
|
|
|
if ( DomainInfo->DomUtf8ComputerName != NULL ) {
|
|
NetpMemoryFree( DomainInfo->DomUtf8ComputerName );
|
|
DomainInfo->DomUtf8ComputerName = NULL;
|
|
}
|
|
DomainInfo->DomUtf8ComputerNameLength = 0;
|
|
|
|
}
|
|
|
|
NET_API_STATUS
|
|
NlSetComputerName(
|
|
PDOMAIN_INFO DomainInfo,
|
|
LPWSTR ComputerName,
|
|
LPWSTR DnsHostName OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set a computed computername for an domain.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Domain the computername is being defined for.
|
|
|
|
ComputerName - Computername of this machine for the domain.
|
|
|
|
DnsHostName - DNS Hostname of this machine for the domain.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
NET_API_STATUS NetStatus;
|
|
|
|
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"Setting our computer name to %ws %ws\n", ComputerName, DnsHostName ));
|
|
//
|
|
// Copy the netbios computer name into the structure.
|
|
//
|
|
|
|
wcscpy( DomainInfo->DomUncUnicodeComputerName, L"\\\\" );
|
|
NetStatus = I_NetNameCanonicalize(
|
|
NULL,
|
|
ComputerName,
|
|
DomainInfo->DomUncUnicodeComputerName+2,
|
|
sizeof(DomainInfo->DomUncUnicodeComputerName)-2*sizeof(WCHAR),
|
|
NAMETYPE_COMPUTER,
|
|
0 );
|
|
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"ComputerName %ws is invalid\n",
|
|
ComputerName ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlInitUnicodeString( &DomainInfo->DomUnicodeComputerNameString,
|
|
DomainInfo->DomUncUnicodeComputerName+2 );
|
|
|
|
Status = RtlUpcaseUnicodeToOemN( DomainInfo->DomOemComputerName,
|
|
sizeof(DomainInfo->DomOemComputerName),
|
|
&DomainInfo->DomOemComputerNameLength,
|
|
DomainInfo->DomUnicodeComputerNameString.Buffer,
|
|
DomainInfo->DomUnicodeComputerNameString.Length);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"Unable to convert computer name to OEM %ws %lx\n",
|
|
ComputerName, Status ));
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
DomainInfo->DomOemComputerName[DomainInfo->DomOemComputerNameLength] = '\0';
|
|
|
|
//
|
|
// Copy the UTF-8 version of the netbios computer name into the structure.
|
|
//
|
|
|
|
DomainInfo->DomUtf8ComputerName = NetpAllocUtf8StrFromWStr( ComputerName );
|
|
|
|
if (DomainInfo->DomUtf8ComputerName == NULL ) {
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"Unable to convert computer name to UTF8 %ws\n",
|
|
DnsHostName ));
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
DomainInfo->DomUtf8ComputerNameLength = strlen( DomainInfo->DomUtf8ComputerName );
|
|
|
|
//
|
|
// Copy the DNS Hostname into the structure.
|
|
//
|
|
|
|
if ( DnsHostName != NULL ) {
|
|
if ( !RtlCreateUnicodeString( &DomainInfo->DomUnicodeDnsHostNameString, DnsHostName ) ) {
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"Unable to RtlCreateUnicodeString for host name %ws\n",
|
|
DnsHostName ));
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
DomainInfo->DomUtf8DnsHostName =
|
|
NetpAllocUtf8StrFromWStr( DnsHostName );
|
|
if (DomainInfo->DomUtf8DnsHostName == NULL ) {
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"Unable to convert host name to UTF8 %ws\n",
|
|
DnsHostName ));
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
} else {
|
|
RtlInitUnicodeString( &DomainInfo->DomUnicodeDnsHostNameString, NULL );
|
|
DomainInfo->DomUtf8DnsHostName = NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef _DC_NETLOGON
|
|
#ifdef notdef
|
|
// ?? placeholder for telling DS
|
|
//
|
|
// Tell SAM what the computername for this domain is.
|
|
//
|
|
|
|
Status = SpmDbSetDomainServerName(
|
|
&DomainInfo->DomUnicodeDomainNameString,
|
|
&DomainInfo->DomUnicodeComputerNameString );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"Unable to SpmDbSetDomainServerName to %ws %lx\n",
|
|
ComputerName, Status ));
|
|
Status = NetpNtStatusToApiStatus( Status );
|
|
goto Cleanup;
|
|
}
|
|
#endif // notdef
|
|
#endif // _DC_NETLOGON
|
|
|
|
//
|
|
// All done.
|
|
//
|
|
NetStatus = NERR_Success;
|
|
|
|
Cleanup:
|
|
//
|
|
// On error, clear everything out.
|
|
//
|
|
if ( NetStatus != NERR_Success ) {
|
|
NlFreeComputerName( DomainInfo );
|
|
}
|
|
return NetStatus;
|
|
}
|
|
|
|
|
|
|
|
#ifdef _DC_NETLOGON
|
|
#ifdef MULTIHOSTED_DOMAIN
|
|
// Handle DnsHostName too
|
|
NET_API_STATUS
|
|
NlAssignComputerName(
|
|
PDOMAIN_INFO DomainInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
|
|
Assign a computername to a domain. Register that computername
|
|
with the SMB server as verification of it's validity.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Domain the computername is being defined for.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
DWORD ComputerOrdinal;
|
|
|
|
DWORD DefaultComputerOrdinal;
|
|
DWORD MaximumComputerOrdinal = 0;
|
|
DWORD OrdinalFromRegistry = 0;
|
|
DWORD TotalRetryCount = 0;
|
|
|
|
WCHAR ComputerName[CNLEN+1];
|
|
|
|
|
|
|
|
#ifdef notdef
|
|
//
|
|
// Compute the default ordinal.
|
|
//
|
|
|
|
NlGetDomainIndex( &DefaultComputerOrdinal, &MaximumComputerOrdinal );
|
|
|
|
|
|
//
|
|
// Get the value of the "EmulatedComputerName". If the name is specified
|
|
// in the registry, use that name and don't fall back to any other name.
|
|
//
|
|
|
|
DataSize = sizeof(ComputerName);
|
|
// ?? Read DnsNameForm, NetbiosName, and CurrentDnsName from the machine object
|
|
NetStatus = RegQueryValueExW( DomainKeyHandle,
|
|
NL_DOMAIN_EMULATED_COMPUTER_NAME,
|
|
0, // Reserved
|
|
&KeyType,
|
|
(LPBYTE)&ComputerName,
|
|
&DataSize );
|
|
|
|
if ( NetStatus != ERROR_FILE_NOT_FOUND ) {
|
|
|
|
if ( NetStatus != ERROR_SUCCESS || KeyType != REG_SZ ) {
|
|
// ??: write an event.
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"NlAssignComputerName: Cannot read %ws registry key %ld.\n",
|
|
NL_DOMAIN_EMULATED_COMPUTER_NAME,
|
|
NetStatus ));
|
|
} else {
|
|
|
|
//
|
|
// Register the computer name.
|
|
//
|
|
|
|
NetStatus = NlServerComputerNameAdd(
|
|
dns too
|
|
DomainInfo->DomUnicodeDomainName,
|
|
ComputerName );
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
// ??: write an event.
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"NlAssignComputerName: Cannot register computername %ws with SMB server %ld.\n",
|
|
ComputerName,
|
|
NetStatus ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Save it.
|
|
//
|
|
|
|
NetStatus = NlSetComputerName( DomainInfo, ComputerName, DnsHostName );
|
|
goto Cleanup;
|
|
|
|
}
|
|
|
|
}
|
|
#endif // notdef
|
|
|
|
|
|
//
|
|
// Get the value of the "EmulatedComputerOrdinal" indicating what our first
|
|
// try as a computername should be.
|
|
//
|
|
|
|
#ifdef notdef
|
|
DataSize = sizeof(ComputerOrdinal);
|
|
// ?? Read DnsNameForm, NetbiosName, and CurrentDnsName from the machine object
|
|
NetStatus = RegQueryValueExW( DomainKeyHandle,
|
|
NL_DOMAIN_EMULATED_COMPUTER_ORDINAL,
|
|
0, // Reserved
|
|
&KeyType,
|
|
(LPBYTE)&OrdinalFromRegistry,
|
|
&DataSize );
|
|
|
|
if ( NetStatus != ERROR_SUCCESS ) {
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"NlAssignComputerName: Cannot query %ws key (using defaults) %ld.\n",
|
|
NL_DOMAIN_EMULATED_COMPUTER_ORDINAL,
|
|
NetStatus ));
|
|
|
|
ComputerOrdinal = DefaultComputerOrdinal;
|
|
|
|
//
|
|
// Validate the returned data.
|
|
//
|
|
|
|
} else if ( KeyType != REG_DWORD || DataSize != sizeof(OrdinalFromRegistry) ) {
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"NlAssignComputerName: Key %ws size/type wrong.\n",
|
|
NL_DOMAIN_EMULATED_COMPUTER_ORDINAL ));
|
|
|
|
ComputerOrdinal = DefaultComputerOrdinal;
|
|
|
|
//
|
|
// Use the ordinal from the registry
|
|
//
|
|
|
|
} else {
|
|
ComputerOrdinal = OrdinalFromRegistry;
|
|
}
|
|
#else // notdef
|
|
ComputerOrdinal = OrdinalFromRegistry;
|
|
#endif // notdef
|
|
|
|
|
|
//
|
|
// Loop trying the oridinal number to compute a computer name.
|
|
//
|
|
|
|
for (;;) {
|
|
WCHAR OrdinalString[12];
|
|
|
|
//
|
|
// Build the computer name to test.
|
|
//
|
|
// DOMAIN________N
|
|
//
|
|
// where DOMAIN is the domain name, N is the ordinal number, and
|
|
// there are enough _'s to pad to DNLEN.
|
|
//
|
|
|
|
wcscpy( ComputerName, DomainInfo->DomUnicodeDomainName );
|
|
wcsncpy( &ComputerName[DomainInfo->DomUnicodeDomainNameString.Length/sizeof(WCHAR)],
|
|
L"________________",
|
|
DNLEN-DomainInfo->DomUnicodeDomainNameString.Length/sizeof(WCHAR) );
|
|
ultow( ComputerOrdinal, OrdinalString, 10 );
|
|
wcscpy( &ComputerName[DNLEN-wcslen(OrdinalString)],
|
|
OrdinalString );
|
|
|
|
//
|
|
// Try to register the computer name.
|
|
//
|
|
|
|
NetStatus = NlServerComputerNameAdd(
|
|
DomainInfo->DomUnicodeDomainName,
|
|
ComputerName );
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
|
|
//
|
|
// If this name is in conflict with an existing name,
|
|
// try another ordinal.
|
|
//
|
|
// Simply increment the ordinal to try.
|
|
// Don't try ordinals that conflict with other existing Domain Controllers.
|
|
//
|
|
|
|
if ( NetStatus == NERR_DuplicateName ) {
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"NlAssignComputerName: Computername %ws is duplicate (Try another.)\n",
|
|
ComputerName,
|
|
NetStatus ));
|
|
|
|
//
|
|
// Allow several attempts to add the computername.
|
|
//
|
|
|
|
TotalRetryCount ++;
|
|
|
|
if ( TotalRetryCount < 100 ) {
|
|
ComputerOrdinal = max(ComputerOrdinal + 1, MaximumComputerOrdinal*2 );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"NlAssignComputerName: Cannot register computername %ws with SMB server %ld.\n",
|
|
ComputerName,
|
|
NetStatus ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If we've made it here, we have a valid computername.
|
|
//
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
#ifdef notdef
|
|
//
|
|
// Write the chosen ordinal to the registry so we don't have to work so hard
|
|
// next time.
|
|
//
|
|
|
|
NetStatus = RegSetValueExW( DomainKeyHandle,
|
|
NL_DOMAIN_EMULATED_COMPUTER_ORDINAL,
|
|
0, // Reserved
|
|
REG_DWORD,
|
|
(LPBYTE)&ComputerOrdinal,
|
|
sizeof(ComputerOrdinal) );
|
|
|
|
if ( NetStatus != ERROR_SUCCESS ) {
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"NlAssignComputerName: Cannot set %ws key (ignored) %ld.\n",
|
|
NL_DOMAIN_EMULATED_COMPUTER_ORDINAL,
|
|
NetStatus ));
|
|
}
|
|
|
|
|
|
//
|
|
// Done.
|
|
//
|
|
|
|
NetStatus = NlSetComputerName( DomainInfo, ComputerName, DnsHostName );
|
|
#endif // notdef
|
|
|
|
Cleanup:
|
|
#ifdef notdef
|
|
if ( DomainKeyHandle != NULL ) {
|
|
RegCloseKey( DomainKeyHandle );
|
|
}
|
|
#endif // notdef
|
|
|
|
if ( NetStatus == NERR_Success ) {
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"Assigned computer name: %ws\n",
|
|
ComputerName ));
|
|
}
|
|
|
|
return NetStatus;
|
|
|
|
}
|
|
#endif // MULTIHOSTED_DOMAIN
|
|
|
|
|
|
|
|
VOID
|
|
NlDomainThread(
|
|
IN LPVOID DomainInfoParam
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Perform role change operations that are potentially time consuming.
|
|
|
|
As such, this routine runs in a separate thread specific to the domain.
|
|
|
|
Arguments:
|
|
|
|
DomainInfoParam - Domain who's role is to be updated.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
This routine logs any error it detects, but it doesn't call NlExit.
|
|
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus;
|
|
|
|
PDOMAIN_INFO DomainInfo = (PDOMAIN_INFO) DomainInfoParam;
|
|
DWORD DomFlags;
|
|
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"Domain thread started\n"));
|
|
|
|
|
|
//
|
|
// Loop forever.
|
|
//
|
|
// We only want one thread per domain. Therefore, this thread
|
|
// stays around doing not only what was requested before it started,
|
|
// but also those tasks that are queued later.
|
|
//
|
|
|
|
for (;;) {
|
|
|
|
//
|
|
// If we've been asked to terminate,
|
|
// do so.
|
|
//
|
|
|
|
EnterCriticalSection(&NlGlobalDomainCritSect);
|
|
if ( (DomainInfo->DomFlags & DOM_THREAD_TERMINATE) != 0 ||
|
|
NlGlobalTerminate ) {
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"Domain thread asked to terminate\n"));
|
|
DomainInfo->DomFlags &= ~DOM_THREAD_RUNNING;
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// If there are things to do,
|
|
// pick one thing to do and
|
|
// save it so we can safely drop the crit sect.
|
|
//
|
|
|
|
if ( DomainInfo->DomFlags & DOM_CREATION_NEEDED ) {
|
|
DomFlags = DOM_CREATION_NEEDED;
|
|
|
|
} else if ( DomainInfo->DomFlags & DOM_ROLE_UPDATE_NEEDED ) {
|
|
DomFlags = DOM_ROLE_UPDATE_NEEDED;
|
|
|
|
} else if ( DomainInfo->DomFlags & DOM_TRUST_UPDATE_NEEDED ) {
|
|
DomFlags = DOM_TRUST_UPDATE_NEEDED;
|
|
|
|
} else if ( DomainInfo->DomFlags & DOM_API_TIMEOUT_NEEDED ) {
|
|
DomFlags = DOM_API_TIMEOUT_NEEDED;
|
|
|
|
//
|
|
// Pick up all work items accociate with the primary announcement
|
|
//
|
|
} else if ( DomainInfo->DomFlags & DOM_PRIMARY_ANNOUNCE_FLAGS ) {
|
|
DomFlags = DomainInfo->DomFlags & DOM_PRIMARY_ANNOUNCE_FLAGS;
|
|
|
|
} else {
|
|
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"Domain thread exitting\n"));
|
|
DomainInfo->DomFlags &= ~DOM_THREAD_RUNNING;
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
return;
|
|
}
|
|
|
|
DomainInfo->DomFlags &= ~DomFlags;
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
// If phase 2 of domain creation is needed,
|
|
// do it now.
|
|
//
|
|
|
|
if ( DomFlags & DOM_CREATION_NEEDED ) {
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"Domain thread started doing create phase 2\n"));
|
|
|
|
//
|
|
// Do the time intensive portion of creating the domain.
|
|
//
|
|
|
|
(VOID) NlCreateDomainPhase2( DomainInfo, FALSE );
|
|
|
|
} else if ( DomFlags & DOM_ROLE_UPDATE_NEEDED ) {
|
|
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"Domain thread started doing update role\n"));
|
|
|
|
(VOID) NlUpdateRole( DomainInfo );
|
|
|
|
} else if ( DomFlags & DOM_TRUST_UPDATE_NEEDED ) {
|
|
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"Domain thread started doing update trust list\n"));
|
|
|
|
(VOID) NlInitTrustList( DomainInfo );
|
|
|
|
} else if ( DomFlags & DOM_API_TIMEOUT_NEEDED ) {
|
|
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"Domain thread started doing API timeout\n"));
|
|
|
|
NlTimeoutApiClientSession( DomainInfo );
|
|
|
|
} else if ( DomFlags & DOM_PRIMARY_ANNOUNCE_FLAGS ) {
|
|
DWORD AnnounceFlags = 0;
|
|
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"Domain thread started doing primary announecement 0x%lx\n", DomFlags ));
|
|
|
|
//
|
|
// If we need to do immediate announcement,
|
|
// indicate so to the worker routine
|
|
//
|
|
if ( DomFlags & DOM_PRIMARY_ANNOUNCE_IMMEDIATE ) {
|
|
AnnounceFlags = ANNOUNCE_IMMEDIATE;
|
|
|
|
//
|
|
// Otherwise, if we only need to continue announcement,
|
|
// indicate so to the worker routine
|
|
//
|
|
} else if ( (DomFlags & DOM_PRIMARY_ANNOUNCE_NEEDED) == 0 &&
|
|
(DomFlags & DOM_PRIMARY_ANNOUNCE_CONTINUE) != 0 ) {
|
|
AnnounceFlags = ANNOUNCE_CONTINUE;
|
|
}
|
|
|
|
NlPrimaryAnnouncement( AnnounceFlags );
|
|
|
|
//
|
|
// Internal consistency check
|
|
//
|
|
|
|
} else {
|
|
|
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|
"Invalid DomFlags %lx\n",
|
|
DomFlags ));
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
NlStopDomainThread(
|
|
PDOMAIN_INFO DomainInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Stops the domain thread if it is running and waits for it to
|
|
stop.
|
|
|
|
Arguments:
|
|
|
|
NONE
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
{
|
|
|
|
//
|
|
// Only stop the thread if it's running
|
|
//
|
|
|
|
EnterCriticalSection( &NlGlobalDomainCritSect );
|
|
if ( DomainInfo->DomFlags & DOM_THREAD_RUNNING ) {
|
|
|
|
//
|
|
// Ask the thread to stop running.
|
|
//
|
|
|
|
DomainInfo->DomFlags |= DOM_THREAD_TERMINATE;
|
|
|
|
//
|
|
// Loop waiting for it to stop.
|
|
//
|
|
|
|
while ( DomainInfo->DomFlags & DOM_THREAD_RUNNING ) {
|
|
LeaveCriticalSection( &NlGlobalDomainCritSect );
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"NlStopDomainThread: Sleeping a second waiting for thread to stop.\n"));
|
|
Sleep( 1000 );
|
|
EnterCriticalSection( &NlGlobalDomainCritSect );
|
|
}
|
|
|
|
//
|
|
// Domain thread no longer needs to terminate
|
|
//
|
|
|
|
DomainInfo->DomFlags &= ~DOM_THREAD_TERMINATE;
|
|
|
|
}
|
|
LeaveCriticalSection( &NlGlobalDomainCritSect );
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NET_API_STATUS
|
|
NlStartDomainThread(
|
|
PDOMAIN_INFO DomainInfo,
|
|
PDWORD DomFlags
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Start the domain thread if it is not already running.
|
|
|
|
The domain thread is simply one of the worker threads. However, we
|
|
ensure that only one worker thread is working on a single domain at once.
|
|
That ensures that slow items (such as NlUpdateRole) don't consume more than
|
|
one worker thread and are themselves serialized.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Domain the thread is to be started for.
|
|
|
|
DomFlags - Specifies which operations the Domain Thread is to perform
|
|
|
|
Return Value:
|
|
|
|
NO_ERROR
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Tell the thread what work it has to do.
|
|
//
|
|
|
|
EnterCriticalSection( &NlGlobalDomainCritSect );
|
|
DomainInfo->DomFlags |= *DomFlags;
|
|
|
|
//
|
|
// If the domain thread is already running, do nothing.
|
|
//
|
|
|
|
if ( DomainInfo->DomFlags & DOM_THREAD_RUNNING ) {
|
|
NlPrintDom((NL_DOMAIN, DomainInfo,
|
|
"The domain thread is already running %lx.\n",
|
|
*DomFlags ));
|
|
LeaveCriticalSection( &NlGlobalDomainCritSect );
|
|
return NO_ERROR;
|
|
}
|
|
|
|
//
|
|
// Start the thread
|
|
//
|
|
// Make this a high priority thread to avoid 100's of trusted domain discoveries.
|
|
//
|
|
|
|
DomainInfo->DomFlags &= ~DOM_THREAD_TERMINATE;
|
|
|
|
if ( NlQueueWorkItem( &DomainInfo->DomThreadWorkItem, TRUE, TRUE ) ) {
|
|
DomainInfo->DomFlags |= DOM_THREAD_RUNNING;
|
|
}
|
|
LeaveCriticalSection( &NlGlobalDomainCritSect );
|
|
|
|
return NO_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
NTSTATUS
|
|
NlUpdateDatabaseRole(
|
|
IN PDOMAIN_INFO DomainInfo,
|
|
IN DWORD Role
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Update the role of the Sam database to match the current role of the domain.
|
|
|
|
Netlogon sets the role of the domain to be the same as the role in SAM account domain.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Hosted Domain this database is for.
|
|
|
|
Role - Our new Role.
|
|
RoleInvalid implies the domain is being deleted.
|
|
|
|
Return Value:
|
|
|
|
NT status code.
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS Status;
|
|
|
|
POLICY_LSA_SERVER_ROLE DesiredLsaRole;
|
|
|
|
//
|
|
// Convert the role to SAM/LSA specific values.
|
|
//
|
|
|
|
switch ( Role ) {
|
|
case RolePrimary:
|
|
DesiredLsaRole = PolicyServerRolePrimary;
|
|
break;
|
|
case RoleInvalid:
|
|
Status = STATUS_SUCCESS;
|
|
goto Cleanup;
|
|
case RoleBackup:
|
|
DesiredLsaRole = PolicyServerRoleBackup;
|
|
break;
|
|
default:
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"NlUpdateDatabaseRole: Netlogon's role isn't valid %ld.\n",
|
|
Role ));
|
|
Status = STATUS_INVALID_DOMAIN_ROLE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Ensure the changelog knows the current role.
|
|
// (This is really only needed on startup and if netlogon.dll has
|
|
// been unloaded. Otherwise, the LSA will do this notification
|
|
// when the role is really changed.)
|
|
//
|
|
|
|
if ( NlGlobalNetlogonUnloaded &&
|
|
NlGlobalChangeLogRole == ChangeLogUnknown ) {
|
|
NlPrint((NL_INIT,
|
|
"Set changelog role after netlogon.dll unload\n" ));
|
|
Status = NetpNotifyRole ( DesiredLsaRole );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"NlUpdateDatabaseRole: Cannot NetpNotifyRole: %lx\n",
|
|
Status ));
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
Status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// Free locally used resources.
|
|
//
|
|
Cleanup:
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PCLIENT_SESSION
|
|
NlRefDomClientSession(
|
|
IN PDOMAIN_INFO DomainInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Increment the reference count on the ClientSession structure for the domain.
|
|
If the ClientSession structure doesn't exist, this routine will FAIL.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Domain whose ClientSession reference count is to be incremented.
|
|
|
|
Return Value:
|
|
|
|
Pointer to the client session structure whose reference count was
|
|
properly incremented.
|
|
|
|
NULL - The ClientSession structure doesn't exist
|
|
|
|
--*/
|
|
{
|
|
PCLIENT_SESSION ClientSession;
|
|
LOCK_TRUST_LIST( DomainInfo );
|
|
if ( DomainInfo->DomClientSession != NULL ) {
|
|
ClientSession = DomainInfo->DomClientSession;
|
|
NlRefClientSession( ClientSession );
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
return ClientSession;
|
|
} else {
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
return NULL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PCLIENT_SESSION
|
|
NlRefDomParentClientSession(
|
|
IN PDOMAIN_INFO DomainInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Increment the reference count on the ParentClientSession structure for the domain.
|
|
If the ParentClientSession structure doesn't exist, this routine will FAIL.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Domain whose ParentClientSession reference count is to be incremented.
|
|
|
|
Return Value:
|
|
|
|
Pointer to the client session structure whose reference count was
|
|
properly incremented.
|
|
|
|
NULL - The ParentClientSession structure doesn't exist
|
|
|
|
--*/
|
|
{
|
|
PCLIENT_SESSION ClientSession;
|
|
LOCK_TRUST_LIST( DomainInfo );
|
|
if ( DomainInfo->DomParentClientSession != NULL ) {
|
|
ClientSession = DomainInfo->DomParentClientSession;
|
|
NlRefClientSession( ClientSession );
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
return ClientSession;
|
|
} else {
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
return NULL;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
VOID
|
|
NlDeleteDomClientSession(
|
|
IN PDOMAIN_INFO DomainInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete the domain's ClientSession stucture (If it exists)
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Domain whose ClientSession is to be deleted
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PCLIENT_SESSION ClientSession;
|
|
|
|
//
|
|
// Delete the client session
|
|
//
|
|
|
|
LOCK_TRUST_LIST( DomainInfo );
|
|
if ( DomainInfo->DomClientSession != NULL ) {
|
|
|
|
//
|
|
// Don't allow any new references.
|
|
//
|
|
|
|
ClientSession = DomainInfo->DomClientSession;
|
|
DomainInfo->DomClientSession = NULL;
|
|
NlFreeClientSession( ClientSession );
|
|
|
|
//
|
|
// Don't leave a straggling pointer to the deleted ClientSession
|
|
//
|
|
if ( IsPrimaryDomain(DomainInfo) ) {
|
|
NlGlobalClientSession = NULL;
|
|
}
|
|
|
|
//
|
|
// Wait for us to be the last reference.
|
|
//
|
|
|
|
while ( ClientSession->CsReferenceCount != 1 ) {
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"NlDeleteDomClientSession: Sleeping a second waiting for ClientSession RefCount to zero.\n"));
|
|
Sleep( 1000 );
|
|
LOCK_TRUST_LIST( DomainInfo );
|
|
}
|
|
|
|
NlUnrefClientSession( ClientSession );
|
|
|
|
}
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
|
|
}
|
|
|
|
|
|
VOID
|
|
NlDeleteDomParentClientSession(
|
|
IN PDOMAIN_INFO DomainInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete the domain's ClientSession stucture (If it exists)
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Domain whose ClientSession is to be deleted
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PCLIENT_SESSION ClientSession;
|
|
|
|
//
|
|
// Delete the client session
|
|
//
|
|
|
|
LOCK_TRUST_LIST( DomainInfo );
|
|
if ( DomainInfo->DomParentClientSession != NULL ) {
|
|
|
|
//
|
|
// Don't allow any new references.
|
|
//
|
|
|
|
ClientSession = DomainInfo->DomParentClientSession;
|
|
DomainInfo->DomParentClientSession = NULL;
|
|
NlFreeClientSession( ClientSession );
|
|
|
|
|
|
//
|
|
// Wait for us to be the last reference.
|
|
//
|
|
|
|
while ( ClientSession->CsReferenceCount != 1 ) {
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"NlDeleteDomParentClientSession: Sleeping a second waiting for ClientSession RefCount to zero.\n"));
|
|
Sleep( 1000 );
|
|
LOCK_TRUST_LIST( DomainInfo );
|
|
}
|
|
|
|
NlUnrefClientSession( ClientSession );
|
|
|
|
}
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
|
|
}
|
|
|
|
|
|
NET_API_STATUS
|
|
NlUpdateRole(
|
|
IN PDOMAIN_INFO DomainInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determines the role of this machine, sets that role in the Netlogon service,
|
|
the server service, and browser.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Hosted domain who's role is to be updated.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
This routine logs any error it detects, but it doesn't call NlExit.
|
|
|
|
--*/
|
|
{
|
|
LONG NetStatus;
|
|
NTSTATUS Status;
|
|
|
|
NETLOGON_ROLE NewRole;
|
|
BOOL NewPdcDoReplication;
|
|
BOOL PdcToConnectTo = FALSE;
|
|
BOOL ReplLocked = FALSE;
|
|
DWORD i;
|
|
|
|
LPWSTR AllocatedBuffer = NULL;
|
|
LPWSTR CapturedDnsDomainName;
|
|
LPWSTR CapturedDnsForestName;
|
|
GUID CapturedDomainGuidBuffer;
|
|
GUID *CapturedDomainGuid;
|
|
LPWSTR ChangeLogFile;
|
|
ULONG InternalFlags = 0;
|
|
|
|
PNL_DC_CACHE_ENTRY DomainControllerCacheEntry = NULL;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
BOOLEAN ThisIsPdc;
|
|
BOOLEAN Nt4MixedDomain;
|
|
|
|
//
|
|
// Allocate a buffer for storage local to this procedure.
|
|
// (Don't put it on the stack since we don't want to commit a huge stack.)
|
|
//
|
|
|
|
AllocatedBuffer = LocalAlloc( 0, sizeof(WCHAR) *
|
|
((NL_MAX_DNS_LENGTH+1) +
|
|
(NL_MAX_DNS_LENGTH+1) +
|
|
(MAX_PATH+1) ) );
|
|
|
|
if ( AllocatedBuffer == NULL ) {
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
CapturedDnsDomainName = AllocatedBuffer;
|
|
CapturedDnsForestName = &CapturedDnsDomainName[NL_MAX_DNS_LENGTH+1];
|
|
ChangeLogFile = &CapturedDnsForestName[NL_MAX_DNS_LENGTH+1];
|
|
|
|
|
|
//
|
|
// Get the information used to determine role from the DS
|
|
//
|
|
|
|
NetStatus = NlGetRoleInformation(
|
|
DomainInfo,
|
|
&ThisIsPdc,
|
|
&Nt4MixedDomain );
|
|
|
|
if ( NetStatus != ERROR_SUCCESS ) {
|
|
|
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|
"NlUpdateRole: Failed to NlGetRoleInformation. %ld\n",
|
|
NetStatus ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//
|
|
// Determine the current role of this machine.
|
|
//
|
|
|
|
if ( ThisIsPdc ) {
|
|
NewRole = RolePrimary;
|
|
NewPdcDoReplication = FALSE;
|
|
|
|
if ( Nt4MixedDomain || NlGlobalParameters.AllowReplInNonMixed ) {
|
|
NewPdcDoReplication = TRUE;
|
|
}
|
|
|
|
} else {
|
|
NewRole = RoleBackup;
|
|
NewPdcDoReplication = FALSE;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// If the role has changed, tell everybody.
|
|
//
|
|
|
|
if ( DomainInfo->DomRole != NewRole ) {
|
|
|
|
NlPrintDom((NL_DOMAIN, DomainInfo,
|
|
"Changing role from %s to %s.\n",
|
|
(DomainInfo->DomRole == RolePrimary) ? "PDC" :
|
|
(DomainInfo->DomRole == RoleBackup ? "BDC" : "NONE" ),
|
|
(NewRole == RolePrimary) ? "PDC" :
|
|
(NewRole == RoleBackup ? "BDC" : "NONE" ) ));
|
|
|
|
// ??: Shouldn't there be some synchronization here.
|
|
DomainInfo->DomRole = NewRole;
|
|
|
|
//
|
|
// Create a ClientSession structure.
|
|
//
|
|
// Even the PDC has a client session to itself. It is used (for instance)
|
|
// when the PDC changes its own machine account password.
|
|
//
|
|
|
|
LOCK_TRUST_LIST( DomainInfo );
|
|
|
|
//
|
|
// Allocate the Client Session structure used to talk to the PDC.
|
|
//
|
|
// DomClientSession will only be non-null if a previous promotion
|
|
// to PDC failed.
|
|
//
|
|
|
|
if ( DomainInfo->DomClientSession == NULL ) {
|
|
DomainInfo->DomClientSession = NlAllocateClientSession(
|
|
DomainInfo,
|
|
&DomainInfo->DomUnicodeDomainNameString,
|
|
&DomainInfo->DomUnicodeDnsDomainNameString,
|
|
DomainInfo->DomAccountDomainId,
|
|
DomainInfo->DomDomainGuid,
|
|
CS_DIRECT_TRUST |
|
|
(DomainInfo->DomUnicodeDnsDomainNameString.Length != 0 ? CS_NT5_DOMAIN_TRUST : 0),
|
|
ServerSecureChannel,
|
|
0 ); // No trust attributes
|
|
|
|
if ( DomainInfo->DomClientSession == NULL ) {
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"Cannot allocate PDC ClientSession\n"));
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Save a copy of the client session for convenience.
|
|
// A BDC has only one client session to its PDC.
|
|
//
|
|
if ( IsPrimaryDomain(DomainInfo) ) {
|
|
NlGlobalClientSession = DomainInfo->DomClientSession;
|
|
}
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
|
|
//
|
|
// If this machine is now a PDC,
|
|
// Perform PDC-specific initialization.
|
|
//
|
|
|
|
if ( DomainInfo->DomRole == RolePrimary ) {
|
|
|
|
//
|
|
// The first time this machine is promoted to PDC,
|
|
// Do some "one-time" initialization.
|
|
|
|
EnterCriticalSection( &NlGlobalDomainCritSect );
|
|
if ( (DomainInfo->DomFlags & DOM_PROMOTED_BEFORE) == 0 ) {
|
|
|
|
//
|
|
// Initialize the server session table to contain all the BDCs.
|
|
// On demotion, we don't delete the table entries. We just leave
|
|
// them around until the next promotion.
|
|
//
|
|
|
|
Status = NlBuildNtBdcList(DomainInfo);
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
LeaveCriticalSection( &NlGlobalDomainCritSect );
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"Cannot initialize NT BDC list: 0x%lx\n",
|
|
Status ));
|
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Flag that we don't need to run this code again.
|
|
//
|
|
|
|
DomainInfo->DomFlags |= DOM_PROMOTED_BEFORE;
|
|
}
|
|
LeaveCriticalSection( &NlGlobalDomainCritSect );
|
|
|
|
//
|
|
// Free the list of failed user logons that could
|
|
// exist if this machine was a BDC
|
|
//
|
|
|
|
LOCK_TRUST_LIST( DomainInfo );
|
|
while ( !IsListEmpty(&DomainInfo->DomFailedUserLogonList) ) {
|
|
ListEntry = RemoveHeadList( &DomainInfo->DomFailedUserLogonList );
|
|
|
|
//
|
|
// Free the logon structure
|
|
//
|
|
LocalFree( CONTAINING_RECORD(ListEntry, NL_FAILED_USER_LOGON, FuNext) );
|
|
}
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
}
|
|
|
|
|
|
//
|
|
// Tell the browser and the SMB server about our new role.
|
|
//
|
|
// Do this before the NetpLogonGetDCName since this registers the computer
|
|
// name of an hosted domain in the browser allowing the response from
|
|
// the PDC to be heard.
|
|
//
|
|
|
|
NlBrowserUpdate( DomainInfo, DomainInfo->DomRole );
|
|
|
|
|
|
|
|
//
|
|
// Check to see if the PDC is up and running.
|
|
//
|
|
// When NetpDcGetName is called from netlogon,
|
|
// it has both the Netbios and DNS domain name available for the primary
|
|
// domain. That can trick DsGetDcName into returning DNS host name of a
|
|
// DC in the primary domain. However, on IPX only systems, that won't work.
|
|
// Avoid that problem by not passing the DNS domain name of the primary domain
|
|
// if there are no DNS servers.
|
|
//
|
|
// Avoid having anything locked while calling NetpDcGetName.
|
|
// It calls back into Netlogon and locks heaven only knows what.
|
|
|
|
CapturedDomainGuid = NlCaptureDomainInfo( DomainInfo,
|
|
CapturedDnsDomainName,
|
|
&CapturedDomainGuidBuffer );
|
|
NlCaptureDnsForestName( CapturedDnsForestName );
|
|
|
|
NetStatus = NetpDcGetName(
|
|
DomainInfo,
|
|
DomainInfo->DomUnicodeComputerNameString.Buffer,
|
|
NULL, // No account name
|
|
0, // No account control bits
|
|
DomainInfo->DomUnicodeDomainName,
|
|
NlDnsHasDnsServers() ? CapturedDnsDomainName : NULL,
|
|
CapturedDnsForestName,
|
|
DomainInfo->DomAccountDomainId,
|
|
CapturedDomainGuid,
|
|
NULL, // Site name not needed for PDC query
|
|
DS_FORCE_REDISCOVERY |
|
|
DS_PDC_REQUIRED |
|
|
DS_AVOID_SELF, // Avoid responding to this call ourself
|
|
InternalFlags,
|
|
NL_DC_MAX_TIMEOUT + NlGlobalParameters.ExpectedDialupDelay*1000,
|
|
MAX_DC_RETRIES,
|
|
NULL,
|
|
&DomainControllerCacheEntry );
|
|
|
|
//
|
|
// If we've been asked to terminate,
|
|
// do so.
|
|
//
|
|
|
|
if ( (DomainInfo->DomFlags & DOM_THREAD_TERMINATE) != 0 ||
|
|
NlGlobalTerminate ) {
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"Domain thread asked to terminate\n"));
|
|
NetStatus = ERROR_OPERATION_ABORTED;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Handle the case where the PDC isn't up.
|
|
//
|
|
|
|
if ( NetStatus != NERR_Success) {
|
|
|
|
//
|
|
// Handle starting a BDC when there is no current primary in
|
|
// this domain.
|
|
//
|
|
|
|
if ( DomainInfo->DomRole == RoleBackup ) {
|
|
|
|
// ??: Log hosted domain name with this message
|
|
NlpWriteEventlog( SERVICE_UIC_M_NETLOGON_NO_DC,
|
|
EVENTLOG_WARNING_TYPE,
|
|
NULL,
|
|
0,
|
|
NULL,
|
|
0 );
|
|
|
|
//
|
|
// Start normally but defer authentication with the
|
|
// primary until it starts.
|
|
//
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// There is a primary dc running in this domain
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Since there already is a primary in the domain,
|
|
// we cannot become the primary.
|
|
//
|
|
|
|
if ( DomainInfo->DomRole == RolePrimary) {
|
|
|
|
//
|
|
// Don't worry if this is a BDC telling us that we're the PDC.
|
|
//
|
|
|
|
if ( (DomainControllerCacheEntry->UnicodeNetbiosDcName != NULL) &&
|
|
NlNameCompare( DomainInfo->DomUnicodeComputerNameString.Buffer,
|
|
DomainControllerCacheEntry->UnicodeNetbiosDcName,
|
|
NAMETYPE_COMPUTER) != 0 ){
|
|
LPWSTR AlertStrings[2];
|
|
|
|
//
|
|
// alert admin.
|
|
//
|
|
|
|
AlertStrings[0] = DomainControllerCacheEntry->UnicodeNetbiosDcName;
|
|
AlertStrings[1] = NULL; // Needed for RAISE_ALERT_TOO
|
|
|
|
// ??: Log hosted domain name with this message
|
|
// ??: Log the name of the other PDC (Put it in message too)
|
|
NlpWriteEventlog( SERVICE_UIC_M_NETLOGON_DC_CFLCT,
|
|
EVENTLOG_ERROR_TYPE,
|
|
NULL,
|
|
0,
|
|
AlertStrings,
|
|
1 | NETP_RAISE_ALERT_TOO );
|
|
NetStatus = SERVICE_UIC_M_NETLOGON_DC_CFLCT;
|
|
goto Done;
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// If we're a BDC in the domain,
|
|
// sanity check the PDC.
|
|
//
|
|
|
|
} else {
|
|
|
|
//
|
|
// Indicate that there is a primary to connect to.
|
|
//
|
|
|
|
PdcToConnectTo = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Tell SAM/LSA about the new role
|
|
//
|
|
|
|
(VOID) NlUpdateDatabaseRole( DomainInfo, DomainInfo->DomRole );
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Ensure there is only one hosted domain.
|
|
//
|
|
|
|
NlAssert( IsPrimaryDomain( DomainInfo ) );
|
|
|
|
EnterCriticalSection( &NlGlobalReplicatorCritSect );
|
|
ReplLocked = TRUE;
|
|
|
|
//
|
|
// If we're to replicate to NT 4 BDC's,
|
|
// remember that.
|
|
//
|
|
|
|
if ( NewPdcDoReplication != NlGlobalPdcDoReplication ) {
|
|
NlGlobalPdcDoReplication = NewPdcDoReplication;
|
|
|
|
if ( NlGlobalPdcDoReplication ) {
|
|
NlPrintDom((NL_DOMAIN, DomainInfo,
|
|
"Setting this machine to be a PDC that replicates to NT 4 BDCs\n" ));
|
|
|
|
//
|
|
// Update the NlGlobalDBInfoArray for the various databases.
|
|
//
|
|
|
|
for ( i = 0; i < NUM_DBS; i++ ) {
|
|
|
|
if ( i == LSA_DB) {
|
|
//
|
|
// Initialize LSA database info.
|
|
//
|
|
|
|
Status = NlInitLsaDBInfo( DomainInfo, LSA_DB );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"Cannot NlInitLsaDBInfo %lx\n",
|
|
Status ));
|
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|
goto Cleanup;
|
|
}
|
|
} else {
|
|
|
|
//
|
|
// Initialize the Sam domain.
|
|
//
|
|
|
|
Status = NlInitSamDBInfo( DomainInfo, i );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"Cannot NlInitSamDBInfo (%ws) %lx\n",
|
|
NlGlobalDBInfoArray[i].DBName,
|
|
Status ));
|
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If we haven't done so already,
|
|
// setup a session to the PDC.
|
|
//
|
|
|
|
if ( DomainInfo->DomRole == RoleBackup && PdcToConnectTo ) {
|
|
PCLIENT_SESSION ClientSession;
|
|
|
|
//
|
|
// On a BDC, set up a session to the PDC now.
|
|
//
|
|
|
|
ClientSession = NlRefDomClientSession( DomainInfo );
|
|
|
|
if ( ClientSession != NULL ) {
|
|
|
|
if ( NlTimeoutSetWriterClientSession(
|
|
ClientSession,
|
|
WRITER_WAIT_PERIOD )) {
|
|
|
|
if ( ClientSession->CsState != CS_AUTHENTICATED ) {
|
|
NET_API_STATUS TmpNetStatus;
|
|
|
|
//
|
|
// Reset the current DC.
|
|
//
|
|
|
|
NlSetStatusClientSession( ClientSession, STATUS_NO_LOGON_SERVERS );
|
|
|
|
//
|
|
// Set the PDC info in the Client Session structure.
|
|
//
|
|
|
|
TmpNetStatus = NlSetServerClientSession(
|
|
ClientSession,
|
|
DomainControllerCacheEntry,
|
|
FALSE, // was not discovery with account
|
|
FALSE ); // not the session refresh
|
|
|
|
if ( TmpNetStatus == NO_ERROR ) {
|
|
|
|
//
|
|
// NT 5 BDCs only support NT 5 PDCs
|
|
//
|
|
EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|
ClientSession->CsDiscoveryFlags |= CS_DISCOVERY_HAS_DS|CS_DISCOVERY_IS_CLOSE;
|
|
LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
|
|
|
|
//
|
|
// Setup a session to the PDC.
|
|
//
|
|
// Avoid this step if we are in the process of starting
|
|
// when we run in the main thread where we don't want to
|
|
// hang on indefinitely long RPC calls made during the
|
|
// session setup
|
|
//
|
|
if ( NlGlobalChangeLogNetlogonState != NetlogonStarting ) {
|
|
(VOID) NlSessionSetup( ClientSession );
|
|
// NlSessionSetup logged the error.
|
|
}
|
|
}
|
|
}
|
|
NlResetWriterClientSession( ClientSession );
|
|
|
|
}
|
|
|
|
NlUnrefClientSession( ClientSession );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If we're a normal BDC
|
|
// we delete the change log to prevent confusion if we ever get promoted.
|
|
//
|
|
|
|
if ( IsPrimaryDomain(DomainInfo) ) {
|
|
|
|
if ( DomainInfo->DomRole == RoleBackup ) {
|
|
|
|
wcscpy( ChangeLogFile, NlGlobalChangeLogFilePrefix );
|
|
wcscat( ChangeLogFile, CHANGELOG_FILE_POSTFIX );
|
|
|
|
if ( DeleteFileW( ChangeLogFile ) ) {
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"NlUpdateRole: Deleted change log since this is now a BDC.\n" ));
|
|
}
|
|
}
|
|
|
|
//
|
|
// Delete the redo log.
|
|
// (NT 5 doesn't use the redo log any more. This is simply cleanup.)
|
|
//
|
|
|
|
wcscpy( ChangeLogFile, NlGlobalChangeLogFilePrefix );
|
|
wcscat( ChangeLogFile, REDO_FILE_POSTFIX );
|
|
|
|
if ( DeleteFileW( ChangeLogFile ) ) {
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"NlUpdateRole: Deleted redo log since NT 5 doesn't use it.\n" ));
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//
|
|
// Register the appropriate DNS names for this role.
|
|
//
|
|
// Avoid this operation at service startup (the appropriate service
|
|
// notifications or timer expire will trigger DNS updates in the
|
|
// main loop instead). These registrations can be lengthy and we
|
|
// don't want to spend too much time on start up. Also, these DNS
|
|
// updates may be secure which will result in calls into Kerberos
|
|
// that may not be started yet on startup.
|
|
//
|
|
|
|
if ( NlGlobalChangeLogNetlogonState != NetlogonStarting ) {
|
|
NetStatus = NlDnsAddDomainRecords( DomainInfo, 0 );
|
|
|
|
//
|
|
// On success, scavenge through the list
|
|
// of records and update DNS in a worker thread
|
|
//
|
|
if ( NetStatus != NO_ERROR ) {
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"NlUpdateRole: Couldn't register DNS names %ld\n", NetStatus ));
|
|
goto Cleanup;
|
|
} else {
|
|
NlDnsForceScavenge( FALSE, // don't refresh domain records: we've done it already
|
|
FALSE ); // don't force re-register
|
|
}
|
|
}
|
|
|
|
NetStatus = NERR_Success;
|
|
goto Done;
|
|
|
|
Cleanup: {
|
|
|
|
LPWSTR MsgStrings[1];
|
|
|
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|
"NlUpdateRole Failed %ld",
|
|
NetStatus ));
|
|
|
|
MsgStrings[0] = (LPWSTR) ULongToPtr( NetStatus );
|
|
|
|
// ??: Log hosted domain name with this message
|
|
NlpWriteEventlog( NELOG_NetlogonSystemError,
|
|
EVENTLOG_ERROR_TYPE,
|
|
(LPBYTE)&NetStatus,
|
|
sizeof(NetStatus),
|
|
MsgStrings,
|
|
1 | NETP_LAST_MESSAGE_IS_NETSTATUS );
|
|
|
|
}
|
|
|
|
//
|
|
// All done
|
|
//
|
|
|
|
Done:
|
|
//
|
|
// If the operation failed,
|
|
// indicate that we need to try again periodically.
|
|
//
|
|
if ( NetStatus != NO_ERROR ) {
|
|
DomainInfo->DomRole = RoleInvalid;
|
|
}
|
|
|
|
if ( DomainControllerCacheEntry != NULL) {
|
|
NetpDcDerefCacheEntry( DomainControllerCacheEntry );
|
|
}
|
|
if ( ReplLocked ) {
|
|
LeaveCriticalSection( &NlGlobalReplicatorCritSect );
|
|
}
|
|
|
|
if ( AllocatedBuffer != NULL ) {
|
|
LocalFree( AllocatedBuffer );
|
|
}
|
|
|
|
return NetStatus;
|
|
|
|
}
|
|
#endif // _DC_NETLOGON
|
|
|
|
|
|
NET_API_STATUS
|
|
NlCreateDomainPhase1(
|
|
IN LPWSTR DomainName OPTIONAL,
|
|
IN LPWSTR DnsDomainName OPTIONAL,
|
|
IN PSID DomainSid OPTIONAL,
|
|
IN GUID *DomainGuid OPTIONAL,
|
|
IN LPWSTR ComputerName,
|
|
IN LPWSTR DnsHostName OPTIONAL,
|
|
IN BOOLEAN CallNlExitOnFailure,
|
|
IN ULONG DomainFlags,
|
|
OUT PDOMAIN_INFO *ReturnedDomainInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Create a new domain object to the point where the remainder of the object
|
|
can be created asynchronously in a domain specific worker thread.
|
|
|
|
Arguments:
|
|
|
|
DomainName - Netbios Name of the domain to host.
|
|
|
|
DnsDomainName - DNS name of the domain to host.
|
|
NULL if the domain has no DNS Domain Name.
|
|
|
|
DomainSid - DomainSid of the specified domain.
|
|
|
|
DomainGuid - GUID of the specified domain.
|
|
|
|
ComputerName - Name of this computer in the specified domain.
|
|
NULL if not the primary domain for the DC.
|
|
|
|
DnsHostName - DNS Host name of this computer in the specified domain.
|
|
NULL if the domain has no DNS host name or if not the primary domain
|
|
for the DC.
|
|
|
|
CallNlExitOnFailure - TRUE if NlExit should be called on failure.
|
|
|
|
DomainFlags - Specifies proporties of this domain such as primary domain,
|
|
non-domain NC, forest entry.
|
|
|
|
ReturnedDomainInfo - On success, returns a pointer to a referenced DomainInfo
|
|
structure. It is the callers responsibility to call NlDereferenceDomain.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
NET_API_STATUS NetStatus;
|
|
|
|
BOOLEAN CanCallNlDeleteDomain = FALSE;
|
|
|
|
PDOMAIN_INFO DomainInfo = NULL;
|
|
DWORD DomainSidSize = 0;
|
|
|
|
LPBYTE Where;
|
|
ULONG i;
|
|
DWORD DomFlags = 0;
|
|
|
|
BOOL DomainCreated = FALSE;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
EnterCriticalSection(&NlGlobalDomainCritSect);
|
|
NlPrint(( NL_DOMAIN, "%ws: Adding new domain\n",
|
|
(DomainName != NULL) ? DomainName : DnsDomainName ));
|
|
|
|
if ( DomainSid != NULL ) {
|
|
DomainSidSize = RtlLengthSid( DomainSid );
|
|
}
|
|
|
|
|
|
//
|
|
// See if the domain already exists.
|
|
//
|
|
|
|
if ( DomainName != NULL ) {
|
|
DomainInfo = NlFindNetbiosDomain( DomainName, FALSE );
|
|
} else if ( DnsDomainName != NULL ) {
|
|
LPSTR Utf8DnsDomainName = NetpAllocUtf8StrFromWStr( DnsDomainName );
|
|
|
|
if ( Utf8DnsDomainName == NULL ) {
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
if ( CallNlExitOnFailure ) {
|
|
NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL );
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
DomainInfo = NlFindDnsDomain( Utf8DnsDomainName,
|
|
DomainGuid,
|
|
TRUE, // look up NDNCs too
|
|
FALSE, // don't check alias names
|
|
NULL ); // don't care if alias name matched
|
|
|
|
NetpMemoryFree( Utf8DnsDomainName );
|
|
}
|
|
|
|
if ( DomainInfo != NULL ) {
|
|
DomainCreated = FALSE;
|
|
#ifdef _DC_NETLOGON
|
|
DomainInfo->DomFlags &= ~DOM_DOMAIN_REFRESH_PENDING;
|
|
#endif // _DC_NETLOGON
|
|
|
|
} else {
|
|
DomainCreated = TRUE;
|
|
|
|
//
|
|
// Allocate a structure describing the new domain.
|
|
//
|
|
|
|
DomainInfo = LocalAlloc(
|
|
LMEM_ZEROINIT,
|
|
ROUND_UP_COUNT( sizeof(DOMAIN_INFO), ALIGN_DWORD) +
|
|
DomainSidSize );
|
|
|
|
if ( DomainInfo == NULL ) {
|
|
NetStatus = GetLastError();
|
|
if ( CallNlExitOnFailure ) {
|
|
NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Create an interim reference count for this domain.
|
|
// (Once for the reference by this routine.)
|
|
//
|
|
|
|
DomainInfo->ReferenceCount = 1;
|
|
NlGlobalServicedDomainCount ++;
|
|
|
|
#ifdef _DC_NETLOGON
|
|
//
|
|
// Set the domain flags
|
|
//
|
|
|
|
DomainInfo->DomFlags |= DomainFlags;
|
|
if ( DomainInfo->DomFlags & DOM_PRIMARY_DOMAIN ) {
|
|
NlGlobalDomainInfo = DomainInfo;
|
|
}
|
|
|
|
//
|
|
// Set the role we play in this domain
|
|
//
|
|
|
|
if ( NlGlobalMemberWorkstation ) {
|
|
DomainInfo->DomRole = RoleMemberWorkstation;
|
|
} else if ( DomainInfo->DomFlags & DOM_NON_DOMAIN_NC ) {
|
|
DomainInfo->DomRole = RoleNdnc;
|
|
} else if ( DomainInfo->DomFlags & DOM_REAL_DOMAIN ) {
|
|
DomainInfo->DomRole = RoleInvalid; // For real domains, force the role update
|
|
}
|
|
#endif // _DC_NETLOGON
|
|
|
|
//
|
|
// Initialize other constants.
|
|
//
|
|
|
|
RtlInitUnicodeString( &DomainInfo->DomUnicodeComputerNameString, NULL );
|
|
|
|
InitializeListHead(&DomainInfo->DomNext);
|
|
#ifdef _DC_NETLOGON
|
|
InitializeListHead( &DomainInfo->DomTrustList );
|
|
InitializeListHead( &DomainInfo->DomServerSessionTable );
|
|
InitializeListHead( &DomainInfo->DomFailedUserLogonList );
|
|
#endif // _DC_NETLOGON
|
|
NlInitializeWorkItem(&DomainInfo->DomThreadWorkItem, NlDomainThread, DomainInfo);
|
|
|
|
try {
|
|
InitializeCriticalSection( &DomainInfo->DomTrustListCritSect );
|
|
#ifdef _DC_NETLOGON
|
|
InitializeCriticalSection( &DomainInfo->DomServerSessionTableCritSect );
|
|
#endif // _DC_NETLOGON
|
|
} except( EXCEPTION_EXECUTE_HANDLER ) {
|
|
NlPrint(( NL_CRITICAL, "%ws: Cannot InitializeCriticalSections for domain\n",
|
|
DomainName ));
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
if ( CallNlExitOnFailure ) {
|
|
NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL );
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
|
|
|
|
//
|
|
// If the caller passed in a ComputerName,
|
|
// use it.
|
|
//
|
|
|
|
if ( ComputerName != NULL ) {
|
|
|
|
NetStatus = NlSetComputerName( DomainInfo, ComputerName, DnsHostName );
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"%ws: Cannot set ComputerName\n",
|
|
DomainName ));
|
|
if ( CallNlExitOnFailure ) {
|
|
NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL );
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Copy the domain id onto the end of the allocated buffer.
|
|
// (ULONG aligned)
|
|
//
|
|
|
|
Where = (LPBYTE)(DomainInfo+1);
|
|
Where = ROUND_UP_POINTER( Where, ALIGN_DWORD );
|
|
if ( DomainSid != NULL ) {
|
|
RtlCopyMemory( Where, DomainSid, DomainSidSize );
|
|
DomainInfo->DomAccountDomainId = (PSID) Where;
|
|
Where += DomainSidSize;
|
|
}
|
|
|
|
//
|
|
// Set the domain names in the structure.
|
|
//
|
|
|
|
NetStatus = NlSetDomainNameInDomainInfo( DomainInfo, DnsDomainName, DomainName, DomainGuid, NULL, NULL, NULL );
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"%ws: Cannot set DnsDomainName\n",
|
|
DomainName ));
|
|
if ( CallNlExitOnFailure ) {
|
|
NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL );
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Open the LSA for real domain
|
|
//
|
|
// ?? I'll need to identify which hosted domain here.
|
|
|
|
if ( DomainInfo->DomFlags & DOM_REAL_DOMAIN ) {
|
|
|
|
Status = LsaIOpenPolicyTrusted( &DomainInfo->DomLsaPolicyHandle );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NlPrint((NL_CRITICAL,
|
|
"%ws: Can't LsaIOpenPolicyTrusted: 0x%lx.\n",
|
|
DomainName,
|
|
Status ));
|
|
NetStatus = NetpNtStatusToApiStatus(Status);
|
|
if ( CallNlExitOnFailure ) {
|
|
NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogError, NULL);
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Open Sam
|
|
//
|
|
// ?? I'll need to identify which hosted domain here.
|
|
//
|
|
|
|
Status = SamIConnect(
|
|
NULL, // No server name
|
|
&DomainInfo->DomSamServerHandle,
|
|
0, // Ignore desired access
|
|
TRUE ); // Trusted client
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NlPrint((NL_CRITICAL,
|
|
"%ws: Can't SamIConnect: 0x%lx.\n",
|
|
DomainName,
|
|
Status ));
|
|
NetStatus = NetpNtStatusToApiStatus(Status);
|
|
if ( CallNlExitOnFailure ) {
|
|
NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogError, NULL);
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Open the Account domain.
|
|
//
|
|
|
|
Status = SamrOpenDomain( DomainInfo->DomSamServerHandle,
|
|
DOMAIN_ALL_ACCESS,
|
|
DomainInfo->DomAccountDomainId,
|
|
&DomainInfo->DomSamAccountDomainHandle );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"%ws: ACCOUNT: Cannot SamrOpenDomain: %lx\n",
|
|
DomainName,
|
|
Status ));
|
|
DomainInfo->DomSamAccountDomainHandle = NULL;
|
|
NetStatus = NetpNtStatusToApiStatus(Status);
|
|
if ( CallNlExitOnFailure ) {
|
|
NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogError, NULL);
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Open the Builtin domain.
|
|
//
|
|
|
|
Status = SamrOpenDomain( DomainInfo->DomSamServerHandle,
|
|
DOMAIN_ALL_ACCESS,
|
|
BuiltinDomainSid,
|
|
&DomainInfo->DomSamBuiltinDomainHandle );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NlPrint(( NL_CRITICAL,
|
|
"%ws: BUILTIN: Cannot SamrOpenDomain: %lx\n",
|
|
DomainName,
|
|
Status ));
|
|
DomainInfo->DomSamBuiltinDomainHandle = NULL;
|
|
NetStatus = NetpNtStatusToApiStatus(Status);
|
|
if ( CallNlExitOnFailure ) {
|
|
NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogError, NULL);
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Only link the entry in if we just created it.
|
|
// Wait to link the entry in until it is fully initialized.
|
|
//
|
|
|
|
if ( DomainCreated ) {
|
|
//
|
|
// Link the domain into the appropriate list of domains
|
|
//
|
|
// Increment the reference count for being on the global list.
|
|
//
|
|
|
|
DomainInfo->ReferenceCount ++;
|
|
if ( DomainInfo->DomFlags & DOM_REAL_DOMAIN ) {
|
|
InsertTailList(&NlGlobalServicedDomains, &DomainInfo->DomNext);
|
|
} else if ( DomainInfo->DomFlags & DOM_NON_DOMAIN_NC ) {
|
|
InsertTailList(&NlGlobalServicedNdncs, &DomainInfo->DomNext);
|
|
}
|
|
|
|
CanCallNlDeleteDomain = TRUE;
|
|
}
|
|
|
|
|
|
NetStatus = NERR_Success;
|
|
|
|
|
|
//
|
|
// Free Locally used resources
|
|
//
|
|
Cleanup:
|
|
|
|
//
|
|
// Return a pointer to the DomainInfo struct to the caller.
|
|
//
|
|
if (NetStatus == NERR_Success) {
|
|
*ReturnedDomainInfo = DomainInfo;
|
|
|
|
//
|
|
// Cleanup on error.
|
|
//
|
|
} else {
|
|
|
|
|
|
//
|
|
// If we created the domain,
|
|
// handle deleting it.
|
|
//
|
|
|
|
if ( DomainCreated ) {
|
|
|
|
//
|
|
// If we've initialized to the point where we can call
|
|
// we can call NlDeleteDomain, do so.
|
|
//
|
|
|
|
if ( CanCallNlDeleteDomain ) {
|
|
DomainInfo->ReferenceCount --;
|
|
(VOID) NlDeleteDomain( DomainInfo );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Dereference the domain on error.
|
|
//
|
|
if (DomainInfo != NULL) {
|
|
NlDereferenceDomain( DomainInfo );
|
|
}
|
|
|
|
}
|
|
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
return NetStatus;
|
|
}
|
|
|
|
#ifdef _DC_NETLOGON
|
|
NET_API_STATUS
|
|
NlCreateDomainPhase2(
|
|
IN PDOMAIN_INFO DomainInfo,
|
|
IN BOOLEAN CallNlExitOnFailure
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Finish creating a new domain to host.
|
|
|
|
Phase 2 of creation is designed to be called from a worker thread. It
|
|
contains all time intensive portions of domain creation.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Pointer to domain to finish creating.
|
|
|
|
CallNlExitOnFailure - TRUE if NlExit should be called on failure.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
If this is the primary domain for this DC, NlExit is called upon failure.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
NET_API_STATUS NetStatus;
|
|
|
|
ULONG i;
|
|
|
|
BOOL DomainCreated;
|
|
ULONG AccountRid = 0;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"Create domain phase 2\n"));
|
|
|
|
#ifdef MULTIHOSTED_DOMAIN
|
|
//
|
|
// If a new computername is needed for this machine,
|
|
// assign one.
|
|
//
|
|
|
|
if ( DomainInfo->DomOemComputerNameLength == 0 ) {
|
|
|
|
NetStatus = NlAssignComputerName( DomainInfo );
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
// ??: Write event
|
|
NlPrintDom((NL_CRITICAL, DomainInfo,
|
|
"can't NlAssignComputerName %ld.\n",
|
|
NetStatus ));
|
|
if ( CallNlExitOnFailure ) {
|
|
NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL );
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// If we've been asked to terminate,
|
|
// do so.
|
|
//
|
|
|
|
if ( (DomainInfo->DomFlags & DOM_THREAD_TERMINATE) != 0 ||
|
|
NlGlobalTerminate ) {
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"Domain thread asked to terminate\n"));
|
|
NetStatus = ERROR_OPERATION_ABORTED;
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
#endif // MULTIHOSTED_DOMAIN
|
|
|
|
|
|
//
|
|
// Determine role from DS
|
|
//
|
|
|
|
NetStatus = NlUpdateRole( DomainInfo );
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
|
|
//
|
|
// Having another PDC in the domain isn't fatal.
|
|
// (Continue running in the RoleInvalid state until the matter is
|
|
// resolved.)
|
|
//
|
|
if ( NetStatus != SERVICE_UIC_M_NETLOGON_DC_CFLCT ) {
|
|
NlPrintDom((NL_INIT, DomainInfo,
|
|
"Couldn't NlUpdateRole %ld 0x%lx.\n",
|
|
NetStatus, NetStatus ));
|
|
// NlUpdateRole logged the error.
|
|
if ( CallNlExitOnFailure ) {
|
|
NlExit( NELOG_NetlogonSystemError, NetStatus, DontLogError, NULL );
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Determine the RID for our computer account
|
|
//
|
|
|
|
Status = NlSamOpenNamedUser( DomainInfo,
|
|
DomainInfo->DomClientSession->CsAccountName,
|
|
NULL,
|
|
&AccountRid,
|
|
NULL );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"Cannot NlSamOpenNamedUser 0x%lx\n",
|
|
Status ));
|
|
if ( CallNlExitOnFailure ) {
|
|
NlExit( SERVICE_UIC_M_DATABASE_ERROR, Status, LogErrorAndNtStatus, NULL);
|
|
}
|
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|
goto Cleanup;
|
|
}
|
|
|
|
NlAssert( AccountRid != 0 );
|
|
DomainInfo->DomDcComputerAccountRid = AccountRid;
|
|
|
|
//
|
|
// Determine the trust list from the LSA.
|
|
//
|
|
|
|
if ( !GiveInstallHints( FALSE ) ) {
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Status = NlInitTrustList( DomainInfo );
|
|
|
|
if ( !NT_SUCCESS(Status) ) {
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"Cannot NlInitTrustList %lX\n",
|
|
Status ));
|
|
NetStatus = NetpNtStatusToApiStatus( Status );
|
|
if ( CallNlExitOnFailure ) {
|
|
NlExit( NELOG_NetlogonFailedToUpdateTrustList, NetStatus, LogErrorAndNtStatus, NULL);
|
|
}
|
|
goto Cleanup;
|
|
}
|
|
|
|
NetStatus = NERR_Success;
|
|
|
|
|
|
//
|
|
// Free Locally used resources
|
|
//
|
|
Cleanup:
|
|
|
|
return NetStatus;
|
|
}
|
|
#endif // _DC_NETLOGON
|
|
|
|
|
|
GUID *
|
|
NlCaptureDomainInfo (
|
|
IN PDOMAIN_INFO DomainInfo,
|
|
OUT WCHAR DnsDomainName[NL_MAX_DNS_LENGTH+1] OPTIONAL,
|
|
OUT GUID *DomainGuid OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Captures a copy of the DnsDomainName and domain GUID for a domain
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Specifies the hosted domain to return the DNS domain name for.
|
|
|
|
DnsDomainName - Returns the DNS name of the domain.
|
|
If there is none, an empty string is returned.
|
|
|
|
DomainGuid - Returns the domain GUID of the domain.
|
|
If there is none, a zero GUID is returned.
|
|
|
|
Return Value:
|
|
|
|
If there is a domain GUID, returns a pointer to the passed in DomainGuid buffer.
|
|
If not, returns NULL
|
|
|
|
--*/
|
|
{
|
|
GUID *ReturnGuid;
|
|
|
|
LOCK_TRUST_LIST( DomainInfo );
|
|
if ( ARGUMENT_PRESENT( DnsDomainName )) {
|
|
if ( DomainInfo->DomUnicodeDnsDomainName == NULL ) {
|
|
*DnsDomainName = L'\0';
|
|
} else {
|
|
wcscpy( DnsDomainName, DomainInfo->DomUnicodeDnsDomainName );
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// If the caller wants the domain GUID to be returned,
|
|
// return it.
|
|
//
|
|
if ( ARGUMENT_PRESENT( DomainGuid )) {
|
|
*DomainGuid = DomainInfo->DomDomainGuidBuffer;
|
|
if ( DomainInfo->DomDomainGuid == NULL ) {
|
|
ReturnGuid = NULL;
|
|
} else {
|
|
ReturnGuid = DomainGuid;
|
|
}
|
|
} else {
|
|
ReturnGuid = NULL;
|
|
}
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
|
|
return ReturnGuid;
|
|
}
|
|
|
|
VOID
|
|
NlFreeDnsDomainDomainInfo(
|
|
IN PDOMAIN_INFO DomainInfo
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees the DNS domain in the DomainInfo structure.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Domain to free the DNS domain name for.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
--*/
|
|
{
|
|
|
|
//
|
|
// Free the previous allocated block.
|
|
//
|
|
|
|
EnterCriticalSection(&NlGlobalDomainCritSect);
|
|
LOCK_TRUST_LIST( DomainInfo );
|
|
if ( DomainInfo->DomUnicodeDnsDomainName != NULL ) {
|
|
LocalFree( DomainInfo->DomUnicodeDnsDomainName );
|
|
}
|
|
if ( DomainInfo->DomUtf8DnsDomainNameAlias != NULL ) {
|
|
NetpMemoryFree( DomainInfo->DomUtf8DnsDomainNameAlias );
|
|
}
|
|
DomainInfo->DomUnicodeDnsDomainName = NULL;
|
|
DomainInfo->DomUtf8DnsDomainName = NULL;
|
|
DomainInfo->DomUtf8DnsDomainNameAlias = NULL;
|
|
DomainInfo->DomUnicodeDnsDomainNameString.Buffer = NULL;
|
|
DomainInfo->DomUnicodeDnsDomainNameString.MaximumLength = 0;
|
|
DomainInfo->DomUnicodeDnsDomainNameString.Length = 0;
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
}
|
|
|
|
NET_API_STATUS
|
|
NlSetDomainForestRoot(
|
|
IN PDOMAIN_INFO DomainInfo,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
The routine sets the DOM_FOREST_ROOT bit on the DomainInfo.
|
|
|
|
It simply compares the name of the domain with the name of the forest and sets the bit.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - The domain being set
|
|
|
|
Context - Not Used
|
|
|
|
Return Value:
|
|
|
|
Success (not used).
|
|
|
|
--*/
|
|
{
|
|
|
|
//
|
|
// Only set the bit if netlogon is running,
|
|
//
|
|
|
|
if ( NlGlobalDomainsInitialized ) {
|
|
|
|
EnterCriticalSection( &NlGlobalDnsForestNameCritSect );
|
|
EnterCriticalSection( &NlGlobalDomainCritSect );
|
|
|
|
if ( NlEqualDnsNameU( &NlGlobalUnicodeDnsForestNameString,
|
|
&DomainInfo->DomUnicodeDnsDomainNameString ) ) {
|
|
|
|
DomainInfo->DomFlags |= DOM_FOREST_ROOT;
|
|
|
|
} else {
|
|
DomainInfo->DomFlags &= ~DOM_FOREST_ROOT;
|
|
}
|
|
|
|
LeaveCriticalSection( &NlGlobalDomainCritSect );
|
|
LeaveCriticalSection( &NlGlobalDnsForestNameCritSect );
|
|
}
|
|
|
|
UNREFERENCED_PARAMETER( Context );
|
|
return NO_ERROR;
|
|
}
|
|
|
|
NET_API_STATUS
|
|
NlSetDomainNameInDomainInfo(
|
|
IN PDOMAIN_INFO DomainInfo,
|
|
IN LPWSTR DnsDomainName OPTIONAL,
|
|
IN LPWSTR NetbiosDomainName OPTIONAL,
|
|
IN GUID *DomainGuid OPTIONAL,
|
|
OUT PBOOLEAN DnsDomainNameChanged OPTIONAL,
|
|
OUT PBOOLEAN NetbiosDomainNameChanged OPTIONAL,
|
|
OUT PBOOLEAN DomainGuidChanged OPTIONAL
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the DNS domain name into the DomainInfo structure.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - Domain to set the DNS domain name for.
|
|
|
|
DnsDomainName - DNS name of the domain to host.
|
|
NULL if the domain has no DNS Domain Name.
|
|
|
|
NetbiosDomainName - Netbios name of the domain to host.
|
|
NULL if the domain has no Netbios Domain Name.
|
|
|
|
DomainGuid - Guid of the domain to host.
|
|
NULL if the domain has no GUID.
|
|
|
|
DnsDomainNameChanged - Returns TRUE if the DNS domain name is different
|
|
than the current value.
|
|
|
|
NetbiosDomainNameChanged - Returns TRUE if the Netbios domain name is different
|
|
than the current value.
|
|
|
|
DomainGuidChanged - Returns TRUE if the domain GUID is different
|
|
than the current value.
|
|
|
|
Return Value:
|
|
|
|
Status of operation.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
NET_API_STATUS NetStatus;
|
|
|
|
DWORD UnicodeDnsDomainNameSize;
|
|
|
|
LPSTR Utf8DnsDomainName = NULL;
|
|
|
|
DWORD Utf8DnsDomainNameSize;
|
|
LPBYTE Where;
|
|
ULONG i;
|
|
LPBYTE AllocatedBlock = NULL;
|
|
BOOLEAN LocalDnsDomainNameChanged = FALSE;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
if ( ARGUMENT_PRESENT( DnsDomainNameChanged) ) {
|
|
*DnsDomainNameChanged = FALSE;
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT( NetbiosDomainNameChanged) ) {
|
|
*NetbiosDomainNameChanged = FALSE;
|
|
}
|
|
|
|
if ( ARGUMENT_PRESENT( DomainGuidChanged ) ) {
|
|
*DomainGuidChanged = FALSE;
|
|
}
|
|
|
|
//
|
|
// Copy the Netbios domain name into the structure if it has changed.
|
|
//
|
|
// ?? The below assumes that for real domains Netbios domain name
|
|
// cannot change to NULL. This needs to be revisited when/if we go
|
|
// Netbios-less.
|
|
//
|
|
|
|
EnterCriticalSection(&NlGlobalDomainCritSect);
|
|
LOCK_TRUST_LIST( DomainInfo );
|
|
if ( NetbiosDomainName != NULL &&
|
|
NlNameCompare( NetbiosDomainName,
|
|
DomainInfo->DomUnicodeDomainName,
|
|
NAMETYPE_DOMAIN ) != 0 ) {
|
|
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"Setting Netbios domain name to %ws\n", NetbiosDomainName ));
|
|
|
|
NetStatus = I_NetNameCanonicalize(
|
|
NULL,
|
|
NetbiosDomainName,
|
|
DomainInfo->DomUnicodeDomainName,
|
|
sizeof(DomainInfo->DomUnicodeDomainName),
|
|
NAMETYPE_DOMAIN,
|
|
0 );
|
|
|
|
|
|
if ( NetStatus != NERR_Success ) {
|
|
NlPrint(( NL_CRITICAL, "%ws: DomainName is invalid\n", NetbiosDomainName ));
|
|
goto Cleanup;
|
|
}
|
|
|
|
RtlInitUnicodeString( &DomainInfo->DomUnicodeDomainNameString,
|
|
DomainInfo->DomUnicodeDomainName );
|
|
|
|
Status = RtlUpcaseUnicodeToOemN( DomainInfo->DomOemDomainName,
|
|
sizeof(DomainInfo->DomOemDomainName),
|
|
&DomainInfo->DomOemDomainNameLength,
|
|
DomainInfo->DomUnicodeDomainNameString.Buffer,
|
|
DomainInfo->DomUnicodeDomainNameString.Length);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
NlPrint(( NL_CRITICAL, "%ws: Unable to convert Domain name to OEM 0x%lx\n", DomainName, Status ));
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
DomainInfo->DomOemDomainName[DomainInfo->DomOemDomainNameLength] = '\0';
|
|
|
|
//
|
|
// Set the account domain.
|
|
//
|
|
|
|
if ( NlGlobalMemberWorkstation ) {
|
|
DomainInfo->DomUnicodeAccountDomainNameString =
|
|
DomainInfo->DomUnicodeComputerNameString;
|
|
} else {
|
|
DomainInfo->DomUnicodeAccountDomainNameString =
|
|
DomainInfo->DomUnicodeDomainNameString;
|
|
}
|
|
|
|
//
|
|
// Tell the caller that the name has changed.
|
|
//
|
|
if ( ARGUMENT_PRESENT( NetbiosDomainNameChanged) ) {
|
|
*NetbiosDomainNameChanged = TRUE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// If the new name is the same as the old name,
|
|
// avoid setting the name.
|
|
//
|
|
|
|
if ( !NlEqualDnsName( DnsDomainName, DomainInfo->DomUnicodeDnsDomainName )) {
|
|
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"Setting DNS domain name to %ws\n", DnsDomainName ));
|
|
|
|
|
|
//
|
|
// Convert the DNS domain name to the various forms.
|
|
//
|
|
|
|
if ( DnsDomainName != NULL ) {
|
|
ULONG NameLen = wcslen(DnsDomainName);
|
|
if ( NameLen > NL_MAX_DNS_LENGTH ) {
|
|
NetStatus = ERROR_INVALID_DOMAINNAME;
|
|
goto Cleanup;
|
|
}
|
|
UnicodeDnsDomainNameSize = NameLen * sizeof(WCHAR) + sizeof(WCHAR);
|
|
|
|
Utf8DnsDomainName = NetpAllocUtf8StrFromWStr( DnsDomainName );
|
|
if ( Utf8DnsDomainName == NULL ) {
|
|
NetStatus = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
Utf8DnsDomainNameSize = strlen(Utf8DnsDomainName) + 1;
|
|
if ( (Utf8DnsDomainNameSize-1) > NL_MAX_DNS_LENGTH ) {
|
|
NetStatus = ERROR_INVALID_DOMAINNAME;
|
|
goto Cleanup;
|
|
}
|
|
|
|
} else {
|
|
UnicodeDnsDomainNameSize = 0;
|
|
Utf8DnsDomainNameSize = 0;
|
|
}
|
|
|
|
//
|
|
// Allocate a new block for the names.
|
|
//
|
|
|
|
if ( UnicodeDnsDomainNameSize != 0 ) {
|
|
AllocatedBlock = LocalAlloc(
|
|
0,
|
|
UnicodeDnsDomainNameSize +
|
|
Utf8DnsDomainNameSize );
|
|
|
|
if ( AllocatedBlock == NULL ) {
|
|
NetStatus = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
Where = AllocatedBlock;
|
|
}
|
|
|
|
//
|
|
// Free the previous allocated block.
|
|
//
|
|
NlFreeDnsDomainDomainInfo( DomainInfo );
|
|
|
|
|
|
//
|
|
// Copy the Unicode DNS Domain name after that.
|
|
// (WCHAR aligned)
|
|
//
|
|
|
|
if ( UnicodeDnsDomainNameSize != 0 ) {
|
|
RtlCopyMemory( Where, DnsDomainName, UnicodeDnsDomainNameSize );
|
|
DomainInfo->DomUnicodeDnsDomainName = (LPWSTR) Where;
|
|
DomainInfo->DomUnicodeDnsDomainNameString.Buffer = (LPWSTR) Where;
|
|
DomainInfo->DomUnicodeDnsDomainNameString.MaximumLength = (USHORT) UnicodeDnsDomainNameSize;
|
|
DomainInfo->DomUnicodeDnsDomainNameString.Length = (USHORT)UnicodeDnsDomainNameSize - sizeof(WCHAR);
|
|
|
|
Where += UnicodeDnsDomainNameSize;
|
|
|
|
//
|
|
// Copy the Utf8 DNS Domain name after that.
|
|
// (byte aligned)
|
|
//
|
|
|
|
if ( Utf8DnsDomainNameSize != 0 ) {
|
|
RtlCopyMemory( Where, Utf8DnsDomainName, Utf8DnsDomainNameSize );
|
|
DomainInfo->DomUtf8DnsDomainName = Where;
|
|
Where += Utf8DnsDomainNameSize;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Tell the caller that the name has changed.
|
|
//
|
|
|
|
LocalDnsDomainNameChanged = TRUE;
|
|
if ( ARGUMENT_PRESENT( DnsDomainNameChanged) ) {
|
|
*DnsDomainNameChanged = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Copy the domain GUID if it has changed.
|
|
//
|
|
|
|
if ( DomainGuid != NULL || DomainInfo->DomDomainGuid != NULL) {
|
|
|
|
if ( (DomainGuid == NULL && DomainInfo->DomDomainGuid != NULL) ||
|
|
(DomainGuid != NULL && DomainInfo->DomDomainGuid == NULL) ||
|
|
!IsEqualGUID( DomainGuid, DomainInfo->DomDomainGuid ) ) {
|
|
|
|
|
|
//
|
|
// Set the domain GUID.
|
|
//
|
|
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"Setting Domain GUID to " ));
|
|
NlpDumpGuid( NL_DOMAIN, DomainGuid );
|
|
NlPrint(( NL_DOMAIN, "\n" ));
|
|
|
|
if ( DomainGuid != NULL ) {
|
|
DomainInfo->DomDomainGuidBuffer = *DomainGuid;
|
|
DomainInfo->DomDomainGuid = &DomainInfo->DomDomainGuidBuffer;
|
|
} else {
|
|
RtlZeroMemory( &DomainInfo->DomDomainGuidBuffer, sizeof( DomainInfo->DomDomainGuidBuffer ) );
|
|
DomainInfo->DomDomainGuid = NULL;
|
|
}
|
|
|
|
//
|
|
// Tell the caller that the GUID has changed.
|
|
//
|
|
if ( ARGUMENT_PRESENT( DomainGuidChanged ) ) {
|
|
*DomainGuidChanged = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
NetStatus = NO_ERROR;
|
|
|
|
//
|
|
// Free any locally used resources.
|
|
//
|
|
Cleanup:
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
if ( Utf8DnsDomainName != NULL ) {
|
|
NetpMemoryFree( Utf8DnsDomainName );
|
|
}
|
|
|
|
//
|
|
// If the DNS domain name changed,
|
|
// determine if the domain is now at the root of the forest.
|
|
//
|
|
|
|
if ( LocalDnsDomainNameChanged ) {
|
|
(VOID) NlSetDomainForestRoot( DomainInfo, NULL );
|
|
}
|
|
|
|
return NetStatus;
|
|
|
|
}
|
|
|
|
PDOMAIN_INFO
|
|
NlFindNetbiosDomain(
|
|
LPCWSTR DomainName,
|
|
BOOLEAN DefaultToPrimary
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will look up a domain given a Netbios domain name.
|
|
|
|
Arguments:
|
|
|
|
DomainName - The name of the domain to look up.
|
|
|
|
DefaultToPrimary - Return the primary domain if DomainName is NULL or
|
|
can't be found.
|
|
|
|
Return Value:
|
|
|
|
NULL - No such domain exists
|
|
|
|
A pointer to the domain found. The found domain should be dereferenced
|
|
using NlDereferenceDomain.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY DomainEntry;
|
|
|
|
PDOMAIN_INFO DomainInfo = NULL;
|
|
|
|
|
|
EnterCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
|
|
//
|
|
// If domain was specified,
|
|
// try to return primary domain.
|
|
//
|
|
|
|
if ( DomainName != NULL ) {
|
|
UNICODE_STRING DomainNameString;
|
|
|
|
RtlInitUnicodeString( &DomainNameString, DomainName );
|
|
|
|
|
|
//
|
|
// Loop trying to find this domain name.
|
|
//
|
|
|
|
for (DomainEntry = NlGlobalServicedDomains.Flink ;
|
|
DomainEntry != &NlGlobalServicedDomains;
|
|
DomainEntry = DomainEntry->Flink ) {
|
|
|
|
DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, DomNext);
|
|
|
|
//
|
|
// If this domain is not to be deleted,
|
|
// check it for match
|
|
//
|
|
if ( (DomainInfo->DomFlags & DOM_DELETED) == 0 &&
|
|
RtlEqualDomainName( &DomainInfo->DomUnicodeDomainNameString,
|
|
&DomainNameString ) ) {
|
|
break;
|
|
}
|
|
|
|
DomainInfo = NULL;
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we're to default to the primary domain,
|
|
// do so.
|
|
//
|
|
|
|
if ( DefaultToPrimary && DomainInfo == NULL ) {
|
|
if ( !IsListEmpty( &NlGlobalServicedDomains ) ) {
|
|
DomainInfo = CONTAINING_RECORD(NlGlobalServicedDomains.Flink, DOMAIN_INFO, DomNext);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reference the domain.
|
|
//
|
|
|
|
if ( DomainInfo != NULL ) {
|
|
DomainInfo->ReferenceCount ++;
|
|
}
|
|
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
return DomainInfo;
|
|
}
|
|
|
|
PDOMAIN_INFO
|
|
NlFindDnsDomain(
|
|
IN LPCSTR DnsDomainName OPTIONAL,
|
|
IN GUID *DomainGuid OPTIONAL,
|
|
IN BOOLEAN DefaultToNdnc,
|
|
IN BOOLEAN CheckAliasName,
|
|
OUT PBOOLEAN AliasNameMatched OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will look up a domain given a DNS domain name.
|
|
|
|
Arguments:
|
|
|
|
DnsDomainName - The name of the DNS domain to look up.
|
|
|
|
DomainGuid - If specified (and non-zero), the GUID of the domain to
|
|
match.
|
|
|
|
DefaultToNdnc - Return the non-domain NC if domain can't be found.
|
|
|
|
CheckAliasName - If TRUE, the DNS domain name aliases of hosted
|
|
domains will be checked for match.
|
|
|
|
AliasNameMatched - Set to TRUE if the returned domain was found as
|
|
the result of name alias match; otherwise set to FALSE.
|
|
|
|
Note:
|
|
|
|
The match is first perfomed against real hosted domains in the
|
|
following order: first for the domain name, then for the domain
|
|
alias (if CheckAliasName is TRUE), and lastly for the domain GUID.
|
|
This order is important to set correctly AliasNameMatched.
|
|
Specifically, this is needed to return the right domain name (either
|
|
active or alias) to the old DC locator client that verifies response
|
|
based only on the domain name and not the GUID.
|
|
|
|
If none of the real hosted domains satisfy the searh, NDNCs are searched
|
|
if DefaultToNdnc is TRUE. NDNCs don't have name aliases.
|
|
|
|
Return Value:
|
|
|
|
NULL - No such domain exists
|
|
|
|
A pointer to the domain found. The found domain should be dereferenced
|
|
using NlDereferenceDomain.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY DomainEntry;
|
|
|
|
PDOMAIN_INFO DomainInfo = NULL;
|
|
|
|
//
|
|
// Initialization
|
|
//
|
|
|
|
if ( AliasNameMatched != NULL ) {
|
|
*AliasNameMatched = FALSE;
|
|
}
|
|
|
|
//
|
|
// If the specified GUID is zero,
|
|
// Treat it as though none were specified.
|
|
//
|
|
|
|
if ( DomainGuid != NULL &&
|
|
IsEqualGUID( DomainGuid, &NlGlobalZeroGuid) ) {
|
|
DomainGuid = NULL;
|
|
}
|
|
|
|
EnterCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
//
|
|
// If parameters were specified,
|
|
// use them.
|
|
//
|
|
|
|
if ( DnsDomainName != NULL || DomainGuid != NULL ) {
|
|
|
|
//
|
|
// Loop trying to find this domain name.
|
|
//
|
|
|
|
for (DomainEntry = NlGlobalServicedDomains.Flink ;
|
|
DomainEntry != &NlGlobalServicedDomains;
|
|
DomainEntry = DomainEntry->Flink ) {
|
|
|
|
DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, DomNext);
|
|
|
|
//
|
|
// If this entry is not to be deleted,
|
|
// check it for match
|
|
//
|
|
if ( (DomainInfo->DomFlags & DOM_DELETED) == 0 ) {
|
|
|
|
//
|
|
// Check for the active domain name match
|
|
//
|
|
if ( DomainInfo->DomUtf8DnsDomainName != NULL &&
|
|
NlEqualDnsNameUtf8( DomainInfo->DomUtf8DnsDomainName, DnsDomainName ) ) {
|
|
break;
|
|
}
|
|
|
|
//
|
|
// If we are instructed to check the alias name, do it
|
|
//
|
|
if ( CheckAliasName &&
|
|
DomainInfo->DomUtf8DnsDomainNameAlias != NULL &&
|
|
NlEqualDnsNameUtf8( DomainInfo->DomUtf8DnsDomainNameAlias, DnsDomainName ) ) {
|
|
|
|
if ( AliasNameMatched != NULL ) {
|
|
*AliasNameMatched = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Finally, check for the GUID match
|
|
//
|
|
if ( DomainGuid != NULL && DomainInfo->DomDomainGuid != NULL ) {
|
|
if ( IsEqualGUID( DomainInfo->DomDomainGuid, DomainGuid ) ) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
DomainInfo = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we're to default to non-domain NC,
|
|
// do so.
|
|
//
|
|
|
|
if ( DefaultToNdnc && DomainInfo == NULL ) {
|
|
for (DomainEntry = NlGlobalServicedNdncs.Flink ;
|
|
DomainEntry != &NlGlobalServicedNdncs;
|
|
DomainEntry = DomainEntry->Flink ) {
|
|
|
|
DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, DomNext);
|
|
|
|
//
|
|
// If this entry is not to be deleted,
|
|
// check it for match
|
|
//
|
|
if ( (DomainInfo->DomFlags & DOM_DELETED) == 0 &&
|
|
DomainInfo->DomUtf8DnsDomainName != NULL &&
|
|
NlEqualDnsNameUtf8( DomainInfo->DomUtf8DnsDomainName, DnsDomainName ) ) {
|
|
break;
|
|
}
|
|
|
|
DomainInfo = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Reference the domain.
|
|
//
|
|
|
|
if ( DomainInfo != NULL ) {
|
|
DomainInfo->ReferenceCount ++;
|
|
}
|
|
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
return DomainInfo;
|
|
}
|
|
|
|
PDOMAIN_INFO
|
|
NlFindDomain(
|
|
LPCWSTR DomainName OPTIONAL,
|
|
GUID *DomainGuid OPTIONAL,
|
|
BOOLEAN DefaultToPrimary
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will look up a domain given a either a netbios or DNS domain name.
|
|
|
|
Arguments:
|
|
|
|
DomainName - The name of the domain to look up.
|
|
NULL implies the primary domain (ignoring DefaultToPrimary)
|
|
|
|
DomainGuid - If specified (and non-zero), the GUID of the domain to
|
|
match.
|
|
|
|
DefaultToPrimary - Return the primary domain if DomainName
|
|
can't be found.
|
|
|
|
Return Value:
|
|
|
|
NULL - No such domain exists
|
|
|
|
A pointer to the domain found. The found domain should be dereferenced
|
|
using NlDereferenceDomain.
|
|
|
|
--*/
|
|
{
|
|
PDOMAIN_INFO DomainInfo;
|
|
|
|
//
|
|
// If no specific domain is needed,
|
|
// use the default.
|
|
//
|
|
|
|
if ( DomainName == NULL ) {
|
|
|
|
DomainInfo = NlFindNetbiosDomain( NULL, TRUE );
|
|
|
|
//
|
|
// See if the requested domain is supported.
|
|
//
|
|
} else {
|
|
|
|
//
|
|
// Lookup the domain name as Netbios domain name.
|
|
//
|
|
|
|
DomainInfo = NlFindNetbiosDomain(
|
|
DomainName,
|
|
FALSE );
|
|
|
|
if ( DomainInfo == NULL ) {
|
|
LPSTR LocalDnsDomainName;
|
|
|
|
//
|
|
// Lookup the domain name as though it is a DNS domain name.
|
|
//
|
|
|
|
LocalDnsDomainName = NetpAllocUtf8StrFromWStr( DomainName );
|
|
|
|
if ( LocalDnsDomainName != NULL ) {
|
|
|
|
DomainInfo = NlFindDnsDomain(
|
|
LocalDnsDomainName,
|
|
DomainGuid,
|
|
FALSE, // don't lookup NDNCs
|
|
FALSE, // don't check alias names
|
|
NULL ); // don't care if alias name matched
|
|
|
|
NetpMemoryFree( LocalDnsDomainName );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( DomainInfo == NULL && DefaultToPrimary ) {
|
|
DomainInfo = NlFindNetbiosDomain( NULL, TRUE );
|
|
}
|
|
|
|
}
|
|
|
|
return DomainInfo;
|
|
}
|
|
|
|
|
|
NET_API_STATUS
|
|
NlEnumerateDomains(
|
|
IN BOOLEAN EnumerateNdncsToo,
|
|
PDOMAIN_ENUM_CALLBACK Callback,
|
|
PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine enumerates all the hosted domains and calls back the specified
|
|
callback routine with the specified context.
|
|
|
|
Arguments:
|
|
|
|
EnumerateNdncsToo - If TRUE, NDNCs will be enumerated in addition to domains
|
|
Callback - The callback routine to call.
|
|
Context - Context for the routine.
|
|
|
|
Return Value:
|
|
|
|
Status of operation (mostly status of allocations).
|
|
|
|
--*/
|
|
{
|
|
NET_API_STATUS NetStatus = NERR_Success;
|
|
PLIST_ENTRY DomainEntry;
|
|
PDOMAIN_INFO DomainInfo;
|
|
PDOMAIN_INFO DomainToDereference = NULL;
|
|
PLIST_ENTRY ServicedList;
|
|
ULONG DomainOrNdnc;
|
|
|
|
EnterCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
for ( DomainOrNdnc = 0; DomainOrNdnc < 2; DomainOrNdnc++ ) {
|
|
|
|
//
|
|
// On the first loop, enumerate real domains
|
|
//
|
|
if ( DomainOrNdnc == 0 ) {
|
|
ServicedList = &NlGlobalServicedDomains;
|
|
|
|
//
|
|
// On the second loop, enumerate NDNCs if so requested
|
|
//
|
|
} else {
|
|
if ( EnumerateNdncsToo ) {
|
|
ServicedList = &NlGlobalServicedNdncs;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Enumerate domains/NDNCs
|
|
//
|
|
|
|
for (DomainEntry = ServicedList->Flink ;
|
|
DomainEntry != ServicedList;
|
|
DomainEntry = DomainEntry->Flink ) {
|
|
|
|
//
|
|
// Reference the next domain in the list
|
|
//
|
|
|
|
DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, DomNext);
|
|
|
|
//
|
|
// Skip this domain if it is to be deleted
|
|
//
|
|
|
|
if ( DomainInfo->DomFlags & DOM_DELETED ) {
|
|
continue;
|
|
}
|
|
|
|
DomainInfo->ReferenceCount ++;
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
//
|
|
// Dereference any domain previously referenced.
|
|
//
|
|
if ( DomainToDereference != NULL) {
|
|
NlDereferenceDomain( DomainToDereference );
|
|
DomainToDereference = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// Call into the callback routine with this network.
|
|
//
|
|
|
|
NetStatus = (Callback)(DomainInfo, Context);
|
|
|
|
EnterCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
DomainToDereference = DomainInfo;
|
|
|
|
if (NetStatus != NERR_Success) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
//
|
|
// Dereference the last domain
|
|
//
|
|
if ( DomainToDereference != NULL) {
|
|
NlDereferenceDomain( DomainToDereference );
|
|
}
|
|
|
|
return NetStatus;
|
|
|
|
}
|
|
|
|
PDOMAIN_INFO
|
|
NlFindDomainByServerName(
|
|
LPWSTR ServerName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will look up a domain given the assigned server name.
|
|
|
|
Arguments:
|
|
|
|
ServerName - The name of the server for the domain to look up.
|
|
|
|
Return Value:
|
|
|
|
NULL - No such domain exists
|
|
|
|
A pointer to the domain found. The found domain should be dereferenced
|
|
using NlDereferenceDomain.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
PLIST_ENTRY DomainEntry;
|
|
|
|
PDOMAIN_INFO DomainInfo = NULL;
|
|
|
|
EnterCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
|
|
//
|
|
// If server wasn't specified,
|
|
// try to return primary domain.
|
|
//
|
|
|
|
if ( ServerName == NULL || *ServerName == L'\0' ) {
|
|
|
|
//
|
|
// If we're to default to the primary domain,
|
|
// do so.
|
|
//
|
|
|
|
if ( !IsListEmpty( &NlGlobalServicedDomains ) ) {
|
|
DomainInfo = CONTAINING_RECORD(NlGlobalServicedDomains.Flink, DOMAIN_INFO, DomNext);
|
|
|
|
//
|
|
// Ensure that this domain is not to be deleted
|
|
//
|
|
if ( DomainInfo->DomFlags & DOM_DELETED ) {
|
|
DomainInfo = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If a server name was specified,
|
|
// look it up in the list of domains.
|
|
//
|
|
|
|
} else {
|
|
UNICODE_STRING ServerNameString;
|
|
|
|
//
|
|
// Remove leading \\'s before conversion.
|
|
//
|
|
|
|
if ( IS_PATH_SEPARATOR(ServerName[0]) &&
|
|
IS_PATH_SEPARATOR(ServerName[1]) ) {
|
|
ServerName += 2;
|
|
}
|
|
|
|
RtlInitUnicodeString( &ServerNameString, ServerName );
|
|
|
|
//
|
|
// Loop trying to find this server name.
|
|
//
|
|
|
|
for (DomainEntry = NlGlobalServicedDomains.Flink ;
|
|
DomainEntry != &NlGlobalServicedDomains;
|
|
DomainEntry = DomainEntry->Flink ) {
|
|
|
|
DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, DomNext);
|
|
|
|
//
|
|
// If this domain is not to be deleted,
|
|
// check it for match
|
|
//
|
|
if ( (DomainInfo->DomFlags & DOM_DELETED) == 0 &&
|
|
RtlEqualComputerName( &DomainInfo->DomUnicodeComputerNameString,
|
|
&ServerNameString ) ) {
|
|
break;
|
|
}
|
|
|
|
DomainInfo = NULL;
|
|
|
|
}
|
|
|
|
//
|
|
// If the server name wasn't found,
|
|
// perhaps it was a DNS host name.
|
|
//
|
|
|
|
if ( DomainInfo == NULL ) {
|
|
|
|
//
|
|
// Loop trying to find this server name.
|
|
//
|
|
|
|
for (DomainEntry = NlGlobalServicedDomains.Flink ;
|
|
DomainEntry != &NlGlobalServicedDomains;
|
|
DomainEntry = DomainEntry->Flink ) {
|
|
|
|
DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, DomNext);
|
|
|
|
//
|
|
// If this domain is not to be deleted,
|
|
// check it for match
|
|
//
|
|
if ( (DomainInfo->DomFlags & DOM_DELETED) == 0 &&
|
|
DomainInfo->DomUnicodeDnsHostNameString.Length != 0 &&
|
|
NlEqualDnsName( DomainInfo->DomUnicodeDnsHostNameString.Buffer,
|
|
ServerName ) ) {
|
|
break;
|
|
}
|
|
|
|
DomainInfo = NULL;
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Reference the domain.
|
|
//
|
|
//Cleanup:
|
|
if ( DomainInfo != NULL ) {
|
|
DomainInfo->ReferenceCount ++;
|
|
} else {
|
|
NlPrint((NL_CRITICAL,"NlFindDomainByServerName failed %ws\n", ServerName ));
|
|
}
|
|
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
return DomainInfo;
|
|
}
|
|
|
|
|
|
VOID
|
|
NlDereferenceDomain(
|
|
IN PDOMAIN_INFO DomainInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Decrement the reference count on a domain.
|
|
|
|
If the reference count goes to 0, remove the domain.
|
|
|
|
On entry, the global NlGlobalDomainCritSect may not be locked
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - The domain to dereference
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG ReferenceCount;
|
|
ULONG Index;
|
|
PLIST_ENTRY ListEntry;
|
|
|
|
//
|
|
// Decrement the reference count
|
|
//
|
|
|
|
EnterCriticalSection(&NlGlobalDomainCritSect);
|
|
ReferenceCount = -- DomainInfo->ReferenceCount;
|
|
|
|
//
|
|
// If this is not the last reference,
|
|
// just return
|
|
//
|
|
|
|
if ( ReferenceCount != 0 ) {
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Otherwise proceed with delinking
|
|
// and delete the domain structure
|
|
//
|
|
|
|
NlAssert( DomainInfo->DomFlags & DOM_DELETED );
|
|
|
|
//
|
|
// Remove the entry from the list of serviced domains
|
|
//
|
|
|
|
RemoveEntryList(&DomainInfo->DomNext);
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo,
|
|
"Domain RefCount is zero. Domain being rundown.\n"));
|
|
|
|
#ifdef _DC_NETLOGON
|
|
//
|
|
// Stop the domain thread.
|
|
//
|
|
|
|
NlStopDomainThread( DomainInfo );
|
|
|
|
|
|
//
|
|
// Delete any client session
|
|
//
|
|
|
|
LOCK_TRUST_LIST( DomainInfo );
|
|
if ( DomainInfo->DomParentClientSession != NULL ) {
|
|
NlUnrefClientSession( DomainInfo->DomParentClientSession );
|
|
DomainInfo->DomParentClientSession = NULL;
|
|
}
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
|
|
NlDeleteDomClientSession( DomainInfo );
|
|
|
|
//
|
|
// Tell the browser and the SMB server that this domain is gone.
|
|
//
|
|
|
|
if ( !NlGlobalMemberWorkstation &&
|
|
(DomainInfo->DomFlags & DOM_REAL_DOMAIN) != 0 ) {
|
|
NlBrowserUpdate( DomainInfo, RoleInvalid );
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// Close the SAM and LSA handles
|
|
//
|
|
if ( DomainInfo->DomSamServerHandle != NULL ) {
|
|
Status = SamrCloseHandle( &DomainInfo->DomSamServerHandle);
|
|
NlAssert( NT_SUCCESS(Status) || Status == STATUS_INVALID_SERVER_STATE );
|
|
}
|
|
if ( DomainInfo->DomSamAccountDomainHandle != NULL ) {
|
|
Status = SamrCloseHandle( &DomainInfo->DomSamAccountDomainHandle);
|
|
NlAssert( NT_SUCCESS(Status) || Status == STATUS_INVALID_SERVER_STATE );
|
|
}
|
|
if ( DomainInfo->DomSamBuiltinDomainHandle != NULL ) {
|
|
Status = SamrCloseHandle( &DomainInfo->DomSamBuiltinDomainHandle);
|
|
NlAssert( NT_SUCCESS(Status) || Status == STATUS_INVALID_SERVER_STATE );
|
|
}
|
|
if ( DomainInfo->DomLsaPolicyHandle != NULL ) {
|
|
Status = LsarClose( &DomainInfo->DomLsaPolicyHandle );
|
|
NlAssert( NT_SUCCESS(Status) );
|
|
}
|
|
|
|
//
|
|
// Free the server session table.
|
|
//
|
|
|
|
LOCK_SERVER_SESSION_TABLE( DomainInfo );
|
|
|
|
while ( (ListEntry = DomainInfo->DomServerSessionTable.Flink) !=
|
|
&DomainInfo->DomServerSessionTable ) {
|
|
|
|
PSERVER_SESSION ServerSession;
|
|
|
|
ServerSession =
|
|
CONTAINING_RECORD(ListEntry, SERVER_SESSION, SsSeqList);
|
|
|
|
// Indicate we no longer need the server session anymore.
|
|
if ( ServerSession->SsFlags & SS_BDC ) {
|
|
ServerSession->SsFlags |= SS_BDC_FORCE_DELETE;
|
|
}
|
|
|
|
NlFreeServerSession( ServerSession );
|
|
}
|
|
|
|
|
|
if ( DomainInfo->DomServerSessionHashTable != NULL ) {
|
|
NetpMemoryFree( DomainInfo->DomServerSessionHashTable );
|
|
DomainInfo->DomServerSessionHashTable = NULL;
|
|
}
|
|
if ( DomainInfo->DomServerSessionTdoNameHashTable != NULL ) {
|
|
NetpMemoryFree( DomainInfo->DomServerSessionTdoNameHashTable );
|
|
DomainInfo->DomServerSessionTdoNameHashTable = NULL;
|
|
}
|
|
UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
|
|
DeleteCriticalSection( &DomainInfo->DomServerSessionTableCritSect );
|
|
|
|
|
|
//
|
|
// Timeout any async discoveries.
|
|
//
|
|
// The MainLoop thread may no longer be running to complete them.
|
|
// ?? Walk pool of async discovery thread here. Perhaps ref count didn't
|
|
// reach 0 and we didn't even get this far.
|
|
|
|
|
|
|
|
|
|
//
|
|
// Free the Trust List
|
|
//
|
|
|
|
LOCK_TRUST_LIST( DomainInfo );
|
|
|
|
while ( (ListEntry = DomainInfo->DomTrustList.Flink) != &DomainInfo->DomTrustList ) {
|
|
PCLIENT_SESSION ClientSession;
|
|
|
|
ClientSession =
|
|
CONTAINING_RECORD(ListEntry, CLIENT_SESSION, CsNext );
|
|
|
|
//
|
|
// Free the session.
|
|
//
|
|
NlFreeClientSession( ClientSession );
|
|
}
|
|
|
|
//
|
|
// Free the list of failed user logons
|
|
//
|
|
|
|
while ( !IsListEmpty(&DomainInfo->DomFailedUserLogonList) ) {
|
|
PNL_FAILED_USER_LOGON FailedUserLogon;
|
|
|
|
ListEntry = RemoveHeadList( &DomainInfo->DomFailedUserLogonList );
|
|
FailedUserLogon = CONTAINING_RECORD(ListEntry, NL_FAILED_USER_LOGON, FuNext );
|
|
|
|
//
|
|
// Free the logon structure
|
|
//
|
|
LocalFree( FailedUserLogon );
|
|
}
|
|
|
|
#endif // _DC_NETLOGON
|
|
|
|
//
|
|
// Free the Forest Trust List
|
|
//
|
|
|
|
if ( DomainInfo->DomForestTrustList != NULL ) {
|
|
MIDL_user_free( DomainInfo->DomForestTrustList );
|
|
DomainInfo->DomForestTrustList = NULL;
|
|
}
|
|
|
|
UNLOCK_TRUST_LIST( DomainInfo );
|
|
|
|
//
|
|
// Mark all DNS names we still have registered for
|
|
// deregistration. However, avoid this on shutdown,
|
|
// let the DNS shutdown routine do the cleanup as
|
|
// appropriate.
|
|
//
|
|
|
|
if ( !NlGlobalTerminate ) {
|
|
(VOID) NlDnsAddDomainRecords( DomainInfo, 0 );
|
|
}
|
|
|
|
//
|
|
// Dereference all covered sites
|
|
// Free the covered sites lists
|
|
//
|
|
EnterCriticalSection( &NlGlobalSiteCritSect );
|
|
if ( DomainInfo->CoveredSites != NULL ) {
|
|
for ( Index = 0; Index < DomainInfo->CoveredSitesCount; Index++ ) {
|
|
NlDerefSiteEntry( (DomainInfo->CoveredSites)[Index].CoveredSite );
|
|
}
|
|
LocalFree( DomainInfo->CoveredSites );
|
|
DomainInfo->CoveredSites = NULL;
|
|
DomainInfo->CoveredSitesCount = 0;
|
|
}
|
|
if ( DomainInfo->GcCoveredSites != NULL ) {
|
|
for ( Index = 0; Index < DomainInfo->GcCoveredSitesCount; Index++ ) {
|
|
NlDerefSiteEntry( (DomainInfo->GcCoveredSites)[Index].CoveredSite );
|
|
}
|
|
LocalFree( DomainInfo->GcCoveredSites );
|
|
DomainInfo->GcCoveredSites = NULL;
|
|
DomainInfo->GcCoveredSitesCount = 0;
|
|
}
|
|
LeaveCriticalSection( &NlGlobalSiteCritSect );
|
|
|
|
//
|
|
// Free the computer name.
|
|
//
|
|
|
|
NlFreeComputerName( DomainInfo );
|
|
|
|
//
|
|
// Free the DnsDomain name.
|
|
//
|
|
NlFreeDnsDomainDomainInfo( DomainInfo );
|
|
|
|
|
|
//
|
|
// Free the Domain Info structure.
|
|
//
|
|
DeleteCriticalSection( &DomainInfo->DomTrustListCritSect );
|
|
|
|
if ( IsPrimaryDomain(DomainInfo ) ) {
|
|
NlGlobalDomainInfo = NULL;
|
|
}
|
|
(VOID) LocalFree( DomainInfo );
|
|
|
|
NlGlobalServicedDomainCount --;
|
|
|
|
}
|
|
|
|
VOID
|
|
NlDeleteDomain(
|
|
IN PDOMAIN_INFO DomainInfo
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Force a domain to be deleted.
|
|
|
|
Arguments:
|
|
|
|
DomainInfo - The domain to delete
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
NlPrintDom(( NL_DOMAIN, DomainInfo, "NlDeleteDomain called\n"));
|
|
|
|
//
|
|
// Indicate that the domain is to be deleted.
|
|
//
|
|
// Don't remove it from the list of serviced
|
|
// domains because we may walk the list in
|
|
// NlEnumerateDomains which temporarily
|
|
// releases the crit sect.
|
|
//
|
|
|
|
EnterCriticalSection(&NlGlobalDomainCritSect);
|
|
NlAssert( (DomainInfo->DomFlags & DOM_DELETED) == 0 );
|
|
DomainInfo->DomFlags |= DOM_DELETED;
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
}
|
|
|
|
VOID
|
|
NlUninitializeDomains(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete all of the domains.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
ULONG LoopIndex;
|
|
PLIST_ENTRY ServicedList;
|
|
|
|
if ( NlGlobalDomainsInitialized ) {
|
|
NlGlobalDomainsInitialized = FALSE;
|
|
//
|
|
// Loop through the domains deleting each of them
|
|
//
|
|
|
|
EnterCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
for ( LoopIndex = 0; LoopIndex < 2; LoopIndex++ ) {
|
|
if ( LoopIndex == 0 ) {
|
|
ServicedList = &NlGlobalServicedDomains;
|
|
} else {
|
|
ServicedList = &NlGlobalServicedNdncs;
|
|
}
|
|
|
|
while (!IsListEmpty(ServicedList)) {
|
|
|
|
PDOMAIN_INFO DomainInfo = CONTAINING_RECORD(ServicedList->Flink, DOMAIN_INFO, DomNext);
|
|
|
|
//
|
|
// If domain is already marked for deletion,
|
|
// add our reference so that we can wait
|
|
// until only our reference remains
|
|
//
|
|
if ( DomainInfo->DomFlags & DOM_DELETED ) {
|
|
DomainInfo->ReferenceCount ++;
|
|
|
|
//
|
|
// Otherwise, mark the domain for deletion
|
|
//
|
|
} else {
|
|
NlDeleteDomain( DomainInfo );
|
|
}
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
//
|
|
// Wait for any other references to disappear
|
|
//
|
|
|
|
if ( DomainInfo->ReferenceCount != 1 ) {
|
|
EnterCriticalSection(&NlGlobalDomainCritSect);
|
|
while ( DomainInfo->ReferenceCount != 1 ) {
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
NlPrintDom(( NL_CRITICAL, DomainInfo,
|
|
"NlUnitializeDomains: Sleeping a second waiting for Domain RefCount to zero.\n"));
|
|
Sleep( 1000 );
|
|
EnterCriticalSection(&NlGlobalDomainCritSect);
|
|
}
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
}
|
|
|
|
//
|
|
// Actually delink and delete structure by removing the last reference
|
|
//
|
|
|
|
NlAssert( DomainInfo->ReferenceCount == 1 );
|
|
NlDereferenceDomain( DomainInfo );
|
|
|
|
|
|
EnterCriticalSection(&NlGlobalDomainCritSect);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
LeaveCriticalSection(&NlGlobalDomainCritSect);
|
|
DeleteCriticalSection( &NlGlobalDomainCritSect );
|
|
}
|
|
}
|