/*++ 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 ); } }