Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5231 lines
144 KiB

  1. /*++
  2. Copyright (c) 1995-1996 Microsoft Corporation
  3. Module Name:
  4. domain.c
  5. Abstract:
  6. Code to manage multiple domains hosted on a DC.
  7. Author:
  8. Cliff Van Dyke (CliffV) 11-Jan-1995
  9. Revision History:
  10. --*/
  11. //
  12. // Common include files.
  13. //
  14. #include "logonsrv.h" // Include files common to entire service
  15. #pragma hdrstop
  16. //
  17. // Include files specific to this .c file
  18. //
  19. // Serialized by NlGlobalDomainCritSect
  20. LIST_ENTRY NlGlobalServicedDomains = {0}; // Real domains we service
  21. LIST_ENTRY NlGlobalServicedNdncs = {0}; // Non-domain NCs we service
  22. BOOL NlGlobalDomainsInitialized = FALSE;
  23. NET_API_STATUS
  24. NlGetDomainName(
  25. OUT LPWSTR *DomainName,
  26. OUT LPWSTR *DnsDomainName,
  27. OUT PSID *AccountDomainSid,
  28. OUT PSID *PrimaryDomainSid,
  29. OUT GUID **PrimaryDomainGuid,
  30. OUT PBOOLEAN DnsForestNameChanged OPTIONAL
  31. )
  32. /*++
  33. Routine Description:
  34. This routine gets the primary domain name and domain SID from the LSA.
  35. Arguments:
  36. DomainName - Returns name of the primary domain. Free this buffer using LocalFree.
  37. DnsDomainName - Returns the DNS domain name of the primary domain.
  38. The returned name has a trailing . since the name is an absolute name.
  39. The allocated buffer must be freed via LocalFree.
  40. Returns NO_ERROR and a pointer to a NULL buffer if there is no
  41. domain name configured.
  42. AccountDomainSid - Returns Account Domain Sid of this machine. Free this buffer using LocalFree.
  43. PrimaryDomainSid - Returns Primary Domain Sid of this machine. Free this buffer using LocalFree.
  44. Only return on workstations.
  45. PrimaryDomainGuid - Returns Primary Domain GUID of this machine. Free this buffer using LocalFree.
  46. DnsForestNameChanged: Returns TRUE if the tree name changed.
  47. Return Value:
  48. Status of the operation.
  49. Calls NlExit on failures.
  50. --*/
  51. {
  52. NTSTATUS Status;
  53. NET_API_STATUS NetStatus;
  54. PLSAPR_POLICY_INFORMATION PrimaryDomainInfo = NULL;
  55. PLSAPR_POLICY_INFORMATION AccountDomainInfo = NULL;
  56. LSAPR_HANDLE PolicyHandle = NULL;
  57. ULONG DomainSidSize;
  58. ULONG DnsDomainNameLength;
  59. //
  60. // Initialization
  61. //
  62. *DomainName = NULL;
  63. *DnsDomainName = NULL;
  64. *AccountDomainSid = NULL;
  65. *PrimaryDomainSid = NULL;
  66. *PrimaryDomainGuid = NULL;
  67. //
  68. // Open the LSA policy
  69. //
  70. // ?? I'll need to identify which trusted domain here.
  71. Status = LsaIOpenPolicyTrusted( &PolicyHandle );
  72. if ( !NT_SUCCESS(Status) ) {
  73. NlPrint((NL_CRITICAL,
  74. "NlGetDomainName: Can't LsaIOpenPolicyTrusted: 0x%lx.\n",
  75. Status ));
  76. NetStatus = NetpNtStatusToApiStatus(Status);
  77. NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogError, NULL);
  78. goto Cleanup;
  79. }
  80. //
  81. // Get the Account Domain info from the LSA.
  82. //
  83. Status = LsarQueryInformationPolicy(
  84. PolicyHandle,
  85. PolicyAccountDomainInformation,
  86. &AccountDomainInfo );
  87. if ( !NT_SUCCESS(Status) ) {
  88. NlPrint((NL_CRITICAL,
  89. "NlGetDomainName: Can't LsarQueryInformationPolicy (AccountDomain): 0x%lx.\n",
  90. Status ));
  91. NetStatus = NetpNtStatusToApiStatus(Status);
  92. NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogError, NULL);
  93. goto Cleanup;
  94. }
  95. if ( AccountDomainInfo->PolicyAccountDomainInfo.DomainName.Length == 0 ||
  96. AccountDomainInfo->PolicyAccountDomainInfo.DomainName.Length >
  97. DNLEN * sizeof(WCHAR) ||
  98. AccountDomainInfo->PolicyAccountDomainInfo.DomainSid == NULL ) {
  99. NlPrint((NL_CRITICAL, "Account domain info from LSA invalid.\n"));
  100. //
  101. // Avoid event log error in safe mode where our exit is expected
  102. //
  103. NlExit( SERVICE_UIC_M_UAS_INVALID_ROLE,
  104. NO_ERROR,
  105. LsaISafeMode() ? DontLogError : LogError,
  106. NULL );
  107. NetStatus = SERVICE_UIC_M_UAS_INVALID_ROLE;
  108. goto Cleanup;
  109. }
  110. //
  111. // Copy the Account domain id into a buffer to return to the caller.
  112. //
  113. DomainSidSize =
  114. RtlLengthSid( (PSID)AccountDomainInfo->PolicyAccountDomainInfo.DomainSid );
  115. *AccountDomainSid = LocalAlloc( 0, DomainSidSize );
  116. if ( *AccountDomainSid == NULL ) {
  117. NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
  118. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  119. goto Cleanup;
  120. }
  121. RtlCopyMemory( *AccountDomainSid,
  122. (PSID)AccountDomainInfo->PolicyAccountDomainInfo.DomainSid,
  123. DomainSidSize );
  124. //
  125. // Get the Primary Domain info from the LSA.
  126. //
  127. Status = LsarQueryInformationPolicy(
  128. PolicyHandle,
  129. PolicyDnsDomainInformation,
  130. &PrimaryDomainInfo );
  131. if ( !NT_SUCCESS(Status) ) {
  132. NlPrint((NL_CRITICAL,
  133. "NlGetDomainName: Can't LsarQueryInformationPolicy (DnsDomain): 0x%lx.\n",
  134. Status ));
  135. NetStatus = NetpNtStatusToApiStatus(Status);
  136. NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogError, NULL);
  137. goto Cleanup;
  138. }
  139. if ( PrimaryDomainInfo->PolicyDnsDomainInfo.Name.Length == 0 ||
  140. PrimaryDomainInfo->PolicyDnsDomainInfo.Name.Length >
  141. DNLEN * sizeof(WCHAR) ||
  142. PrimaryDomainInfo->PolicyDnsDomainInfo.Sid == NULL ) {
  143. NlPrint((NL_CRITICAL, "Primary domain info from LSA invalid.\n"));
  144. // Ditch the sysvol shares in case this is a repair mode boot
  145. NlGlobalParameters.SysVolReady = FALSE;
  146. NlCreateSysvolShares();
  147. //
  148. // Avoid event log error in safe mode where our exit is expected
  149. //
  150. NlExit( SERVICE_UIC_M_UAS_INVALID_ROLE,
  151. NO_ERROR,
  152. LsaISafeMode() ? DontLogError : LogError,
  153. NULL );
  154. NetStatus = SERVICE_UIC_M_UAS_INVALID_ROLE;
  155. goto Cleanup;
  156. }
  157. //
  158. // On a DC, we must have DNS domain name
  159. //
  160. if ( !NlGlobalMemberWorkstation &&
  161. (PrimaryDomainInfo->PolicyDnsDomainInfo.DnsDomainName.Length == 0 ||
  162. PrimaryDomainInfo->PolicyDnsDomainInfo.DnsDomainName.Length >
  163. NL_MAX_DNS_LENGTH*sizeof(WCHAR)) ) {
  164. NlExit( SERVICE_UIC_M_UAS_INVALID_ROLE, NO_ERROR, LogError, NULL );
  165. NetStatus = SERVICE_UIC_M_UAS_INVALID_ROLE;
  166. goto Cleanup;
  167. }
  168. //
  169. // Copy the Primary domain id into a buffer to return to the caller.
  170. //
  171. if ( NlGlobalMemberWorkstation ) {
  172. DomainSidSize =
  173. RtlLengthSid( (PSID)PrimaryDomainInfo->PolicyDnsDomainInfo.Sid );
  174. *PrimaryDomainSid = LocalAlloc( 0, DomainSidSize );
  175. if ( *PrimaryDomainSid == NULL ) {
  176. NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
  177. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  178. goto Cleanup;
  179. }
  180. RtlCopyMemory( *PrimaryDomainSid,
  181. (PSID)PrimaryDomainInfo->PolicyDnsDomainInfo.Sid,
  182. DomainSidSize );
  183. }
  184. //
  185. // Copy the Primary domain name into a buffer to return to the caller.
  186. //
  187. *DomainName = LocalAlloc( 0,
  188. PrimaryDomainInfo->PolicyDnsDomainInfo.Name.Length + sizeof(WCHAR) );
  189. if ( *DomainName == NULL ) {
  190. NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
  191. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  192. goto Cleanup;
  193. }
  194. RtlCopyMemory( *DomainName,
  195. PrimaryDomainInfo->PolicyDnsDomainInfo.Name.Buffer,
  196. PrimaryDomainInfo->PolicyDnsDomainInfo.Name.Length );
  197. (*DomainName)[
  198. PrimaryDomainInfo->PolicyDnsDomainInfo.Name.Length /
  199. sizeof(WCHAR)] = L'\0';
  200. //
  201. // Copy the DNS Primary domain name into a buffer to return to the caller.
  202. //
  203. DnsDomainNameLength = PrimaryDomainInfo->PolicyDnsDomainInfo.DnsDomainName.Length / sizeof(WCHAR);
  204. if ( DnsDomainNameLength != 0 ) {
  205. *DnsDomainName = LocalAlloc( 0, (DnsDomainNameLength+2) * sizeof(WCHAR));
  206. if ( *DnsDomainName == NULL ) {
  207. NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
  208. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  209. goto Cleanup;
  210. }
  211. RtlCopyMemory( *DnsDomainName,
  212. PrimaryDomainInfo->PolicyDnsDomainInfo.DnsDomainName.Buffer,
  213. DnsDomainNameLength*sizeof(WCHAR) );
  214. if ( (*DnsDomainName)[DnsDomainNameLength-1] != L'.' ) {
  215. (*DnsDomainName)[DnsDomainNameLength++] = L'.';
  216. }
  217. (*DnsDomainName)[DnsDomainNameLength] = L'\0';
  218. }
  219. //
  220. // Get the GUID of the domain we're a member of
  221. //
  222. if ( IsEqualGUID( &PrimaryDomainInfo->PolicyDnsDomainInfo.DomainGuid, &NlGlobalZeroGuid) ) {
  223. *PrimaryDomainGuid = NULL;
  224. } else {
  225. *PrimaryDomainGuid = LocalAlloc( 0, sizeof(GUID) );
  226. if ( *PrimaryDomainGuid == NULL ) {
  227. NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
  228. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  229. goto Cleanup;
  230. }
  231. **PrimaryDomainGuid = PrimaryDomainInfo->PolicyDnsDomainInfo.DomainGuid;
  232. }
  233. //
  234. // Set the name of the tree this domain is in.
  235. //
  236. NetStatus = NlSetDnsForestName( (PUNICODE_STRING)&PrimaryDomainInfo->PolicyDnsDomainInfo.DnsForestName,
  237. DnsForestNameChanged );
  238. if ( NetStatus != NO_ERROR ) {
  239. NlPrint((NL_CRITICAL, "Can't NlSetDnsForestName %ld\n", NetStatus ));
  240. NlExit( SERVICE_UIC_RESOURCE, NetStatus, LogError, NULL);
  241. goto Cleanup;
  242. }
  243. NetStatus = NERR_Success;
  244. //
  245. // Return
  246. //
  247. Cleanup:
  248. if ( NetStatus != NERR_Success ) {
  249. if ( *PrimaryDomainSid != NULL ) {
  250. LocalFree (*PrimaryDomainSid);
  251. *PrimaryDomainSid = NULL;
  252. }
  253. if ( *AccountDomainSid != NULL ) {
  254. LocalFree (*AccountDomainSid);
  255. *AccountDomainSid = NULL;
  256. }
  257. if ( *DomainName != NULL ) {
  258. LocalFree (*DomainName);
  259. *DomainName = NULL;
  260. }
  261. if ( *DnsDomainName != NULL ) {
  262. NetApiBufferFree(*DnsDomainName);
  263. *DnsDomainName = NULL;
  264. }
  265. if ( *PrimaryDomainGuid != NULL ) {
  266. LocalFree (*PrimaryDomainGuid);
  267. *PrimaryDomainGuid = NULL;
  268. }
  269. }
  270. if ( AccountDomainInfo != NULL ) {
  271. LsaIFree_LSAPR_POLICY_INFORMATION(
  272. PolicyAccountDomainInformation,
  273. AccountDomainInfo );
  274. }
  275. if ( PrimaryDomainInfo != NULL ) {
  276. LsaIFree_LSAPR_POLICY_INFORMATION(
  277. PolicyDnsDomainInformation,
  278. PrimaryDomainInfo );
  279. }
  280. if ( PolicyHandle != NULL ) {
  281. Status = LsarClose( &PolicyHandle );
  282. NlAssert( NT_SUCCESS(Status) );
  283. }
  284. return NetStatus;
  285. }
  286. NET_API_STATUS
  287. NlGetDnsHostName(
  288. OUT LPWSTR *DnsHostName
  289. )
  290. /*++
  291. Routine Description:
  292. This routine gets DnsHostName of this machine.
  293. Arguments:
  294. DnsHostName - Returns the DNS Host Name of the machine.
  295. Will return a NULL pointer if this machine has no DNS host name.
  296. Free this buffer using LocalFree.
  297. Return Value:
  298. Status of the operation.
  299. --*/
  300. {
  301. NET_API_STATUS NetStatus;
  302. WCHAR LocalDnsUnicodeHostName[NL_MAX_DNS_LENGTH+1];
  303. ULONG LocalDnsUnicodeHostNameLen;
  304. //
  305. // Get the DNS host name.
  306. //
  307. *DnsHostName = NULL;
  308. LocalDnsUnicodeHostNameLen = sizeof( LocalDnsUnicodeHostName ) / sizeof(WCHAR);
  309. if ( !GetComputerNameExW( ComputerNameDnsFullyQualified,
  310. LocalDnsUnicodeHostName,
  311. &LocalDnsUnicodeHostNameLen )) {
  312. NetStatus = GetLastError();
  313. //
  314. // If we're not running TCP,
  315. // simply use the Netbios name.
  316. //
  317. if ( NetStatus == ERROR_FILE_NOT_FOUND ) {
  318. *DnsHostName = NULL;
  319. NetStatus = NO_ERROR;
  320. goto Cleanup;
  321. } else {
  322. NlPrint(( NL_CRITICAL,
  323. "Cannot GetComputerNameExW() %ld\n",
  324. NetStatus ));
  325. goto Cleanup;
  326. }
  327. }
  328. //
  329. // Copy the string into an allocated buffer.
  330. //
  331. *DnsHostName = NetpAllocWStrFromWStr( LocalDnsUnicodeHostName );
  332. if ( *DnsHostName == NULL ) {
  333. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  334. goto Cleanup;
  335. }
  336. NetStatus = NO_ERROR;
  337. Cleanup:
  338. return NetStatus;;
  339. }
  340. NTSTATUS
  341. NlGetNdncNames(
  342. OUT PDS_NAME_RESULTW **NdncNames,
  343. OUT GUID **NdncGuids,
  344. OUT PULONG NameCount
  345. )
  346. /*++
  347. Routine Description:
  348. Get the names of non-domain NCs we host from the DS
  349. Arguments:
  350. NdncNames - Returns an array of pointers to DS_NAME_RESULT structures
  351. describing NDNC names. The number of returned DS_NAME_RESULT structures
  352. is given by NameCount. Each returned DS_NAME_RESULT structure must be freed
  353. by calling DsFreeNameResultW after which the NdncNames array itself must be
  354. freed by calling LocalFree.
  355. NameCount - Returns the number of DS_NAME_RESULT structures in the NdncNames array
  356. Return Value:
  357. Status of operation.
  358. --*/
  359. {
  360. NET_API_STATUS NetStatus;
  361. NTSTATUS Status = STATUS_SUCCESS;
  362. ULONG LocalReAllocLoopCount = 0;
  363. PDSNAME *DnList = NULL;
  364. ULONG DnListSize = 0;
  365. ULONG DnListEntryCount = 0;
  366. HANDLE hDs = NULL;
  367. LPWSTR NameToCrack;
  368. PDS_NAME_RESULTW CrackedName = NULL;
  369. PDS_NAME_RESULTW *LocalNdncNames = NULL;
  370. GUID *LocalNdncGuids = NULL;
  371. ULONG LocalNameCount = 0;
  372. ULONG Index;
  373. //
  374. // Pre-allocate some memory for the list of NDNC DNs.
  375. // Let's guess we are going to have 4 DNs of maximum
  376. // DNS name size.
  377. //
  378. DnListSize = 4 * ( sizeof(DSNAME) + DNS_MAX_NAME_LENGTH*sizeof(WCHAR) );
  379. DnList = LocalAlloc( 0, DnListSize );
  380. if ( DnList == NULL ) {
  381. Status = STATUS_NO_MEMORY;
  382. goto Cleanup;
  383. }
  384. //
  385. // Get the list of NDNC DNs
  386. //
  387. Status = NlGetConfigurationNamesList(
  388. DSCONFIGNAMELIST_NCS,
  389. DSCNL_NCS_NDNCS | DSCNL_NCS_LOCAL_MASTER,
  390. &DnListSize,
  391. DnList );
  392. //
  393. // If the buffer was small, keep reallocating it until
  394. // it's big enough
  395. //
  396. while( Status == STATUS_BUFFER_TOO_SMALL ) {
  397. PDSNAME *TmpDnList = NULL;
  398. //
  399. // Guard against infinite loop
  400. //
  401. NlAssert( LocalReAllocLoopCount < 20 );
  402. if ( LocalReAllocLoopCount >= 20 ) {
  403. Status = STATUS_INTERNAL_ERROR;
  404. goto Cleanup;
  405. }
  406. //
  407. // Reallocate the memory as much as needed
  408. //
  409. TmpDnList = LocalReAlloc( DnList,
  410. DnListSize,
  411. LMEM_MOVEABLE );
  412. if ( TmpDnList == NULL ) {
  413. Status = STATUS_NO_MEMORY;
  414. goto Cleanup;
  415. }
  416. DnList = TmpDnList;
  417. //
  418. // Call it again
  419. //
  420. Status = NlGetConfigurationNamesList(
  421. DSCONFIGNAMELIST_NCS,
  422. DSCNL_NCS_NDNCS | DSCNL_NCS_LOCAL_MASTER,
  423. &DnListSize,
  424. DnList );
  425. LocalReAllocLoopCount ++;
  426. }
  427. //
  428. // Error out on failure
  429. //
  430. if ( !NT_SUCCESS(Status) ) {
  431. goto Cleanup;
  432. }
  433. //
  434. // Get the number of entries returned
  435. //
  436. for ( Index = 0; DnList[Index] != NULL; Index ++ ) {
  437. DnListEntryCount ++;
  438. }
  439. //
  440. // If there are no entries, we are done
  441. //
  442. if ( DnListEntryCount == 0 ) {
  443. NlPrint(( NL_CRITICAL, "NlGetNdncNames: GetConfigurationNamesList returned 0 entries\n" ));
  444. goto Cleanup;
  445. }
  446. //
  447. // Allocate a buffer to store the canonical NDNC names
  448. //
  449. LocalNdncNames = LocalAlloc( LMEM_ZEROINIT, DnListEntryCount * sizeof(PDS_NAME_RESULTW) );
  450. if ( LocalNdncNames == NULL ) {
  451. Status = STATUS_NO_MEMORY;
  452. goto Cleanup;
  453. }
  454. //
  455. // Allocate a buffer to store the NDNC GUIDs
  456. //
  457. LocalNdncGuids = LocalAlloc( LMEM_ZEROINIT, DnListEntryCount * sizeof(GUID) );
  458. if( LocalNdncGuids == NULL ) {
  459. Status = STATUS_NO_MEMORY;
  460. goto Cleanup;
  461. }
  462. //
  463. // Crack each DN into canonical form
  464. //
  465. for ( Index = 0; DnList[Index] != NULL; Index ++ ) {
  466. NameToCrack = DnList[Index]->StringName;
  467. NetStatus = DsCrackNamesW(
  468. NULL, // No need to bind to DS for syntactical mapping
  469. DS_NAME_FLAG_SYNTACTICAL_ONLY, // only syntactical mapping
  470. DS_FQDN_1779_NAME, // Translate from DN
  471. DS_CANONICAL_NAME, // Translate to canonical form
  472. 1, // 1 name to translate
  473. &NameToCrack, // name to translate
  474. &CrackedName ); // cracked name
  475. //
  476. // Use this name if it was cracked successfully
  477. //
  478. if ( NetStatus != NO_ERROR ) {
  479. NlPrint(( NL_CRITICAL, "NlGetNdncNames: DsCrackNamesW failed for %ws: 0x%lx\n",
  480. NameToCrack,
  481. NetStatus ));
  482. } else if ( CrackedName->rItems[0].status != DS_NAME_NO_ERROR ) {
  483. NlPrint(( NL_CRITICAL, "NlGetNdncNames: DsCrackNamesW substatus error for %ws: 0x%lx\n",
  484. NameToCrack,
  485. CrackedName->rItems[0].status ));
  486. } else if ( CrackedName->cItems != 1 ) {
  487. NlPrint(( NL_CRITICAL, "NlGetNdncNames: DsCrackNamesW returned %lu names for %ws\n",
  488. CrackedName->cItems,
  489. NameToCrack ));
  490. } else {
  491. LocalNdncNames[LocalNameCount] = CrackedName;
  492. LocalNdncGuids[LocalNameCount] = DnList[Index]->Guid;
  493. LocalNameCount ++;
  494. CrackedName = NULL;
  495. }
  496. if ( CrackedName != NULL ) {
  497. DsFreeNameResultW( CrackedName );
  498. CrackedName = NULL;
  499. }
  500. }
  501. //
  502. // Success
  503. //
  504. Status = STATUS_SUCCESS;
  505. Cleanup:
  506. if ( DnList != NULL ) {
  507. LocalFree( DnList );
  508. }
  509. //
  510. // Return the data on success
  511. //
  512. if ( NT_SUCCESS(Status) ) {
  513. *NdncNames = LocalNdncNames;
  514. *NdncGuids = LocalNdncGuids;
  515. *NameCount = LocalNameCount;
  516. } else {
  517. if ( LocalNdncNames != NULL ) {
  518. for ( Index = 0; Index < LocalNameCount; Index++ ) {
  519. DsFreeNameResultW( LocalNdncNames[Index] );
  520. }
  521. LocalFree( LocalNdncNames );
  522. }
  523. if ( LocalNdncGuids != NULL ) {
  524. LocalFree( LocalNdncGuids );
  525. }
  526. }
  527. return Status;
  528. }
  529. NET_API_STATUS
  530. NlUpdateServicedNdncs(
  531. IN LPWSTR ComputerName,
  532. IN LPWSTR DnsHostName,
  533. IN BOOLEAN CallNlExitOnFailure,
  534. OUT PBOOLEAN ServicedNdncChanged OPTIONAL
  535. )
  536. /*++
  537. Routine Description:
  538. Update the serviced non-domain NC list.
  539. Arguments:
  540. ComputerName - Name of this computer.
  541. DnsHostName - DNS Host name of this computer in the specified domain.
  542. CallNlExitOnFailure - TRUE if NlExit should be called on failure.
  543. ServicedNdncChanged - Set to TRUE if the list of NDNCs changed.
  544. Return Value:
  545. Status of operation.
  546. --*/
  547. {
  548. NET_API_STATUS NetStatus = NO_ERROR;
  549. NTSTATUS Status;
  550. PDS_NAME_RESULTW *NdncNames = NULL;
  551. GUID *NdncGuids = NULL;
  552. ULONG NdncCount = 0;
  553. ULONG CurrentNdncCount = 0;
  554. ULONG DeletedNdncCount = 0;
  555. ULONG NdncIndex;
  556. BOOLEAN LocalServicedNdncChanged = FALSE;
  557. PLIST_ENTRY DomainEntry;
  558. PDOMAIN_INFO DomainInfo;
  559. PDOMAIN_INFO *DeletedNdncArray = NULL;
  560. //
  561. // Avoid this operation in the setup mode when
  562. // we may not fully function as a DC as in the
  563. // case of a NT4 to NT5 DC upgrade.
  564. //
  565. if ( NlDoingSetup() ) {
  566. NlPrint(( NL_MISC, "NlUpdateServicedNdncs: avoid NDNC update in setup mode\n" ));
  567. NetStatus = NO_ERROR;
  568. goto Cleanup;
  569. }
  570. //
  571. // If for some reason we have no DNS host name,
  572. // we don't support non-domain NC -- silently
  573. // ignore this update.
  574. //
  575. if ( DnsHostName == NULL ) {
  576. NlPrint(( NL_CRITICAL,
  577. "NlUpdateServicedNdncs: Ignoring update since DnsHostName is NULL\n" ));
  578. NetStatus = NO_ERROR;
  579. goto Cleanup;
  580. }
  581. //
  582. // Get the NDNC names from the DS
  583. //
  584. Status = NlGetNdncNames( &NdncNames,
  585. &NdncGuids,
  586. &NdncCount );
  587. if ( !NT_SUCCESS(Status) ) {
  588. NetStatus = NetpNtStatusToApiStatus( Status );
  589. if ( CallNlExitOnFailure ) {
  590. NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogErrorAndNetStatus, NULL );
  591. }
  592. goto Cleanup;
  593. }
  594. //
  595. // Allocate an array to store pointers to NDNCs
  596. // that we may delete
  597. //
  598. EnterCriticalSection(&NlGlobalDomainCritSect);
  599. for ( DomainEntry = NlGlobalServicedNdncs.Flink ;
  600. DomainEntry != &NlGlobalServicedNdncs;
  601. DomainEntry = DomainEntry->Flink ) {
  602. CurrentNdncCount ++;
  603. }
  604. if ( CurrentNdncCount > 0 ) {
  605. DeletedNdncArray = LocalAlloc( LMEM_ZEROINIT, CurrentNdncCount * sizeof(PDOMAIN_INFO) );
  606. if ( DeletedNdncArray == NULL ) {
  607. LeaveCriticalSection(&NlGlobalDomainCritSect);
  608. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  609. goto Cleanup;
  610. }
  611. }
  612. //
  613. // Loop through NDNC entries we have and determine
  614. // whether the entry should be deleted
  615. //
  616. for ( DomainEntry = NlGlobalServicedNdncs.Flink ;
  617. DomainEntry != &NlGlobalServicedNdncs;
  618. DomainEntry = DomainEntry->Flink ) {
  619. DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, DomNext);
  620. //
  621. // Skip this entry if it's already marked for deletion
  622. //
  623. if ( DomainInfo->DomFlags & DOM_DELETED ) {
  624. continue;
  625. }
  626. //
  627. // Loop through the DS suplied NDNC names and see if
  628. // we already have this NDNC in our list
  629. //
  630. for ( NdncIndex = 0; NdncIndex < NdncCount; NdncIndex++ ) {
  631. if ( NlEqualDnsName( (LPCWSTR) DomainInfo->DomUnicodeDnsDomainName,
  632. (LPCWSTR) NdncNames[NdncIndex]->rItems[0].pDomain ) ) {
  633. break;
  634. }
  635. }
  636. //
  637. // If this NDNC that we have no longer exists,
  638. // mark it for deletion
  639. //
  640. if ( NdncIndex == NdncCount ) {
  641. NlDeleteDomain( DomainInfo );
  642. //
  643. // Remember that this entry should be deleted
  644. //
  645. DeletedNdncArray[DeletedNdncCount] = DomainInfo;
  646. DeletedNdncCount ++;
  647. LocalServicedNdncChanged = TRUE;
  648. }
  649. }
  650. //
  651. // Add NDNCs that we don't already have
  652. //
  653. for ( NdncIndex = 0; NdncIndex < NdncCount; NdncIndex++ ) {
  654. DomainInfo = NULL;
  655. for ( DomainEntry = NlGlobalServicedNdncs.Flink ;
  656. DomainEntry != &NlGlobalServicedNdncs;
  657. DomainEntry = DomainEntry->Flink ) {
  658. DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, DomNext);
  659. //
  660. // If this entry is not to be deleted,
  661. // check it for match
  662. //
  663. if ( (DomainInfo->DomFlags & DOM_DELETED) == 0 &&
  664. NlEqualDnsName( (LPCWSTR) DomainInfo->DomUnicodeDnsDomainName,
  665. (LPCWSTR) NdncNames[NdncIndex]->rItems[0].pDomain ) ) {
  666. break;
  667. }
  668. DomainInfo = NULL;
  669. }
  670. //
  671. // Add this NDNC to our list if we don't have it already
  672. //
  673. if ( DomainInfo == NULL ) {
  674. NetStatus = NlCreateDomainPhase1( NULL, // No Netbios name for NDNC
  675. NdncNames[NdncIndex]->rItems[0].pDomain,
  676. NULL, // No SID for NDNC
  677. &NdncGuids[NdncIndex],
  678. ComputerName,
  679. DnsHostName,
  680. CallNlExitOnFailure,
  681. DOM_NON_DOMAIN_NC, // This is NDNC
  682. &DomainInfo );
  683. if ( NetStatus == NO_ERROR ) {
  684. LocalServicedNdncChanged = TRUE;
  685. NlDereferenceDomain( DomainInfo );
  686. } else if ( CallNlExitOnFailure ) {
  687. // NlExit was already called
  688. break;
  689. }
  690. }
  691. }
  692. LeaveCriticalSection(&NlGlobalDomainCritSect);
  693. //
  694. // Now that the domain crit sect isn't locked
  695. // we can safely unlink and delete the unneeded NDNCs
  696. // by removing the last reference
  697. //
  698. for ( NdncIndex = 0; NdncIndex < DeletedNdncCount; NdncIndex++ ) {
  699. NlDereferenceDomain( DeletedNdncArray[NdncIndex] );
  700. }
  701. Cleanup:
  702. if ( NdncNames != NULL ) {
  703. for ( NdncIndex = 0; NdncIndex < NdncCount; NdncIndex++ ) {
  704. DsFreeNameResultW( NdncNames[NdncIndex] );
  705. }
  706. LocalFree( NdncNames );
  707. }
  708. if ( NdncGuids != NULL ) {
  709. LocalFree( NdncGuids );
  710. }
  711. if ( DeletedNdncArray != NULL ) {
  712. LocalFree( DeletedNdncArray );
  713. }
  714. if ( NetStatus == NO_ERROR && ServicedNdncChanged != NULL ) {
  715. *ServicedNdncChanged = LocalServicedNdncChanged;
  716. }
  717. return NetStatus;
  718. }
  719. NTSTATUS
  720. NlUpdateDnsRootAlias(
  721. IN PDOMAIN_INFO DomainInfo,
  722. OUT PBOOL AliasNamesChanged OPTIONAL
  723. )
  724. /*++
  725. Routine Description:
  726. Update the aliases for DNS domain and forest names.
  727. Arguments:
  728. DomainInfo - Domain whose alias names should be updated.
  729. AliasNamesChanged - Set to TRUE if either the domain name
  730. alias or the forest name alias changed.
  731. Return Value:
  732. Status of operation.
  733. --*/
  734. {
  735. NTSTATUS Status = STATUS_SUCCESS;
  736. LPWSTR DnsDomainNameAlias = NULL;
  737. LPWSTR DnsForestNameAlias = NULL;
  738. LPSTR Utf8DnsDomainNameAlias = NULL;
  739. LPSTR Utf8DnsForestNameAlias = NULL;
  740. //
  741. // Initialization
  742. //
  743. if ( AliasNamesChanged != NULL ) {
  744. *AliasNamesChanged = FALSE;
  745. }
  746. //
  747. // Avoid this operation in the setup mode when
  748. // we may not fully function as a DC as in the
  749. // case of a NT4 to NT5 DC upgrade.
  750. //
  751. if ( NlDoingSetup() ) {
  752. NlPrint(( NL_MISC, "NlUpdateDnsRootAlias: avoid DnsRootAlias update in setup mode\n" ));
  753. Status = STATUS_SUCCESS;
  754. goto Cleanup;
  755. }
  756. //
  757. // Allocate the buffers
  758. //
  759. DnsDomainNameAlias = LocalAlloc( LMEM_ZEROINIT,
  760. DNS_MAX_NAME_BUFFER_LENGTH * sizeof(WCHAR) );
  761. if ( DnsDomainNameAlias == NULL ) {
  762. Status = STATUS_NO_MEMORY;
  763. goto Cleanup;
  764. }
  765. DnsForestNameAlias = LocalAlloc( LMEM_ZEROINIT,
  766. DNS_MAX_NAME_BUFFER_LENGTH * sizeof(WCHAR) );
  767. if ( DnsForestNameAlias == NULL ) {
  768. Status = STATUS_NO_MEMORY;
  769. goto Cleanup;
  770. }
  771. //
  772. // Get the name aliases from the DS
  773. //
  774. Status = NlGetDnsRootAlias( DnsDomainNameAlias, DnsForestNameAlias );
  775. if ( !NT_SUCCESS(Status) ) {
  776. NlPrint(( NL_CRITICAL,
  777. "NlUpdateDnsRootAlias: NlGetDnsRootAlias failed 0x%lx\n",
  778. Status ));
  779. goto Cleanup;
  780. }
  781. //
  782. // Convert the names to UTF-8
  783. //
  784. if ( wcslen(DnsDomainNameAlias) > 0 ) {
  785. Utf8DnsDomainNameAlias = NetpAllocUtf8StrFromWStr( DnsDomainNameAlias );
  786. if ( Utf8DnsDomainNameAlias == NULL ) {
  787. Status = STATUS_NO_MEMORY;
  788. goto Cleanup;
  789. }
  790. }
  791. if ( wcslen(DnsForestNameAlias) > 0 ) {
  792. Utf8DnsForestNameAlias = NetpAllocUtf8StrFromWStr( DnsForestNameAlias );
  793. if ( Utf8DnsForestNameAlias == NULL ) {
  794. Status = STATUS_NO_MEMORY;
  795. goto Cleanup;
  796. }
  797. }
  798. //
  799. // Update the DNS domain name alias
  800. //
  801. EnterCriticalSection( &NlGlobalDomainCritSect );
  802. //
  803. // Ignore this update if the name alias is same as the active one
  804. //
  805. // Note: NlEqualDnsNameUtf8 checks for NULL on input
  806. //
  807. if ( NlEqualDnsNameUtf8(DomainInfo->DomUtf8DnsDomainName,
  808. Utf8DnsDomainNameAlias) ) {
  809. NlPrint(( NL_CRITICAL,
  810. "NlUpdateDnsRootAlias: Ignoring DnsDomainNameAlias update for same active name: %s %s\n",
  811. DomainInfo->DomUtf8DnsDomainName,
  812. Utf8DnsDomainNameAlias ));
  813. //
  814. // Ignore this update if the name alias is same as the current name alias
  815. //
  816. } else if ( NlEqualDnsNameUtf8(DomainInfo->DomUtf8DnsDomainNameAlias,
  817. Utf8DnsDomainNameAlias) ) {
  818. NlPrint(( NL_CRITICAL,
  819. "NlUpdateDnsRootAlias: Ignoring DnsDomainNameAlias update for same alias name: %s %s\n",
  820. DomainInfo->DomUtf8DnsDomainNameAlias,
  821. Utf8DnsDomainNameAlias ));
  822. //
  823. // Otherwise update the alias
  824. //
  825. } else {
  826. if ( AliasNamesChanged != NULL ) {
  827. *AliasNamesChanged = TRUE;
  828. }
  829. NlPrint(( NL_DOMAIN,
  830. "NlUpdateDnsRootAlias: Updating DnsDomainNameAlias from %s to %s\n",
  831. DomainInfo->DomUtf8DnsDomainNameAlias,
  832. Utf8DnsDomainNameAlias ));
  833. if ( DomainInfo->DomUtf8DnsDomainNameAlias != NULL ) {
  834. NetpMemoryFree( DomainInfo->DomUtf8DnsDomainNameAlias );
  835. DomainInfo->DomUtf8DnsDomainNameAlias = NULL;
  836. }
  837. DomainInfo->DomUtf8DnsDomainNameAlias = Utf8DnsDomainNameAlias;
  838. Utf8DnsDomainNameAlias = NULL;
  839. }
  840. LeaveCriticalSection( &NlGlobalDomainCritSect );
  841. //
  842. // Update the DNS forest name alias
  843. //
  844. EnterCriticalSection( &NlGlobalDnsForestNameCritSect );
  845. //
  846. // Ignore this update if the name alias is same as the active one
  847. //
  848. // Note: NlEqualDnsNameUtf8 checks for NULL on input
  849. //
  850. if ( NlEqualDnsNameUtf8(NlGlobalUtf8DnsForestName,
  851. Utf8DnsForestNameAlias) ) {
  852. NlPrint(( NL_CRITICAL,
  853. "NlUpdateDnsRootAlias: Ignoring DnsForestNameAlias update for same active name: %s %s\n",
  854. NlGlobalUtf8DnsForestName,
  855. Utf8DnsForestNameAlias));
  856. //
  857. // Ignore this update if the name alias is same as the current name alias
  858. //
  859. } else if ( NlEqualDnsNameUtf8(NlGlobalUtf8DnsForestNameAlias,
  860. Utf8DnsForestNameAlias) ) {
  861. NlPrint(( NL_CRITICAL,
  862. "NlUpdateDnsRootAlias: Ignoring DnsForestNameAlias update for same alias name: %s %s\n",
  863. NlGlobalUtf8DnsForestNameAlias,
  864. Utf8DnsForestNameAlias));
  865. } else {
  866. if ( AliasNamesChanged != NULL ) {
  867. *AliasNamesChanged = TRUE;
  868. }
  869. NlPrint(( NL_DOMAIN,
  870. "NlUpdateDnsRootAlias: Updating DnsForestNameAlias from %s to %s\n",
  871. NlGlobalUtf8DnsForestNameAlias,
  872. Utf8DnsForestNameAlias ));
  873. if ( NlGlobalUtf8DnsForestNameAlias != NULL ) {
  874. NetpMemoryFree( NlGlobalUtf8DnsForestNameAlias );
  875. NlGlobalUtf8DnsForestNameAlias = NULL;
  876. }
  877. NlGlobalUtf8DnsForestNameAlias = Utf8DnsForestNameAlias;
  878. Utf8DnsForestNameAlias = NULL;
  879. }
  880. LeaveCriticalSection( &NlGlobalDnsForestNameCritSect );
  881. Status = STATUS_SUCCESS;
  882. Cleanup:
  883. if ( DnsDomainNameAlias != NULL ) {
  884. LocalFree( DnsDomainNameAlias );
  885. }
  886. if ( DnsForestNameAlias != NULL ) {
  887. LocalFree( DnsForestNameAlias );
  888. }
  889. if ( Utf8DnsDomainNameAlias != NULL ) {
  890. NetpMemoryFree( Utf8DnsDomainNameAlias );
  891. }
  892. if ( Utf8DnsForestNameAlias != NULL ) {
  893. NetpMemoryFree( Utf8DnsForestNameAlias );
  894. }
  895. return Status;
  896. }
  897. NET_API_STATUS
  898. NlInitializeDomains(
  899. VOID
  900. )
  901. /*++
  902. Routine Description:
  903. Initialize brdomain.c and create the primary domain.
  904. Arguments:
  905. None
  906. Return Value:
  907. Status of operation.
  908. --*/
  909. {
  910. NET_API_STATUS NetStatus;
  911. NTSTATUS Status;
  912. PDOMAIN_INFO DomainInfo = NULL;
  913. LPWSTR ComputerName = NULL;
  914. LPWSTR DnsHostName = NULL;
  915. LPWSTR DomainName = NULL;
  916. LPWSTR DnsDomainName = NULL;
  917. PSID AccountDomainSid = NULL;
  918. PSID PrimaryDomainSid = NULL;
  919. GUID *DomainGuid = NULL;
  920. //
  921. // Initialize globals
  922. //
  923. try {
  924. InitializeCriticalSection( &NlGlobalDomainCritSect );
  925. } except( EXCEPTION_EXECUTE_HANDLER ) {
  926. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  927. NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL );
  928. goto Cleanup;
  929. }
  930. InitializeListHead(&NlGlobalServicedDomains);
  931. InitializeListHead(&NlGlobalServicedNdncs);
  932. NlGlobalDomainsInitialized = TRUE;
  933. //
  934. // Get the computername and domain name of this machine
  935. // (in both the Netbios and DNS forms).
  936. //
  937. NetStatus = NetpGetComputerName( &ComputerName );
  938. if ( NetStatus != NERR_Success ) {
  939. NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL );
  940. goto Cleanup;
  941. }
  942. NlGlobalUnicodeComputerName = NetpAllocWStrFromWStr( ComputerName );
  943. if ( NlGlobalUnicodeComputerName == NULL ) {
  944. NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
  945. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  946. goto Cleanup;
  947. }
  948. NetStatus = NlGetDomainName( &DomainName,
  949. &DnsDomainName,
  950. &AccountDomainSid,
  951. &PrimaryDomainSid,
  952. &DomainGuid,
  953. NULL );
  954. if ( NetStatus != NERR_Success ) {
  955. // NlExit was already called
  956. goto Cleanup;
  957. }
  958. // Be consistent.
  959. // Avoid getting a DNS Host Name if we have no DNS domain name.
  960. if ( DnsDomainName != NULL ) {
  961. NetStatus = NlGetDnsHostName( &DnsHostName );
  962. if ( NetStatus != NERR_Success ) {
  963. NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL );
  964. goto Cleanup;
  965. }
  966. }
  967. //
  968. // Create the Domain Info struct and initialize it.
  969. //
  970. NetStatus = NlCreateDomainPhase1( DomainName,
  971. DnsDomainName,
  972. AccountDomainSid,
  973. DomainGuid,
  974. ComputerName,
  975. DnsHostName,
  976. TRUE, // Call NlExit on failure
  977. DOM_REAL_DOMAIN | DOM_PRIMARY_DOMAIN, // Primary domain of this machine
  978. &DomainInfo );
  979. if ( NetStatus != NERR_Success ) {
  980. // NlExit was already called
  981. goto Cleanup;
  982. }
  983. //
  984. // Finish workstation initialization.
  985. //
  986. if ( NlGlobalMemberWorkstation ) {
  987. //
  988. // Ensure the primary and account domain ID are different.
  989. //
  990. if ( RtlEqualSid( PrimaryDomainSid, AccountDomainSid ) ) {
  991. LPWSTR AlertStrings[3];
  992. //
  993. // alert admin.
  994. //
  995. AlertStrings[0] = DomainInfo->DomUnicodeComputerNameString.Buffer;
  996. AlertStrings[1] = DomainInfo->DomUnicodeDomainName;
  997. AlertStrings[2] = NULL;
  998. //
  999. // Save the info in the eventlog
  1000. //
  1001. NlpWriteEventlog(
  1002. ALERT_NetLogonSidConflict,
  1003. EVENTLOG_ERROR_TYPE,
  1004. AccountDomainSid,
  1005. RtlLengthSid( AccountDomainSid ),
  1006. AlertStrings,
  1007. 2 );
  1008. //
  1009. // This isn't fatal. (Just drop through)
  1010. //
  1011. }
  1012. LOCK_TRUST_LIST( DomainInfo );
  1013. NlAssert( DomainInfo->DomClientSession == NULL );
  1014. //
  1015. // Allocate the Client Session structure.
  1016. //
  1017. DomainInfo->DomClientSession = NlAllocateClientSession(
  1018. DomainInfo,
  1019. &DomainInfo->DomUnicodeDomainNameString,
  1020. &DomainInfo->DomUnicodeDnsDomainNameString,
  1021. PrimaryDomainSid,
  1022. DomainInfo->DomDomainGuid,
  1023. CS_DIRECT_TRUST |
  1024. (DomainInfo->DomUnicodeDnsDomainNameString.Length != 0 ? CS_NT5_DOMAIN_TRUST : 0),
  1025. WorkstationSecureChannel,
  1026. 0 ); // No trust attributes
  1027. if ( DomainInfo->DomClientSession == NULL ) {
  1028. UNLOCK_TRUST_LIST( DomainInfo );
  1029. NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
  1030. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1031. goto Cleanup;
  1032. }
  1033. //
  1034. // Save a copy of the client session for convenience.
  1035. // A workstation only has one client session.
  1036. //
  1037. NlGlobalClientSession = DomainInfo->DomClientSession;
  1038. UNLOCK_TRUST_LIST( DomainInfo );
  1039. //
  1040. // Finish DC initialization.
  1041. //
  1042. } else {
  1043. //
  1044. // Do the time intensive portion of creating the domain.
  1045. //
  1046. NetStatus = NlCreateDomainPhase2( DomainInfo,
  1047. TRUE ); // Call NlExit on failure
  1048. if ( NetStatus != NERR_Success ) {
  1049. // NlExit was already called
  1050. goto Cleanup;
  1051. }
  1052. //
  1053. // Initialize the list of non-domain NCs we host
  1054. //
  1055. NetStatus = NlUpdateServicedNdncs( ComputerName,
  1056. DnsHostName,
  1057. TRUE, // Call NlExit on failure
  1058. NULL ); // Don't care if NDNC list changed
  1059. if ( NetStatus != NO_ERROR ) {
  1060. // NlExit was already called
  1061. goto Cleanup;
  1062. }
  1063. //
  1064. // Update the domain and forest name aliases
  1065. //
  1066. Status = NlUpdateDnsRootAlias( DomainInfo,
  1067. NULL ); // don't care if name changed
  1068. if ( !NT_SUCCESS(Status) ) {
  1069. NetStatus = NetpNtStatusToApiStatus( Status );
  1070. NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogErrorAndNetStatus, NULL );
  1071. goto Cleanup;
  1072. }
  1073. }
  1074. NetStatus = NERR_Success;
  1075. //
  1076. // Free locally used resources
  1077. //
  1078. Cleanup:
  1079. if ( DomainInfo != NULL ) {
  1080. NlDereferenceDomain( DomainInfo );
  1081. }
  1082. if ( ComputerName != NULL ) {
  1083. (VOID)NetApiBufferFree( ComputerName );
  1084. }
  1085. if ( DnsHostName != NULL ) {
  1086. (VOID)LocalFree( DnsHostName );
  1087. }
  1088. if ( DomainName != NULL ) {
  1089. (VOID)LocalFree( DomainName );
  1090. }
  1091. if ( DnsDomainName != NULL ) {
  1092. (VOID)LocalFree( DnsDomainName );
  1093. }
  1094. if ( AccountDomainSid != NULL ) {
  1095. (VOID)LocalFree( AccountDomainSid );
  1096. }
  1097. if ( PrimaryDomainSid != NULL ) {
  1098. (VOID)LocalFree( PrimaryDomainSid );
  1099. }
  1100. if ( DomainGuid != NULL ) {
  1101. (VOID)LocalFree( DomainGuid );
  1102. }
  1103. return NetStatus;
  1104. }
  1105. VOID
  1106. NlFreeComputerName(
  1107. PDOMAIN_INFO DomainInfo
  1108. )
  1109. /*++
  1110. Routine Description:
  1111. Free the ComputerName fields for this domain.
  1112. Arguments:
  1113. DomainInfo - Domain the computername is being defined for.
  1114. ComputerName - Computername of this machine for the domain.
  1115. DnsHostName - DNS Hostname of this machine for the domain.
  1116. Return Value:
  1117. Status of operation.
  1118. --*/
  1119. {
  1120. DomainInfo->DomUncUnicodeComputerName[0] = L'\0';
  1121. RtlInitUnicodeString( &DomainInfo->DomUnicodeComputerNameString,
  1122. DomainInfo->DomUncUnicodeComputerName );
  1123. if ( DomainInfo->DomUnicodeDnsHostNameString.Buffer != NULL ) {
  1124. RtlFreeUnicodeString( &DomainInfo->DomUnicodeDnsHostNameString );
  1125. RtlInitUnicodeString( &DomainInfo->DomUnicodeDnsHostNameString, NULL );
  1126. }
  1127. if ( DomainInfo->DomUtf8DnsHostName != NULL) {
  1128. NetpMemoryFree( DomainInfo->DomUtf8DnsHostName );
  1129. DomainInfo->DomUtf8DnsHostName = NULL;
  1130. }
  1131. DomainInfo->DomOemComputerName[0] = '\0';
  1132. DomainInfo->DomOemComputerNameLength = 0;
  1133. if ( DomainInfo->DomUtf8ComputerName != NULL ) {
  1134. NetpMemoryFree( DomainInfo->DomUtf8ComputerName );
  1135. DomainInfo->DomUtf8ComputerName = NULL;
  1136. }
  1137. DomainInfo->DomUtf8ComputerNameLength = 0;
  1138. }
  1139. NET_API_STATUS
  1140. NlSetComputerName(
  1141. PDOMAIN_INFO DomainInfo,
  1142. LPWSTR ComputerName,
  1143. LPWSTR DnsHostName OPTIONAL
  1144. )
  1145. /*++
  1146. Routine Description:
  1147. Set a computed computername for an domain.
  1148. Arguments:
  1149. DomainInfo - Domain the computername is being defined for.
  1150. ComputerName - Computername of this machine for the domain.
  1151. DnsHostName - DNS Hostname of this machine for the domain.
  1152. Return Value:
  1153. Status of operation.
  1154. --*/
  1155. {
  1156. NTSTATUS Status;
  1157. NET_API_STATUS NetStatus;
  1158. NlPrintDom(( NL_DOMAIN, DomainInfo,
  1159. "Setting our computer name to %ws %ws\n", ComputerName, DnsHostName ));
  1160. //
  1161. // Copy the netbios computer name into the structure.
  1162. //
  1163. wcscpy( DomainInfo->DomUncUnicodeComputerName, L"\\\\" );
  1164. NetStatus = I_NetNameCanonicalize(
  1165. NULL,
  1166. ComputerName,
  1167. DomainInfo->DomUncUnicodeComputerName+2,
  1168. sizeof(DomainInfo->DomUncUnicodeComputerName)-2*sizeof(WCHAR),
  1169. NAMETYPE_COMPUTER,
  1170. 0 );
  1171. if ( NetStatus != NERR_Success ) {
  1172. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1173. "ComputerName %ws is invalid\n",
  1174. ComputerName ));
  1175. goto Cleanup;
  1176. }
  1177. RtlInitUnicodeString( &DomainInfo->DomUnicodeComputerNameString,
  1178. DomainInfo->DomUncUnicodeComputerName+2 );
  1179. Status = RtlUpcaseUnicodeToOemN( DomainInfo->DomOemComputerName,
  1180. sizeof(DomainInfo->DomOemComputerName),
  1181. &DomainInfo->DomOemComputerNameLength,
  1182. DomainInfo->DomUnicodeComputerNameString.Buffer,
  1183. DomainInfo->DomUnicodeComputerNameString.Length);
  1184. if (!NT_SUCCESS(Status)) {
  1185. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1186. "Unable to convert computer name to OEM %ws %lx\n",
  1187. ComputerName, Status ));
  1188. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1189. goto Cleanup;
  1190. }
  1191. DomainInfo->DomOemComputerName[DomainInfo->DomOemComputerNameLength] = '\0';
  1192. //
  1193. // Copy the UTF-8 version of the netbios computer name into the structure.
  1194. //
  1195. DomainInfo->DomUtf8ComputerName = NetpAllocUtf8StrFromWStr( ComputerName );
  1196. if (DomainInfo->DomUtf8ComputerName == NULL ) {
  1197. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1198. "Unable to convert computer name to UTF8 %ws\n",
  1199. DnsHostName ));
  1200. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1201. goto Cleanup;
  1202. }
  1203. DomainInfo->DomUtf8ComputerNameLength = strlen( DomainInfo->DomUtf8ComputerName );
  1204. //
  1205. // Copy the DNS Hostname into the structure.
  1206. //
  1207. if ( DnsHostName != NULL ) {
  1208. if ( !RtlCreateUnicodeString( &DomainInfo->DomUnicodeDnsHostNameString, DnsHostName ) ) {
  1209. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1210. "Unable to RtlCreateUnicodeString for host name %ws\n",
  1211. DnsHostName ));
  1212. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1213. goto Cleanup;
  1214. }
  1215. DomainInfo->DomUtf8DnsHostName =
  1216. NetpAllocUtf8StrFromWStr( DnsHostName );
  1217. if (DomainInfo->DomUtf8DnsHostName == NULL ) {
  1218. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1219. "Unable to convert host name to UTF8 %ws\n",
  1220. DnsHostName ));
  1221. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1222. goto Cleanup;
  1223. }
  1224. } else {
  1225. RtlInitUnicodeString( &DomainInfo->DomUnicodeDnsHostNameString, NULL );
  1226. DomainInfo->DomUtf8DnsHostName = NULL;
  1227. }
  1228. #ifdef _DC_NETLOGON
  1229. #ifdef notdef
  1230. // ?? placeholder for telling DS
  1231. //
  1232. // Tell SAM what the computername for this domain is.
  1233. //
  1234. Status = SpmDbSetDomainServerName(
  1235. &DomainInfo->DomUnicodeDomainNameString,
  1236. &DomainInfo->DomUnicodeComputerNameString );
  1237. if ( !NT_SUCCESS(Status) ) {
  1238. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1239. "Unable to SpmDbSetDomainServerName to %ws %lx\n",
  1240. ComputerName, Status ));
  1241. Status = NetpNtStatusToApiStatus( Status );
  1242. goto Cleanup;
  1243. }
  1244. #endif // notdef
  1245. #endif // _DC_NETLOGON
  1246. //
  1247. // All done.
  1248. //
  1249. NetStatus = NERR_Success;
  1250. Cleanup:
  1251. //
  1252. // On error, clear everything out.
  1253. //
  1254. if ( NetStatus != NERR_Success ) {
  1255. NlFreeComputerName( DomainInfo );
  1256. }
  1257. return NetStatus;
  1258. }
  1259. #ifdef _DC_NETLOGON
  1260. #ifdef MULTIHOSTED_DOMAIN
  1261. // Handle DnsHostName too
  1262. NET_API_STATUS
  1263. NlAssignComputerName(
  1264. PDOMAIN_INFO DomainInfo
  1265. )
  1266. /*++
  1267. Routine Description:
  1268. Assign a computername to a domain. Register that computername
  1269. with the SMB server as verification of it's validity.
  1270. Arguments:
  1271. DomainInfo - Domain the computername is being defined for.
  1272. Return Value:
  1273. Status of operation.
  1274. --*/
  1275. {
  1276. NET_API_STATUS NetStatus;
  1277. DWORD ComputerOrdinal;
  1278. DWORD DefaultComputerOrdinal;
  1279. DWORD MaximumComputerOrdinal = 0;
  1280. DWORD OrdinalFromRegistry = 0;
  1281. DWORD TotalRetryCount = 0;
  1282. WCHAR ComputerName[CNLEN+1];
  1283. #ifdef notdef
  1284. //
  1285. // Compute the default ordinal.
  1286. //
  1287. NlGetDomainIndex( &DefaultComputerOrdinal, &MaximumComputerOrdinal );
  1288. //
  1289. // Get the value of the "EmulatedComputerName". If the name is specified
  1290. // in the registry, use that name and don't fall back to any other name.
  1291. //
  1292. DataSize = sizeof(ComputerName);
  1293. // ?? Read DnsNameForm, NetbiosName, and CurrentDnsName from the machine object
  1294. NetStatus = RegQueryValueExW( DomainKeyHandle,
  1295. NL_DOMAIN_EMULATED_COMPUTER_NAME,
  1296. 0, // Reserved
  1297. &KeyType,
  1298. (LPBYTE)&ComputerName,
  1299. &DataSize );
  1300. if ( NetStatus != ERROR_FILE_NOT_FOUND ) {
  1301. if ( NetStatus != ERROR_SUCCESS || KeyType != REG_SZ ) {
  1302. // ??: write an event.
  1303. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1304. "NlAssignComputerName: Cannot read %ws registry key %ld.\n",
  1305. NL_DOMAIN_EMULATED_COMPUTER_NAME,
  1306. NetStatus ));
  1307. } else {
  1308. //
  1309. // Register the computer name.
  1310. //
  1311. NetStatus = NlServerComputerNameAdd(
  1312. dns too
  1313. DomainInfo->DomUnicodeDomainName,
  1314. ComputerName );
  1315. if ( NetStatus != NERR_Success ) {
  1316. // ??: write an event.
  1317. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1318. "NlAssignComputerName: Cannot register computername %ws with SMB server %ld.\n",
  1319. ComputerName,
  1320. NetStatus ));
  1321. goto Cleanup;
  1322. }
  1323. //
  1324. // Save it.
  1325. //
  1326. NetStatus = NlSetComputerName( DomainInfo, ComputerName, DnsHostName );
  1327. goto Cleanup;
  1328. }
  1329. }
  1330. #endif // notdef
  1331. //
  1332. // Get the value of the "EmulatedComputerOrdinal" indicating what our first
  1333. // try as a computername should be.
  1334. //
  1335. #ifdef notdef
  1336. DataSize = sizeof(ComputerOrdinal);
  1337. // ?? Read DnsNameForm, NetbiosName, and CurrentDnsName from the machine object
  1338. NetStatus = RegQueryValueExW( DomainKeyHandle,
  1339. NL_DOMAIN_EMULATED_COMPUTER_ORDINAL,
  1340. 0, // Reserved
  1341. &KeyType,
  1342. (LPBYTE)&OrdinalFromRegistry,
  1343. &DataSize );
  1344. if ( NetStatus != ERROR_SUCCESS ) {
  1345. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1346. "NlAssignComputerName: Cannot query %ws key (using defaults) %ld.\n",
  1347. NL_DOMAIN_EMULATED_COMPUTER_ORDINAL,
  1348. NetStatus ));
  1349. ComputerOrdinal = DefaultComputerOrdinal;
  1350. //
  1351. // Validate the returned data.
  1352. //
  1353. } else if ( KeyType != REG_DWORD || DataSize != sizeof(OrdinalFromRegistry) ) {
  1354. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1355. "NlAssignComputerName: Key %ws size/type wrong.\n",
  1356. NL_DOMAIN_EMULATED_COMPUTER_ORDINAL ));
  1357. ComputerOrdinal = DefaultComputerOrdinal;
  1358. //
  1359. // Use the ordinal from the registry
  1360. //
  1361. } else {
  1362. ComputerOrdinal = OrdinalFromRegistry;
  1363. }
  1364. #else // notdef
  1365. ComputerOrdinal = OrdinalFromRegistry;
  1366. #endif // notdef
  1367. //
  1368. // Loop trying the oridinal number to compute a computer name.
  1369. //
  1370. for (;;) {
  1371. WCHAR OrdinalString[12];
  1372. //
  1373. // Build the computer name to test.
  1374. //
  1375. // DOMAIN________N
  1376. //
  1377. // where DOMAIN is the domain name, N is the ordinal number, and
  1378. // there are enough _'s to pad to DNLEN.
  1379. //
  1380. wcscpy( ComputerName, DomainInfo->DomUnicodeDomainName );
  1381. wcsncpy( &ComputerName[DomainInfo->DomUnicodeDomainNameString.Length/sizeof(WCHAR)],
  1382. L"________________",
  1383. DNLEN-DomainInfo->DomUnicodeDomainNameString.Length/sizeof(WCHAR) );
  1384. ultow( ComputerOrdinal, OrdinalString, 10 );
  1385. wcscpy( &ComputerName[DNLEN-wcslen(OrdinalString)],
  1386. OrdinalString );
  1387. //
  1388. // Try to register the computer name.
  1389. //
  1390. NetStatus = NlServerComputerNameAdd(
  1391. DomainInfo->DomUnicodeDomainName,
  1392. ComputerName );
  1393. if ( NetStatus != NERR_Success ) {
  1394. //
  1395. // If this name is in conflict with an existing name,
  1396. // try another ordinal.
  1397. //
  1398. // Simply increment the ordinal to try.
  1399. // Don't try ordinals that conflict with other existing Domain Controllers.
  1400. //
  1401. if ( NetStatus == NERR_DuplicateName ) {
  1402. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1403. "NlAssignComputerName: Computername %ws is duplicate (Try another.)\n",
  1404. ComputerName,
  1405. NetStatus ));
  1406. //
  1407. // Allow several attempts to add the computername.
  1408. //
  1409. TotalRetryCount ++;
  1410. if ( TotalRetryCount < 100 ) {
  1411. ComputerOrdinal = max(ComputerOrdinal + 1, MaximumComputerOrdinal*2 );
  1412. continue;
  1413. }
  1414. }
  1415. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1416. "NlAssignComputerName: Cannot register computername %ws with SMB server %ld.\n",
  1417. ComputerName,
  1418. NetStatus ));
  1419. goto Cleanup;
  1420. }
  1421. //
  1422. // If we've made it here, we have a valid computername.
  1423. //
  1424. break;
  1425. }
  1426. #ifdef notdef
  1427. //
  1428. // Write the chosen ordinal to the registry so we don't have to work so hard
  1429. // next time.
  1430. //
  1431. NetStatus = RegSetValueExW( DomainKeyHandle,
  1432. NL_DOMAIN_EMULATED_COMPUTER_ORDINAL,
  1433. 0, // Reserved
  1434. REG_DWORD,
  1435. (LPBYTE)&ComputerOrdinal,
  1436. sizeof(ComputerOrdinal) );
  1437. if ( NetStatus != ERROR_SUCCESS ) {
  1438. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1439. "NlAssignComputerName: Cannot set %ws key (ignored) %ld.\n",
  1440. NL_DOMAIN_EMULATED_COMPUTER_ORDINAL,
  1441. NetStatus ));
  1442. }
  1443. //
  1444. // Done.
  1445. //
  1446. NetStatus = NlSetComputerName( DomainInfo, ComputerName, DnsHostName );
  1447. #endif // notdef
  1448. Cleanup:
  1449. #ifdef notdef
  1450. if ( DomainKeyHandle != NULL ) {
  1451. RegCloseKey( DomainKeyHandle );
  1452. }
  1453. #endif // notdef
  1454. if ( NetStatus == NERR_Success ) {
  1455. NlPrintDom(( NL_DOMAIN, DomainInfo,
  1456. "Assigned computer name: %ws\n",
  1457. ComputerName ));
  1458. }
  1459. return NetStatus;
  1460. }
  1461. #endif // MULTIHOSTED_DOMAIN
  1462. VOID
  1463. NlDomainThread(
  1464. IN LPVOID DomainInfoParam
  1465. )
  1466. /*++
  1467. Routine Description:
  1468. Perform role change operations that are potentially time consuming.
  1469. As such, this routine runs in a separate thread specific to the domain.
  1470. Arguments:
  1471. DomainInfoParam - Domain who's role is to be updated.
  1472. Return Value:
  1473. None.
  1474. This routine logs any error it detects, but it doesn't call NlExit.
  1475. --*/
  1476. {
  1477. NET_API_STATUS NetStatus;
  1478. PDOMAIN_INFO DomainInfo = (PDOMAIN_INFO) DomainInfoParam;
  1479. DWORD DomFlags;
  1480. NlPrintDom(( NL_DOMAIN, DomainInfo,
  1481. "Domain thread started\n"));
  1482. //
  1483. // Loop forever.
  1484. //
  1485. // We only want one thread per domain. Therefore, this thread
  1486. // stays around doing not only what was requested before it started,
  1487. // but also those tasks that are queued later.
  1488. //
  1489. for (;;) {
  1490. //
  1491. // If we've been asked to terminate,
  1492. // do so.
  1493. //
  1494. EnterCriticalSection(&NlGlobalDomainCritSect);
  1495. if ( (DomainInfo->DomFlags & DOM_THREAD_TERMINATE) != 0 ||
  1496. NlGlobalTerminate ) {
  1497. NlPrintDom(( NL_DOMAIN, DomainInfo,
  1498. "Domain thread asked to terminate\n"));
  1499. DomainInfo->DomFlags &= ~DOM_THREAD_RUNNING;
  1500. LeaveCriticalSection(&NlGlobalDomainCritSect);
  1501. return;
  1502. }
  1503. //
  1504. // If there are things to do,
  1505. // pick one thing to do and
  1506. // save it so we can safely drop the crit sect.
  1507. //
  1508. if ( DomainInfo->DomFlags & DOM_CREATION_NEEDED ) {
  1509. DomFlags = DOM_CREATION_NEEDED;
  1510. } else if ( DomainInfo->DomFlags & DOM_ROLE_UPDATE_NEEDED ) {
  1511. DomFlags = DOM_ROLE_UPDATE_NEEDED;
  1512. } else if ( DomainInfo->DomFlags & DOM_TRUST_UPDATE_NEEDED ) {
  1513. DomFlags = DOM_TRUST_UPDATE_NEEDED;
  1514. } else if ( DomainInfo->DomFlags & DOM_API_TIMEOUT_NEEDED ) {
  1515. DomFlags = DOM_API_TIMEOUT_NEEDED;
  1516. //
  1517. // Pick up all work items accociate with the primary announcement
  1518. //
  1519. } else if ( DomainInfo->DomFlags & DOM_PRIMARY_ANNOUNCE_FLAGS ) {
  1520. DomFlags = DomainInfo->DomFlags & DOM_PRIMARY_ANNOUNCE_FLAGS;
  1521. } else {
  1522. NlPrintDom(( NL_DOMAIN, DomainInfo,
  1523. "Domain thread exitting\n"));
  1524. DomainInfo->DomFlags &= ~DOM_THREAD_RUNNING;
  1525. LeaveCriticalSection(&NlGlobalDomainCritSect);
  1526. return;
  1527. }
  1528. DomainInfo->DomFlags &= ~DomFlags;
  1529. LeaveCriticalSection(&NlGlobalDomainCritSect);
  1530. //
  1531. // If phase 2 of domain creation is needed,
  1532. // do it now.
  1533. //
  1534. if ( DomFlags & DOM_CREATION_NEEDED ) {
  1535. NlPrintDom(( NL_DOMAIN, DomainInfo,
  1536. "Domain thread started doing create phase 2\n"));
  1537. //
  1538. // Do the time intensive portion of creating the domain.
  1539. //
  1540. (VOID) NlCreateDomainPhase2( DomainInfo, FALSE );
  1541. } else if ( DomFlags & DOM_ROLE_UPDATE_NEEDED ) {
  1542. NlPrintDom(( NL_DOMAIN, DomainInfo,
  1543. "Domain thread started doing update role\n"));
  1544. (VOID) NlUpdateRole( DomainInfo );
  1545. } else if ( DomFlags & DOM_TRUST_UPDATE_NEEDED ) {
  1546. NlPrintDom(( NL_DOMAIN, DomainInfo,
  1547. "Domain thread started doing update trust list\n"));
  1548. (VOID) NlInitTrustList( DomainInfo );
  1549. } else if ( DomFlags & DOM_API_TIMEOUT_NEEDED ) {
  1550. NlPrintDom(( NL_DOMAIN, DomainInfo,
  1551. "Domain thread started doing API timeout\n"));
  1552. NlTimeoutApiClientSession( DomainInfo );
  1553. } else if ( DomFlags & DOM_PRIMARY_ANNOUNCE_FLAGS ) {
  1554. DWORD AnnounceFlags = 0;
  1555. NlPrintDom(( NL_DOMAIN, DomainInfo,
  1556. "Domain thread started doing primary announecement 0x%lx\n", DomFlags ));
  1557. //
  1558. // If we need to do immediate announcement,
  1559. // indicate so to the worker routine
  1560. //
  1561. if ( DomFlags & DOM_PRIMARY_ANNOUNCE_IMMEDIATE ) {
  1562. AnnounceFlags = ANNOUNCE_IMMEDIATE;
  1563. //
  1564. // Otherwise, if we only need to continue announcement,
  1565. // indicate so to the worker routine
  1566. //
  1567. } else if ( (DomFlags & DOM_PRIMARY_ANNOUNCE_NEEDED) == 0 &&
  1568. (DomFlags & DOM_PRIMARY_ANNOUNCE_CONTINUE) != 0 ) {
  1569. AnnounceFlags = ANNOUNCE_CONTINUE;
  1570. }
  1571. NlPrimaryAnnouncement( AnnounceFlags );
  1572. //
  1573. // Internal consistency check
  1574. //
  1575. } else {
  1576. NlPrintDom((NL_CRITICAL, DomainInfo,
  1577. "Invalid DomFlags %lx\n",
  1578. DomFlags ));
  1579. }
  1580. }
  1581. }
  1582. VOID
  1583. NlStopDomainThread(
  1584. PDOMAIN_INFO DomainInfo
  1585. )
  1586. /*++
  1587. Routine Description:
  1588. Stops the domain thread if it is running and waits for it to
  1589. stop.
  1590. Arguments:
  1591. NONE
  1592. Return Value:
  1593. NONE
  1594. --*/
  1595. {
  1596. //
  1597. // Only stop the thread if it's running
  1598. //
  1599. EnterCriticalSection( &NlGlobalDomainCritSect );
  1600. if ( DomainInfo->DomFlags & DOM_THREAD_RUNNING ) {
  1601. //
  1602. // Ask the thread to stop running.
  1603. //
  1604. DomainInfo->DomFlags |= DOM_THREAD_TERMINATE;
  1605. //
  1606. // Loop waiting for it to stop.
  1607. //
  1608. while ( DomainInfo->DomFlags & DOM_THREAD_RUNNING ) {
  1609. LeaveCriticalSection( &NlGlobalDomainCritSect );
  1610. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1611. "NlStopDomainThread: Sleeping a second waiting for thread to stop.\n"));
  1612. Sleep( 1000 );
  1613. EnterCriticalSection( &NlGlobalDomainCritSect );
  1614. }
  1615. //
  1616. // Domain thread no longer needs to terminate
  1617. //
  1618. DomainInfo->DomFlags &= ~DOM_THREAD_TERMINATE;
  1619. }
  1620. LeaveCriticalSection( &NlGlobalDomainCritSect );
  1621. return;
  1622. }
  1623. NET_API_STATUS
  1624. NlStartDomainThread(
  1625. PDOMAIN_INFO DomainInfo,
  1626. PDWORD DomFlags
  1627. )
  1628. /*++
  1629. Routine Description:
  1630. Start the domain thread if it is not already running.
  1631. The domain thread is simply one of the worker threads. However, we
  1632. ensure that only one worker thread is working on a single domain at once.
  1633. That ensures that slow items (such as NlUpdateRole) don't consume more than
  1634. one worker thread and are themselves serialized.
  1635. Arguments:
  1636. DomainInfo - Domain the thread is to be started for.
  1637. DomFlags - Specifies which operations the Domain Thread is to perform
  1638. Return Value:
  1639. NO_ERROR
  1640. --*/
  1641. {
  1642. //
  1643. // Tell the thread what work it has to do.
  1644. //
  1645. EnterCriticalSection( &NlGlobalDomainCritSect );
  1646. DomainInfo->DomFlags |= *DomFlags;
  1647. //
  1648. // If the domain thread is already running, do nothing.
  1649. //
  1650. if ( DomainInfo->DomFlags & DOM_THREAD_RUNNING ) {
  1651. NlPrintDom((NL_DOMAIN, DomainInfo,
  1652. "The domain thread is already running %lx.\n",
  1653. *DomFlags ));
  1654. LeaveCriticalSection( &NlGlobalDomainCritSect );
  1655. return NO_ERROR;
  1656. }
  1657. //
  1658. // Start the thread
  1659. //
  1660. // Make this a high priority thread to avoid 100's of trusted domain discoveries.
  1661. //
  1662. DomainInfo->DomFlags &= ~DOM_THREAD_TERMINATE;
  1663. if ( NlQueueWorkItem( &DomainInfo->DomThreadWorkItem, TRUE, TRUE ) ) {
  1664. DomainInfo->DomFlags |= DOM_THREAD_RUNNING;
  1665. }
  1666. LeaveCriticalSection( &NlGlobalDomainCritSect );
  1667. return NO_ERROR;
  1668. }
  1669. NTSTATUS
  1670. NlUpdateDatabaseRole(
  1671. IN PDOMAIN_INFO DomainInfo,
  1672. IN DWORD Role
  1673. )
  1674. /*++
  1675. Routine Description:
  1676. Update the role of the Sam database to match the current role of the domain.
  1677. Netlogon sets the role of the domain to be the same as the role in SAM account domain.
  1678. Arguments:
  1679. DomainInfo - Hosted Domain this database is for.
  1680. Role - Our new Role.
  1681. RoleInvalid implies the domain is being deleted.
  1682. Return Value:
  1683. NT status code.
  1684. --*/
  1685. {
  1686. NTSTATUS Status;
  1687. POLICY_LSA_SERVER_ROLE DesiredLsaRole;
  1688. //
  1689. // Convert the role to SAM/LSA specific values.
  1690. //
  1691. switch ( Role ) {
  1692. case RolePrimary:
  1693. DesiredLsaRole = PolicyServerRolePrimary;
  1694. break;
  1695. case RoleInvalid:
  1696. Status = STATUS_SUCCESS;
  1697. goto Cleanup;
  1698. case RoleBackup:
  1699. DesiredLsaRole = PolicyServerRoleBackup;
  1700. break;
  1701. default:
  1702. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1703. "NlUpdateDatabaseRole: Netlogon's role isn't valid %ld.\n",
  1704. Role ));
  1705. Status = STATUS_INVALID_DOMAIN_ROLE;
  1706. goto Cleanup;
  1707. }
  1708. //
  1709. // Ensure the changelog knows the current role.
  1710. // (This is really only needed on startup and if netlogon.dll has
  1711. // been unloaded. Otherwise, the LSA will do this notification
  1712. // when the role is really changed.)
  1713. //
  1714. if ( NlGlobalNetlogonUnloaded &&
  1715. NlGlobalChangeLogRole == ChangeLogUnknown ) {
  1716. NlPrint((NL_INIT,
  1717. "Set changelog role after netlogon.dll unload\n" ));
  1718. Status = NetpNotifyRole ( DesiredLsaRole );
  1719. if ( !NT_SUCCESS(Status) ) {
  1720. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1721. "NlUpdateDatabaseRole: Cannot NetpNotifyRole: %lx\n",
  1722. Status ));
  1723. goto Cleanup;
  1724. }
  1725. }
  1726. Status = STATUS_SUCCESS;
  1727. //
  1728. // Free locally used resources.
  1729. //
  1730. Cleanup:
  1731. return Status;
  1732. }
  1733. PCLIENT_SESSION
  1734. NlRefDomClientSession(
  1735. IN PDOMAIN_INFO DomainInfo
  1736. )
  1737. /*++
  1738. Routine Description:
  1739. Increment the reference count on the ClientSession structure for the domain.
  1740. If the ClientSession structure doesn't exist, this routine will FAIL.
  1741. Arguments:
  1742. DomainInfo - Domain whose ClientSession reference count is to be incremented.
  1743. Return Value:
  1744. Pointer to the client session structure whose reference count was
  1745. properly incremented.
  1746. NULL - The ClientSession structure doesn't exist
  1747. --*/
  1748. {
  1749. PCLIENT_SESSION ClientSession;
  1750. LOCK_TRUST_LIST( DomainInfo );
  1751. if ( DomainInfo->DomClientSession != NULL ) {
  1752. ClientSession = DomainInfo->DomClientSession;
  1753. NlRefClientSession( ClientSession );
  1754. UNLOCK_TRUST_LIST( DomainInfo );
  1755. return ClientSession;
  1756. } else {
  1757. UNLOCK_TRUST_LIST( DomainInfo );
  1758. return NULL;
  1759. }
  1760. }
  1761. PCLIENT_SESSION
  1762. NlRefDomParentClientSession(
  1763. IN PDOMAIN_INFO DomainInfo
  1764. )
  1765. /*++
  1766. Routine Description:
  1767. Increment the reference count on the ParentClientSession structure for the domain.
  1768. If the ParentClientSession structure doesn't exist, this routine will FAIL.
  1769. Arguments:
  1770. DomainInfo - Domain whose ParentClientSession reference count is to be incremented.
  1771. Return Value:
  1772. Pointer to the client session structure whose reference count was
  1773. properly incremented.
  1774. NULL - The ParentClientSession structure doesn't exist
  1775. --*/
  1776. {
  1777. PCLIENT_SESSION ClientSession;
  1778. LOCK_TRUST_LIST( DomainInfo );
  1779. if ( DomainInfo->DomParentClientSession != NULL ) {
  1780. ClientSession = DomainInfo->DomParentClientSession;
  1781. NlRefClientSession( ClientSession );
  1782. UNLOCK_TRUST_LIST( DomainInfo );
  1783. return ClientSession;
  1784. } else {
  1785. UNLOCK_TRUST_LIST( DomainInfo );
  1786. return NULL;
  1787. }
  1788. }
  1789. VOID
  1790. NlDeleteDomClientSession(
  1791. IN PDOMAIN_INFO DomainInfo
  1792. )
  1793. /*++
  1794. Routine Description:
  1795. Delete the domain's ClientSession stucture (If it exists)
  1796. Arguments:
  1797. DomainInfo - Domain whose ClientSession is to be deleted
  1798. Return Value:
  1799. None.
  1800. --*/
  1801. {
  1802. PCLIENT_SESSION ClientSession;
  1803. //
  1804. // Delete the client session
  1805. //
  1806. LOCK_TRUST_LIST( DomainInfo );
  1807. if ( DomainInfo->DomClientSession != NULL ) {
  1808. //
  1809. // Don't allow any new references.
  1810. //
  1811. ClientSession = DomainInfo->DomClientSession;
  1812. DomainInfo->DomClientSession = NULL;
  1813. NlFreeClientSession( ClientSession );
  1814. //
  1815. // Don't leave a straggling pointer to the deleted ClientSession
  1816. //
  1817. if ( IsPrimaryDomain(DomainInfo) ) {
  1818. NlGlobalClientSession = NULL;
  1819. }
  1820. //
  1821. // Wait for us to be the last reference.
  1822. //
  1823. while ( ClientSession->CsReferenceCount != 1 ) {
  1824. UNLOCK_TRUST_LIST( DomainInfo );
  1825. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1826. "NlDeleteDomClientSession: Sleeping a second waiting for ClientSession RefCount to zero.\n"));
  1827. Sleep( 1000 );
  1828. LOCK_TRUST_LIST( DomainInfo );
  1829. }
  1830. NlUnrefClientSession( ClientSession );
  1831. }
  1832. UNLOCK_TRUST_LIST( DomainInfo );
  1833. }
  1834. VOID
  1835. NlDeleteDomParentClientSession(
  1836. IN PDOMAIN_INFO DomainInfo
  1837. )
  1838. /*++
  1839. Routine Description:
  1840. Delete the domain's ClientSession stucture (If it exists)
  1841. Arguments:
  1842. DomainInfo - Domain whose ClientSession is to be deleted
  1843. Return Value:
  1844. None.
  1845. --*/
  1846. {
  1847. PCLIENT_SESSION ClientSession;
  1848. //
  1849. // Delete the client session
  1850. //
  1851. LOCK_TRUST_LIST( DomainInfo );
  1852. if ( DomainInfo->DomParentClientSession != NULL ) {
  1853. //
  1854. // Don't allow any new references.
  1855. //
  1856. ClientSession = DomainInfo->DomParentClientSession;
  1857. DomainInfo->DomParentClientSession = NULL;
  1858. NlFreeClientSession( ClientSession );
  1859. //
  1860. // Wait for us to be the last reference.
  1861. //
  1862. while ( ClientSession->CsReferenceCount != 1 ) {
  1863. UNLOCK_TRUST_LIST( DomainInfo );
  1864. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1865. "NlDeleteDomParentClientSession: Sleeping a second waiting for ClientSession RefCount to zero.\n"));
  1866. Sleep( 1000 );
  1867. LOCK_TRUST_LIST( DomainInfo );
  1868. }
  1869. NlUnrefClientSession( ClientSession );
  1870. }
  1871. UNLOCK_TRUST_LIST( DomainInfo );
  1872. }
  1873. NET_API_STATUS
  1874. NlUpdateRole(
  1875. IN PDOMAIN_INFO DomainInfo
  1876. )
  1877. /*++
  1878. Routine Description:
  1879. Determines the role of this machine, sets that role in the Netlogon service,
  1880. the server service, and browser.
  1881. Arguments:
  1882. DomainInfo - Hosted domain who's role is to be updated.
  1883. Return Value:
  1884. Status of operation.
  1885. This routine logs any error it detects, but it doesn't call NlExit.
  1886. --*/
  1887. {
  1888. LONG NetStatus;
  1889. NTSTATUS Status;
  1890. NETLOGON_ROLE NewRole;
  1891. BOOL NewPdcDoReplication;
  1892. BOOL PdcToConnectTo = FALSE;
  1893. BOOL ReplLocked = FALSE;
  1894. DWORD i;
  1895. LPWSTR AllocatedBuffer = NULL;
  1896. LPWSTR CapturedDnsDomainName;
  1897. LPWSTR CapturedDnsForestName;
  1898. GUID CapturedDomainGuidBuffer;
  1899. GUID *CapturedDomainGuid;
  1900. LPWSTR ChangeLogFile;
  1901. ULONG InternalFlags = 0;
  1902. PNL_DC_CACHE_ENTRY DomainControllerCacheEntry = NULL;
  1903. PLIST_ENTRY ListEntry;
  1904. BOOLEAN ThisIsPdc;
  1905. BOOLEAN Nt4MixedDomain;
  1906. //
  1907. // Allocate a buffer for storage local to this procedure.
  1908. // (Don't put it on the stack since we don't want to commit a huge stack.)
  1909. //
  1910. AllocatedBuffer = LocalAlloc( 0, sizeof(WCHAR) *
  1911. ((NL_MAX_DNS_LENGTH+1) +
  1912. (NL_MAX_DNS_LENGTH+1) +
  1913. (MAX_PATH+1) ) );
  1914. if ( AllocatedBuffer == NULL ) {
  1915. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1916. goto Cleanup;
  1917. }
  1918. CapturedDnsDomainName = AllocatedBuffer;
  1919. CapturedDnsForestName = &CapturedDnsDomainName[NL_MAX_DNS_LENGTH+1];
  1920. ChangeLogFile = &CapturedDnsForestName[NL_MAX_DNS_LENGTH+1];
  1921. //
  1922. // Get the information used to determine role from the DS
  1923. //
  1924. NetStatus = NlGetRoleInformation(
  1925. DomainInfo,
  1926. &ThisIsPdc,
  1927. &Nt4MixedDomain );
  1928. if ( NetStatus != ERROR_SUCCESS ) {
  1929. NlPrintDom((NL_CRITICAL, DomainInfo,
  1930. "NlUpdateRole: Failed to NlGetRoleInformation. %ld\n",
  1931. NetStatus ));
  1932. goto Cleanup;
  1933. }
  1934. //
  1935. // Determine the current role of this machine.
  1936. //
  1937. if ( ThisIsPdc ) {
  1938. NewRole = RolePrimary;
  1939. NewPdcDoReplication = FALSE;
  1940. if ( Nt4MixedDomain || NlGlobalParameters.AllowReplInNonMixed ) {
  1941. NewPdcDoReplication = TRUE;
  1942. }
  1943. } else {
  1944. NewRole = RoleBackup;
  1945. NewPdcDoReplication = FALSE;
  1946. }
  1947. //
  1948. // If the role has changed, tell everybody.
  1949. //
  1950. if ( DomainInfo->DomRole != NewRole ) {
  1951. NlPrintDom((NL_DOMAIN, DomainInfo,
  1952. "Changing role from %s to %s.\n",
  1953. (DomainInfo->DomRole == RolePrimary) ? "PDC" :
  1954. (DomainInfo->DomRole == RoleBackup ? "BDC" : "NONE" ),
  1955. (NewRole == RolePrimary) ? "PDC" :
  1956. (NewRole == RoleBackup ? "BDC" : "NONE" ) ));
  1957. // ??: Shouldn't there be some synchronization here.
  1958. DomainInfo->DomRole = NewRole;
  1959. //
  1960. // Create a ClientSession structure.
  1961. //
  1962. // Even the PDC has a client session to itself. It is used (for instance)
  1963. // when the PDC changes its own machine account password.
  1964. //
  1965. LOCK_TRUST_LIST( DomainInfo );
  1966. //
  1967. // Allocate the Client Session structure used to talk to the PDC.
  1968. //
  1969. // DomClientSession will only be non-null if a previous promotion
  1970. // to PDC failed.
  1971. //
  1972. if ( DomainInfo->DomClientSession == NULL ) {
  1973. DomainInfo->DomClientSession = NlAllocateClientSession(
  1974. DomainInfo,
  1975. &DomainInfo->DomUnicodeDomainNameString,
  1976. &DomainInfo->DomUnicodeDnsDomainNameString,
  1977. DomainInfo->DomAccountDomainId,
  1978. DomainInfo->DomDomainGuid,
  1979. CS_DIRECT_TRUST |
  1980. (DomainInfo->DomUnicodeDnsDomainNameString.Length != 0 ? CS_NT5_DOMAIN_TRUST : 0),
  1981. ServerSecureChannel,
  1982. 0 ); // No trust attributes
  1983. if ( DomainInfo->DomClientSession == NULL ) {
  1984. UNLOCK_TRUST_LIST( DomainInfo );
  1985. NlPrintDom(( NL_CRITICAL, DomainInfo,
  1986. "Cannot allocate PDC ClientSession\n"));
  1987. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1988. goto Cleanup;
  1989. }
  1990. }
  1991. //
  1992. // Save a copy of the client session for convenience.
  1993. // A BDC has only one client session to its PDC.
  1994. //
  1995. if ( IsPrimaryDomain(DomainInfo) ) {
  1996. NlGlobalClientSession = DomainInfo->DomClientSession;
  1997. }
  1998. UNLOCK_TRUST_LIST( DomainInfo );
  1999. //
  2000. // If this machine is now a PDC,
  2001. // Perform PDC-specific initialization.
  2002. //
  2003. if ( DomainInfo->DomRole == RolePrimary ) {
  2004. //
  2005. // The first time this machine is promoted to PDC,
  2006. // Do some "one-time" initialization.
  2007. EnterCriticalSection( &NlGlobalDomainCritSect );
  2008. if ( (DomainInfo->DomFlags & DOM_PROMOTED_BEFORE) == 0 ) {
  2009. //
  2010. // Initialize the server session table to contain all the BDCs.
  2011. // On demotion, we don't delete the table entries. We just leave
  2012. // them around until the next promotion.
  2013. //
  2014. Status = NlBuildNtBdcList(DomainInfo);
  2015. if ( !NT_SUCCESS(Status) ) {
  2016. LeaveCriticalSection( &NlGlobalDomainCritSect );
  2017. NlPrintDom(( NL_CRITICAL, DomainInfo,
  2018. "Cannot initialize NT BDC list: 0x%lx\n",
  2019. Status ));
  2020. NetStatus = NetpNtStatusToApiStatus( Status );
  2021. goto Cleanup;
  2022. }
  2023. //
  2024. // Flag that we don't need to run this code again.
  2025. //
  2026. DomainInfo->DomFlags |= DOM_PROMOTED_BEFORE;
  2027. }
  2028. LeaveCriticalSection( &NlGlobalDomainCritSect );
  2029. //
  2030. // Free the list of failed user logons that could
  2031. // exist if this machine was a BDC
  2032. //
  2033. LOCK_TRUST_LIST( DomainInfo );
  2034. while ( !IsListEmpty(&DomainInfo->DomFailedUserLogonList) ) {
  2035. ListEntry = RemoveHeadList( &DomainInfo->DomFailedUserLogonList );
  2036. //
  2037. // Free the logon structure
  2038. //
  2039. LocalFree( CONTAINING_RECORD(ListEntry, NL_FAILED_USER_LOGON, FuNext) );
  2040. }
  2041. UNLOCK_TRUST_LIST( DomainInfo );
  2042. }
  2043. //
  2044. // Tell the browser and the SMB server about our new role.
  2045. //
  2046. // Do this before the NetpLogonGetDCName since this registers the computer
  2047. // name of an hosted domain in the browser allowing the response from
  2048. // the PDC to be heard.
  2049. //
  2050. NlBrowserUpdate( DomainInfo, DomainInfo->DomRole );
  2051. //
  2052. // Check to see if the PDC is up and running.
  2053. //
  2054. // When NetpDcGetName is called from netlogon,
  2055. // it has both the Netbios and DNS domain name available for the primary
  2056. // domain. That can trick DsGetDcName into returning DNS host name of a
  2057. // DC in the primary domain. However, on IPX only systems, that won't work.
  2058. // Avoid that problem by not passing the DNS domain name of the primary domain
  2059. // if there are no DNS servers.
  2060. //
  2061. // Avoid having anything locked while calling NetpDcGetName.
  2062. // It calls back into Netlogon and locks heaven only knows what.
  2063. CapturedDomainGuid = NlCaptureDomainInfo( DomainInfo,
  2064. CapturedDnsDomainName,
  2065. &CapturedDomainGuidBuffer );
  2066. NlCaptureDnsForestName( CapturedDnsForestName );
  2067. NetStatus = NetpDcGetName(
  2068. DomainInfo,
  2069. DomainInfo->DomUnicodeComputerNameString.Buffer,
  2070. NULL, // No account name
  2071. 0, // No account control bits
  2072. DomainInfo->DomUnicodeDomainName,
  2073. NlDnsHasDnsServers() ? CapturedDnsDomainName : NULL,
  2074. CapturedDnsForestName,
  2075. DomainInfo->DomAccountDomainId,
  2076. CapturedDomainGuid,
  2077. NULL, // Site name not needed for PDC query
  2078. DS_FORCE_REDISCOVERY |
  2079. DS_PDC_REQUIRED |
  2080. DS_AVOID_SELF, // Avoid responding to this call ourself
  2081. InternalFlags,
  2082. NL_DC_MAX_TIMEOUT + NlGlobalParameters.ExpectedDialupDelay*1000,
  2083. MAX_DC_RETRIES,
  2084. NULL,
  2085. &DomainControllerCacheEntry );
  2086. //
  2087. // If we've been asked to terminate,
  2088. // do so.
  2089. //
  2090. if ( (DomainInfo->DomFlags & DOM_THREAD_TERMINATE) != 0 ||
  2091. NlGlobalTerminate ) {
  2092. NlPrintDom(( NL_DOMAIN, DomainInfo,
  2093. "Domain thread asked to terminate\n"));
  2094. NetStatus = ERROR_OPERATION_ABORTED;
  2095. goto Cleanup;
  2096. }
  2097. //
  2098. // Handle the case where the PDC isn't up.
  2099. //
  2100. if ( NetStatus != NERR_Success) {
  2101. //
  2102. // Handle starting a BDC when there is no current primary in
  2103. // this domain.
  2104. //
  2105. if ( DomainInfo->DomRole == RoleBackup ) {
  2106. // ??: Log hosted domain name with this message
  2107. NlpWriteEventlog( SERVICE_UIC_M_NETLOGON_NO_DC,
  2108. EVENTLOG_WARNING_TYPE,
  2109. NULL,
  2110. 0,
  2111. NULL,
  2112. 0 );
  2113. //
  2114. // Start normally but defer authentication with the
  2115. // primary until it starts.
  2116. //
  2117. }
  2118. //
  2119. // There is a primary dc running in this domain
  2120. //
  2121. } else {
  2122. //
  2123. // Since there already is a primary in the domain,
  2124. // we cannot become the primary.
  2125. //
  2126. if ( DomainInfo->DomRole == RolePrimary) {
  2127. //
  2128. // Don't worry if this is a BDC telling us that we're the PDC.
  2129. //
  2130. if ( (DomainControllerCacheEntry->UnicodeNetbiosDcName != NULL) &&
  2131. NlNameCompare( DomainInfo->DomUnicodeComputerNameString.Buffer,
  2132. DomainControllerCacheEntry->UnicodeNetbiosDcName,
  2133. NAMETYPE_COMPUTER) != 0 ){
  2134. LPWSTR AlertStrings[2];
  2135. //
  2136. // alert admin.
  2137. //
  2138. AlertStrings[0] = DomainControllerCacheEntry->UnicodeNetbiosDcName;
  2139. AlertStrings[1] = NULL; // Needed for RAISE_ALERT_TOO
  2140. // ??: Log hosted domain name with this message
  2141. // ??: Log the name of the other PDC (Put it in message too)
  2142. NlpWriteEventlog( SERVICE_UIC_M_NETLOGON_DC_CFLCT,
  2143. EVENTLOG_ERROR_TYPE,
  2144. NULL,
  2145. 0,
  2146. AlertStrings,
  2147. 1 | NETP_RAISE_ALERT_TOO );
  2148. NetStatus = SERVICE_UIC_M_NETLOGON_DC_CFLCT;
  2149. goto Done;
  2150. }
  2151. //
  2152. // If we're a BDC in the domain,
  2153. // sanity check the PDC.
  2154. //
  2155. } else {
  2156. //
  2157. // Indicate that there is a primary to connect to.
  2158. //
  2159. PdcToConnectTo = TRUE;
  2160. }
  2161. }
  2162. //
  2163. // Tell SAM/LSA about the new role
  2164. //
  2165. (VOID) NlUpdateDatabaseRole( DomainInfo, DomainInfo->DomRole );
  2166. }
  2167. //
  2168. // Ensure there is only one hosted domain.
  2169. //
  2170. NlAssert( IsPrimaryDomain( DomainInfo ) );
  2171. EnterCriticalSection( &NlGlobalReplicatorCritSect );
  2172. ReplLocked = TRUE;
  2173. //
  2174. // If we're to replicate to NT 4 BDC's,
  2175. // remember that.
  2176. //
  2177. if ( NewPdcDoReplication != NlGlobalPdcDoReplication ) {
  2178. NlGlobalPdcDoReplication = NewPdcDoReplication;
  2179. if ( NlGlobalPdcDoReplication ) {
  2180. NlPrintDom((NL_DOMAIN, DomainInfo,
  2181. "Setting this machine to be a PDC that replicates to NT 4 BDCs\n" ));
  2182. //
  2183. // Update the NlGlobalDBInfoArray for the various databases.
  2184. //
  2185. for ( i = 0; i < NUM_DBS; i++ ) {
  2186. if ( i == LSA_DB) {
  2187. //
  2188. // Initialize LSA database info.
  2189. //
  2190. Status = NlInitLsaDBInfo( DomainInfo, LSA_DB );
  2191. if ( !NT_SUCCESS(Status) ) {
  2192. NlPrintDom(( NL_CRITICAL, DomainInfo,
  2193. "Cannot NlInitLsaDBInfo %lx\n",
  2194. Status ));
  2195. NetStatus = NetpNtStatusToApiStatus( Status );
  2196. goto Cleanup;
  2197. }
  2198. } else {
  2199. //
  2200. // Initialize the Sam domain.
  2201. //
  2202. Status = NlInitSamDBInfo( DomainInfo, i );
  2203. if ( !NT_SUCCESS(Status) ) {
  2204. NlPrintDom(( NL_CRITICAL, DomainInfo,
  2205. "Cannot NlInitSamDBInfo (%ws) %lx\n",
  2206. NlGlobalDBInfoArray[i].DBName,
  2207. Status ));
  2208. NetStatus = NetpNtStatusToApiStatus( Status );
  2209. goto Cleanup;
  2210. }
  2211. }
  2212. }
  2213. }
  2214. }
  2215. //
  2216. // If we haven't done so already,
  2217. // setup a session to the PDC.
  2218. //
  2219. if ( DomainInfo->DomRole == RoleBackup && PdcToConnectTo ) {
  2220. PCLIENT_SESSION ClientSession;
  2221. //
  2222. // On a BDC, set up a session to the PDC now.
  2223. //
  2224. ClientSession = NlRefDomClientSession( DomainInfo );
  2225. if ( ClientSession != NULL ) {
  2226. if ( NlTimeoutSetWriterClientSession(
  2227. ClientSession,
  2228. WRITER_WAIT_PERIOD )) {
  2229. if ( ClientSession->CsState != CS_AUTHENTICATED ) {
  2230. NET_API_STATUS TmpNetStatus;
  2231. //
  2232. // Reset the current DC.
  2233. //
  2234. NlSetStatusClientSession( ClientSession, STATUS_NO_LOGON_SERVERS );
  2235. //
  2236. // Set the PDC info in the Client Session structure.
  2237. //
  2238. TmpNetStatus = NlSetServerClientSession(
  2239. ClientSession,
  2240. DomainControllerCacheEntry,
  2241. FALSE, // was not discovery with account
  2242. FALSE ); // not the session refresh
  2243. if ( TmpNetStatus == NO_ERROR ) {
  2244. //
  2245. // NT 5 BDCs only support NT 5 PDCs
  2246. //
  2247. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  2248. ClientSession->CsDiscoveryFlags |= CS_DISCOVERY_HAS_DS|CS_DISCOVERY_IS_CLOSE;
  2249. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  2250. //
  2251. // Setup a session to the PDC.
  2252. //
  2253. // Avoid this step if we are in the process of starting
  2254. // when we run in the main thread where we don't want to
  2255. // hang on indefinitely long RPC calls made during the
  2256. // session setup
  2257. //
  2258. if ( NlGlobalChangeLogNetlogonState != NetlogonStarting ) {
  2259. (VOID) NlSessionSetup( ClientSession );
  2260. // NlSessionSetup logged the error.
  2261. }
  2262. }
  2263. }
  2264. NlResetWriterClientSession( ClientSession );
  2265. }
  2266. NlUnrefClientSession( ClientSession );
  2267. }
  2268. }
  2269. //
  2270. // If we're a normal BDC
  2271. // we delete the change log to prevent confusion if we ever get promoted.
  2272. //
  2273. if ( IsPrimaryDomain(DomainInfo) ) {
  2274. if ( DomainInfo->DomRole == RoleBackup ) {
  2275. wcscpy( ChangeLogFile, NlGlobalChangeLogFilePrefix );
  2276. wcscat( ChangeLogFile, CHANGELOG_FILE_POSTFIX );
  2277. if ( DeleteFileW( ChangeLogFile ) ) {
  2278. NlPrintDom(( NL_DOMAIN, DomainInfo,
  2279. "NlUpdateRole: Deleted change log since this is now a BDC.\n" ));
  2280. }
  2281. }
  2282. //
  2283. // Delete the redo log.
  2284. // (NT 5 doesn't use the redo log any more. This is simply cleanup.)
  2285. //
  2286. wcscpy( ChangeLogFile, NlGlobalChangeLogFilePrefix );
  2287. wcscat( ChangeLogFile, REDO_FILE_POSTFIX );
  2288. if ( DeleteFileW( ChangeLogFile ) ) {
  2289. NlPrintDom(( NL_DOMAIN, DomainInfo,
  2290. "NlUpdateRole: Deleted redo log since NT 5 doesn't use it.\n" ));
  2291. }
  2292. }
  2293. //
  2294. // Register the appropriate DNS names for this role.
  2295. //
  2296. // Avoid this operation at service startup (the appropriate service
  2297. // notifications or timer expire will trigger DNS updates in the
  2298. // main loop instead). These registrations can be lengthy and we
  2299. // don't want to spend too much time on start up. Also, these DNS
  2300. // updates may be secure which will result in calls into Kerberos
  2301. // that may not be started yet on startup.
  2302. //
  2303. if ( NlGlobalChangeLogNetlogonState != NetlogonStarting ) {
  2304. NetStatus = NlDnsAddDomainRecords( DomainInfo, 0 );
  2305. //
  2306. // On success, scavenge through the list
  2307. // of records and update DNS in a worker thread
  2308. //
  2309. if ( NetStatus != NO_ERROR ) {
  2310. NlPrintDom(( NL_CRITICAL, DomainInfo,
  2311. "NlUpdateRole: Couldn't register DNS names %ld\n", NetStatus ));
  2312. goto Cleanup;
  2313. } else {
  2314. NlDnsForceScavenge( FALSE, // don't refresh domain records: we've done it already
  2315. FALSE ); // don't force re-register
  2316. }
  2317. }
  2318. NetStatus = NERR_Success;
  2319. goto Done;
  2320. Cleanup: {
  2321. LPWSTR MsgStrings[1];
  2322. NlPrintDom((NL_CRITICAL, DomainInfo,
  2323. "NlUpdateRole Failed %ld",
  2324. NetStatus ));
  2325. MsgStrings[0] = (LPWSTR) ULongToPtr( NetStatus );
  2326. // ??: Log hosted domain name with this message
  2327. NlpWriteEventlog( NELOG_NetlogonSystemError,
  2328. EVENTLOG_ERROR_TYPE,
  2329. (LPBYTE)&NetStatus,
  2330. sizeof(NetStatus),
  2331. MsgStrings,
  2332. 1 | NETP_LAST_MESSAGE_IS_NETSTATUS );
  2333. }
  2334. //
  2335. // All done
  2336. //
  2337. Done:
  2338. //
  2339. // If the operation failed,
  2340. // indicate that we need to try again periodically.
  2341. //
  2342. if ( NetStatus != NO_ERROR ) {
  2343. DomainInfo->DomRole = RoleInvalid;
  2344. }
  2345. if ( DomainControllerCacheEntry != NULL) {
  2346. NetpDcDerefCacheEntry( DomainControllerCacheEntry );
  2347. }
  2348. if ( ReplLocked ) {
  2349. LeaveCriticalSection( &NlGlobalReplicatorCritSect );
  2350. }
  2351. if ( AllocatedBuffer != NULL ) {
  2352. LocalFree( AllocatedBuffer );
  2353. }
  2354. return NetStatus;
  2355. }
  2356. #endif // _DC_NETLOGON
  2357. NET_API_STATUS
  2358. NlCreateDomainPhase1(
  2359. IN LPWSTR DomainName OPTIONAL,
  2360. IN LPWSTR DnsDomainName OPTIONAL,
  2361. IN PSID DomainSid OPTIONAL,
  2362. IN GUID *DomainGuid OPTIONAL,
  2363. IN LPWSTR ComputerName,
  2364. IN LPWSTR DnsHostName OPTIONAL,
  2365. IN BOOLEAN CallNlExitOnFailure,
  2366. IN ULONG DomainFlags,
  2367. OUT PDOMAIN_INFO *ReturnedDomainInfo
  2368. )
  2369. /*++
  2370. Routine Description:
  2371. Create a new domain object to the point where the remainder of the object
  2372. can be created asynchronously in a domain specific worker thread.
  2373. Arguments:
  2374. DomainName - Netbios Name of the domain to host.
  2375. DnsDomainName - DNS name of the domain to host.
  2376. NULL if the domain has no DNS Domain Name.
  2377. DomainSid - DomainSid of the specified domain.
  2378. DomainGuid - GUID of the specified domain.
  2379. ComputerName - Name of this computer in the specified domain.
  2380. NULL if not the primary domain for the DC.
  2381. DnsHostName - DNS Host name of this computer in the specified domain.
  2382. NULL if the domain has no DNS host name or if not the primary domain
  2383. for the DC.
  2384. CallNlExitOnFailure - TRUE if NlExit should be called on failure.
  2385. DomainFlags - Specifies proporties of this domain such as primary domain,
  2386. non-domain NC, forest entry.
  2387. ReturnedDomainInfo - On success, returns a pointer to a referenced DomainInfo
  2388. structure. It is the callers responsibility to call NlDereferenceDomain.
  2389. Return Value:
  2390. Status of operation.
  2391. --*/
  2392. {
  2393. NTSTATUS Status;
  2394. NET_API_STATUS NetStatus;
  2395. BOOLEAN CanCallNlDeleteDomain = FALSE;
  2396. PDOMAIN_INFO DomainInfo = NULL;
  2397. DWORD DomainSidSize = 0;
  2398. LPBYTE Where;
  2399. ULONG i;
  2400. DWORD DomFlags = 0;
  2401. BOOL DomainCreated = FALSE;
  2402. //
  2403. // Initialization
  2404. //
  2405. EnterCriticalSection(&NlGlobalDomainCritSect);
  2406. NlPrint(( NL_DOMAIN, "%ws: Adding new domain\n",
  2407. (DomainName != NULL) ? DomainName : DnsDomainName ));
  2408. if ( DomainSid != NULL ) {
  2409. DomainSidSize = RtlLengthSid( DomainSid );
  2410. }
  2411. //
  2412. // See if the domain already exists.
  2413. //
  2414. if ( DomainName != NULL ) {
  2415. DomainInfo = NlFindNetbiosDomain( DomainName, FALSE );
  2416. } else if ( DnsDomainName != NULL ) {
  2417. LPSTR Utf8DnsDomainName = NetpAllocUtf8StrFromWStr( DnsDomainName );
  2418. if ( Utf8DnsDomainName == NULL ) {
  2419. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2420. if ( CallNlExitOnFailure ) {
  2421. NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL );
  2422. }
  2423. goto Cleanup;
  2424. }
  2425. DomainInfo = NlFindDnsDomain( Utf8DnsDomainName,
  2426. DomainGuid,
  2427. TRUE, // look up NDNCs too
  2428. FALSE, // don't check alias names
  2429. NULL ); // don't care if alias name matched
  2430. NetpMemoryFree( Utf8DnsDomainName );
  2431. }
  2432. if ( DomainInfo != NULL ) {
  2433. DomainCreated = FALSE;
  2434. #ifdef _DC_NETLOGON
  2435. DomainInfo->DomFlags &= ~DOM_DOMAIN_REFRESH_PENDING;
  2436. #endif // _DC_NETLOGON
  2437. } else {
  2438. DomainCreated = TRUE;
  2439. //
  2440. // Allocate a structure describing the new domain.
  2441. //
  2442. DomainInfo = LocalAlloc(
  2443. LMEM_ZEROINIT,
  2444. ROUND_UP_COUNT( sizeof(DOMAIN_INFO), ALIGN_DWORD) +
  2445. DomainSidSize );
  2446. if ( DomainInfo == NULL ) {
  2447. NetStatus = GetLastError();
  2448. if ( CallNlExitOnFailure ) {
  2449. NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
  2450. }
  2451. goto Cleanup;
  2452. }
  2453. //
  2454. // Create an interim reference count for this domain.
  2455. // (Once for the reference by this routine.)
  2456. //
  2457. DomainInfo->ReferenceCount = 1;
  2458. NlGlobalServicedDomainCount ++;
  2459. #ifdef _DC_NETLOGON
  2460. //
  2461. // Set the domain flags
  2462. //
  2463. DomainInfo->DomFlags |= DomainFlags;
  2464. if ( DomainInfo->DomFlags & DOM_PRIMARY_DOMAIN ) {
  2465. NlGlobalDomainInfo = DomainInfo;
  2466. }
  2467. //
  2468. // Set the role we play in this domain
  2469. //
  2470. if ( NlGlobalMemberWorkstation ) {
  2471. DomainInfo->DomRole = RoleMemberWorkstation;
  2472. } else if ( DomainInfo->DomFlags & DOM_NON_DOMAIN_NC ) {
  2473. DomainInfo->DomRole = RoleNdnc;
  2474. } else if ( DomainInfo->DomFlags & DOM_REAL_DOMAIN ) {
  2475. DomainInfo->DomRole = RoleInvalid; // For real domains, force the role update
  2476. }
  2477. #endif // _DC_NETLOGON
  2478. //
  2479. // Initialize other constants.
  2480. //
  2481. RtlInitUnicodeString( &DomainInfo->DomUnicodeComputerNameString, NULL );
  2482. InitializeListHead(&DomainInfo->DomNext);
  2483. #ifdef _DC_NETLOGON
  2484. InitializeListHead( &DomainInfo->DomTrustList );
  2485. InitializeListHead( &DomainInfo->DomServerSessionTable );
  2486. InitializeListHead( &DomainInfo->DomFailedUserLogonList );
  2487. #endif // _DC_NETLOGON
  2488. NlInitializeWorkItem(&DomainInfo->DomThreadWorkItem, NlDomainThread, DomainInfo);
  2489. try {
  2490. InitializeCriticalSection( &DomainInfo->DomTrustListCritSect );
  2491. #ifdef _DC_NETLOGON
  2492. InitializeCriticalSection( &DomainInfo->DomServerSessionTableCritSect );
  2493. #endif // _DC_NETLOGON
  2494. } except( EXCEPTION_EXECUTE_HANDLER ) {
  2495. NlPrint(( NL_CRITICAL, "%ws: Cannot InitializeCriticalSections for domain\n",
  2496. DomainName ));
  2497. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2498. if ( CallNlExitOnFailure ) {
  2499. NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL );
  2500. }
  2501. goto Cleanup;
  2502. }
  2503. //
  2504. // If the caller passed in a ComputerName,
  2505. // use it.
  2506. //
  2507. if ( ComputerName != NULL ) {
  2508. NetStatus = NlSetComputerName( DomainInfo, ComputerName, DnsHostName );
  2509. if ( NetStatus != NERR_Success ) {
  2510. NlPrint(( NL_CRITICAL,
  2511. "%ws: Cannot set ComputerName\n",
  2512. DomainName ));
  2513. if ( CallNlExitOnFailure ) {
  2514. NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL );
  2515. }
  2516. goto Cleanup;
  2517. }
  2518. }
  2519. //
  2520. // Copy the domain id onto the end of the allocated buffer.
  2521. // (ULONG aligned)
  2522. //
  2523. Where = (LPBYTE)(DomainInfo+1);
  2524. Where = ROUND_UP_POINTER( Where, ALIGN_DWORD );
  2525. if ( DomainSid != NULL ) {
  2526. RtlCopyMemory( Where, DomainSid, DomainSidSize );
  2527. DomainInfo->DomAccountDomainId = (PSID) Where;
  2528. Where += DomainSidSize;
  2529. }
  2530. //
  2531. // Set the domain names in the structure.
  2532. //
  2533. NetStatus = NlSetDomainNameInDomainInfo( DomainInfo, DnsDomainName, DomainName, DomainGuid, NULL, NULL, NULL );
  2534. if ( NetStatus != NERR_Success ) {
  2535. NlPrint(( NL_CRITICAL,
  2536. "%ws: Cannot set DnsDomainName\n",
  2537. DomainName ));
  2538. if ( CallNlExitOnFailure ) {
  2539. NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL );
  2540. }
  2541. goto Cleanup;
  2542. }
  2543. //
  2544. // Open the LSA for real domain
  2545. //
  2546. // ?? I'll need to identify which hosted domain here.
  2547. if ( DomainInfo->DomFlags & DOM_REAL_DOMAIN ) {
  2548. Status = LsaIOpenPolicyTrusted( &DomainInfo->DomLsaPolicyHandle );
  2549. if ( !NT_SUCCESS(Status) ) {
  2550. NlPrint((NL_CRITICAL,
  2551. "%ws: Can't LsaIOpenPolicyTrusted: 0x%lx.\n",
  2552. DomainName,
  2553. Status ));
  2554. NetStatus = NetpNtStatusToApiStatus(Status);
  2555. if ( CallNlExitOnFailure ) {
  2556. NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogError, NULL);
  2557. }
  2558. goto Cleanup;
  2559. }
  2560. //
  2561. // Open Sam
  2562. //
  2563. // ?? I'll need to identify which hosted domain here.
  2564. //
  2565. Status = SamIConnect(
  2566. NULL, // No server name
  2567. &DomainInfo->DomSamServerHandle,
  2568. 0, // Ignore desired access
  2569. TRUE ); // Trusted client
  2570. if ( !NT_SUCCESS(Status) ) {
  2571. NlPrint((NL_CRITICAL,
  2572. "%ws: Can't SamIConnect: 0x%lx.\n",
  2573. DomainName,
  2574. Status ));
  2575. NetStatus = NetpNtStatusToApiStatus(Status);
  2576. if ( CallNlExitOnFailure ) {
  2577. NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogError, NULL);
  2578. }
  2579. goto Cleanup;
  2580. }
  2581. //
  2582. // Open the Account domain.
  2583. //
  2584. Status = SamrOpenDomain( DomainInfo->DomSamServerHandle,
  2585. DOMAIN_ALL_ACCESS,
  2586. DomainInfo->DomAccountDomainId,
  2587. &DomainInfo->DomSamAccountDomainHandle );
  2588. if ( !NT_SUCCESS(Status) ) {
  2589. NlPrint(( NL_CRITICAL,
  2590. "%ws: ACCOUNT: Cannot SamrOpenDomain: %lx\n",
  2591. DomainName,
  2592. Status ));
  2593. DomainInfo->DomSamAccountDomainHandle = NULL;
  2594. NetStatus = NetpNtStatusToApiStatus(Status);
  2595. if ( CallNlExitOnFailure ) {
  2596. NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogError, NULL);
  2597. }
  2598. goto Cleanup;
  2599. }
  2600. //
  2601. // Open the Builtin domain.
  2602. //
  2603. Status = SamrOpenDomain( DomainInfo->DomSamServerHandle,
  2604. DOMAIN_ALL_ACCESS,
  2605. BuiltinDomainSid,
  2606. &DomainInfo->DomSamBuiltinDomainHandle );
  2607. if ( !NT_SUCCESS(Status) ) {
  2608. NlPrint(( NL_CRITICAL,
  2609. "%ws: BUILTIN: Cannot SamrOpenDomain: %lx\n",
  2610. DomainName,
  2611. Status ));
  2612. DomainInfo->DomSamBuiltinDomainHandle = NULL;
  2613. NetStatus = NetpNtStatusToApiStatus(Status);
  2614. if ( CallNlExitOnFailure ) {
  2615. NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogError, NULL);
  2616. }
  2617. goto Cleanup;
  2618. }
  2619. }
  2620. }
  2621. //
  2622. // Only link the entry in if we just created it.
  2623. // Wait to link the entry in until it is fully initialized.
  2624. //
  2625. if ( DomainCreated ) {
  2626. //
  2627. // Link the domain into the appropriate list of domains
  2628. //
  2629. // Increment the reference count for being on the global list.
  2630. //
  2631. DomainInfo->ReferenceCount ++;
  2632. if ( DomainInfo->DomFlags & DOM_REAL_DOMAIN ) {
  2633. InsertTailList(&NlGlobalServicedDomains, &DomainInfo->DomNext);
  2634. } else if ( DomainInfo->DomFlags & DOM_NON_DOMAIN_NC ) {
  2635. InsertTailList(&NlGlobalServicedNdncs, &DomainInfo->DomNext);
  2636. }
  2637. CanCallNlDeleteDomain = TRUE;
  2638. }
  2639. NetStatus = NERR_Success;
  2640. //
  2641. // Free Locally used resources
  2642. //
  2643. Cleanup:
  2644. //
  2645. // Return a pointer to the DomainInfo struct to the caller.
  2646. //
  2647. if (NetStatus == NERR_Success) {
  2648. *ReturnedDomainInfo = DomainInfo;
  2649. //
  2650. // Cleanup on error.
  2651. //
  2652. } else {
  2653. //
  2654. // If we created the domain,
  2655. // handle deleting it.
  2656. //
  2657. if ( DomainCreated ) {
  2658. //
  2659. // If we've initialized to the point where we can call
  2660. // we can call NlDeleteDomain, do so.
  2661. //
  2662. if ( CanCallNlDeleteDomain ) {
  2663. DomainInfo->ReferenceCount --;
  2664. (VOID) NlDeleteDomain( DomainInfo );
  2665. }
  2666. }
  2667. //
  2668. // Dereference the domain on error.
  2669. //
  2670. if (DomainInfo != NULL) {
  2671. NlDereferenceDomain( DomainInfo );
  2672. }
  2673. }
  2674. LeaveCriticalSection(&NlGlobalDomainCritSect);
  2675. return NetStatus;
  2676. }
  2677. #ifdef _DC_NETLOGON
  2678. NET_API_STATUS
  2679. NlCreateDomainPhase2(
  2680. IN PDOMAIN_INFO DomainInfo,
  2681. IN BOOLEAN CallNlExitOnFailure
  2682. )
  2683. /*++
  2684. Routine Description:
  2685. Finish creating a new domain to host.
  2686. Phase 2 of creation is designed to be called from a worker thread. It
  2687. contains all time intensive portions of domain creation.
  2688. Arguments:
  2689. DomainInfo - Pointer to domain to finish creating.
  2690. CallNlExitOnFailure - TRUE if NlExit should be called on failure.
  2691. Return Value:
  2692. Status of operation.
  2693. If this is the primary domain for this DC, NlExit is called upon failure.
  2694. --*/
  2695. {
  2696. NTSTATUS Status;
  2697. NET_API_STATUS NetStatus;
  2698. ULONG i;
  2699. BOOL DomainCreated;
  2700. ULONG AccountRid = 0;
  2701. //
  2702. // Initialization
  2703. //
  2704. NlPrintDom(( NL_DOMAIN, DomainInfo,
  2705. "Create domain phase 2\n"));
  2706. #ifdef MULTIHOSTED_DOMAIN
  2707. //
  2708. // If a new computername is needed for this machine,
  2709. // assign one.
  2710. //
  2711. if ( DomainInfo->DomOemComputerNameLength == 0 ) {
  2712. NetStatus = NlAssignComputerName( DomainInfo );
  2713. if ( NetStatus != NERR_Success ) {
  2714. // ??: Write event
  2715. NlPrintDom((NL_CRITICAL, DomainInfo,
  2716. "can't NlAssignComputerName %ld.\n",
  2717. NetStatus ));
  2718. if ( CallNlExitOnFailure ) {
  2719. NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL );
  2720. }
  2721. goto Cleanup;
  2722. }
  2723. //
  2724. // If we've been asked to terminate,
  2725. // do so.
  2726. //
  2727. if ( (DomainInfo->DomFlags & DOM_THREAD_TERMINATE) != 0 ||
  2728. NlGlobalTerminate ) {
  2729. NlPrintDom(( NL_DOMAIN, DomainInfo,
  2730. "Domain thread asked to terminate\n"));
  2731. NetStatus = ERROR_OPERATION_ABORTED;
  2732. goto Cleanup;
  2733. }
  2734. }
  2735. #endif // MULTIHOSTED_DOMAIN
  2736. //
  2737. // Determine role from DS
  2738. //
  2739. NetStatus = NlUpdateRole( DomainInfo );
  2740. if ( NetStatus != NERR_Success ) {
  2741. //
  2742. // Having another PDC in the domain isn't fatal.
  2743. // (Continue running in the RoleInvalid state until the matter is
  2744. // resolved.)
  2745. //
  2746. if ( NetStatus != SERVICE_UIC_M_NETLOGON_DC_CFLCT ) {
  2747. NlPrintDom((NL_INIT, DomainInfo,
  2748. "Couldn't NlUpdateRole %ld 0x%lx.\n",
  2749. NetStatus, NetStatus ));
  2750. // NlUpdateRole logged the error.
  2751. if ( CallNlExitOnFailure ) {
  2752. NlExit( NELOG_NetlogonSystemError, NetStatus, DontLogError, NULL );
  2753. }
  2754. goto Cleanup;
  2755. }
  2756. }
  2757. //
  2758. // Determine the RID for our computer account
  2759. //
  2760. Status = NlSamOpenNamedUser( DomainInfo,
  2761. DomainInfo->DomClientSession->CsAccountName,
  2762. NULL,
  2763. &AccountRid,
  2764. NULL );
  2765. if ( !NT_SUCCESS(Status) ) {
  2766. NlPrintDom(( NL_CRITICAL, DomainInfo,
  2767. "Cannot NlSamOpenNamedUser 0x%lx\n",
  2768. Status ));
  2769. if ( CallNlExitOnFailure ) {
  2770. NlExit( SERVICE_UIC_M_DATABASE_ERROR, Status, LogErrorAndNtStatus, NULL);
  2771. }
  2772. NetStatus = NetpNtStatusToApiStatus( Status );
  2773. goto Cleanup;
  2774. }
  2775. NlAssert( AccountRid != 0 );
  2776. DomainInfo->DomDcComputerAccountRid = AccountRid;
  2777. //
  2778. // Determine the trust list from the LSA.
  2779. //
  2780. if ( !GiveInstallHints( FALSE ) ) {
  2781. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2782. goto Cleanup;
  2783. }
  2784. Status = NlInitTrustList( DomainInfo );
  2785. if ( !NT_SUCCESS(Status) ) {
  2786. NlPrintDom(( NL_CRITICAL, DomainInfo,
  2787. "Cannot NlInitTrustList %lX\n",
  2788. Status ));
  2789. NetStatus = NetpNtStatusToApiStatus( Status );
  2790. if ( CallNlExitOnFailure ) {
  2791. NlExit( NELOG_NetlogonFailedToUpdateTrustList, NetStatus, LogErrorAndNtStatus, NULL);
  2792. }
  2793. goto Cleanup;
  2794. }
  2795. NetStatus = NERR_Success;
  2796. //
  2797. // Free Locally used resources
  2798. //
  2799. Cleanup:
  2800. return NetStatus;
  2801. }
  2802. #endif // _DC_NETLOGON
  2803. GUID *
  2804. NlCaptureDomainInfo (
  2805. IN PDOMAIN_INFO DomainInfo,
  2806. OUT WCHAR DnsDomainName[NL_MAX_DNS_LENGTH+1] OPTIONAL,
  2807. OUT GUID *DomainGuid OPTIONAL
  2808. )
  2809. /*++
  2810. Routine Description:
  2811. Captures a copy of the DnsDomainName and domain GUID for a domain
  2812. Arguments:
  2813. DomainInfo - Specifies the hosted domain to return the DNS domain name for.
  2814. DnsDomainName - Returns the DNS name of the domain.
  2815. If there is none, an empty string is returned.
  2816. DomainGuid - Returns the domain GUID of the domain.
  2817. If there is none, a zero GUID is returned.
  2818. Return Value:
  2819. If there is a domain GUID, returns a pointer to the passed in DomainGuid buffer.
  2820. If not, returns NULL
  2821. --*/
  2822. {
  2823. GUID *ReturnGuid;
  2824. LOCK_TRUST_LIST( DomainInfo );
  2825. if ( ARGUMENT_PRESENT( DnsDomainName )) {
  2826. if ( DomainInfo->DomUnicodeDnsDomainName == NULL ) {
  2827. *DnsDomainName = L'\0';
  2828. } else {
  2829. wcscpy( DnsDomainName, DomainInfo->DomUnicodeDnsDomainName );
  2830. }
  2831. }
  2832. //
  2833. // If the caller wants the domain GUID to be returned,
  2834. // return it.
  2835. //
  2836. if ( ARGUMENT_PRESENT( DomainGuid )) {
  2837. *DomainGuid = DomainInfo->DomDomainGuidBuffer;
  2838. if ( DomainInfo->DomDomainGuid == NULL ) {
  2839. ReturnGuid = NULL;
  2840. } else {
  2841. ReturnGuid = DomainGuid;
  2842. }
  2843. } else {
  2844. ReturnGuid = NULL;
  2845. }
  2846. UNLOCK_TRUST_LIST( DomainInfo );
  2847. return ReturnGuid;
  2848. }
  2849. VOID
  2850. NlFreeDnsDomainDomainInfo(
  2851. IN PDOMAIN_INFO DomainInfo
  2852. )
  2853. /*++
  2854. Routine Description:
  2855. Frees the DNS domain in the DomainInfo structure.
  2856. Arguments:
  2857. DomainInfo - Domain to free the DNS domain name for.
  2858. Return Value:
  2859. Status of operation.
  2860. --*/
  2861. {
  2862. //
  2863. // Free the previous allocated block.
  2864. //
  2865. EnterCriticalSection(&NlGlobalDomainCritSect);
  2866. LOCK_TRUST_LIST( DomainInfo );
  2867. if ( DomainInfo->DomUnicodeDnsDomainName != NULL ) {
  2868. LocalFree( DomainInfo->DomUnicodeDnsDomainName );
  2869. }
  2870. if ( DomainInfo->DomUtf8DnsDomainNameAlias != NULL ) {
  2871. NetpMemoryFree( DomainInfo->DomUtf8DnsDomainNameAlias );
  2872. }
  2873. DomainInfo->DomUnicodeDnsDomainName = NULL;
  2874. DomainInfo->DomUtf8DnsDomainName = NULL;
  2875. DomainInfo->DomUtf8DnsDomainNameAlias = NULL;
  2876. DomainInfo->DomUnicodeDnsDomainNameString.Buffer = NULL;
  2877. DomainInfo->DomUnicodeDnsDomainNameString.MaximumLength = 0;
  2878. DomainInfo->DomUnicodeDnsDomainNameString.Length = 0;
  2879. UNLOCK_TRUST_LIST( DomainInfo );
  2880. LeaveCriticalSection(&NlGlobalDomainCritSect);
  2881. }
  2882. NET_API_STATUS
  2883. NlSetDomainForestRoot(
  2884. IN PDOMAIN_INFO DomainInfo,
  2885. IN PVOID Context
  2886. )
  2887. /*++
  2888. Routine Description:
  2889. The routine sets the DOM_FOREST_ROOT bit on the DomainInfo.
  2890. It simply compares the name of the domain with the name of the forest and sets the bit.
  2891. Arguments:
  2892. DomainInfo - The domain being set
  2893. Context - Not Used
  2894. Return Value:
  2895. Success (not used).
  2896. --*/
  2897. {
  2898. //
  2899. // Only set the bit if netlogon is running,
  2900. //
  2901. if ( NlGlobalDomainsInitialized ) {
  2902. EnterCriticalSection( &NlGlobalDnsForestNameCritSect );
  2903. EnterCriticalSection( &NlGlobalDomainCritSect );
  2904. if ( NlEqualDnsNameU( &NlGlobalUnicodeDnsForestNameString,
  2905. &DomainInfo->DomUnicodeDnsDomainNameString ) ) {
  2906. DomainInfo->DomFlags |= DOM_FOREST_ROOT;
  2907. } else {
  2908. DomainInfo->DomFlags &= ~DOM_FOREST_ROOT;
  2909. }
  2910. LeaveCriticalSection( &NlGlobalDomainCritSect );
  2911. LeaveCriticalSection( &NlGlobalDnsForestNameCritSect );
  2912. }
  2913. UNREFERENCED_PARAMETER( Context );
  2914. return NO_ERROR;
  2915. }
  2916. NET_API_STATUS
  2917. NlSetDomainNameInDomainInfo(
  2918. IN PDOMAIN_INFO DomainInfo,
  2919. IN LPWSTR DnsDomainName OPTIONAL,
  2920. IN LPWSTR NetbiosDomainName OPTIONAL,
  2921. IN GUID *DomainGuid OPTIONAL,
  2922. OUT PBOOLEAN DnsDomainNameChanged OPTIONAL,
  2923. OUT PBOOLEAN NetbiosDomainNameChanged OPTIONAL,
  2924. OUT PBOOLEAN DomainGuidChanged OPTIONAL
  2925. )
  2926. /*++
  2927. Routine Description:
  2928. Sets the DNS domain name into the DomainInfo structure.
  2929. Arguments:
  2930. DomainInfo - Domain to set the DNS domain name for.
  2931. DnsDomainName - DNS name of the domain to host.
  2932. NULL if the domain has no DNS Domain Name.
  2933. NetbiosDomainName - Netbios name of the domain to host.
  2934. NULL if the domain has no Netbios Domain Name.
  2935. DomainGuid - Guid of the domain to host.
  2936. NULL if the domain has no GUID.
  2937. DnsDomainNameChanged - Returns TRUE if the DNS domain name is different
  2938. than the current value.
  2939. NetbiosDomainNameChanged - Returns TRUE if the Netbios domain name is different
  2940. than the current value.
  2941. DomainGuidChanged - Returns TRUE if the domain GUID is different
  2942. than the current value.
  2943. Return Value:
  2944. Status of operation.
  2945. --*/
  2946. {
  2947. NTSTATUS Status;
  2948. NET_API_STATUS NetStatus;
  2949. DWORD UnicodeDnsDomainNameSize;
  2950. LPSTR Utf8DnsDomainName = NULL;
  2951. DWORD Utf8DnsDomainNameSize;
  2952. LPBYTE Where;
  2953. ULONG i;
  2954. LPBYTE AllocatedBlock = NULL;
  2955. BOOLEAN LocalDnsDomainNameChanged = FALSE;
  2956. //
  2957. // Initialization
  2958. //
  2959. if ( ARGUMENT_PRESENT( DnsDomainNameChanged) ) {
  2960. *DnsDomainNameChanged = FALSE;
  2961. }
  2962. if ( ARGUMENT_PRESENT( NetbiosDomainNameChanged) ) {
  2963. *NetbiosDomainNameChanged = FALSE;
  2964. }
  2965. if ( ARGUMENT_PRESENT( DomainGuidChanged ) ) {
  2966. *DomainGuidChanged = FALSE;
  2967. }
  2968. //
  2969. // Copy the Netbios domain name into the structure if it has changed.
  2970. //
  2971. // ?? The below assumes that for real domains Netbios domain name
  2972. // cannot change to NULL. This needs to be revisited when/if we go
  2973. // Netbios-less.
  2974. //
  2975. EnterCriticalSection(&NlGlobalDomainCritSect);
  2976. LOCK_TRUST_LIST( DomainInfo );
  2977. if ( NetbiosDomainName != NULL &&
  2978. NlNameCompare( NetbiosDomainName,
  2979. DomainInfo->DomUnicodeDomainName,
  2980. NAMETYPE_DOMAIN ) != 0 ) {
  2981. NlPrintDom(( NL_DOMAIN, DomainInfo,
  2982. "Setting Netbios domain name to %ws\n", NetbiosDomainName ));
  2983. NetStatus = I_NetNameCanonicalize(
  2984. NULL,
  2985. NetbiosDomainName,
  2986. DomainInfo->DomUnicodeDomainName,
  2987. sizeof(DomainInfo->DomUnicodeDomainName),
  2988. NAMETYPE_DOMAIN,
  2989. 0 );
  2990. if ( NetStatus != NERR_Success ) {
  2991. NlPrint(( NL_CRITICAL, "%ws: DomainName is invalid\n", NetbiosDomainName ));
  2992. goto Cleanup;
  2993. }
  2994. RtlInitUnicodeString( &DomainInfo->DomUnicodeDomainNameString,
  2995. DomainInfo->DomUnicodeDomainName );
  2996. Status = RtlUpcaseUnicodeToOemN( DomainInfo->DomOemDomainName,
  2997. sizeof(DomainInfo->DomOemDomainName),
  2998. &DomainInfo->DomOemDomainNameLength,
  2999. DomainInfo->DomUnicodeDomainNameString.Buffer,
  3000. DomainInfo->DomUnicodeDomainNameString.Length);
  3001. if (!NT_SUCCESS(Status)) {
  3002. NlPrint(( NL_CRITICAL, "%ws: Unable to convert Domain name to OEM 0x%lx\n", DomainName, Status ));
  3003. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  3004. goto Cleanup;
  3005. }
  3006. DomainInfo->DomOemDomainName[DomainInfo->DomOemDomainNameLength] = '\0';
  3007. //
  3008. // Set the account domain.
  3009. //
  3010. if ( NlGlobalMemberWorkstation ) {
  3011. DomainInfo->DomUnicodeAccountDomainNameString =
  3012. DomainInfo->DomUnicodeComputerNameString;
  3013. } else {
  3014. DomainInfo->DomUnicodeAccountDomainNameString =
  3015. DomainInfo->DomUnicodeDomainNameString;
  3016. }
  3017. //
  3018. // Tell the caller that the name has changed.
  3019. //
  3020. if ( ARGUMENT_PRESENT( NetbiosDomainNameChanged) ) {
  3021. *NetbiosDomainNameChanged = TRUE;
  3022. }
  3023. }
  3024. //
  3025. // If the new name is the same as the old name,
  3026. // avoid setting the name.
  3027. //
  3028. if ( !NlEqualDnsName( DnsDomainName, DomainInfo->DomUnicodeDnsDomainName )) {
  3029. NlPrintDom(( NL_DOMAIN, DomainInfo,
  3030. "Setting DNS domain name to %ws\n", DnsDomainName ));
  3031. //
  3032. // Convert the DNS domain name to the various forms.
  3033. //
  3034. if ( DnsDomainName != NULL ) {
  3035. ULONG NameLen = wcslen(DnsDomainName);
  3036. if ( NameLen > NL_MAX_DNS_LENGTH ) {
  3037. NetStatus = ERROR_INVALID_DOMAINNAME;
  3038. goto Cleanup;
  3039. }
  3040. UnicodeDnsDomainNameSize = NameLen * sizeof(WCHAR) + sizeof(WCHAR);
  3041. Utf8DnsDomainName = NetpAllocUtf8StrFromWStr( DnsDomainName );
  3042. if ( Utf8DnsDomainName == NULL ) {
  3043. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  3044. goto Cleanup;
  3045. }
  3046. Utf8DnsDomainNameSize = strlen(Utf8DnsDomainName) + 1;
  3047. if ( (Utf8DnsDomainNameSize-1) > NL_MAX_DNS_LENGTH ) {
  3048. NetStatus = ERROR_INVALID_DOMAINNAME;
  3049. goto Cleanup;
  3050. }
  3051. } else {
  3052. UnicodeDnsDomainNameSize = 0;
  3053. Utf8DnsDomainNameSize = 0;
  3054. }
  3055. //
  3056. // Allocate a new block for the names.
  3057. //
  3058. if ( UnicodeDnsDomainNameSize != 0 ) {
  3059. AllocatedBlock = LocalAlloc(
  3060. 0,
  3061. UnicodeDnsDomainNameSize +
  3062. Utf8DnsDomainNameSize );
  3063. if ( AllocatedBlock == NULL ) {
  3064. NetStatus = GetLastError();
  3065. goto Cleanup;
  3066. }
  3067. Where = AllocatedBlock;
  3068. }
  3069. //
  3070. // Free the previous allocated block.
  3071. //
  3072. NlFreeDnsDomainDomainInfo( DomainInfo );
  3073. //
  3074. // Copy the Unicode DNS Domain name after that.
  3075. // (WCHAR aligned)
  3076. //
  3077. if ( UnicodeDnsDomainNameSize != 0 ) {
  3078. RtlCopyMemory( Where, DnsDomainName, UnicodeDnsDomainNameSize );
  3079. DomainInfo->DomUnicodeDnsDomainName = (LPWSTR) Where;
  3080. DomainInfo->DomUnicodeDnsDomainNameString.Buffer = (LPWSTR) Where;
  3081. DomainInfo->DomUnicodeDnsDomainNameString.MaximumLength = (USHORT) UnicodeDnsDomainNameSize;
  3082. DomainInfo->DomUnicodeDnsDomainNameString.Length = (USHORT)UnicodeDnsDomainNameSize - sizeof(WCHAR);
  3083. Where += UnicodeDnsDomainNameSize;
  3084. //
  3085. // Copy the Utf8 DNS Domain name after that.
  3086. // (byte aligned)
  3087. //
  3088. if ( Utf8DnsDomainNameSize != 0 ) {
  3089. RtlCopyMemory( Where, Utf8DnsDomainName, Utf8DnsDomainNameSize );
  3090. DomainInfo->DomUtf8DnsDomainName = Where;
  3091. Where += Utf8DnsDomainNameSize;
  3092. }
  3093. }
  3094. //
  3095. // Tell the caller that the name has changed.
  3096. //
  3097. LocalDnsDomainNameChanged = TRUE;
  3098. if ( ARGUMENT_PRESENT( DnsDomainNameChanged) ) {
  3099. *DnsDomainNameChanged = TRUE;
  3100. }
  3101. }
  3102. //
  3103. // Copy the domain GUID if it has changed.
  3104. //
  3105. if ( DomainGuid != NULL || DomainInfo->DomDomainGuid != NULL) {
  3106. if ( (DomainGuid == NULL && DomainInfo->DomDomainGuid != NULL) ||
  3107. (DomainGuid != NULL && DomainInfo->DomDomainGuid == NULL) ||
  3108. !IsEqualGUID( DomainGuid, DomainInfo->DomDomainGuid ) ) {
  3109. //
  3110. // Set the domain GUID.
  3111. //
  3112. NlPrintDom(( NL_DOMAIN, DomainInfo,
  3113. "Setting Domain GUID to " ));
  3114. NlpDumpGuid( NL_DOMAIN, DomainGuid );
  3115. NlPrint(( NL_DOMAIN, "\n" ));
  3116. if ( DomainGuid != NULL ) {
  3117. DomainInfo->DomDomainGuidBuffer = *DomainGuid;
  3118. DomainInfo->DomDomainGuid = &DomainInfo->DomDomainGuidBuffer;
  3119. } else {
  3120. RtlZeroMemory( &DomainInfo->DomDomainGuidBuffer, sizeof( DomainInfo->DomDomainGuidBuffer ) );
  3121. DomainInfo->DomDomainGuid = NULL;
  3122. }
  3123. //
  3124. // Tell the caller that the GUID has changed.
  3125. //
  3126. if ( ARGUMENT_PRESENT( DomainGuidChanged ) ) {
  3127. *DomainGuidChanged = TRUE;
  3128. }
  3129. }
  3130. }
  3131. NetStatus = NO_ERROR;
  3132. //
  3133. // Free any locally used resources.
  3134. //
  3135. Cleanup:
  3136. UNLOCK_TRUST_LIST( DomainInfo );
  3137. LeaveCriticalSection(&NlGlobalDomainCritSect);
  3138. if ( Utf8DnsDomainName != NULL ) {
  3139. NetpMemoryFree( Utf8DnsDomainName );
  3140. }
  3141. //
  3142. // If the DNS domain name changed,
  3143. // determine if the domain is now at the root of the forest.
  3144. //
  3145. if ( LocalDnsDomainNameChanged ) {
  3146. (VOID) NlSetDomainForestRoot( DomainInfo, NULL );
  3147. }
  3148. return NetStatus;
  3149. }
  3150. PDOMAIN_INFO
  3151. NlFindNetbiosDomain(
  3152. LPCWSTR DomainName,
  3153. BOOLEAN DefaultToPrimary
  3154. )
  3155. /*++
  3156. Routine Description:
  3157. This routine will look up a domain given a Netbios domain name.
  3158. Arguments:
  3159. DomainName - The name of the domain to look up.
  3160. DefaultToPrimary - Return the primary domain if DomainName is NULL or
  3161. can't be found.
  3162. Return Value:
  3163. NULL - No such domain exists
  3164. A pointer to the domain found. The found domain should be dereferenced
  3165. using NlDereferenceDomain.
  3166. --*/
  3167. {
  3168. NTSTATUS Status;
  3169. PLIST_ENTRY DomainEntry;
  3170. PDOMAIN_INFO DomainInfo = NULL;
  3171. EnterCriticalSection(&NlGlobalDomainCritSect);
  3172. //
  3173. // If domain was specified,
  3174. // try to return primary domain.
  3175. //
  3176. if ( DomainName != NULL ) {
  3177. UNICODE_STRING DomainNameString;
  3178. RtlInitUnicodeString( &DomainNameString, DomainName );
  3179. //
  3180. // Loop trying to find this domain name.
  3181. //
  3182. for (DomainEntry = NlGlobalServicedDomains.Flink ;
  3183. DomainEntry != &NlGlobalServicedDomains;
  3184. DomainEntry = DomainEntry->Flink ) {
  3185. DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, DomNext);
  3186. //
  3187. // If this domain is not to be deleted,
  3188. // check it for match
  3189. //
  3190. if ( (DomainInfo->DomFlags & DOM_DELETED) == 0 &&
  3191. RtlEqualDomainName( &DomainInfo->DomUnicodeDomainNameString,
  3192. &DomainNameString ) ) {
  3193. break;
  3194. }
  3195. DomainInfo = NULL;
  3196. }
  3197. }
  3198. //
  3199. // If we're to default to the primary domain,
  3200. // do so.
  3201. //
  3202. if ( DefaultToPrimary && DomainInfo == NULL ) {
  3203. if ( !IsListEmpty( &NlGlobalServicedDomains ) ) {
  3204. DomainInfo = CONTAINING_RECORD(NlGlobalServicedDomains.Flink, DOMAIN_INFO, DomNext);
  3205. }
  3206. }
  3207. //
  3208. // Reference the domain.
  3209. //
  3210. if ( DomainInfo != NULL ) {
  3211. DomainInfo->ReferenceCount ++;
  3212. }
  3213. LeaveCriticalSection(&NlGlobalDomainCritSect);
  3214. return DomainInfo;
  3215. }
  3216. PDOMAIN_INFO
  3217. NlFindDnsDomain(
  3218. IN LPCSTR DnsDomainName OPTIONAL,
  3219. IN GUID *DomainGuid OPTIONAL,
  3220. IN BOOLEAN DefaultToNdnc,
  3221. IN BOOLEAN CheckAliasName,
  3222. OUT PBOOLEAN AliasNameMatched OPTIONAL
  3223. )
  3224. /*++
  3225. Routine Description:
  3226. This routine will look up a domain given a DNS domain name.
  3227. Arguments:
  3228. DnsDomainName - The name of the DNS domain to look up.
  3229. DomainGuid - If specified (and non-zero), the GUID of the domain to
  3230. match.
  3231. DefaultToNdnc - Return the non-domain NC if domain can't be found.
  3232. CheckAliasName - If TRUE, the DNS domain name aliases of hosted
  3233. domains will be checked for match.
  3234. AliasNameMatched - Set to TRUE if the returned domain was found as
  3235. the result of name alias match; otherwise set to FALSE.
  3236. Note:
  3237. The match is first perfomed against real hosted domains in the
  3238. following order: first for the domain name, then for the domain
  3239. alias (if CheckAliasName is TRUE), and lastly for the domain GUID.
  3240. This order is important to set correctly AliasNameMatched.
  3241. Specifically, this is needed to return the right domain name (either
  3242. active or alias) to the old DC locator client that verifies response
  3243. based only on the domain name and not the GUID.
  3244. If none of the real hosted domains satisfy the searh, NDNCs are searched
  3245. if DefaultToNdnc is TRUE. NDNCs don't have name aliases.
  3246. Return Value:
  3247. NULL - No such domain exists
  3248. A pointer to the domain found. The found domain should be dereferenced
  3249. using NlDereferenceDomain.
  3250. --*/
  3251. {
  3252. NTSTATUS Status;
  3253. PLIST_ENTRY DomainEntry;
  3254. PDOMAIN_INFO DomainInfo = NULL;
  3255. //
  3256. // Initialization
  3257. //
  3258. if ( AliasNameMatched != NULL ) {
  3259. *AliasNameMatched = FALSE;
  3260. }
  3261. //
  3262. // If the specified GUID is zero,
  3263. // Treat it as though none were specified.
  3264. //
  3265. if ( DomainGuid != NULL &&
  3266. IsEqualGUID( DomainGuid, &NlGlobalZeroGuid) ) {
  3267. DomainGuid = NULL;
  3268. }
  3269. EnterCriticalSection(&NlGlobalDomainCritSect);
  3270. //
  3271. // If parameters were specified,
  3272. // use them.
  3273. //
  3274. if ( DnsDomainName != NULL || DomainGuid != NULL ) {
  3275. //
  3276. // Loop trying to find this domain name.
  3277. //
  3278. for (DomainEntry = NlGlobalServicedDomains.Flink ;
  3279. DomainEntry != &NlGlobalServicedDomains;
  3280. DomainEntry = DomainEntry->Flink ) {
  3281. DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, DomNext);
  3282. //
  3283. // If this entry is not to be deleted,
  3284. // check it for match
  3285. //
  3286. if ( (DomainInfo->DomFlags & DOM_DELETED) == 0 ) {
  3287. //
  3288. // Check for the active domain name match
  3289. //
  3290. if ( DomainInfo->DomUtf8DnsDomainName != NULL &&
  3291. NlEqualDnsNameUtf8( DomainInfo->DomUtf8DnsDomainName, DnsDomainName ) ) {
  3292. break;
  3293. }
  3294. //
  3295. // If we are instructed to check the alias name, do it
  3296. //
  3297. if ( CheckAliasName &&
  3298. DomainInfo->DomUtf8DnsDomainNameAlias != NULL &&
  3299. NlEqualDnsNameUtf8( DomainInfo->DomUtf8DnsDomainNameAlias, DnsDomainName ) ) {
  3300. if ( AliasNameMatched != NULL ) {
  3301. *AliasNameMatched = TRUE;
  3302. }
  3303. break;
  3304. }
  3305. //
  3306. // Finally, check for the GUID match
  3307. //
  3308. if ( DomainGuid != NULL && DomainInfo->DomDomainGuid != NULL ) {
  3309. if ( IsEqualGUID( DomainInfo->DomDomainGuid, DomainGuid ) ) {
  3310. break;
  3311. }
  3312. }
  3313. }
  3314. DomainInfo = NULL;
  3315. }
  3316. }
  3317. //
  3318. // If we're to default to non-domain NC,
  3319. // do so.
  3320. //
  3321. if ( DefaultToNdnc && DomainInfo == NULL ) {
  3322. for (DomainEntry = NlGlobalServicedNdncs.Flink ;
  3323. DomainEntry != &NlGlobalServicedNdncs;
  3324. DomainEntry = DomainEntry->Flink ) {
  3325. DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, DomNext);
  3326. //
  3327. // If this entry is not to be deleted,
  3328. // check it for match
  3329. //
  3330. if ( (DomainInfo->DomFlags & DOM_DELETED) == 0 &&
  3331. DomainInfo->DomUtf8DnsDomainName != NULL &&
  3332. NlEqualDnsNameUtf8( DomainInfo->DomUtf8DnsDomainName, DnsDomainName ) ) {
  3333. break;
  3334. }
  3335. DomainInfo = NULL;
  3336. }
  3337. }
  3338. //
  3339. // Reference the domain.
  3340. //
  3341. if ( DomainInfo != NULL ) {
  3342. DomainInfo->ReferenceCount ++;
  3343. }
  3344. LeaveCriticalSection(&NlGlobalDomainCritSect);
  3345. return DomainInfo;
  3346. }
  3347. PDOMAIN_INFO
  3348. NlFindDomain(
  3349. LPCWSTR DomainName OPTIONAL,
  3350. GUID *DomainGuid OPTIONAL,
  3351. BOOLEAN DefaultToPrimary
  3352. )
  3353. /*++
  3354. Routine Description:
  3355. This routine will look up a domain given a either a netbios or DNS domain name.
  3356. Arguments:
  3357. DomainName - The name of the domain to look up.
  3358. NULL implies the primary domain (ignoring DefaultToPrimary)
  3359. DomainGuid - If specified (and non-zero), the GUID of the domain to
  3360. match.
  3361. DefaultToPrimary - Return the primary domain if DomainName
  3362. can't be found.
  3363. Return Value:
  3364. NULL - No such domain exists
  3365. A pointer to the domain found. The found domain should be dereferenced
  3366. using NlDereferenceDomain.
  3367. --*/
  3368. {
  3369. PDOMAIN_INFO DomainInfo;
  3370. //
  3371. // If no specific domain is needed,
  3372. // use the default.
  3373. //
  3374. if ( DomainName == NULL ) {
  3375. DomainInfo = NlFindNetbiosDomain( NULL, TRUE );
  3376. //
  3377. // See if the requested domain is supported.
  3378. //
  3379. } else {
  3380. //
  3381. // Lookup the domain name as Netbios domain name.
  3382. //
  3383. DomainInfo = NlFindNetbiosDomain(
  3384. DomainName,
  3385. FALSE );
  3386. if ( DomainInfo == NULL ) {
  3387. LPSTR LocalDnsDomainName;
  3388. //
  3389. // Lookup the domain name as though it is a DNS domain name.
  3390. //
  3391. LocalDnsDomainName = NetpAllocUtf8StrFromWStr( DomainName );
  3392. if ( LocalDnsDomainName != NULL ) {
  3393. DomainInfo = NlFindDnsDomain(
  3394. LocalDnsDomainName,
  3395. DomainGuid,
  3396. FALSE, // don't lookup NDNCs
  3397. FALSE, // don't check alias names
  3398. NULL ); // don't care if alias name matched
  3399. NetpMemoryFree( LocalDnsDomainName );
  3400. }
  3401. }
  3402. if ( DomainInfo == NULL && DefaultToPrimary ) {
  3403. DomainInfo = NlFindNetbiosDomain( NULL, TRUE );
  3404. }
  3405. }
  3406. return DomainInfo;
  3407. }
  3408. NET_API_STATUS
  3409. NlEnumerateDomains(
  3410. IN BOOLEAN EnumerateNdncsToo,
  3411. PDOMAIN_ENUM_CALLBACK Callback,
  3412. PVOID Context
  3413. )
  3414. /*++
  3415. Routine Description:
  3416. This routine enumerates all the hosted domains and calls back the specified
  3417. callback routine with the specified context.
  3418. Arguments:
  3419. EnumerateNdncsToo - If TRUE, NDNCs will be enumerated in addition to domains
  3420. Callback - The callback routine to call.
  3421. Context - Context for the routine.
  3422. Return Value:
  3423. Status of operation (mostly status of allocations).
  3424. --*/
  3425. {
  3426. NET_API_STATUS NetStatus = NERR_Success;
  3427. PLIST_ENTRY DomainEntry;
  3428. PDOMAIN_INFO DomainInfo;
  3429. PDOMAIN_INFO DomainToDereference = NULL;
  3430. PLIST_ENTRY ServicedList;
  3431. ULONG DomainOrNdnc;
  3432. EnterCriticalSection(&NlGlobalDomainCritSect);
  3433. for ( DomainOrNdnc = 0; DomainOrNdnc < 2; DomainOrNdnc++ ) {
  3434. //
  3435. // On the first loop, enumerate real domains
  3436. //
  3437. if ( DomainOrNdnc == 0 ) {
  3438. ServicedList = &NlGlobalServicedDomains;
  3439. //
  3440. // On the second loop, enumerate NDNCs if so requested
  3441. //
  3442. } else {
  3443. if ( EnumerateNdncsToo ) {
  3444. ServicedList = &NlGlobalServicedNdncs;
  3445. } else {
  3446. break;
  3447. }
  3448. }
  3449. //
  3450. // Enumerate domains/NDNCs
  3451. //
  3452. for (DomainEntry = ServicedList->Flink ;
  3453. DomainEntry != ServicedList;
  3454. DomainEntry = DomainEntry->Flink ) {
  3455. //
  3456. // Reference the next domain in the list
  3457. //
  3458. DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, DomNext);
  3459. //
  3460. // Skip this domain if it is to be deleted
  3461. //
  3462. if ( DomainInfo->DomFlags & DOM_DELETED ) {
  3463. continue;
  3464. }
  3465. DomainInfo->ReferenceCount ++;
  3466. LeaveCriticalSection(&NlGlobalDomainCritSect);
  3467. //
  3468. // Dereference any domain previously referenced.
  3469. //
  3470. if ( DomainToDereference != NULL) {
  3471. NlDereferenceDomain( DomainToDereference );
  3472. DomainToDereference = NULL;
  3473. }
  3474. //
  3475. // Call into the callback routine with this network.
  3476. //
  3477. NetStatus = (Callback)(DomainInfo, Context);
  3478. EnterCriticalSection(&NlGlobalDomainCritSect);
  3479. DomainToDereference = DomainInfo;
  3480. if (NetStatus != NERR_Success) {
  3481. break;
  3482. }
  3483. }
  3484. }
  3485. LeaveCriticalSection(&NlGlobalDomainCritSect);
  3486. //
  3487. // Dereference the last domain
  3488. //
  3489. if ( DomainToDereference != NULL) {
  3490. NlDereferenceDomain( DomainToDereference );
  3491. }
  3492. return NetStatus;
  3493. }
  3494. PDOMAIN_INFO
  3495. NlFindDomainByServerName(
  3496. LPWSTR ServerName
  3497. )
  3498. /*++
  3499. Routine Description:
  3500. This routine will look up a domain given the assigned server name.
  3501. Arguments:
  3502. ServerName - The name of the server for the domain to look up.
  3503. Return Value:
  3504. NULL - No such domain exists
  3505. A pointer to the domain found. The found domain should be dereferenced
  3506. using NlDereferenceDomain.
  3507. --*/
  3508. {
  3509. NTSTATUS Status;
  3510. PLIST_ENTRY DomainEntry;
  3511. PDOMAIN_INFO DomainInfo = NULL;
  3512. EnterCriticalSection(&NlGlobalDomainCritSect);
  3513. //
  3514. // If server wasn't specified,
  3515. // try to return primary domain.
  3516. //
  3517. if ( ServerName == NULL || *ServerName == L'\0' ) {
  3518. //
  3519. // If we're to default to the primary domain,
  3520. // do so.
  3521. //
  3522. if ( !IsListEmpty( &NlGlobalServicedDomains ) ) {
  3523. DomainInfo = CONTAINING_RECORD(NlGlobalServicedDomains.Flink, DOMAIN_INFO, DomNext);
  3524. //
  3525. // Ensure that this domain is not to be deleted
  3526. //
  3527. if ( DomainInfo->DomFlags & DOM_DELETED ) {
  3528. DomainInfo = NULL;
  3529. }
  3530. }
  3531. //
  3532. // If a server name was specified,
  3533. // look it up in the list of domains.
  3534. //
  3535. } else {
  3536. UNICODE_STRING ServerNameString;
  3537. //
  3538. // Remove leading \\'s before conversion.
  3539. //
  3540. if ( IS_PATH_SEPARATOR(ServerName[0]) &&
  3541. IS_PATH_SEPARATOR(ServerName[1]) ) {
  3542. ServerName += 2;
  3543. }
  3544. RtlInitUnicodeString( &ServerNameString, ServerName );
  3545. //
  3546. // Loop trying to find this server name.
  3547. //
  3548. for (DomainEntry = NlGlobalServicedDomains.Flink ;
  3549. DomainEntry != &NlGlobalServicedDomains;
  3550. DomainEntry = DomainEntry->Flink ) {
  3551. DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, DomNext);
  3552. //
  3553. // If this domain is not to be deleted,
  3554. // check it for match
  3555. //
  3556. if ( (DomainInfo->DomFlags & DOM_DELETED) == 0 &&
  3557. RtlEqualComputerName( &DomainInfo->DomUnicodeComputerNameString,
  3558. &ServerNameString ) ) {
  3559. break;
  3560. }
  3561. DomainInfo = NULL;
  3562. }
  3563. //
  3564. // If the server name wasn't found,
  3565. // perhaps it was a DNS host name.
  3566. //
  3567. if ( DomainInfo == NULL ) {
  3568. //
  3569. // Loop trying to find this server name.
  3570. //
  3571. for (DomainEntry = NlGlobalServicedDomains.Flink ;
  3572. DomainEntry != &NlGlobalServicedDomains;
  3573. DomainEntry = DomainEntry->Flink ) {
  3574. DomainInfo = CONTAINING_RECORD(DomainEntry, DOMAIN_INFO, DomNext);
  3575. //
  3576. // If this domain is not to be deleted,
  3577. // check it for match
  3578. //
  3579. if ( (DomainInfo->DomFlags & DOM_DELETED) == 0 &&
  3580. DomainInfo->DomUnicodeDnsHostNameString.Length != 0 &&
  3581. NlEqualDnsName( DomainInfo->DomUnicodeDnsHostNameString.Buffer,
  3582. ServerName ) ) {
  3583. break;
  3584. }
  3585. DomainInfo = NULL;
  3586. }
  3587. }
  3588. }
  3589. //
  3590. // Reference the domain.
  3591. //
  3592. //Cleanup:
  3593. if ( DomainInfo != NULL ) {
  3594. DomainInfo->ReferenceCount ++;
  3595. } else {
  3596. NlPrint((NL_CRITICAL,"NlFindDomainByServerName failed %ws\n", ServerName ));
  3597. }
  3598. LeaveCriticalSection(&NlGlobalDomainCritSect);
  3599. return DomainInfo;
  3600. }
  3601. VOID
  3602. NlDereferenceDomain(
  3603. IN PDOMAIN_INFO DomainInfo
  3604. )
  3605. /*++
  3606. Routine Description:
  3607. Decrement the reference count on a domain.
  3608. If the reference count goes to 0, remove the domain.
  3609. On entry, the global NlGlobalDomainCritSect may not be locked
  3610. Arguments:
  3611. DomainInfo - The domain to dereference
  3612. Return Value:
  3613. None
  3614. --*/
  3615. {
  3616. NTSTATUS Status;
  3617. ULONG ReferenceCount;
  3618. ULONG Index;
  3619. PLIST_ENTRY ListEntry;
  3620. //
  3621. // Decrement the reference count
  3622. //
  3623. EnterCriticalSection(&NlGlobalDomainCritSect);
  3624. ReferenceCount = -- DomainInfo->ReferenceCount;
  3625. //
  3626. // If this is not the last reference,
  3627. // just return
  3628. //
  3629. if ( ReferenceCount != 0 ) {
  3630. LeaveCriticalSection(&NlGlobalDomainCritSect);
  3631. return;
  3632. }
  3633. //
  3634. // Otherwise proceed with delinking
  3635. // and delete the domain structure
  3636. //
  3637. NlAssert( DomainInfo->DomFlags & DOM_DELETED );
  3638. //
  3639. // Remove the entry from the list of serviced domains
  3640. //
  3641. RemoveEntryList(&DomainInfo->DomNext);
  3642. LeaveCriticalSection(&NlGlobalDomainCritSect);
  3643. NlPrintDom(( NL_DOMAIN, DomainInfo,
  3644. "Domain RefCount is zero. Domain being rundown.\n"));
  3645. #ifdef _DC_NETLOGON
  3646. //
  3647. // Stop the domain thread.
  3648. //
  3649. NlStopDomainThread( DomainInfo );
  3650. //
  3651. // Delete any client session
  3652. //
  3653. LOCK_TRUST_LIST( DomainInfo );
  3654. if ( DomainInfo->DomParentClientSession != NULL ) {
  3655. NlUnrefClientSession( DomainInfo->DomParentClientSession );
  3656. DomainInfo->DomParentClientSession = NULL;
  3657. }
  3658. UNLOCK_TRUST_LIST( DomainInfo );
  3659. NlDeleteDomClientSession( DomainInfo );
  3660. //
  3661. // Tell the browser and the SMB server that this domain is gone.
  3662. //
  3663. if ( !NlGlobalMemberWorkstation &&
  3664. (DomainInfo->DomFlags & DOM_REAL_DOMAIN) != 0 ) {
  3665. NlBrowserUpdate( DomainInfo, RoleInvalid );
  3666. }
  3667. //
  3668. // Close the SAM and LSA handles
  3669. //
  3670. if ( DomainInfo->DomSamServerHandle != NULL ) {
  3671. Status = SamrCloseHandle( &DomainInfo->DomSamServerHandle);
  3672. NlAssert( NT_SUCCESS(Status) || Status == STATUS_INVALID_SERVER_STATE );
  3673. }
  3674. if ( DomainInfo->DomSamAccountDomainHandle != NULL ) {
  3675. Status = SamrCloseHandle( &DomainInfo->DomSamAccountDomainHandle);
  3676. NlAssert( NT_SUCCESS(Status) || Status == STATUS_INVALID_SERVER_STATE );
  3677. }
  3678. if ( DomainInfo->DomSamBuiltinDomainHandle != NULL ) {
  3679. Status = SamrCloseHandle( &DomainInfo->DomSamBuiltinDomainHandle);
  3680. NlAssert( NT_SUCCESS(Status) || Status == STATUS_INVALID_SERVER_STATE );
  3681. }
  3682. if ( DomainInfo->DomLsaPolicyHandle != NULL ) {
  3683. Status = LsarClose( &DomainInfo->DomLsaPolicyHandle );
  3684. NlAssert( NT_SUCCESS(Status) );
  3685. }
  3686. //
  3687. // Free the server session table.
  3688. //
  3689. LOCK_SERVER_SESSION_TABLE( DomainInfo );
  3690. while ( (ListEntry = DomainInfo->DomServerSessionTable.Flink) !=
  3691. &DomainInfo->DomServerSessionTable ) {
  3692. PSERVER_SESSION ServerSession;
  3693. ServerSession =
  3694. CONTAINING_RECORD(ListEntry, SERVER_SESSION, SsSeqList);
  3695. // Indicate we no longer need the server session anymore.
  3696. if ( ServerSession->SsFlags & SS_BDC ) {
  3697. ServerSession->SsFlags |= SS_BDC_FORCE_DELETE;
  3698. }
  3699. NlFreeServerSession( ServerSession );
  3700. }
  3701. if ( DomainInfo->DomServerSessionHashTable != NULL ) {
  3702. NetpMemoryFree( DomainInfo->DomServerSessionHashTable );
  3703. DomainInfo->DomServerSessionHashTable = NULL;
  3704. }
  3705. if ( DomainInfo->DomServerSessionTdoNameHashTable != NULL ) {
  3706. NetpMemoryFree( DomainInfo->DomServerSessionTdoNameHashTable );
  3707. DomainInfo->DomServerSessionTdoNameHashTable = NULL;
  3708. }
  3709. UNLOCK_SERVER_SESSION_TABLE( DomainInfo );
  3710. DeleteCriticalSection( &DomainInfo->DomServerSessionTableCritSect );
  3711. //
  3712. // Timeout any async discoveries.
  3713. //
  3714. // The MainLoop thread may no longer be running to complete them.
  3715. // ?? Walk pool of async discovery thread here. Perhaps ref count didn't
  3716. // reach 0 and we didn't even get this far.
  3717. //
  3718. // Free the Trust List
  3719. //
  3720. LOCK_TRUST_LIST( DomainInfo );
  3721. while ( (ListEntry = DomainInfo->DomTrustList.Flink) != &DomainInfo->DomTrustList ) {
  3722. PCLIENT_SESSION ClientSession;
  3723. ClientSession =
  3724. CONTAINING_RECORD(ListEntry, CLIENT_SESSION, CsNext );
  3725. //
  3726. // Free the session.
  3727. //
  3728. NlFreeClientSession( ClientSession );
  3729. }
  3730. //
  3731. // Free the list of failed user logons
  3732. //
  3733. while ( !IsListEmpty(&DomainInfo->DomFailedUserLogonList) ) {
  3734. PNL_FAILED_USER_LOGON FailedUserLogon;
  3735. ListEntry = RemoveHeadList( &DomainInfo->DomFailedUserLogonList );
  3736. FailedUserLogon = CONTAINING_RECORD(ListEntry, NL_FAILED_USER_LOGON, FuNext );
  3737. //
  3738. // Free the logon structure
  3739. //
  3740. LocalFree( FailedUserLogon );
  3741. }
  3742. #endif // _DC_NETLOGON
  3743. //
  3744. // Free the Forest Trust List
  3745. //
  3746. if ( DomainInfo->DomForestTrustList != NULL ) {
  3747. MIDL_user_free( DomainInfo->DomForestTrustList );
  3748. DomainInfo->DomForestTrustList = NULL;
  3749. }
  3750. UNLOCK_TRUST_LIST( DomainInfo );
  3751. //
  3752. // Mark all DNS names we still have registered for
  3753. // deregistration. However, avoid this on shutdown,
  3754. // let the DNS shutdown routine do the cleanup as
  3755. // appropriate.
  3756. //
  3757. if ( !NlGlobalTerminate ) {
  3758. (VOID) NlDnsAddDomainRecords( DomainInfo, 0 );
  3759. }
  3760. //
  3761. // Dereference all covered sites
  3762. // Free the covered sites lists
  3763. //
  3764. EnterCriticalSection( &NlGlobalSiteCritSect );
  3765. if ( DomainInfo->CoveredSites != NULL ) {
  3766. for ( Index = 0; Index < DomainInfo->CoveredSitesCount; Index++ ) {
  3767. NlDerefSiteEntry( (DomainInfo->CoveredSites)[Index].CoveredSite );
  3768. }
  3769. LocalFree( DomainInfo->CoveredSites );
  3770. DomainInfo->CoveredSites = NULL;
  3771. DomainInfo->CoveredSitesCount = 0;
  3772. }
  3773. if ( DomainInfo->GcCoveredSites != NULL ) {
  3774. for ( Index = 0; Index < DomainInfo->GcCoveredSitesCount; Index++ ) {
  3775. NlDerefSiteEntry( (DomainInfo->GcCoveredSites)[Index].CoveredSite );
  3776. }
  3777. LocalFree( DomainInfo->GcCoveredSites );
  3778. DomainInfo->GcCoveredSites = NULL;
  3779. DomainInfo->GcCoveredSitesCount = 0;
  3780. }
  3781. LeaveCriticalSection( &NlGlobalSiteCritSect );
  3782. //
  3783. // Free the computer name.
  3784. //
  3785. NlFreeComputerName( DomainInfo );
  3786. //
  3787. // Free the DnsDomain name.
  3788. //
  3789. NlFreeDnsDomainDomainInfo( DomainInfo );
  3790. //
  3791. // Free the Domain Info structure.
  3792. //
  3793. DeleteCriticalSection( &DomainInfo->DomTrustListCritSect );
  3794. if ( IsPrimaryDomain(DomainInfo ) ) {
  3795. NlGlobalDomainInfo = NULL;
  3796. }
  3797. (VOID) LocalFree( DomainInfo );
  3798. NlGlobalServicedDomainCount --;
  3799. }
  3800. VOID
  3801. NlDeleteDomain(
  3802. IN PDOMAIN_INFO DomainInfo
  3803. )
  3804. /*++
  3805. Routine Description:
  3806. Force a domain to be deleted.
  3807. Arguments:
  3808. DomainInfo - The domain to delete
  3809. Return Value:
  3810. None
  3811. --*/
  3812. {
  3813. NlPrintDom(( NL_DOMAIN, DomainInfo, "NlDeleteDomain called\n"));
  3814. //
  3815. // Indicate that the domain is to be deleted.
  3816. //
  3817. // Don't remove it from the list of serviced
  3818. // domains because we may walk the list in
  3819. // NlEnumerateDomains which temporarily
  3820. // releases the crit sect.
  3821. //
  3822. EnterCriticalSection(&NlGlobalDomainCritSect);
  3823. NlAssert( (DomainInfo->DomFlags & DOM_DELETED) == 0 );
  3824. DomainInfo->DomFlags |= DOM_DELETED;
  3825. LeaveCriticalSection(&NlGlobalDomainCritSect);
  3826. }
  3827. VOID
  3828. NlUninitializeDomains(
  3829. VOID
  3830. )
  3831. /*++
  3832. Routine Description:
  3833. Delete all of the domains.
  3834. Arguments:
  3835. None.
  3836. Return Value:
  3837. None
  3838. --*/
  3839. {
  3840. ULONG LoopIndex;
  3841. PLIST_ENTRY ServicedList;
  3842. if ( NlGlobalDomainsInitialized ) {
  3843. NlGlobalDomainsInitialized = FALSE;
  3844. //
  3845. // Loop through the domains deleting each of them
  3846. //
  3847. EnterCriticalSection(&NlGlobalDomainCritSect);
  3848. for ( LoopIndex = 0; LoopIndex < 2; LoopIndex++ ) {
  3849. if ( LoopIndex == 0 ) {
  3850. ServicedList = &NlGlobalServicedDomains;
  3851. } else {
  3852. ServicedList = &NlGlobalServicedNdncs;
  3853. }
  3854. while (!IsListEmpty(ServicedList)) {
  3855. PDOMAIN_INFO DomainInfo = CONTAINING_RECORD(ServicedList->Flink, DOMAIN_INFO, DomNext);
  3856. //
  3857. // If domain is already marked for deletion,
  3858. // add our reference so that we can wait
  3859. // until only our reference remains
  3860. //
  3861. if ( DomainInfo->DomFlags & DOM_DELETED ) {
  3862. DomainInfo->ReferenceCount ++;
  3863. //
  3864. // Otherwise, mark the domain for deletion
  3865. //
  3866. } else {
  3867. NlDeleteDomain( DomainInfo );
  3868. }
  3869. LeaveCriticalSection(&NlGlobalDomainCritSect);
  3870. //
  3871. // Wait for any other references to disappear
  3872. //
  3873. if ( DomainInfo->ReferenceCount != 1 ) {
  3874. EnterCriticalSection(&NlGlobalDomainCritSect);
  3875. while ( DomainInfo->ReferenceCount != 1 ) {
  3876. LeaveCriticalSection(&NlGlobalDomainCritSect);
  3877. NlPrintDom(( NL_CRITICAL, DomainInfo,
  3878. "NlUnitializeDomains: Sleeping a second waiting for Domain RefCount to zero.\n"));
  3879. Sleep( 1000 );
  3880. EnterCriticalSection(&NlGlobalDomainCritSect);
  3881. }
  3882. LeaveCriticalSection(&NlGlobalDomainCritSect);
  3883. }
  3884. //
  3885. // Actually delink and delete structure by removing the last reference
  3886. //
  3887. NlAssert( DomainInfo->ReferenceCount == 1 );
  3888. NlDereferenceDomain( DomainInfo );
  3889. EnterCriticalSection(&NlGlobalDomainCritSect);
  3890. }
  3891. }
  3892. LeaveCriticalSection(&NlGlobalDomainCritSect);
  3893. DeleteCriticalSection( &NlGlobalDomainCritSect );
  3894. }
  3895. }