Source code of Windows XP (NT5)
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.

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