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

7116 lines
219 KiB

  1. /*--
  2. Copyright (c) 1987-1996 Microsoft Corporation
  3. Module Name:
  4. netlogon.c
  5. Abstract:
  6. Entry point and main thread of Netlogon service.
  7. Author:
  8. Ported from Lan Man 2.0
  9. Environment:
  10. User mode only.
  11. Contains NT-specific code.
  12. Requires ANSI C extensions: slash-slash comments, long external names.
  13. Revision History:
  14. 21-Nov-1990 (madana)
  15. added code for update (reverse replication) and lockout support.
  16. 21-Nov-1990 (madana)
  17. server type support.
  18. 21-May-1991 (cliffv)
  19. Ported to NT. Converted to NT style.
  20. --*/
  21. //
  22. // Common include files.
  23. //
  24. #include "logonsrv.h" // Include files common to entire service
  25. #pragma hdrstop
  26. #include <overflow.h>
  27. //
  28. // Include lsrvdata.h again allocating the actual variables
  29. // this time around.
  30. //
  31. #define LSRVDATA_ALLOCATE
  32. #include "lsrvdata.h"
  33. #undef LSRVDATA_ALLOCATE
  34. //
  35. // Include files specific to this .c file
  36. //
  37. #include <ctype.h> // C library type functions
  38. #include <lmwksta.h> // WKSTA API defines and prototypes
  39. #include <w32timep.h> // W32TimeGetNetlogonServiceBits
  40. extern BOOLEAN SampUsingDsData();
  41. //
  42. // Globals
  43. //
  44. #define INTERROGATE_RESP_DELAY 2000 // may want to tune it
  45. #define MAX_PRIMARY_TRACK_FAIL 3 // Primary pulse slips
  46. //
  47. // RpcInit workitem
  48. WORKER_ITEM NlGlobalRpcInitWorkItem;
  49. BOOLEAN
  50. NetlogonDllInit (
  51. IN PVOID DllHandle,
  52. IN ULONG Reason,
  53. IN PCONTEXT Context OPTIONAL
  54. )
  55. /*++
  56. Routine Description:
  57. This is the DLL initialization routine for netlogon.dll.
  58. Arguments:
  59. Standard.
  60. Return Value:
  61. TRUE iff initialization succeeded.
  62. --*/
  63. {
  64. NTSTATUS Status;
  65. NET_API_STATUS NetStatus;
  66. UNREFERENCED_PARAMETER(DllHandle); // avoid compiler warnings
  67. UNREFERENCED_PARAMETER(Context); // avoid compiler warnings
  68. //
  69. // Handle attaching netlogon.dll to a new process.
  70. //
  71. if (Reason == DLL_PROCESS_ATTACH) {
  72. NlGlobalMsvEnabled = FALSE;
  73. NlGlobalMsvThreadCount = 0;
  74. NlGlobalMsvTerminateEvent = NULL;
  75. if ( !DisableThreadLibraryCalls( DllHandle ) ) {
  76. KdPrint(("NETLOGON.DLL: DisableThreadLibraryCalls failed: %ld\n",
  77. GetLastError() ));
  78. }
  79. Status = NlInitChangeLog();
  80. #if NETLOGONDBG
  81. if ( !NT_SUCCESS( Status ) ) {
  82. KdPrint(("NETLOGON.DLL: Changelog initialization failed: %lx\n",
  83. Status ));
  84. }
  85. #endif // NETLOGONDBG
  86. if ( NT_SUCCESS(Status) ) {
  87. //
  88. // Initialize the Critical Section used to serialize access to
  89. // variables shared by MSV threads and netlogon threads.
  90. //
  91. try {
  92. InitializeCriticalSection( &NlGlobalMsvCritSect );
  93. } except( EXCEPTION_EXECUTE_HANDLER ) {
  94. KdPrint(("NETLOGON.DLL: Cannot initialize NlGlobalMsvCritSect\n"));
  95. Status = STATUS_NO_MEMORY;
  96. }
  97. //
  98. // Initialize the cache of discovered domains.
  99. //
  100. if ( NT_SUCCESS(Status) ) {
  101. NetStatus = NetpDcInitializeCache();
  102. if ( NetStatus != NO_ERROR ) {
  103. KdPrint(("NETLOGON.DLL: Cannot NetpDcinitializeCache\n"));
  104. Status = STATUS_NO_MEMORY;
  105. }
  106. if ( !NT_SUCCESS(Status) ) {
  107. DeleteCriticalSection( &NlGlobalMsvCritSect );
  108. }
  109. }
  110. if ( !NT_SUCCESS(Status) ) {
  111. NlCloseChangeLog();
  112. }
  113. }
  114. //
  115. // Handle detaching netlogon.dll from a process.
  116. //
  117. } else if (Reason == DLL_PROCESS_DETACH) {
  118. Status = NlCloseChangeLog();
  119. #if NETLOGONDBG
  120. if ( !NT_SUCCESS( Status ) ) {
  121. KdPrint(("NETLOGON.DLL: Changelog initialization failed: %lx\n",
  122. Status ));
  123. }
  124. #endif // NETLOGONDBG
  125. //
  126. // Delete the Critical Section used to serialize access to
  127. // variables shared by MSV threads and netlogon threads.
  128. //
  129. DeleteCriticalSection( &NlGlobalMsvCritSect );
  130. //
  131. // Free the cache of discovered DCs.
  132. //
  133. NetpDcUninitializeCache();
  134. } else {
  135. Status = STATUS_SUCCESS;
  136. }
  137. return (BOOLEAN)(NT_SUCCESS(Status));
  138. }
  139. BOOLEAN
  140. NlInitDbSerialNumber(
  141. IN PDOMAIN_INFO DomainInfo,
  142. IN OUT PLARGE_INTEGER SerialNumber,
  143. IN OUT PLARGE_INTEGER CreationTime,
  144. IN DWORD DBIndex
  145. )
  146. /*++
  147. Routine Description:
  148. Set the SerialNumber and CreationTime in the NlGlobalDBInfoArray data
  149. structure.
  150. On the PDC,
  151. Validate that it matches the value found in the change log.
  152. Ensure the values are non-zero.
  153. Arguments:
  154. DomainInfo - Hosted Domain this database is for.
  155. SerialNumber - Specifies the serial number found in the database.
  156. On return, specifies the serial number to write to the database
  157. CreationTime - Specifies the creation time found in the database.
  158. On return, specifies the creation time to write to the database
  159. DBIndex -- DB Index of the database being initialized
  160. Return Value:
  161. TRUE -- iff the serial number and creation time need to be written back
  162. to the database.
  163. --*/
  164. {
  165. BOOLEAN ReturnValue = FALSE;
  166. //
  167. // If we're running as the primary,
  168. // check to see if we are a newly promoted primary that was in
  169. // the middle of a full sync before we were promoted.
  170. //
  171. NlAssert( IsPrimaryDomain( DomainInfo ) );
  172. if ( NlGlobalPdcDoReplication ) {
  173. if ( SerialNumber->QuadPart == 0 || CreationTime->QuadPart == 0 ) {
  174. NlPrint(( NL_CRITICAL,
  175. "NlInitDbSerialNumber: %ws"
  176. ": Pdc has bogus Serial number %lx %lx or Creation Time %lx %lx (reset).\n",
  177. NlGlobalDBInfoArray[DBIndex].DBName,
  178. SerialNumber->HighPart,
  179. SerialNumber->LowPart,
  180. CreationTime->HighPart,
  181. CreationTime->LowPart ));
  182. //
  183. // This is the primary,
  184. // we probably shouldn't be replicating from a partial database,
  185. // but at least set the replication information to something
  186. // reasonable.
  187. //
  188. // This will FORCE a full sync on every BDC since the CreationTime has
  189. // changed. That's the right thing to do since we can't possibly know
  190. // what state this database is in.
  191. //
  192. NlQuerySystemTime( CreationTime );
  193. SerialNumber->QuadPart = 1;
  194. ReturnValue = TRUE;
  195. }
  196. }
  197. //
  198. // The global serial number array has already been initialized
  199. // from the changelog. If that information is wrong, just reset the
  200. // changelog now.
  201. //
  202. LOCK_CHANGELOG();
  203. //
  204. // If there was no serial number in the changelog for this database,
  205. // set it now.
  206. //
  207. if ( NlGlobalChangeLogDesc.SerialNumber[DBIndex].QuadPart == 0 ) {
  208. NlPrint((NL_SYNC, "NlInitDbSerialNumber: %ws"
  209. ": No serial number in change log (set to %lx %lx)\n",
  210. NlGlobalDBInfoArray[DBIndex].DBName,
  211. SerialNumber->HighPart,
  212. SerialNumber->LowPart ));
  213. NlGlobalChangeLogDesc.SerialNumber[DBIndex] = *SerialNumber;
  214. //
  215. // If the serial number in the changelog is greater than the
  216. // serial number in the database, this is caused by the changelog
  217. // being flushed to disk and the SAM database not being flushed.
  218. //
  219. // Cure this problem by deleting the superfluous changelog entries.
  220. //
  221. } else if ( NlGlobalChangeLogDesc.SerialNumber[DBIndex].QuadPart !=
  222. SerialNumber->QuadPart ) {
  223. NlPrint((NL_SYNC, "NlInitDbSerialNumber: %ws"
  224. ": Changelog serial number different than database: "
  225. "ChangeLog = %lx %lx DB = %lx %lx\n",
  226. NlGlobalDBInfoArray[DBIndex].DBName,
  227. NlGlobalChangeLogDesc.SerialNumber[DBIndex].HighPart,
  228. NlGlobalChangeLogDesc.SerialNumber[DBIndex].LowPart,
  229. SerialNumber->HighPart,
  230. SerialNumber->LowPart ));
  231. (VOID) NlFixChangeLog( &NlGlobalChangeLogDesc, DBIndex, *SerialNumber );
  232. } else {
  233. NlPrint((NL_SYNC, "NlInitDbSerialNumber: %ws"
  234. ": Serial number is %lx %lx\n",
  235. NlGlobalDBInfoArray[DBIndex].DBName,
  236. SerialNumber->HighPart,
  237. SerialNumber->LowPart ));
  238. }
  239. //
  240. // In all cases,
  241. // set the globals to match the database.
  242. //
  243. NlGlobalChangeLogDesc.SerialNumber[DBIndex] = *SerialNumber;
  244. NlGlobalDBInfoArray[DBIndex].CreationTime = *CreationTime;
  245. UNLOCK_CHANGELOG();
  246. return ReturnValue;
  247. }
  248. NTSTATUS
  249. NlInitLsaDBInfo(
  250. PDOMAIN_INFO DomainInfo,
  251. DWORD DBIndex
  252. )
  253. /*++
  254. Routine Description:
  255. Initialize NlGlobalDBInfoArray data structure. Some of the LSA
  256. database info is already determined in ValidateStartup functions, so
  257. those values are used here.
  258. Arguments:
  259. DomainInfo - Hosted Domain this database is for.
  260. DBIndex -- DB Index of the database being initialized
  261. Return Value:
  262. NT status code.
  263. --*/
  264. {
  265. NTSTATUS Status;
  266. //
  267. // Initialize LSA database info.
  268. //
  269. NlGlobalDBInfoArray[DBIndex].DBIndex = DBIndex;
  270. NlGlobalDBInfoArray[DBIndex].DBName = L"LSA";
  271. NlGlobalDBInfoArray[DBIndex].DBSessionFlag = SS_LSA_REPL_NEEDED;
  272. NlGlobalDBInfoArray[DBIndex].DBHandle = DomainInfo->DomLsaPolicyHandle;
  273. //
  274. // Forgo this initialization on a workstation.
  275. //
  276. if ( !NlGlobalMemberWorkstation ) {
  277. LARGE_INTEGER SerialNumber;
  278. LARGE_INTEGER CreationTime;
  279. //
  280. // Get the LSA Modified information.
  281. //
  282. Status = LsaIGetSerialNumberPolicy(
  283. NlGlobalDBInfoArray[DBIndex].DBHandle,
  284. &SerialNumber,
  285. &CreationTime );
  286. if ( !NT_SUCCESS(Status) ) {
  287. NlPrintDom(( NL_CRITICAL, DomainInfo,
  288. "NlInitLsaDbInfo: LsaIGetSerialNumberPolicy failed %lx\n",
  289. Status ));
  290. goto Cleanup;
  291. }
  292. //
  293. // Set the SerialNumber and CreationTime in the globals.
  294. //
  295. if ( NlInitDbSerialNumber(
  296. DomainInfo,
  297. &SerialNumber,
  298. &CreationTime,
  299. DBIndex ) ) {
  300. Status = LsaISetSerialNumberPolicy(
  301. NlGlobalDBInfoArray[DBIndex].DBHandle,
  302. &SerialNumber,
  303. &CreationTime,
  304. (BOOLEAN) FALSE );
  305. if ( !NT_SUCCESS(Status) ) {
  306. goto Cleanup;
  307. }
  308. }
  309. }
  310. Status = STATUS_SUCCESS;
  311. Cleanup:
  312. return Status;
  313. }
  314. NTSTATUS
  315. NlInitSamDBInfo(
  316. PDOMAIN_INFO DomainInfo,
  317. DWORD DBIndex
  318. )
  319. /*++
  320. Routine Description:
  321. Initialize NlGlobalDBInfoArray data structure. Some of the SAM database
  322. info is already determined in ValidateStartup functions, so those
  323. values are used here. For BUILTIN database, the database is opened,
  324. database handle is obtained and other DB info
  325. queried and initialized in this function.
  326. Arguments:
  327. DomainInfo - Hosted Domain this database is for.
  328. DBIndex -- DB Index of the database being initialized
  329. Return Value:
  330. NT status code.
  331. --*/
  332. {
  333. NTSTATUS Status;
  334. PSAMPR_DOMAIN_INFO_BUFFER DomainModified = NULL;
  335. PSAMPR_DOMAIN_INFO_BUFFER DomainReplica = NULL;
  336. //
  337. // Initialize SAM database info.
  338. //
  339. NlGlobalDBInfoArray[DBIndex].DBIndex = DBIndex;
  340. if ( DBIndex == SAM_DB ) {
  341. NlGlobalDBInfoArray[DBIndex].DBName = L"SAM";
  342. NlGlobalDBInfoArray[DBIndex].DBSessionFlag = SS_ACCOUNT_REPL_NEEDED;
  343. NlGlobalDBInfoArray[DBIndex].DBHandle = DomainInfo->DomSamAccountDomainHandle;
  344. } else {
  345. NlGlobalDBInfoArray[DBIndex].DBName = L"BUILTIN";
  346. NlGlobalDBInfoArray[DBIndex].DBSessionFlag = SS_BUILTIN_REPL_NEEDED;
  347. NlGlobalDBInfoArray[DBIndex].DBHandle = DomainInfo->DomSamBuiltinDomainHandle;
  348. }
  349. //
  350. // Forgo this initialization on a workstation.
  351. //
  352. if ( !NlGlobalMemberWorkstation ) {
  353. //
  354. // Get the replica source name.
  355. //
  356. Status = SamrQueryInformationDomain(
  357. NlGlobalDBInfoArray[DBIndex].DBHandle,
  358. DomainReplicationInformation,
  359. &DomainReplica );
  360. if ( !NT_SUCCESS(Status) ) {
  361. NlPrintDom(( NL_CRITICAL, DomainInfo,
  362. "NlInitSamDbInfo: %ws: Cannot SamrQueryInformationDomain (Replica): %lx\n",
  363. NlGlobalDBInfoArray[DBIndex].DBName,
  364. Status ));
  365. DomainReplica = NULL;
  366. goto Cleanup;
  367. }
  368. //
  369. // Get the Domain Modified information.
  370. //
  371. Status = SamrQueryInformationDomain(
  372. NlGlobalDBInfoArray[DBIndex].DBHandle,
  373. DomainModifiedInformation,
  374. &DomainModified );
  375. if ( !NT_SUCCESS(Status) ) {
  376. NlPrintDom(( NL_CRITICAL, DomainInfo,
  377. "NlInitSamDbInfo: %ws: Cannot SamrQueryInformationDomain (Modified): %lx\n",
  378. NlGlobalDBInfoArray[DBIndex].DBName,
  379. Status ));
  380. DomainModified = NULL;
  381. goto Cleanup;
  382. }
  383. //
  384. // Set the SerialNumber and CreationTime in the globals.
  385. //
  386. if ( NlInitDbSerialNumber(
  387. DomainInfo,
  388. &DomainModified->Modified.DomainModifiedCount,
  389. &DomainModified->Modified.CreationTime,
  390. DBIndex ) ) {
  391. Status = SamISetSerialNumberDomain(
  392. NlGlobalDBInfoArray[DBIndex].DBHandle,
  393. &DomainModified->Modified.DomainModifiedCount,
  394. &DomainModified->Modified.CreationTime,
  395. (BOOLEAN) FALSE );
  396. if ( !NT_SUCCESS(Status) ) {
  397. NlPrintDom(( NL_CRITICAL, DomainInfo,
  398. "NlInitSamDbInfo: %ws: Cannot SamISetSerialNumberDomain: %lx\n",
  399. NlGlobalDBInfoArray[DBIndex].DBName,
  400. Status ));
  401. goto Cleanup;
  402. }
  403. }
  404. }
  405. Status = STATUS_SUCCESS;
  406. Cleanup:
  407. //
  408. // Free locally used resources.
  409. //
  410. if ( DomainModified != NULL ) {
  411. SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainModified,
  412. DomainModifiedInformation );
  413. }
  414. if ( DomainReplica != NULL ) {
  415. SamIFree_SAMPR_DOMAIN_INFO_BUFFER( DomainReplica,
  416. DomainReplicationInformation );
  417. }
  418. return Status;
  419. }
  420. BOOL
  421. NlCreateSysvolShares(
  422. VOID
  423. )
  424. /*++
  425. Routine Description:
  426. Create the Sysvol and Netlogon shares.
  427. Arguments:
  428. None.
  429. Return Value:
  430. TRUE -- iff initialization is successful.
  431. --*/
  432. {
  433. BOOL RetVal = TRUE;
  434. BOOL NetlogonShareRelatedToSysvolShare = FALSE;
  435. NET_API_STATUS NetStatus;
  436. LPWSTR AllocatedPath = NULL;
  437. LPWSTR PathToShare = NULL;
  438. LPWSTR DomDnsDomainNameAlias = NULL;
  439. LPWSTR AllocatedPathAlias = NULL;
  440. //
  441. // Create the sysvol share.
  442. //
  443. if ( NlGlobalParameters.SysVolReady ) {
  444. NetStatus = NlCreateShare( NlGlobalParameters.UnicodeSysvolPath,
  445. NETLOGON_SYSVOL_SHARE,
  446. TRUE,
  447. TRUE, // update exclusive share access
  448. NlGlobalParameters.AllowExclusiveSysvolShareAccess ) ;
  449. if ( NetStatus != NERR_Success ) {
  450. LPWSTR MsgStrings[2];
  451. NlPrint((NL_CRITICAL, "NlCreateShare %lu\n", NetStatus ));
  452. MsgStrings[0] = NlGlobalParameters.UnicodeSysvolPath;
  453. MsgStrings[1] = (LPWSTR) ULongToPtr( NetStatus );
  454. NlpWriteEventlog (NELOG_NetlogonFailedToCreateShare,
  455. EVENTLOG_ERROR_TYPE,
  456. (LPBYTE) &NetStatus,
  457. sizeof(NetStatus),
  458. MsgStrings,
  459. 2 | NETP_LAST_MESSAGE_IS_NETSTATUS );
  460. /* This isn't fatal. Just continue */
  461. }
  462. } else {
  463. NetStatus = NetShareDel( NULL, NETLOGON_SYSVOL_SHARE, 0);
  464. if ( NetStatus != NERR_Success ) {
  465. if ( NetStatus != NERR_NetNameNotFound ) {
  466. NlPrint((NL_CRITICAL, "NetShareDel SYSVOL failed %lu\n", NetStatus ));
  467. }
  468. /* This isn't fatal. Just continue */
  469. }
  470. }
  471. //
  472. // Create NETLOGON share.
  473. //
  474. //
  475. // Build the default netlogon share path
  476. //
  477. if ( NlGlobalParameters.UnicodeScriptPath == NULL &&
  478. NlGlobalParameters.UnicodeSysvolPath != NULL ) {
  479. PDOMAIN_INFO DomainInfo = NULL;
  480. ULONG Size;
  481. ULONG SysVolSize;
  482. PUCHAR Where;
  483. //
  484. // Get pointer to global domain info.
  485. //
  486. DomainInfo = NlFindNetbiosDomain( NULL, TRUE ); // Primary domain
  487. if ( DomainInfo == NULL ) {
  488. NlPrint((NL_CRITICAL, "NlCreateSysvolShares: Cannot find primary domain.\n" ));
  489. // This can't happen
  490. RetVal = FALSE;
  491. goto Cleanup;
  492. }
  493. //
  494. // Allocate a buffer for the real path
  495. // Avoid this if we have no DNS domain
  496. // name which is the case when we are
  497. // in teh middle of dcpromo and somebody
  498. // started us manually.
  499. //
  500. EnterCriticalSection(&NlGlobalDomainCritSect);
  501. if ( DomainInfo->DomUnicodeDnsDomainNameString.Length > 0 ) {
  502. SysVolSize = wcslen( NlGlobalParameters.UnicodeSysvolPath ) * sizeof(WCHAR);
  503. Size = SysVolSize +
  504. sizeof(WCHAR) +
  505. DomainInfo->DomUnicodeDnsDomainNameString.Length +
  506. sizeof(DEFAULT_SCRIPTS);
  507. AllocatedPath = LocalAlloc( LMEM_ZEROINIT, Size );
  508. if ( AllocatedPath == NULL ) {
  509. LeaveCriticalSection(&NlGlobalDomainCritSect);
  510. NlDereferenceDomain( DomainInfo );
  511. RetVal = FALSE;
  512. goto Cleanup;
  513. }
  514. PathToShare = AllocatedPath;
  515. //
  516. // Build the real path
  517. //
  518. Where = (PUCHAR)PathToShare;
  519. RtlCopyMemory( Where, NlGlobalParameters.UnicodeSysvolPath, SysVolSize );
  520. Where += SysVolSize;
  521. *((WCHAR *)Where) = L'\\';
  522. Where += sizeof(WCHAR);
  523. // Ignore the trailing . on the DNS domain name
  524. RtlCopyMemory( Where,
  525. DomainInfo->DomUnicodeDnsDomainNameString.Buffer,
  526. DomainInfo->DomUnicodeDnsDomainNameString.Length - sizeof(WCHAR) );
  527. Where += DomainInfo->DomUnicodeDnsDomainNameString.Length - sizeof(WCHAR);
  528. //
  529. // At this point the path has the form "...\SYSVOL\SYSVOL\DnsDomainName".
  530. // This is the name of the junction point that points to the actual
  531. // sysvol root directory "...\SYSVOL\domain" (where "domain" is literal).
  532. // On the domain rename, we need to rename the junction point to correspond
  533. // to the current DNS domain name. The old name is stored in the domain
  534. // name alias, so we can rename from "...\SYSVOL\SYSVOL\DnsDomainNameAlias"
  535. // to "...\SYSVOL\SYSVOL\DnsDomainName". Note that if the rename hasn't yet
  536. // happened, DNS domain name alias is actually the future domain name. This
  537. // is OK as the junction named "...\SYSVOL\SYSVOL\DnsDomainNameAlias" will
  538. // not exist and the junction rename will fail properly.
  539. //
  540. if ( DomainInfo->DomUtf8DnsDomainNameAlias != NULL &&
  541. !NlEqualDnsNameUtf8(DomainInfo->DomUtf8DnsDomainName,
  542. DomainInfo->DomUtf8DnsDomainNameAlias) ) {
  543. //
  544. // Get the Unicode alias name
  545. //
  546. DomDnsDomainNameAlias = NetpAllocWStrFromUtf8Str( DomainInfo->DomUtf8DnsDomainNameAlias );
  547. if ( DomDnsDomainNameAlias == NULL ) {
  548. LeaveCriticalSection(&NlGlobalDomainCritSect);
  549. NlDereferenceDomain( DomainInfo );
  550. RetVal = FALSE;
  551. goto Cleanup;
  552. }
  553. //
  554. // Allocate storage for the path corresponding to the alias
  555. //
  556. AllocatedPathAlias = LocalAlloc( LMEM_ZEROINIT,
  557. SysVolSize + // sysvol part of the path
  558. sizeof(WCHAR) + // path separator
  559. wcslen(DomDnsDomainNameAlias)*sizeof(WCHAR) + // domain name part
  560. sizeof(WCHAR) ); // string terminator
  561. if ( AllocatedPathAlias == NULL ) {
  562. LeaveCriticalSection(&NlGlobalDomainCritSect);
  563. NlDereferenceDomain( DomainInfo );
  564. RetVal = FALSE;
  565. goto Cleanup;
  566. }
  567. //
  568. // Fill in the path corresponding to the alias
  569. //
  570. swprintf( AllocatedPathAlias,
  571. L"%ws\\%ws",
  572. NlGlobalParameters.UnicodeSysvolPath,
  573. DomDnsDomainNameAlias );
  574. //
  575. // Rename the junction. Ignore any failure.
  576. //
  577. if ( !MoveFile(AllocatedPathAlias, PathToShare) ) {
  578. NetStatus = GetLastError();
  579. if ( NetStatus != ERROR_FILE_NOT_FOUND ) {
  580. NlPrint(( NL_CRITICAL, "NlCreateSysvolShares: Failed to rename junction: %ws %ws 0x%lx\n",
  581. AllocatedPathAlias,
  582. PathToShare,
  583. NetStatus ));
  584. }
  585. } else {
  586. NlPrint(( NL_INIT, "Renamed SysVol junction from %ws to %ws\n",
  587. AllocatedPathAlias,
  588. PathToShare ));
  589. }
  590. }
  591. //
  592. // Now finish building the share path
  593. //
  594. RtlCopyMemory( Where, DEFAULT_SCRIPTS, sizeof(DEFAULT_SCRIPTS) );
  595. }
  596. LeaveCriticalSection(&NlGlobalDomainCritSect);
  597. NlDereferenceDomain( DomainInfo );
  598. NetlogonShareRelatedToSysvolShare = TRUE;
  599. } else {
  600. PathToShare = NlGlobalParameters.UnicodeScriptPath;
  601. }
  602. if ( NlGlobalParameters.SysVolReady ||
  603. !NetlogonShareRelatedToSysvolShare ) {
  604. if ( PathToShare != NULL ) {
  605. NetStatus = NlCreateShare( PathToShare,
  606. NETLOGON_SCRIPTS_SHARE,
  607. FALSE,
  608. TRUE, // update exclusive share access
  609. NlGlobalParameters.AllowExclusiveScriptsShareAccess ) ;
  610. if ( NetStatus != NERR_Success ) {
  611. LPWSTR MsgStrings[2];
  612. NlPrint((NL_CRITICAL, "NlCreateShare %lu\n", NetStatus ));
  613. MsgStrings[0] = PathToShare;
  614. MsgStrings[1] = (LPWSTR) ULongToPtr( NetStatus );
  615. NlpWriteEventlog (NELOG_NetlogonFailedToCreateShare,
  616. EVENTLOG_ERROR_TYPE,
  617. (LPBYTE) &NetStatus,
  618. sizeof(NetStatus),
  619. MsgStrings,
  620. 2 | NETP_LAST_MESSAGE_IS_NETSTATUS );
  621. /* This isn't fatal. Just continue */
  622. }
  623. }
  624. } else {
  625. NetStatus = NetShareDel( NULL, NETLOGON_SCRIPTS_SHARE, 0);
  626. if ( NetStatus != NERR_Success ) {
  627. if ( NetStatus != NERR_NetNameNotFound ) {
  628. NlPrint((NL_CRITICAL, "NetShareDel NETLOGON failed %lu\n", NetStatus ));
  629. }
  630. /* This isn't fatal. Just continue */
  631. }
  632. }
  633. Cleanup:
  634. if ( AllocatedPath != NULL ) {
  635. LocalFree( AllocatedPath );
  636. }
  637. if ( AllocatedPathAlias != NULL ) {
  638. LocalFree( AllocatedPathAlias );
  639. }
  640. if ( DomDnsDomainNameAlias != NULL ) {
  641. NetApiBufferFree( DomDnsDomainNameAlias );
  642. }
  643. return RetVal;
  644. }
  645. #ifdef _DC_NETLOGON
  646. BOOL
  647. NlInitDomainController(
  648. VOID
  649. )
  650. /*++
  651. Routine Description:
  652. Do Domain Controller specific initialization.
  653. Arguments:
  654. None.
  655. Return Value:
  656. TRUE -- iff initialization is successful.
  657. --*/
  658. {
  659. NTSTATUS Status;
  660. NET_API_STATUS NetStatus;
  661. //
  662. // Ensure the browser doesn't have extra Hosted domains.
  663. //
  664. if ( !GiveInstallHints( FALSE ) ) {
  665. return FALSE;
  666. }
  667. NlBrowserSyncHostedDomains();
  668. //
  669. // Check that the server is installed or install pending
  670. //
  671. if ( !GiveInstallHints( FALSE ) ) {
  672. return FALSE;
  673. }
  674. if ( !NetpIsServiceStarted( SERVICE_SERVER ) ){
  675. NlExit( NERR_ServerNotStarted, ERROR_SERVICE_DEPENDENCY_FAIL, LogError, NULL);
  676. return FALSE;
  677. }
  678. //
  679. // Create SYSVOL and Netlogon shares.
  680. //
  681. if ( !GiveInstallHints( FALSE ) ) {
  682. return FALSE;
  683. }
  684. if ( !NlCreateSysvolShares() ) {
  685. NlExit( SERVICE_UIC_RESOURCE, ERROR_NOT_ENOUGH_MEMORY, LogError, NULL);
  686. return FALSE;
  687. }
  688. //
  689. // Delete the key Netlogon\FullSyncKey
  690. // (This key was used on a BDC in releases prior to NT 5.0 to keep
  691. // synchronization state.)
  692. //
  693. NetStatus = RegDeleteKeyA(
  694. HKEY_LOCAL_MACHINE,
  695. NL_FULL_SYNC_KEY );
  696. if ( NetStatus != NERR_Success ) {
  697. if ( NetStatus != ERROR_FILE_NOT_FOUND ) {
  698. NlPrint((NL_CRITICAL, "Cannot delete Netlogon\\FullSyncKey %lu\n", NetStatus ));
  699. }
  700. /* This isn't fatal. Just continue */
  701. }
  702. //
  703. // Tell LSA whether we emulate NT4.0
  704. //
  705. LsaINotifyNetlogonParametersChangeW(
  706. LsaEmulateNT4,
  707. REG_DWORD,
  708. (PWSTR)&NlGlobalParameters.Nt4Emulator,
  709. sizeof(NlGlobalParameters.Nt4Emulator) );
  710. #ifdef notdef
  711. //
  712. // Initialize any Hosted domains.
  713. //
  714. Status = NlInitializeHostedDomains();
  715. if (!NT_SUCCESS(Status)){
  716. NET_API_STATUS NetStatus = NetpNtStatusToApiStatus(Status);
  717. NlPrint((NL_CRITICAL, "Failed to initialize Hosted domains: 0x%x\n",Status));
  718. NlExit( SERVICE_UIC_M_DATABASE_ERROR, NetStatus, LogErrorAndNtStatus, NULL);
  719. return FALSE;
  720. }
  721. #endif // notdef
  722. //
  723. // Successful initialization.
  724. //
  725. return TRUE;
  726. }
  727. #endif // _DC_NETLOGON
  728. NET_API_STATUS
  729. NlReadPersitantTrustedDomainList(
  730. VOID
  731. )
  732. /*++
  733. Routine Description:
  734. Read the persistant trusted domain list
  735. Arguments:
  736. None.
  737. Return Value:
  738. TRUE -- iff initialization is successful.
  739. --*/
  740. {
  741. NTSTATUS Status;
  742. NET_API_STATUS NetStatus;
  743. PDOMAIN_INFO DomainInfo = NULL;
  744. PDS_DOMAIN_TRUSTSW ForestTrustList = NULL;
  745. ULONG ForestTrustListCount;
  746. ULONG ForestTrustListSize;
  747. PDS_DOMAIN_TRUSTSW RegForestTrustList = NULL;
  748. ULONG RegForestTrustListCount;
  749. ULONG RegForestTrustListSize;
  750. //
  751. // Get pointer to global domain info.
  752. //
  753. DomainInfo = NlFindNetbiosDomain( NULL, TRUE ); // Primary domain
  754. if ( DomainInfo == NULL ) {
  755. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  756. goto Cleanup;
  757. }
  758. //
  759. // Get the cached trusted domain list from the registry.
  760. // (Do this even if the data isn't used to force deletion of the registry entry.)
  761. //
  762. // The TDL was kept in the registry for NT 4. NT 5 keeps it in a binary file.
  763. //
  764. NetStatus = NlReadRegTrustedDomainList (
  765. DomainInfo,
  766. TRUE, // Delete this registry key since we no longer need it.
  767. &RegForestTrustList,
  768. &RegForestTrustListSize,
  769. &RegForestTrustListCount );
  770. if ( NetStatus != NO_ERROR ) {
  771. goto Cleanup;
  772. }
  773. //
  774. // If NCPA has just joined a domain,
  775. // and has pre-determined the trusted domain list for us,
  776. // pick up that list.
  777. //
  778. // When this machine joins a domain,
  779. // NCPA caches the trusted domain list where we can find it. That ensures the
  780. // trusted domain list is available upon reboot even before we dial via RAS. Winlogon
  781. // can therefore get the trusted domain list from us under those circumstances.
  782. //
  783. (VOID) NlReadFileTrustedDomainList (
  784. DomainInfo,
  785. NL_FOREST_BINARY_LOG_FILE_JOIN,
  786. TRUE, // Delete this file since we no longer need it.
  787. DS_DOMAIN_VALID_FLAGS, // Read everything
  788. &ForestTrustList,
  789. &ForestTrustListSize,
  790. &ForestTrustListCount );
  791. //
  792. // If there is a cached list,
  793. // Save it back in the primary file for future starts.
  794. //
  795. if ( ForestTrustListCount ) {
  796. NlPrint(( NL_INIT,
  797. "Replacing trusted domain list with one for newly joined %ws domain.\n",
  798. DomainInfo->DomUnicodeDomainName));
  799. NetStatus = NlWriteFileForestTrustList (
  800. NL_FOREST_BINARY_LOG_FILE,
  801. ForestTrustList,
  802. ForestTrustListCount );
  803. if ( NetStatus != NO_ERROR ) {
  804. LPWSTR MsgStrings[2];
  805. MsgStrings[0] = NL_FOREST_BINARY_LOG_FILE;
  806. MsgStrings[1] = (LPWSTR) ULongToPtr( NetStatus );
  807. NlpWriteEventlog (NELOG_NetlogonFailedFileCreate,
  808. EVENTLOG_ERROR_TYPE,
  809. (LPBYTE) &NetStatus,
  810. sizeof(NetStatus),
  811. MsgStrings,
  812. 2 | NETP_LAST_MESSAGE_IS_NETSTATUS );
  813. }
  814. //
  815. // Indicate that we no longer know what site we're in.
  816. //
  817. NlSetDynamicSiteName( NULL );
  818. //
  819. // Otherwise, read the current one from the binary file.
  820. //
  821. } else {
  822. NlPrint(( NL_INIT, "Getting cached trusted domain list from binary file.\n" ));
  823. (VOID) NlReadFileTrustedDomainList (
  824. DomainInfo,
  825. NL_FOREST_BINARY_LOG_FILE,
  826. FALSE, // Don't delete (Save it for the next boot)
  827. DS_DOMAIN_VALID_FLAGS, // Read everything
  828. &ForestTrustList,
  829. &ForestTrustListSize,
  830. &ForestTrustListCount );
  831. //
  832. // If there is no information in the file,
  833. // use the information from the registry.
  834. //
  835. if ( ForestTrustListCount == 0 ) {
  836. NlPrint(( NL_INIT, "There is no binary file (use registry).\n" ));
  837. ForestTrustList = RegForestTrustList;
  838. RegForestTrustList = NULL;
  839. ForestTrustListSize = RegForestTrustListSize;
  840. ForestTrustListCount = RegForestTrustListCount;
  841. //
  842. // Save the collected information to the binary file.
  843. //
  844. NetStatus = NlWriteFileForestTrustList (
  845. NL_FOREST_BINARY_LOG_FILE,
  846. ForestTrustList,
  847. ForestTrustListCount );
  848. if ( NetStatus != NO_ERROR ) {
  849. LPWSTR MsgStrings[2];
  850. MsgStrings[0] = NL_FOREST_BINARY_LOG_FILE;
  851. MsgStrings[1] = (LPWSTR) ULongToPtr( NetStatus );
  852. NlpWriteEventlog (NELOG_NetlogonFailedFileCreate,
  853. EVENTLOG_ERROR_TYPE,
  854. (LPBYTE) &NetStatus,
  855. sizeof(NetStatus),
  856. MsgStrings,
  857. 2 | NETP_LAST_MESSAGE_IS_NETSTATUS );
  858. }
  859. }
  860. }
  861. //
  862. // In all cases, set the trusted domain list into globals.
  863. //
  864. (VOID) NlSetForestTrustList( DomainInfo,
  865. &ForestTrustList,
  866. ForestTrustListSize,
  867. ForestTrustListCount );
  868. NetStatus = NO_ERROR;
  869. //
  870. // Return
  871. //
  872. Cleanup:
  873. if ( DomainInfo != NULL ) {
  874. NlDereferenceDomain( DomainInfo );
  875. }
  876. if ( ForestTrustList != NULL ) {
  877. NetApiBufferFree( ForestTrustList );
  878. }
  879. if ( RegForestTrustList != NULL ) {
  880. NetApiBufferFree( RegForestTrustList );
  881. }
  882. return NetStatus;
  883. }
  884. BOOL
  885. NlInitWorkstation(
  886. VOID
  887. )
  888. /*++
  889. Routine Description:
  890. Do workstation specific initialization.
  891. Arguments:
  892. None.
  893. Return Value:
  894. TRUE -- iff initialization is successful.
  895. --*/
  896. {
  897. NET_API_STATUS NetStatus;
  898. //
  899. // Get the persistant trusted domain list.
  900. //
  901. NetStatus = NlReadPersitantTrustedDomainList();
  902. if ( NetStatus != NO_ERROR ) {
  903. NlExit( SERVICE_UIC_RESOURCE, NetStatus, LogError, NULL);
  904. return FALSE;
  905. }
  906. return TRUE;
  907. }
  908. NTSTATUS
  909. NlWaitForService(
  910. LPWSTR ServiceName,
  911. ULONG Timeout,
  912. BOOLEAN RequireAutoStart
  913. )
  914. /*++
  915. Routine Description:
  916. Wait up to Timeout seconds for the a service to start.
  917. Arguments:
  918. Timeout - Timeout for event (in seconds).
  919. RequireAutoStart - TRUE if the service start needs to be automatic.
  920. Return Status:
  921. STATUS_SUCCESS - Indicates service successfully initialized.
  922. STATUS_TIMEOUT - Timeout occurred.
  923. --*/
  924. {
  925. NTSTATUS Status;
  926. NET_API_STATUS NetStatus;
  927. SC_HANDLE ScManagerHandle = NULL;
  928. SC_HANDLE ServiceHandle = NULL;
  929. SERVICE_STATUS ServiceStatus;
  930. LPQUERY_SERVICE_CONFIG ServiceConfig;
  931. LPQUERY_SERVICE_CONFIG AllocServiceConfig = NULL;
  932. QUERY_SERVICE_CONFIG DummyServiceConfig;
  933. DWORD ServiceConfigSize;
  934. //
  935. // Open a handle to the Service.
  936. //
  937. ScManagerHandle = OpenSCManager(
  938. NULL,
  939. NULL,
  940. SC_MANAGER_CONNECT );
  941. if (ScManagerHandle == NULL) {
  942. NlPrint(( NL_CRITICAL,
  943. "NlWaitForService: %ws: OpenSCManager failed: %lu\n",
  944. ServiceName,
  945. GetLastError()));
  946. Status = STATUS_TIMEOUT;
  947. goto Cleanup;
  948. }
  949. ServiceHandle = OpenService(
  950. ScManagerHandle,
  951. ServiceName,
  952. SERVICE_QUERY_STATUS | SERVICE_QUERY_CONFIG );
  953. if ( ServiceHandle == NULL ) {
  954. NlPrint(( NL_CRITICAL,
  955. "NlWaitForService: %ws: OpenService failed: %lu\n",
  956. ServiceName,
  957. GetLastError()));
  958. Status = STATUS_TIMEOUT;
  959. goto Cleanup;
  960. }
  961. //
  962. // If need to have automatic service start up and
  963. // If the service isn't configured to be automatically started
  964. // by the service controller, don't bother waiting for it to start.
  965. // Also don't wait if the service is disabled.
  966. //
  967. // ?? Pass "DummyServiceConfig" and "sizeof(..)" since QueryService config
  968. // won't allow a null pointer, yet.
  969. if ( QueryServiceConfig(
  970. ServiceHandle,
  971. &DummyServiceConfig,
  972. sizeof(DummyServiceConfig),
  973. &ServiceConfigSize )) {
  974. ServiceConfig = &DummyServiceConfig;
  975. } else {
  976. NetStatus = GetLastError();
  977. if ( NetStatus != ERROR_INSUFFICIENT_BUFFER ) {
  978. NlPrint(( NL_CRITICAL,
  979. "NlWaitForService: %ws: QueryServiceConfig failed: %lu\n",
  980. ServiceName,
  981. NetStatus));
  982. Status = STATUS_TIMEOUT;
  983. goto Cleanup;
  984. }
  985. AllocServiceConfig = LocalAlloc( 0, ServiceConfigSize );
  986. ServiceConfig = AllocServiceConfig;
  987. if ( AllocServiceConfig == NULL ) {
  988. Status = STATUS_NO_MEMORY;
  989. goto Cleanup;
  990. }
  991. if ( !QueryServiceConfig(
  992. ServiceHandle,
  993. ServiceConfig,
  994. ServiceConfigSize,
  995. &ServiceConfigSize )) {
  996. NlPrint(( NL_CRITICAL,
  997. "NlWaitForService: %ws: QueryServiceConfig failed again: %lu\n",
  998. ServiceName,
  999. GetLastError()));
  1000. Status = STATUS_TIMEOUT;
  1001. goto Cleanup;
  1002. }
  1003. }
  1004. if ( (RequireAutoStart && ServiceConfig->dwStartType != SERVICE_AUTO_START) ||
  1005. (ServiceConfig->dwStartType == SERVICE_DISABLED) ) {
  1006. NlPrint(( NL_CRITICAL,
  1007. "NlWaitForService: %ws Service start type invalid: %lu\n",
  1008. ServiceName,
  1009. ServiceConfig->dwStartType ));
  1010. Status = STATUS_TIMEOUT;
  1011. goto Cleanup;
  1012. }
  1013. //
  1014. // Loop waiting for the service to start.
  1015. // (Convert Timeout to a number of 5 second iterations)
  1016. //
  1017. Timeout = (Timeout+5)/5;
  1018. for (;;) {
  1019. //
  1020. // Query the status of the service.
  1021. //
  1022. if (! QueryServiceStatus( ServiceHandle, &ServiceStatus )) {
  1023. NlPrint(( NL_CRITICAL,
  1024. "NlWaitForService: %ws: QueryServiceStatus failed: %lu\n",
  1025. ServiceName,
  1026. GetLastError() ));
  1027. Status = STATUS_TIMEOUT;
  1028. goto Cleanup;
  1029. }
  1030. //
  1031. // Return or continue waiting depending on the state of
  1032. // the service.
  1033. //
  1034. switch( ServiceStatus.dwCurrentState) {
  1035. case SERVICE_RUNNING:
  1036. Status = STATUS_SUCCESS;
  1037. goto Cleanup;
  1038. case SERVICE_STOPPED:
  1039. //
  1040. // If service failed to start,
  1041. // error out now. The caller has waited long enough to start.
  1042. //
  1043. if ( ServiceStatus.dwWin32ExitCode != ERROR_SERVICE_NEVER_STARTED ){
  1044. NlPrint(( NL_CRITICAL,
  1045. "NlWaitForService: %ws: service couldn't start: %lu %lx\n",
  1046. ServiceName,
  1047. ServiceStatus.dwWin32ExitCode,
  1048. ServiceStatus.dwWin32ExitCode ));
  1049. if ( ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_SPECIFIC_ERROR ) {
  1050. NlPrint(( NL_CRITICAL, " Service specific error code: %lu %lx\n",
  1051. ServiceStatus.dwServiceSpecificExitCode,
  1052. ServiceStatus.dwServiceSpecificExitCode ));
  1053. }
  1054. Status = STATUS_TIMEOUT;
  1055. goto Cleanup;
  1056. }
  1057. //
  1058. // If service has never been started on this boot,
  1059. // continue waiting for it to start.
  1060. //
  1061. break;
  1062. //
  1063. // If service is trying to start up now,
  1064. // continue waiting for it to start.
  1065. //
  1066. case SERVICE_START_PENDING:
  1067. break;
  1068. //
  1069. // Any other state is bogus.
  1070. //
  1071. default:
  1072. NlPrint(( NL_CRITICAL,
  1073. "NlWaitForService: %ws: Invalid service state: %lu\n",
  1074. ServiceName,
  1075. ServiceStatus.dwCurrentState ));
  1076. Status = STATUS_TIMEOUT;
  1077. goto Cleanup;
  1078. }
  1079. //
  1080. // Wait five seconds for the service to start.
  1081. // If it has successfully started, just return now.
  1082. //
  1083. NlPrint(( NL_INIT,
  1084. "NlWaitForService: %ws: wait for service to start\n",
  1085. ServiceName ));
  1086. (VOID) WaitForSingleObject( NlGlobalTerminateEvent, 5 * 1000 );
  1087. if ( NlGlobalTerminate ) {
  1088. Status = STATUS_TIMEOUT;
  1089. goto Cleanup;
  1090. }
  1091. if ( !GiveInstallHints( FALSE ) ) {
  1092. Status = STATUS_TIMEOUT;
  1093. goto Cleanup;
  1094. }
  1095. //
  1096. // If we've waited long enough for the service to start,
  1097. // time out now.
  1098. //
  1099. if ( (--Timeout) == 0 ) {
  1100. Status = STATUS_TIMEOUT;
  1101. goto Cleanup;
  1102. }
  1103. }
  1104. /* NOT REACHED */
  1105. Cleanup:
  1106. if ( ScManagerHandle != NULL ) {
  1107. (VOID) CloseServiceHandle(ScManagerHandle);
  1108. }
  1109. if ( ServiceHandle != NULL ) {
  1110. (VOID) CloseServiceHandle(ServiceHandle);
  1111. }
  1112. if ( AllocServiceConfig != NULL ) {
  1113. LocalFree( AllocServiceConfig );
  1114. }
  1115. return Status;
  1116. }
  1117. VOID
  1118. NlInitTcpRpc(
  1119. IN LPVOID ThreadParam
  1120. )
  1121. /*++
  1122. Routine Description:
  1123. This function initializes TCP RPC for Netlogon. It runs in a separate thread
  1124. so that Netlogon need not depend on RPCSS.
  1125. Arguments:
  1126. None.
  1127. Return Value:
  1128. None.
  1129. --*/
  1130. {
  1131. NTSTATUS Status;
  1132. NET_API_STATUS NetStatus;
  1133. ULONG RetryCount;
  1134. RPC_POLICY RpcPolicy;
  1135. DWORD PortNumber = 0;
  1136. // #define NL_TOTAL_RPC_SLEEP_TIME (5*60*1000) // 5 minutes
  1137. // #define NL_RPC_SLEEP_TIME (10 * 1000) // 10 seconds
  1138. // #define NL_RPC_RETRY_COUNT (NL_TOTAL_RPC_SLEEP_TIME/NL_RPC_SLEEP_TIME)
  1139. //
  1140. // Set up TCP/IP as a non-authenticated transport.
  1141. //
  1142. // The named pipe transport is authenticated. Since Netlogon runs as Local
  1143. // System, Kerberos will authenticate using the machine account. On the BDC/PDC
  1144. // connection, this requires both the PDC and BDC
  1145. // machine account to be in sync. However, netlogon is responsible for making the
  1146. // BDC account in sync by trying the old and new passwords in NlSessionSetup. And
  1147. // Netlogon (or DS replication in the future) is responsible for keeping the PDC password
  1148. // in sync. Thus, it is better to remove Netlogon's dependency on Kerberos authentication.
  1149. //
  1150. //
  1151. // Wait up to 15 minutes for the RPCSS service to start
  1152. //
  1153. Status = NlWaitForService( L"RPCSS", 15 * 60, TRUE );
  1154. if ( Status != STATUS_SUCCESS ) {
  1155. return;
  1156. }
  1157. if ( NlGlobalTerminate ) {
  1158. goto Cleanup;
  1159. }
  1160. //
  1161. // Tell RPC to not fail. That'll ensure RPC uses TCP when it gets added.
  1162. //
  1163. RtlZeroMemory( &RpcPolicy, sizeof(RpcPolicy) );
  1164. RpcPolicy.Length = sizeof(RpcPolicy);
  1165. RpcPolicy.EndpointFlags = RPC_C_DONT_FAIL;
  1166. // The next statement is explicitly commented out
  1167. // because we want to honor admin settings and avoid
  1168. // listening on those NICs which admin has explicitly
  1169. // configured as such.
  1170. //
  1171. // RpcPolicy.NICFlags = RPC_C_BIND_TO_ALL_NICS;
  1172. //
  1173. // If the port number is configured in the registry, use it
  1174. //
  1175. if ( NlReadDwordNetlogonRegValue("DcTcpipPort", &PortNumber) ) {
  1176. CHAR PortNumberStr[16];
  1177. NlPrint(( NL_INIT, "Using RPC server port %lu\n", PortNumber ));
  1178. _ultoa( PortNumber, PortNumberStr, 10 );
  1179. NetStatus = RpcServerUseProtseqEpExA(
  1180. "ncacn_ip_tcp",
  1181. RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
  1182. PortNumberStr, // port number
  1183. NULL, // no security descriptor
  1184. &RpcPolicy );
  1185. if ( NetStatus != NO_ERROR ) {
  1186. LPWSTR MsgStrings[2];
  1187. NlPrint(( NL_CRITICAL, "Can't RpcServerUseProtseq for port %s: %ld (giving up)\n",
  1188. PortNumberStr,
  1189. NetStatus ));
  1190. MsgStrings[0] = NetpAllocWStrFromAStr( PortNumberStr );
  1191. if ( MsgStrings[0] != NULL ) {
  1192. MsgStrings[1] = (LPWSTR) UlongToPtr( NetStatus );
  1193. NlpWriteEventlog( NELOG_NetlogonRpcPortRequestFailure,
  1194. EVENTLOG_ERROR_TYPE,
  1195. NULL,
  1196. 0,
  1197. MsgStrings,
  1198. 2 | NETP_LAST_MESSAGE_IS_NETSTATUS );
  1199. NetApiBufferFree( MsgStrings[0] );
  1200. }
  1201. }
  1202. //
  1203. // Otherwise, don't specify the port number.
  1204. // The end point mapper will choose it dynamically
  1205. // at the time a client connects to us.
  1206. //
  1207. } else {
  1208. NetStatus = RpcServerUseProtseqExW(
  1209. L"ncacn_ip_tcp",
  1210. RPC_C_PROTSEQ_MAX_REQS_DEFAULT,
  1211. NULL, // no security descriptor
  1212. &RpcPolicy );
  1213. if ( NetStatus != NO_ERROR ) {
  1214. NlPrint(( NL_CRITICAL, "Can't RpcServerUseProtseq %ld (giving up)\n", NetStatus ));
  1215. goto Cleanup;
  1216. }
  1217. }
  1218. {
  1219. RPC_BINDING_VECTOR *BindingVector = NULL;
  1220. NetStatus = RpcServerInqBindings(&BindingVector);
  1221. if ( NetStatus != NO_ERROR) {
  1222. NlPrint((NL_CRITICAL, "Can't RpcServerInqBindings %ld\n", NetStatus ));
  1223. goto Cleanup;
  1224. }
  1225. //
  1226. // Some early versions of NT 5 still haven't started RPCSS by the time
  1227. // we get here.
  1228. //
  1229. // for ( RetryCount = NL_RPC_RETRY_COUNT; RetryCount != 0; RetryCount-- ) {
  1230. NetStatus = RpcEpRegister(
  1231. logon_ServerIfHandle,
  1232. BindingVector,
  1233. NULL, // no uuid vector
  1234. L"" // no annotation
  1235. );
  1236. if ( NetStatus != NO_ERROR ) {
  1237. NlPrint((NL_CRITICAL, "Can't RpcEpRegister %ld (giving up)\n", NetStatus ));
  1238. }
  1239. // if (RetryCount == 1 ) {
  1240. // } else {
  1241. // NlPrint((NL_CRITICAL, "Can't RpcEpRegister %ld (trying again)\n", NetStatus ));
  1242. /// (VOID) WaitForSingleObject( NlGlobalTerminateEvent, NL_RPC_SLEEP_TIME );
  1243. // }
  1244. // }
  1245. RpcBindingVectorFree(&BindingVector);
  1246. if ( NetStatus != NO_ERROR) {
  1247. NlPrint((NL_CRITICAL, "Can't RpcEpRegister %ld\n", NetStatus ));
  1248. goto Cleanup;
  1249. }
  1250. NlGlobalTcpIpRpcServerStarted = TRUE;
  1251. }
  1252. //
  1253. // Finish enabling Netlogon functionality.
  1254. //
  1255. Cleanup:
  1256. NlGlobalPartialDisable = FALSE;
  1257. //
  1258. // NlMainLoop avoided doing an immediate announcement when first starting up.
  1259. // To do so would have the BDC call us (the PDC) prior to having TCP/IP RPC enabled.
  1260. // Thus, we do an announcement now to ensure the BDCs do call us as soon as possible
  1261. // after the PDC boots.
  1262. //
  1263. if ( !NlGlobalTerminate && NlGlobalPdcDoReplication ) {
  1264. LOCK_CHANGELOG();
  1265. NlGlobalChangeLogReplicateImmediately = TRUE;
  1266. UNLOCK_CHANGELOG();
  1267. if ( !SetEvent( NlGlobalChangeLogEvent ) ) {
  1268. NlPrint((NL_CRITICAL,
  1269. "Cannot set ChangeLog event: %lu\n",
  1270. GetLastError() ));
  1271. }
  1272. }
  1273. NlPrint((NL_INIT, "NlInitTcpRpc thread finished.\n" ));
  1274. UNREFERENCED_PARAMETER( ThreadParam );
  1275. }
  1276. VOID
  1277. NlpDsNotPaused(
  1278. IN PVOID Context,
  1279. IN BOOLEAN TimedOut
  1280. )
  1281. /*++
  1282. Routine Description:
  1283. Worker routine that gets called when the DS is no longer paused.
  1284. Arguments:
  1285. None.
  1286. Return Value:
  1287. None.
  1288. --*/
  1289. {
  1290. NlGlobalDsPaused = FALSE;
  1291. NlPrint((NL_INIT, "DS is no longer paused.\n" ));
  1292. UNREFERENCED_PARAMETER( Context );
  1293. UNREFERENCED_PARAMETER( TimedOut );
  1294. }
  1295. BOOL
  1296. NlInit(
  1297. VOID
  1298. )
  1299. /*++
  1300. Routine Description:
  1301. Initialize NETLOGON service related data structs after verfiying that
  1302. all conditions for startup have been satisfied. Will also create a
  1303. mailslot to listen to requests from clients and create two shares to
  1304. allow execution of logon scripts.
  1305. Arguments:
  1306. None.
  1307. Return Value:
  1308. TRUE -- iff initialization is successful.
  1309. --*/
  1310. {
  1311. NTSTATUS Status;
  1312. NET_API_STATUS NetStatus;
  1313. LONG RegStatus;
  1314. OBJECT_ATTRIBUTES EventAttributes;
  1315. UNICODE_STRING EventName;
  1316. NT_PRODUCT_TYPE NtProductType;
  1317. WORD wVersionRequested;
  1318. WSADATA wsaData;
  1319. int err;
  1320. HANDLE WmiInitThreadHandle = NULL;
  1321. DWORD ThreadId;
  1322. //
  1323. // Initialize the AuthZ resource manager on DC
  1324. //
  1325. if ( !NlGlobalMemberWorkstation ) {
  1326. NetStatus = NlInitializeAuthzRM();
  1327. if ( NetStatus != NO_ERROR ) {
  1328. NlExit( NELOG_NetlogonSystemError, NetStatus, LogError, NULL );
  1329. return FALSE;
  1330. }
  1331. }
  1332. //
  1333. // Initialize CryptoAPI provider.
  1334. //
  1335. if ( !CryptAcquireContext(
  1336. &NlGlobalCryptProvider,
  1337. NULL,
  1338. NULL,
  1339. PROV_RSA_FULL,
  1340. CRYPT_VERIFYCONTEXT
  1341. ))
  1342. {
  1343. NlGlobalCryptProvider = (HCRYPTPROV)NULL;
  1344. NlExit( NELOG_NetlogonSystemError, GetLastError(), LogError, NULL);
  1345. return FALSE;
  1346. }
  1347. //
  1348. // Let the ChangeLog routines know that Netlogon is started.
  1349. //
  1350. NlGlobalChangeLogNetlogonState = NetlogonStarting;
  1351. //
  1352. // Enable detection of duplicate event log messages
  1353. //
  1354. NetpEventlogSetTimeout ( NlGlobalEventlogHandle,
  1355. NlGlobalParameters.DuplicateEventlogTimeout*1000 );
  1356. //
  1357. // Don't let MaxConcurrentApi dynamically change
  1358. //
  1359. if ( !RtlGetNtProductType( &NtProductType ) ) {
  1360. NtProductType = NtProductWinNt;
  1361. }
  1362. NlGlobalMaxConcurrentApi = NlGlobalParameters.MaxConcurrentApi;
  1363. if ( NlGlobalMaxConcurrentApi == 0 ) {
  1364. if ( NlGlobalMemberWorkstation ) {
  1365. // Default to 1 concurrent API on a member workstation
  1366. if ( NtProductType == NtProductWinNt ) {
  1367. NlGlobalMaxConcurrentApi = 1;
  1368. // Default to 2 concurrent API on a member server
  1369. } else {
  1370. NlGlobalMaxConcurrentApi = 2;
  1371. }
  1372. } else {
  1373. // Default to 1 concurrent API on a DC
  1374. NlGlobalMaxConcurrentApi = 1;
  1375. }
  1376. }
  1377. if ( NlGlobalMaxConcurrentApi != 1 ) {
  1378. // One for the original binding and one for each concurrent logon api
  1379. NlGlobalMaxConcurrentApi += 1;
  1380. }
  1381. //
  1382. // Initialize worker threads.
  1383. //
  1384. if ( !NlGlobalMemberWorkstation ) {
  1385. NetStatus = NlWorkerInitialization();
  1386. if ( NetStatus != NO_ERROR ) {
  1387. NlExit( SERVICE_UIC_RESOURCE, NetStatus, LogError, NULL);
  1388. return FALSE;
  1389. }
  1390. }
  1391. //
  1392. // Check that the redirector is installed, will exit on error.
  1393. //
  1394. if ( !GiveInstallHints( FALSE ) ) {
  1395. return FALSE;
  1396. }
  1397. if ( !NetpIsServiceStarted( SERVICE_WORKSTATION ) ){
  1398. NlExit( NERR_WkstaNotStarted, ERROR_SERVICE_DEPENDENCY_FAIL, LogError, NULL);
  1399. return FALSE;
  1400. }
  1401. //
  1402. // Create well know SID for netlogon.dll
  1403. //
  1404. if ( !GiveInstallHints( FALSE ) ) {
  1405. return FALSE;
  1406. }
  1407. Status = NetpCreateWellKnownSids( NULL );
  1408. if( !NT_SUCCESS( Status ) ) {
  1409. NetStatus = NetpNtStatusToApiStatus( Status );
  1410. NlExit( SERVICE_UIC_RESOURCE, NetStatus, LogError, NULL);
  1411. return FALSE;
  1412. }
  1413. //
  1414. // Create the security descriptors we'll use for the APIs
  1415. //
  1416. Status = NlCreateNetlogonObjects();
  1417. if ( !NT_SUCCESS(Status) ) {
  1418. NET_API_STATUS NetStatus = NetpNtStatusToApiStatus(Status);
  1419. NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNtStatus, NULL);
  1420. return FALSE;
  1421. }
  1422. //
  1423. // Create Timer event
  1424. //
  1425. NlGlobalTimerEvent = CreateEvent(
  1426. NULL, // No special security
  1427. FALSE, // Auto Reset
  1428. FALSE, // No Timers need no attention
  1429. NULL ); // No name
  1430. if ( NlGlobalTimerEvent == NULL ) {
  1431. NlExit( NELOG_NetlogonSystemError, GetLastError(), LogErrorAndNetStatus, NULL);
  1432. return FALSE;
  1433. }
  1434. #if DBG
  1435. //
  1436. // create debug share. Ignore error.
  1437. //
  1438. if( NlCreateShare(
  1439. NlGlobalDebugSharePath,
  1440. DEBUG_SHARE_NAME,
  1441. FALSE,
  1442. FALSE, // don't update exclusive share access
  1443. FALSE ) != NERR_Success ) {
  1444. NlPrint((NL_CRITICAL, "Can't create Debug share (%ws, %ws).\n",
  1445. NlGlobalDebugSharePath, DEBUG_SHARE_NAME ));
  1446. }
  1447. #endif
  1448. //
  1449. // Initialize winsock. We need it for all DNS support.
  1450. //
  1451. if ( !GiveInstallHints( FALSE ) ) {
  1452. return FALSE;
  1453. }
  1454. wVersionRequested = MAKEWORD( 1, 1 );
  1455. err = WSAStartup( wVersionRequested, &wsaData );
  1456. if ( err == 0 ) {
  1457. if ( LOBYTE( wsaData.wVersion ) != 1 ||
  1458. HIBYTE( wsaData.wVersion ) != 1 ) {
  1459. WSACleanup();
  1460. NlPrint((NL_CRITICAL, "Wrong winsock version (continuing) %ld.\n", wsaData.wVersion ));
  1461. } else {
  1462. NlGlobalWinSockInitialized = TRUE;
  1463. }
  1464. } else {
  1465. NlPrint((NL_CRITICAL, "Can't initialize winsock (continuing) %ld.\n", err ));
  1466. }
  1467. //
  1468. // Open the browser so we can send and receive mailslot messages.
  1469. //
  1470. if ( !GiveInstallHints( FALSE ) ) {
  1471. return FALSE;
  1472. }
  1473. if ( !NlBrowserOpen() ) {
  1474. return FALSE;
  1475. }
  1476. //
  1477. // Wait for SAM/LSA to start
  1478. // Do this before the first access to SAM/LSA/DS.
  1479. //
  1480. if ( !GiveInstallHints( FALSE ) ) {
  1481. return FALSE;
  1482. }
  1483. if ( !NlWaitForSamService( TRUE ) ) {
  1484. NlExit( SERVICE_UIC_M_DATABASE_ERROR, ERROR_INVALID_SERVER_STATE, LogError, NULL);
  1485. return FALSE;
  1486. }
  1487. //
  1488. // Re-initialize after netlogon.dll unload
  1489. // See if the DS is running.
  1490. // I only need this since nltest /unload loses all dll state.
  1491. if ( NlGlobalNetlogonUnloaded ) {
  1492. if ( SampUsingDsData() ) {
  1493. NlPrint((NL_INIT,
  1494. "Set DS-running bit after netlogon.dll unload\n" ));
  1495. I_NetLogonSetServiceBits( DS_DS_FLAG, DS_DS_FLAG );
  1496. }
  1497. if ( NetpIsServiceStarted( SERVICE_KDC ) ){
  1498. NlPrint((NL_INIT,
  1499. "Set KDC-running bit after netlogon.dll unload\n" ));
  1500. I_NetLogonSetServiceBits( DS_KDC_FLAG, DS_KDC_FLAG );
  1501. }
  1502. }
  1503. //
  1504. // Initialize the Site lookup code
  1505. //
  1506. NetStatus = NlSiteInitialize();
  1507. if ( NetStatus != NERR_Success ) {
  1508. if ( NetStatus == NELOG_NetlogonBadSiteName ) {
  1509. // Error already logged
  1510. NlExit( NetStatus, NetStatus, DontLogError, NULL);
  1511. } else {
  1512. NlExit( NELOG_NetlogonGetSubnetToSite, NetStatus, LogErrorAndNetStatus, NULL);
  1513. }
  1514. return FALSE;
  1515. }
  1516. //
  1517. // Build a list of transports for later reference
  1518. //
  1519. if ( !GiveInstallHints( FALSE ) ) {
  1520. return FALSE;
  1521. }
  1522. NetStatus = NlTransportOpen();
  1523. if ( NetStatus != NERR_Success ) {
  1524. NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL);
  1525. return FALSE;
  1526. }
  1527. //
  1528. // Initialize the Dynamic Dns code
  1529. //
  1530. NetStatus = NlDnsInitialize();
  1531. if ( NetStatus != NERR_Success ) {
  1532. NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL);
  1533. return FALSE;
  1534. }
  1535. //
  1536. // Initialize the Hosted domain module and the primary domain.
  1537. //
  1538. if ( !GiveInstallHints( FALSE ) ) {
  1539. return FALSE;
  1540. }
  1541. NetStatus = NlInitializeDomains();
  1542. if ( NetStatus != NERR_Success ) {
  1543. // NlExit already called
  1544. return FALSE;
  1545. }
  1546. //
  1547. // Initialize WMI tracing in a separate thread.
  1548. // Ignore any failure.
  1549. //
  1550. WmiInitThreadHandle =
  1551. CreateThread(
  1552. NULL, // No security attributes
  1553. 0,
  1554. (LPTHREAD_START_ROUTINE)
  1555. NlpInitializeTrace,
  1556. NULL,
  1557. 0, // No special creation flags
  1558. &ThreadId );
  1559. if ( WmiInitThreadHandle == NULL ) {
  1560. NlPrint(( NL_CRITICAL, "Can't create WMI init thread %ld\n", GetLastError() ));
  1561. } else {
  1562. CloseHandle( WmiInitThreadHandle );
  1563. }
  1564. //
  1565. // Do Workstation or Domain Controller specific initialization
  1566. //
  1567. if ( !GiveInstallHints( FALSE ) ) {
  1568. return FALSE;
  1569. }
  1570. if ( NlGlobalMemberWorkstation ) {
  1571. if ( !NlInitWorkstation() ) {
  1572. return FALSE;
  1573. }
  1574. } else {
  1575. if ( !NlInitDomainController() ) {
  1576. return FALSE;
  1577. }
  1578. }
  1579. //
  1580. // Create an event that is signalled when the last MSV thread leaves
  1581. // a netlogon routine.
  1582. //
  1583. NlGlobalMsvTerminateEvent = CreateEvent( NULL, // No security attributes
  1584. TRUE, // Must be manually reset
  1585. FALSE, // Initially not signaled
  1586. NULL ); // No name
  1587. if ( NlGlobalMsvTerminateEvent == NULL ) {
  1588. NlExit( NELOG_NetlogonSystemError, GetLastError(), LogErrorAndNetStatus, NULL);
  1589. return FALSE;
  1590. }
  1591. NlGlobalMsvEnabled = TRUE;
  1592. //
  1593. // We are now ready to act as a Netlogon service
  1594. // Enable RPC
  1595. //
  1596. if ( !GiveInstallHints( FALSE ) ) {
  1597. return FALSE;
  1598. }
  1599. NlPrint((NL_INIT,"Starting RPC server.\n"));
  1600. //
  1601. // Tell RPC that Netlogon support the Netlogon security package.
  1602. //
  1603. #ifdef ENABLE_AUTH_RPC
  1604. if ( !NlGlobalMemberWorkstation ) {
  1605. NetStatus = RpcServerRegisterAuthInfo(
  1606. NL_PACKAGE_NAME,
  1607. RPC_C_AUTHN_NETLOGON,
  1608. NULL,
  1609. NULL );
  1610. if ( NetStatus == RPC_S_UNKNOWN_AUTHN_SERVICE ) {
  1611. NlGlobalServerSupportsAuthRpc = FALSE;
  1612. NlPrint((NL_CRITICAL, "Rpc doesn't support Netlogon Authentication service. Disable it.\n" ));
  1613. } else if (NetStatus != NERR_Success) {
  1614. NlExit( NELOG_NetlogonFailedToAddRpcInterface, NetStatus, LogErrorAndNetStatus, NULL );
  1615. return FALSE;
  1616. }
  1617. }
  1618. #endif // ENABLE_AUTH_RPC
  1619. #ifdef ROGUE_DC
  1620. if ( ERROR_SUCCESS != RegOpenKeyExW(
  1621. HKEY_LOCAL_MACHINE,
  1622. L"System\\CurrentControlSet\\Services\\Kdc\\Rogue",
  1623. 0,
  1624. KEY_READ,
  1625. &NlGlobalRogueKey ))
  1626. {
  1627. NlPrint((NL_CRITICAL, "Failed to read the \"Rogue\" key from registry\n"));
  1628. }
  1629. #endif
  1630. //
  1631. // NOTE: Now all RPC servers in lsass.exe (now winlogon) share the same
  1632. // pipe name. However, in order to support communication with
  1633. // version 1.0 of WinNt, it is necessary for the Client Pipe name
  1634. // to remain the same as it was in version 1.0. Mapping to the new
  1635. // name is performed in the Named Pipe File System code.
  1636. //
  1637. NetStatus = RpcpAddInterface ( L"lsass", logon_ServerIfHandle );
  1638. if (NetStatus != NERR_Success) {
  1639. NlExit( NELOG_NetlogonFailedToAddRpcInterface, NetStatus, LogErrorAndNetStatus, NULL );
  1640. return FALSE;
  1641. }
  1642. NlGlobalRpcServerStarted = TRUE;
  1643. //
  1644. // Start TCP/IP transport in another thread to avoid dependency on RPCSS.
  1645. //
  1646. if ( !NlGlobalMemberWorkstation ) {
  1647. HANDLE LocalThreadHandle;
  1648. DWORD ThreadId;
  1649. NlGlobalPartialDisable = TRUE;
  1650. //
  1651. // Queue the TCP/IP initialization to a high priority worker thread.
  1652. // (We'd rather not wait for discovery on 100's of trusted domains.)
  1653. //
  1654. NlInitializeWorkItem( &NlGlobalRpcInitWorkItem, NlInitTcpRpc, NULL );
  1655. if ( !NlQueueWorkItem( &NlGlobalRpcInitWorkItem, TRUE, TRUE ) ) {
  1656. NlGlobalPartialDisable = FALSE;
  1657. NlPrint((NL_CRITICAL, "Can't create TcpRpc Thread\n" ));
  1658. }
  1659. }
  1660. //
  1661. // If the DS isn't backsyncing,
  1662. // avoid overhead of finding out when it is done.
  1663. //
  1664. if ( NlGlobalMemberWorkstation ) {
  1665. NlGlobalDsPaused = FALSE;
  1666. } else {
  1667. NlGlobalDsPaused = LsaIIsDsPaused();
  1668. if ( NlGlobalDsPaused ) {
  1669. NlPrint((NL_INIT, "NlInit: DS is paused.\n" ));
  1670. //
  1671. // Open the event that the DS triggers after it is no longer paused
  1672. //
  1673. NlGlobalDsPausedEvent = OpenEvent( SYNCHRONIZE,
  1674. FALSE,
  1675. DS_SYNCED_EVENT_NAME_W );
  1676. if ( NlGlobalDsPausedEvent == NULL ) {
  1677. NetStatus = GetLastError();
  1678. NlPrint((NL_CRITICAL, "NlInit: Cannot open DS paused event. %ld\n", NetStatus ));
  1679. NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL);
  1680. return FALSE;
  1681. }
  1682. //
  1683. // Register to wait on the event.
  1684. //
  1685. if ( !RegisterWaitForSingleObject(
  1686. &NlGlobalDsPausedWaitHandle,
  1687. NlGlobalDsPausedEvent,
  1688. NlpDsNotPaused, // Callback routine
  1689. NULL, // No context
  1690. -1, // Wait forever
  1691. WT_EXECUTEINWAITTHREAD | // We're quick so reduce the overhead
  1692. WT_EXECUTEONLYONCE ) ) { // Once the DS triggers, we're done
  1693. NetStatus = GetLastError();
  1694. NlPrint((NL_CRITICAL, "NlInit: Cannot register DS paused wait routine. %ld\n", NetStatus ));
  1695. NlExit( NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL);
  1696. return FALSE;
  1697. }
  1698. }
  1699. }
  1700. //
  1701. // Let the ChangeLog routines know that Netlogon is started.
  1702. //
  1703. NlGlobalChangeLogNetlogonState = NetlogonStarted;
  1704. // Set an event telling anyone wanting to call NETLOGON that we're
  1705. // initialized.
  1706. //
  1707. if ( !GiveInstallHints( FALSE ) ) {
  1708. return FALSE;
  1709. }
  1710. RtlInitUnicodeString( &EventName, L"\\NETLOGON_SERVICE_STARTED");
  1711. InitializeObjectAttributes( &EventAttributes, &EventName, 0, 0, NULL );
  1712. Status = NtCreateEvent(
  1713. &NlGlobalStartedEvent,
  1714. SYNCHRONIZE|EVENT_MODIFY_STATE,
  1715. &EventAttributes,
  1716. NotificationEvent,
  1717. (BOOLEAN) FALSE // The event is initially not signaled
  1718. );
  1719. if ( !NT_SUCCESS(Status)) {
  1720. //
  1721. // If the event already exists, a waiting thread beat us to
  1722. // creating it. Just open it.
  1723. //
  1724. if( Status == STATUS_OBJECT_NAME_EXISTS ||
  1725. Status == STATUS_OBJECT_NAME_COLLISION ) {
  1726. Status = NtOpenEvent( &NlGlobalStartedEvent,
  1727. SYNCHRONIZE|EVENT_MODIFY_STATE,
  1728. &EventAttributes );
  1729. }
  1730. if ( !NT_SUCCESS(Status)) {
  1731. NET_API_STATUS NetStatus = NetpNtStatusToApiStatus(Status);
  1732. NlPrint((NL_CRITICAL,
  1733. " Failed to open NETLOGON_SERVICE_STARTED event. %lX\n",
  1734. Status ));
  1735. NlPrint((NL_CRITICAL,
  1736. " Failing to initialize SAM Server.\n"));
  1737. NlExit( SERVICE_UIC_SYSTEM, NetStatus, LogError, NULL);
  1738. return FALSE;
  1739. }
  1740. }
  1741. Status = NtSetEvent( NlGlobalStartedEvent, NULL );
  1742. if ( !NT_SUCCESS(Status)) {
  1743. NET_API_STATUS NetStatus = NetpNtStatusToApiStatus(Status);
  1744. NlPrint((NL_CRITICAL,
  1745. "Failed to set NETLOGON_SERVICE_STARTED event. %lX\n",
  1746. Status ));
  1747. NlPrint((NL_CRITICAL, " Failing to initialize SAM Server.\n"));
  1748. NtClose(NlGlobalStartedEvent);
  1749. NlExit( SERVICE_UIC_SYSTEM, NetStatus, LogError, NULL);
  1750. return FALSE;
  1751. }
  1752. //
  1753. // Don't close the event handle. Closing it would delete the event and
  1754. // a future waiter would never see it be set.
  1755. //
  1756. //
  1757. // Query the Windows Time service to determine if this machine
  1758. // is the server of the Time service and if it is a good time server.
  1759. //
  1760. // We need to make this call after we've started RPC to avoid race
  1761. // conditions between netlogon and w32time. Both services will first
  1762. // start RPC and only then will try to set the service bits in netlogon.
  1763. // The last one up will correctly set the bits through calling
  1764. // W32TimeGetNetlogonServiceBits (in the case of netlogon) or
  1765. // I_NetLogonSetServiceBits (in the case of w32time).
  1766. //
  1767. // ???: Relink to the w32tclnt.lib when w32time folks move it to a
  1768. // public location.
  1769. //
  1770. if ( !NlGlobalMemberWorkstation ) {
  1771. ULONG TimeServiceBits;
  1772. NetStatus = W32TimeGetNetlogonServiceBits( NULL, &TimeServiceBits );
  1773. if ( NetStatus == NO_ERROR ) {
  1774. Status = I_NetLogonSetServiceBits( DS_TIMESERV_FLAG | DS_GOOD_TIMESERV_FLAG,
  1775. TimeServiceBits );
  1776. if ( !NT_SUCCESS(Status) ) {
  1777. NlPrint(( NL_CRITICAL, "Cannot I_NetLogonSetServiceBits %ld\n", Status ));
  1778. }
  1779. } else {
  1780. NlPrint(( NL_CRITICAL, "Cannot W32TimeGetNetlogonServiceBits 0x%lx\n", NetStatus ));
  1781. }
  1782. }
  1783. //
  1784. // we are just about done, this will be final hint
  1785. //
  1786. if ( !GiveInstallHints( TRUE ) ) {
  1787. return FALSE;
  1788. }
  1789. //
  1790. // Successful initialization.
  1791. //
  1792. return TRUE;
  1793. }
  1794. ULONG
  1795. NlGetDomainFlags(
  1796. IN PDOMAIN_INFO DomainInfo
  1797. )
  1798. /*++
  1799. Routine Description:
  1800. Returns the flags describing what capabilities this Domain has.
  1801. Arguments:
  1802. DomainInfo - Domain whose flags are to be returned.
  1803. If NULL, only non-domain specific flags are returned.
  1804. Return Value:
  1805. Status of the operation.
  1806. --*/
  1807. {
  1808. ULONG Flags=0;
  1809. //
  1810. // Grab the global flags.
  1811. //
  1812. LOCK_CHANGELOG();
  1813. Flags |= NlGlobalChangeLogServiceBits;
  1814. UNLOCK_CHANGELOG();
  1815. //
  1816. // A machine that supports the DS also supports an LDAP server
  1817. //
  1818. if ( Flags & DS_DS_FLAG ) {
  1819. Flags |= DS_LDAP_FLAG;
  1820. // NT 5 DCs are always writable
  1821. Flags |= DS_WRITABLE_FLAG;
  1822. }
  1823. //
  1824. // Grab the domain specific flags.
  1825. //
  1826. if ( DomainInfo != NULL ) {
  1827. if ( DomainInfo->DomRole == RolePrimary ) {
  1828. Flags |= DS_PDC_FLAG;
  1829. }
  1830. //
  1831. // If this is NDNC, we are only an LDAP server servicing it.
  1832. // So, set only those two flags and only if the DS is running.
  1833. //
  1834. if ( (DomainInfo->DomFlags & DOM_NON_DOMAIN_NC) != 0 &&
  1835. (Flags & DS_DS_FLAG) != 0 ) {
  1836. Flags = DS_NDNC_FLAG | DS_LDAP_FLAG | DS_WRITABLE_FLAG;
  1837. }
  1838. }
  1839. //
  1840. // If we're emulating AD/UNIX,
  1841. // turn off all of the bits they're not allowed to set.
  1842. //
  1843. #ifdef EMULATE_AD_UNIX
  1844. Flags &= ~(DS_DS_FLAG|DS_PDC_FLAG);
  1845. #endif // EMULATE_AD_UNIX
  1846. return Flags;
  1847. }
  1848. NET_API_STATUS
  1849. BuildSamLogonResponse(
  1850. IN PDOMAIN_INFO DomainInfo,
  1851. IN BOOLEAN UseNameAliases,
  1852. IN USHORT Opcode,
  1853. IN LPCWSTR UnicodeUserName OPTIONAL,
  1854. IN LPCWSTR TransportName,
  1855. IN LPCWSTR UnicodeWorkstationName,
  1856. IN BOOL IsNt5,
  1857. IN DWORD OurIpAddress,
  1858. OUT BYTE ResponseBuffer[NETLOGON_MAX_MS_SIZE],
  1859. OUT LPDWORD ResponseBufferSize
  1860. )
  1861. /*++
  1862. Routine Description:
  1863. Build the response message to a SAM Logon request.
  1864. Arguments:
  1865. DomainInfo - Hosted Domain message came from
  1866. UseNameAliases - TRUE if domain and forest name aliases (not active names)
  1867. should be returned in the response message.
  1868. Opcode - Opcode for the response message
  1869. UnicodeUserName - The name of the user logging on.
  1870. TransportName - Name of transport the request came in on
  1871. UnicodeWorkstationName - Name of the machine the request is from
  1872. IsNt5 - True if this is a response to an NT 5 query.
  1873. OurIpAddress - IP Address of the transport this message was received on.
  1874. 0: Not an IP transport
  1875. ResponseBuffer - Buffer to build the response in
  1876. ResponseBufferSize - Size (in bytes) of the returned message.
  1877. Return Value:
  1878. Status of the operation.
  1879. --*/
  1880. {
  1881. NET_API_STATUS NetStatus;
  1882. PCHAR Where;
  1883. PNETLOGON_SAM_LOGON_RESPONSE SamResponse = (PNETLOGON_SAM_LOGON_RESPONSE) ResponseBuffer;
  1884. ULONG ResponseNtVersion = 0;
  1885. //
  1886. // Pack the pre-NT 5.0 information.
  1887. //
  1888. SamResponse->Opcode = Opcode;
  1889. Where = (PCHAR) SamResponse->UnicodeLogonServer;
  1890. NetpLogonPutUnicodeString( DomainInfo->DomUncUnicodeComputerName,
  1891. sizeof(SamResponse->UnicodeLogonServer),
  1892. &Where );
  1893. NetpLogonPutUnicodeString( (LPWSTR) UnicodeUserName,
  1894. sizeof(SamResponse->UnicodeUserName),
  1895. &Where );
  1896. NetpLogonPutUnicodeString( DomainInfo->DomUnicodeDomainName,
  1897. sizeof(SamResponse->UnicodeDomainName),
  1898. &Where );
  1899. //
  1900. // Append GUID and DNS info if this is NT 5.0 asking,
  1901. //
  1902. if ( IsNt5 ) {
  1903. WORD CompressOffset[3]; // One per compressessed String
  1904. CHAR *CompressUtf8String[3];
  1905. ULONG CompressCount;
  1906. ULONG Utf8StringSize;
  1907. ULONG Flags = 0;
  1908. UCHAR ZeroByte = 0;
  1909. NetpLogonPutGuid( &DomainInfo->DomDomainGuidBuffer,
  1910. &Where );
  1911. // We don't handle the site GUID.
  1912. NetpLogonPutGuid( &NlGlobalZeroGuid,
  1913. &Where );
  1914. //
  1915. // If we're not responding to a message on an IP transport,
  1916. // don't include DNS naming information in the response.
  1917. //
  1918. if ( OurIpAddress == 0 ) {
  1919. //
  1920. // This routine is only called if the original caller used a Netbios domain name.
  1921. // Such a caller shouldn't be returned the DNS domain information. We have
  1922. // no reason to believe he has a DNS server.
  1923. // (This problem is also "fixed" on the client side such that the client ignores
  1924. // the DNS info. We're fixing it here to avoid putting the extra bytes on the wire.)
  1925. //
  1926. // Copy NULL Dns Tree name, dns domain name, and dns host name
  1927. //
  1928. NetpLogonPutBytes( &ZeroByte, 1, &Where );
  1929. NetpLogonPutBytes( &ZeroByte, 1, &Where );
  1930. NetpLogonPutBytes( &ZeroByte, 1, &Where );
  1931. } else {
  1932. //
  1933. // Initialize for copying Cutf-8 strings.
  1934. //
  1935. Utf8StringSize = sizeof(SamResponse->DnsForestName) +
  1936. sizeof(SamResponse->DnsDomainName) +
  1937. sizeof(SamResponse->DnsHostName);
  1938. CompressCount = 0; // No strings compressed yet.
  1939. //
  1940. // Copy the DnsTree name into the message.
  1941. //
  1942. // If we are instructed to use name aliases and
  1943. // there is an alias for forest name, use it.
  1944. // Otherwise, use the active forest name.
  1945. //
  1946. EnterCriticalSection( &NlGlobalDnsForestNameCritSect );
  1947. NetStatus = NlpUtf8ToCutf8( ResponseBuffer,
  1948. (UseNameAliases && NlGlobalUtf8DnsForestNameAlias) ?
  1949. NlGlobalUtf8DnsForestNameAlias :
  1950. NlGlobalUtf8DnsForestName,
  1951. FALSE,
  1952. &Where,
  1953. &Utf8StringSize,
  1954. &CompressCount,
  1955. CompressOffset,
  1956. CompressUtf8String );
  1957. LeaveCriticalSection( &NlGlobalDnsForestNameCritSect );
  1958. if ( NetStatus != NO_ERROR ) {
  1959. NlPrintDom((NL_CRITICAL, DomainInfo,
  1960. "Cannot pack DnsForestName into message %ld\n",
  1961. NetStatus ));
  1962. return NetStatus;
  1963. }
  1964. //
  1965. // Copy the Dns Domain Name after the Tree name.
  1966. //
  1967. // If we are instructed to use name aliases and
  1968. // there is an alias for the domain name, use it.
  1969. // Otherwise, use the active domain name.
  1970. //
  1971. EnterCriticalSection(&NlGlobalDomainCritSect);
  1972. NetStatus = NlpUtf8ToCutf8(
  1973. ResponseBuffer,
  1974. (UseNameAliases && DomainInfo->DomUtf8DnsDomainNameAlias) ?
  1975. DomainInfo->DomUtf8DnsDomainNameAlias :
  1976. DomainInfo->DomUtf8DnsDomainName,
  1977. FALSE,
  1978. &Where,
  1979. &Utf8StringSize,
  1980. &CompressCount,
  1981. CompressOffset,
  1982. CompressUtf8String );
  1983. LeaveCriticalSection(&NlGlobalDomainCritSect);
  1984. if ( NetStatus != NO_ERROR ) {
  1985. NlPrintDom((NL_CRITICAL, DomainInfo,
  1986. "Cannot pack DomainName into message %ld\n",
  1987. NetStatus ));
  1988. return NetStatus;
  1989. }
  1990. //
  1991. // Copy the Dns Host Name after the domain name.
  1992. //
  1993. NetStatus = NlpUtf8ToCutf8(
  1994. ResponseBuffer,
  1995. DomainInfo->DomUtf8DnsHostName,
  1996. FALSE,
  1997. &Where,
  1998. &Utf8StringSize,
  1999. &CompressCount,
  2000. CompressOffset,
  2001. CompressUtf8String );
  2002. if ( NetStatus != NO_ERROR ) {
  2003. NlPrintDom((NL_CRITICAL, DomainInfo,
  2004. "Cannot pack HostName into message %ld\n",
  2005. NetStatus ));
  2006. return NetStatus;
  2007. }
  2008. }
  2009. //
  2010. // Output the IP address of the transport we received the message on.
  2011. //
  2012. SmbPutUlong( Where, ntohl(OurIpAddress));
  2013. Where += sizeof(ULONG);
  2014. //
  2015. // Finally output the flags describing this machine.
  2016. //
  2017. SmbPutUlong( Where, NlGetDomainFlags(DomainInfo) );
  2018. Where += sizeof(ULONG);
  2019. //
  2020. // Tell the caller additional information is present.
  2021. //
  2022. ResponseNtVersion |= NETLOGON_NT_VERSION_5;
  2023. }
  2024. NetpLogonPutNtToken( &Where, ResponseNtVersion );
  2025. *ResponseBufferSize = (DWORD)(Where - (PCHAR)SamResponse);
  2026. //
  2027. // Always good to debug
  2028. //
  2029. NlPrintDom((NL_MAILSLOT, DomainInfo,
  2030. "Ping response '%s' %ws to \\\\%ws on %ws\n",
  2031. NlMailslotOpcode(Opcode),
  2032. UnicodeUserName,
  2033. UnicodeWorkstationName,
  2034. TransportName ));
  2035. return NO_ERROR;
  2036. }
  2037. NET_API_STATUS
  2038. BuildSamLogonResponseEx(
  2039. IN PDOMAIN_INFO DomainInfo,
  2040. IN BOOLEAN UseNameAliases,
  2041. IN USHORT Opcode,
  2042. IN LPCWSTR UnicodeUserName OPTIONAL,
  2043. IN BOOL IsDnsDomainTrustAccount,
  2044. IN LPCWSTR TransportName,
  2045. IN LPCWSTR UnicodeWorkstationName,
  2046. IN PSOCKADDR ClientSockAddr OPTIONAL,
  2047. IN ULONG VersionFlags,
  2048. IN DWORD OurIpAddress,
  2049. OUT BYTE ResponseBuffer[NETLOGON_MAX_MS_SIZE],
  2050. OUT LPDWORD ResponseBufferSize
  2051. )
  2052. /*++
  2053. Routine Description:
  2054. Build the extended response message to a SAM Logon request.
  2055. Arguments:
  2056. DomainInfo - Hosted Domain message came from
  2057. UseNameAliases - TRUE if domain and forest name aliases (not active names)
  2058. should be returned in the response message.
  2059. Opcode - Opcode for the response message
  2060. This is the non-EX version of the opcode.
  2061. UnicodeUserName - The name of the user logging on.
  2062. IsDnsDomainTrustAccount - If TRUE, UnicodeUserName is the
  2063. name of a DNS domain trust account.
  2064. TransportName - Name of transport the request came in on
  2065. UnicodeWorkstationName - The name of the workstation we're responding to.
  2066. ClientSockAddr - Socket Address of the client this request came in on.
  2067. If NULL, the client is this machine.
  2068. VersionFlags - Version flags from the caller.
  2069. OurIpAddress - IP Address of the transport this message was received on.
  2070. 0: Not an IP transport
  2071. ResponseBuffer - Buffer to build the response in
  2072. ResponseBufferSize - Size (in bytes) of the returned message.
  2073. Return Value:
  2074. Status of the operation.
  2075. --*/
  2076. {
  2077. NET_API_STATUS NetStatus;
  2078. PCHAR Where;
  2079. PNETLOGON_SAM_LOGON_RESPONSE_EX SamResponse = (PNETLOGON_SAM_LOGON_RESPONSE_EX) ResponseBuffer;
  2080. ULONG ResponseNtVersion = 0;
  2081. WORD CompressOffset[10];
  2082. CHAR *CompressUtf8String[10];
  2083. ULONG CompressCount = 0;
  2084. ULONG Utf8StringSize;
  2085. ULONG LocalFlags = 0;
  2086. ULONG LocalVersion = 0;
  2087. PNL_SITE_ENTRY ClientSiteEntry = NULL;
  2088. LPWSTR ClientSiteName = NULL;
  2089. WCHAR CapturedSiteName[NL_MAX_DNS_LABEL_LENGTH+1];
  2090. LPSTR LocalUtf8UserName = NULL;
  2091. //
  2092. // Compute the name of the site the client machine is in.
  2093. //
  2094. if ( ClientSockAddr != NULL ) {
  2095. ClientSiteEntry = NlFindSiteEntryBySockAddr( ClientSockAddr );
  2096. if ( ClientSiteEntry == NULL ) {
  2097. WCHAR IpAddressString[NL_SOCK_ADDRESS_LENGTH+1];
  2098. NetpSockAddrToWStr( ClientSockAddr,
  2099. sizeof(SOCKADDR_IN),
  2100. IpAddressString );
  2101. //
  2102. // Passing 0 as the bit mask will force the
  2103. // log output even if DbFlag == 0. We point to
  2104. // this output from the event log written at
  2105. // scavenging time, so don't change the format
  2106. // of the output here.
  2107. //
  2108. NlPrintDom(( 0, DomainInfo,
  2109. "NO_CLIENT_SITE: %ws %ws\n",
  2110. UnicodeWorkstationName,
  2111. IpAddressString ));
  2112. //
  2113. // If this is the first no site client,
  2114. // set the timestamp for this observation window
  2115. //
  2116. EnterCriticalSection( &NlGlobalSiteCritSect );
  2117. if ( NlGlobalNoClientSiteCount == 0 ) {
  2118. NlQuerySystemTime( &NlGlobalNoClientSiteEventTime );
  2119. }
  2120. //
  2121. // Increment the number of clients with no site
  2122. // we hit during this timeout period
  2123. //
  2124. NlGlobalNoClientSiteCount ++;
  2125. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2126. } else {
  2127. ULONG SiteIndex;
  2128. ClientSiteName = ClientSiteEntry->SiteName;
  2129. EnterCriticalSection( &NlGlobalSiteCritSect );
  2130. if ( VersionFlags & NETLOGON_NT_VERSION_GC ) {
  2131. for ( SiteIndex = 0; SiteIndex < DomainInfo->GcCoveredSitesCount; SiteIndex++ ) {
  2132. if ( (DomainInfo->GcCoveredSites)[SiteIndex].CoveredSite == ClientSiteEntry ) {
  2133. LocalFlags |= DS_CLOSEST_FLAG;
  2134. break;
  2135. }
  2136. }
  2137. } else {
  2138. for ( SiteIndex = 0; SiteIndex < DomainInfo->CoveredSitesCount; SiteIndex++ ) {
  2139. if ( (DomainInfo->CoveredSites)[SiteIndex].CoveredSite == ClientSiteEntry ) {
  2140. LocalFlags |= DS_CLOSEST_FLAG;
  2141. break;
  2142. }
  2143. }
  2144. }
  2145. LeaveCriticalSection( &NlGlobalSiteCritSect );
  2146. }
  2147. } else {
  2148. //
  2149. // If this is a loopback call,
  2150. // we already know our site name.
  2151. // (And it is the closest site.)
  2152. //
  2153. if ( VersionFlags & NETLOGON_NT_VERSION_LOCAL ) {
  2154. if ( NlCaptureSiteName( CapturedSiteName ) ) {
  2155. ClientSiteName = CapturedSiteName;
  2156. LocalFlags |= DS_CLOSEST_FLAG;
  2157. }
  2158. } else {
  2159. NlPrintDom((NL_SITE, DomainInfo,
  2160. "Client didn't pass us his IP Address. (No site returned)\n" ));
  2161. }
  2162. }
  2163. //
  2164. // Pack the opcode converting it to the _EX version
  2165. //
  2166. switch ( Opcode ) {
  2167. case LOGON_SAM_LOGON_RESPONSE:
  2168. Opcode = LOGON_SAM_LOGON_RESPONSE_EX; break;
  2169. case LOGON_SAM_PAUSE_RESPONSE:
  2170. Opcode = LOGON_SAM_PAUSE_RESPONSE_EX; break;
  2171. case LOGON_SAM_USER_UNKNOWN:
  2172. Opcode = LOGON_SAM_USER_UNKNOWN_EX; break;
  2173. }
  2174. SamResponse->Opcode = Opcode;
  2175. SamResponse->Sbz = 0;
  2176. //
  2177. // Output the flags describing this machine.
  2178. //
  2179. SamResponse->Flags = LocalFlags | NlGetDomainFlags(DomainInfo);
  2180. //
  2181. // Output the GUID of this domain.
  2182. //
  2183. Where = (PCHAR) &SamResponse->DomainGuid;
  2184. NetpLogonPutGuid( &DomainInfo->DomDomainGuidBuffer,
  2185. &Where );
  2186. //
  2187. // Initialize for copying Cutf-8 strings.
  2188. //
  2189. Utf8StringSize = sizeof(SamResponse->DnsForestName) +
  2190. sizeof(SamResponse->DnsDomainName) +
  2191. sizeof(SamResponse->DnsHostName) +
  2192. sizeof(SamResponse->NetbiosDomainName) +
  2193. sizeof(SamResponse->NetbiosComputerName) +
  2194. sizeof(SamResponse->UserName) +
  2195. sizeof(SamResponse->DcSiteName) +
  2196. sizeof(SamResponse->ClientSiteName);
  2197. CompressCount = 0; // No strings compressed yet.
  2198. //
  2199. // Copy the DnsTree name into the message.
  2200. //
  2201. // If we are instructed to use name aliases and
  2202. // there is an alias for forest name, use it.
  2203. // Otherwise, use the active forest name.
  2204. //
  2205. EnterCriticalSection( &NlGlobalDnsForestNameCritSect );
  2206. NetStatus = NlpUtf8ToCutf8( ResponseBuffer,
  2207. (UseNameAliases && NlGlobalUtf8DnsForestNameAlias) ?
  2208. NlGlobalUtf8DnsForestNameAlias :
  2209. NlGlobalUtf8DnsForestName,
  2210. FALSE,
  2211. &Where,
  2212. &Utf8StringSize,
  2213. &CompressCount,
  2214. CompressOffset,
  2215. CompressUtf8String );
  2216. LeaveCriticalSection( &NlGlobalDnsForestNameCritSect );
  2217. if ( NetStatus != NO_ERROR ) {
  2218. NlPrintDom((NL_CRITICAL, DomainInfo,
  2219. "Cannot pack DnsForestName into message %ld\n",
  2220. NetStatus ));
  2221. goto Cleanup;
  2222. }
  2223. //
  2224. // Copy the Dns Domain Name after the Tree name.
  2225. //
  2226. // If we are instructed to use name aliases and
  2227. // there is an alias for the domain name, use it.
  2228. // Otherwise, use the active domain name.
  2229. //
  2230. EnterCriticalSection(&NlGlobalDomainCritSect);
  2231. NetStatus = NlpUtf8ToCutf8(
  2232. ResponseBuffer,
  2233. (UseNameAliases && DomainInfo->DomUtf8DnsDomainNameAlias) ?
  2234. DomainInfo->DomUtf8DnsDomainNameAlias :
  2235. DomainInfo->DomUtf8DnsDomainName,
  2236. FALSE,
  2237. &Where,
  2238. &Utf8StringSize,
  2239. &CompressCount,
  2240. CompressOffset,
  2241. CompressUtf8String );
  2242. LeaveCriticalSection(&NlGlobalDomainCritSect);
  2243. if ( NetStatus != NO_ERROR ) {
  2244. NlPrintDom((NL_CRITICAL, DomainInfo,
  2245. "Cannot pack DomainName into message %ld\n",
  2246. NetStatus ));
  2247. goto Cleanup;
  2248. }
  2249. //
  2250. // Copy the Dns Host Name after the domain name.
  2251. //
  2252. NetStatus = NlpUtf8ToCutf8(
  2253. ResponseBuffer,
  2254. DomainInfo->DomUtf8DnsHostName,
  2255. FALSE,
  2256. &Where,
  2257. &Utf8StringSize,
  2258. &CompressCount,
  2259. CompressOffset,
  2260. CompressUtf8String );
  2261. if ( NetStatus != NO_ERROR ) {
  2262. NlPrintDom((NL_CRITICAL, DomainInfo,
  2263. "Cannot pack HostName into message %ld\n",
  2264. NetStatus ));
  2265. goto Cleanup;
  2266. }
  2267. //
  2268. // Copy the Netbios domain name
  2269. //
  2270. NetStatus = NlpUnicodeToCutf8(
  2271. ResponseBuffer,
  2272. DomainInfo->DomUnicodeDomainName,
  2273. TRUE,
  2274. &Where,
  2275. &Utf8StringSize,
  2276. &CompressCount,
  2277. CompressOffset,
  2278. CompressUtf8String );
  2279. if ( NetStatus != NO_ERROR ) {
  2280. NlPrintDom((NL_CRITICAL, DomainInfo,
  2281. "Cannot pack Netbios Domain Name into message %ld\n",
  2282. NetStatus ));
  2283. goto Cleanup;
  2284. }
  2285. //
  2286. // Copy the Netbios computer name
  2287. //
  2288. NetStatus = NlpUnicodeToCutf8(
  2289. ResponseBuffer,
  2290. DomainInfo->DomUnicodeComputerNameString.Buffer,
  2291. TRUE,
  2292. &Where,
  2293. &Utf8StringSize,
  2294. &CompressCount,
  2295. CompressOffset,
  2296. CompressUtf8String );
  2297. if ( NetStatus != NO_ERROR ) {
  2298. NlPrintDom((NL_CRITICAL, DomainInfo,
  2299. "Cannot pack Netbios computername into message %ld\n",
  2300. NetStatus ));
  2301. goto Cleanup;
  2302. }
  2303. //
  2304. // Copy the UserName
  2305. //
  2306. if ( UnicodeUserName != NULL && *UnicodeUserName != UNICODE_NULL ) {
  2307. LocalUtf8UserName = NetpAllocUtf8StrFromWStr( UnicodeUserName );
  2308. if ( LocalUtf8UserName == NULL ) {
  2309. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2310. goto Cleanup;
  2311. }
  2312. //
  2313. // For SAM account names we are going to truncate the name
  2314. // to 63 bytes max to fit it into the space allowed for a
  2315. // single label by RFC 1035. Note that this should be fine
  2316. // because valid SAM account names are limited to 20 characters
  2317. // (which is at most 60 bytes for UTF-8 character storage).
  2318. // Therefore our response for long (truncated) SAM names is
  2319. // going to be SAM_USER_UNKNOWN in which case the client will
  2320. // skip the verification of the returned (truncated) account name.
  2321. //
  2322. if ( !IsDnsDomainTrustAccount && // => SAM account name
  2323. strlen(LocalUtf8UserName) > NL_MAX_DNS_LABEL_LENGTH ) {
  2324. NlAssert( Opcode == LOGON_SAM_USER_UNKNOWN_EX );
  2325. NlPrintDom(( (Opcode == LOGON_SAM_USER_UNKNOWN_EX) ? NL_MISC : NL_CRITICAL,
  2326. DomainInfo,
  2327. "BuildSamLogonResponseEx: Truncating SAM account name %ws for Opcode %lu\n",
  2328. UnicodeUserName,
  2329. Opcode ));
  2330. LocalUtf8UserName[ NL_MAX_DNS_LABEL_LENGTH ] = '\0';
  2331. }
  2332. }
  2333. //
  2334. // Always ignore dots for user name (even if this is a DNS domain name)
  2335. // to preserve the last period in the DNS domain trust name.
  2336. //
  2337. NetStatus = NlpUtf8ToCutf8(
  2338. ResponseBuffer,
  2339. LocalUtf8UserName,
  2340. TRUE, // Ignore dots
  2341. &Where,
  2342. &Utf8StringSize,
  2343. &CompressCount,
  2344. CompressOffset,
  2345. CompressUtf8String );
  2346. if ( NetStatus != NO_ERROR ) {
  2347. NlPrintDom((NL_CRITICAL, DomainInfo,
  2348. "Cannot pack User Name into message %ld\n",
  2349. NetStatus ));
  2350. goto Cleanup;
  2351. }
  2352. //
  2353. // Copy the Name of the site this DC is in.
  2354. //
  2355. NetStatus = NlpUtf8ToCutf8( ResponseBuffer,
  2356. NlGlobalUtf8SiteName,
  2357. FALSE,
  2358. &Where,
  2359. &Utf8StringSize,
  2360. &CompressCount,
  2361. CompressOffset,
  2362. CompressUtf8String );
  2363. if ( NetStatus != NO_ERROR ) {
  2364. NlPrintDom((NL_CRITICAL, DomainInfo,
  2365. "Cannot pack DcSiteName into message %ld\n",
  2366. NetStatus ));
  2367. goto Cleanup;
  2368. }
  2369. //
  2370. // Copy the Name of the site the client machine is in.
  2371. //
  2372. NetStatus = NlpUnicodeToCutf8( ResponseBuffer,
  2373. ClientSiteName,
  2374. FALSE,
  2375. &Where,
  2376. &Utf8StringSize,
  2377. &CompressCount,
  2378. CompressOffset,
  2379. CompressUtf8String );
  2380. if ( NetStatus != NO_ERROR ) {
  2381. NlPrintDom((NL_CRITICAL, DomainInfo,
  2382. "Cannot pack ClientSiteName into message %ld\n",
  2383. NetStatus ));
  2384. goto Cleanup;
  2385. }
  2386. //
  2387. // If the caller wants it,
  2388. // output the IP address of the transport we received the message on.
  2389. //
  2390. if ( OurIpAddress &&
  2391. (VersionFlags & NETLOGON_NT_VERSION_5EX_WITH_IP) != 0 ) {
  2392. SOCKADDR_IN DcSockAddrIn;
  2393. CHAR DcSockAddrSize;
  2394. //
  2395. // Convert the IP address to a SockAddr.
  2396. //
  2397. RtlZeroMemory( &DcSockAddrIn, sizeof(DcSockAddrIn) );
  2398. DcSockAddrIn.sin_family = AF_INET;
  2399. DcSockAddrIn.sin_port = 0;
  2400. DcSockAddrIn.sin_addr.S_un.S_addr = OurIpAddress;
  2401. DcSockAddrSize = sizeof(SOCKADDR_IN);
  2402. //
  2403. // Put the size of the SockAddr into the message
  2404. // provided we still have room left
  2405. //
  2406. if ( sizeof(DcSockAddrIn) > Utf8StringSize ) {
  2407. NlPrintDom(( NL_CRITICAL, DomainInfo,
  2408. "Not enough space for socket address 0x%lx 0x%lx\n",
  2409. Utf8StringSize,
  2410. sizeof(DcSockAddrIn) ));
  2411. } else {
  2412. NetpLogonPutBytes( &DcSockAddrSize, 1, &Where ); // This has dedicated field
  2413. //
  2414. // Put the SockAddr itself into the message
  2415. //
  2416. NetpLogonPutBytes( &DcSockAddrIn, sizeof(DcSockAddrIn), &Where );
  2417. //
  2418. // Tell the caller that the size field is there.
  2419. //
  2420. LocalVersion |= NETLOGON_NT_VERSION_5EX_WITH_IP;
  2421. }
  2422. }
  2423. //
  2424. // Set the version of this message.
  2425. //
  2426. NetpLogonPutNtToken( &Where, NETLOGON_NT_VERSION_5EX | LocalVersion );
  2427. *ResponseBufferSize = (DWORD)(Where - (PCHAR)SamResponse);
  2428. NetStatus = NO_ERROR;
  2429. //
  2430. // Free locally used resources;
  2431. //
  2432. Cleanup:
  2433. //
  2434. // Always good to debug
  2435. //
  2436. NlPrintDom((NL_MAILSLOT, DomainInfo,
  2437. "Ping response '%s' %ws to \\\\%ws Site: %ws on %ws\n",
  2438. NlMailslotOpcode(Opcode),
  2439. UnicodeUserName,
  2440. UnicodeWorkstationName,
  2441. ClientSiteName,
  2442. TransportName ));
  2443. if ( LocalUtf8UserName != NULL ) {
  2444. NetpMemoryFree( LocalUtf8UserName );
  2445. }
  2446. if ( ClientSiteEntry != NULL ) {
  2447. NlDerefSiteEntry( ClientSiteEntry );
  2448. }
  2449. return NetStatus;
  2450. }
  2451. #ifdef _DC_NETLOGON
  2452. NTSTATUS
  2453. NlSamVerifyUserAccountEnabled(
  2454. IN PDOMAIN_INFO DomainInfo,
  2455. IN LPCWSTR AccountName,
  2456. IN ULONG AllowableAccountControlBits,
  2457. IN BOOL CheckAccountDisabled
  2458. )
  2459. /*++
  2460. Routine Description:
  2461. Verify whether the user account exists and is enabled.
  2462. This function uses efficient version of SAM account lookup,
  2463. namely SamINetLogonPing (as opposed to SamIOpenNamedUser).
  2464. Arguments:
  2465. DomainInfo - Hosted Domain
  2466. AccountName - The name of the user account to check
  2467. AllowableAccountControlBits - A mask of allowable SAM account types that
  2468. are allowed to satisfy this request.
  2469. CheckAccountDisabled - TRUE if we should return an error if the account
  2470. is disabled.
  2471. Return Value:
  2472. STATUS_SUCCESS -- The account has been verified
  2473. STATUS_NO_SUCH_USER -- The account has failed to verify
  2474. Otherwise, an error returned by SamINetLogonPing
  2475. --*/
  2476. {
  2477. NTSTATUS Status;
  2478. UNICODE_STRING UserNameString;
  2479. BOOLEAN AccountExists;
  2480. ULONG UserAccountControl;
  2481. ULONG Length;
  2482. //
  2483. // Ensure the account name has the correct postfix.
  2484. //
  2485. if ( AllowableAccountControlBits == USER_SERVER_TRUST_ACCOUNT ||
  2486. AllowableAccountControlBits == USER_WORKSTATION_TRUST_ACCOUNT ) {
  2487. Length = wcslen( AccountName );
  2488. if ( Length <= SSI_ACCOUNT_NAME_POSTFIX_LENGTH ) {
  2489. return STATUS_NO_SUCH_USER;
  2490. }
  2491. if ( _wcsicmp(&AccountName[Length - SSI_ACCOUNT_NAME_POSTFIX_LENGTH],
  2492. SSI_ACCOUNT_NAME_POSTFIX) != 0 ) {
  2493. return STATUS_NO_SUCH_USER;
  2494. }
  2495. }
  2496. //
  2497. // User accounts exist only in real domains
  2498. //
  2499. if ( (DomainInfo->DomFlags & DOM_REAL_DOMAIN) == 0 ) {
  2500. NlPrintDom(( NL_CRITICAL, DomainInfo,
  2501. "NlSamVerifyUserAccountEnabled: Domain is not real 0x%lx\n",
  2502. DomainInfo->DomFlags ));
  2503. return STATUS_NO_SUCH_USER;
  2504. }
  2505. RtlInitUnicodeString( &UserNameString, AccountName );
  2506. //
  2507. // Call the expedite version of SAM user lookup
  2508. //
  2509. Status = SamINetLogonPing( DomainInfo->DomSamAccountDomainHandle,
  2510. &UserNameString,
  2511. &AccountExists,
  2512. &UserAccountControl );
  2513. if ( !NT_SUCCESS(Status) ) {
  2514. NlPrintDom(( NL_CRITICAL, DomainInfo,
  2515. "NlSamVerifyUserAccountEnabled: SamINetLogonPing failed 0x%lx\n",
  2516. Status ));
  2517. return Status;
  2518. }
  2519. //
  2520. // If the account doesn't exist,
  2521. // return now
  2522. //
  2523. if ( !AccountExists ) {
  2524. return STATUS_NO_SUCH_USER;
  2525. }
  2526. //
  2527. // Ensure the Account type matches the account type on the account.
  2528. //
  2529. if ( (UserAccountControl & USER_ACCOUNT_TYPE_MASK & AllowableAccountControlBits) == 0 ) {
  2530. NlPrintDom(( NL_CRITICAL, DomainInfo,
  2531. "NlSamVerifyUserAccountEnabled: Invalid account type (0x%lx) instead of 0x%lx for %ws\n",
  2532. UserAccountControl & USER_ACCOUNT_TYPE_MASK,
  2533. AllowableAccountControlBits,
  2534. AccountName ));
  2535. return STATUS_NO_SUCH_USER;
  2536. }
  2537. //
  2538. // Check if the account is disabled if requested
  2539. //
  2540. if ( CheckAccountDisabled ) {
  2541. if ( UserAccountControl & USER_ACCOUNT_DISABLED ) {
  2542. NlPrintDom(( NL_MISC, DomainInfo,
  2543. "NlSamVerifyUserAccountEnabled: %ws account is disabled\n",
  2544. AccountName ));
  2545. return STATUS_NO_SUCH_USER;
  2546. }
  2547. }
  2548. //
  2549. // All checks succeeded
  2550. //
  2551. return STATUS_SUCCESS;
  2552. }
  2553. BOOLEAN
  2554. LogonRequestHandler(
  2555. IN LPCWSTR TransportName,
  2556. IN PDOMAIN_INFO DomainInfo,
  2557. IN BOOLEAN UseNameAliases,
  2558. IN PSID DomainSid OPTIONAL,
  2559. IN DWORD Version,
  2560. IN DWORD VersionFlags,
  2561. IN LPCWSTR UnicodeUserName,
  2562. IN DWORD RequestCount,
  2563. IN LPCWSTR UnicodeWorkstationName,
  2564. IN ULONG AllowableAccountControlBits,
  2565. IN DWORD OurIpAddress,
  2566. IN PSOCKADDR ClientSockAddr OPTIONAL,
  2567. OUT BYTE ResponseBuffer[NETLOGON_MAX_MS_SIZE],
  2568. OUT LPDWORD ResponseBufferSize
  2569. )
  2570. /*++
  2571. Routine Description:
  2572. Respond appropriate to an LM 2.0 or NT 3.x logon request.
  2573. Arguments:
  2574. TransportName - Name of the transport the request came in on
  2575. DomainInfo - Hosted Domain message came from
  2576. UseNameAliases - TRUE if domain and forest name aliases (not active names)
  2577. should be returned in the response message.
  2578. DomainSid - If specified, must match the DomainSid of the sid specified by
  2579. DomainInfo.
  2580. Version - The version of the input message. This parameter determine
  2581. the version of the response.
  2582. VersionFlags - The version flag bit from the input messge
  2583. UnicodeUserName - The name of the user logging on.
  2584. RequestCount - The number of times this user has repeated the logon request.
  2585. UnicodeWorkstationName - The name of the workstation where the user is
  2586. logging onto.
  2587. AllowableAccountControlBits - A mask of allowable SAM account types that
  2588. are allowed to satisfy this request.
  2589. OurIpAddress - IP Address of the transport this message was received on.
  2590. 0: Not an IP transport
  2591. ClientSockAddr - Socket Address of the client this request came in on.
  2592. If NULL, the client is this machine.
  2593. ResponseBuffer - Buffer to build the response in
  2594. ResponseBufferSize - Size (in bytes) of the returned message.
  2595. Return Value:
  2596. TRUE if this query should be responded to (the ResponseBuffer was filled in)
  2597. --*/
  2598. {
  2599. NTSTATUS Status = STATUS_SUCCESS;
  2600. NET_API_STATUS NetStatus = NO_ERROR;
  2601. USHORT Response = 0;
  2602. PCHAR Where;
  2603. ULONG AccountType;
  2604. BOOLEAN MessageBuilt = FALSE;
  2605. BOOLEAN NetlogonPaused = FALSE;
  2606. LPSTR NetlogonPausedReason;
  2607. ULONG ResponseNtVersion = 0;
  2608. BOOL IsDnsDomainTrustAccount = FALSE;
  2609. //
  2610. // If we are emulating NT4.0 domain and the client
  2611. // didn't indicate to neutralize the emulation,
  2612. // treat the client as NT4.0 client. That way we
  2613. // won't leak NT5.0 specific info to the client.
  2614. //
  2615. if ( NlGlobalParameters.Nt4Emulator &&
  2616. (VersionFlags & NETLOGON_NT_VERSION_AVOID_NT4EMUL) == 0 ) {
  2617. //
  2618. // Pick up the only bit that existed in NT4.0
  2619. //
  2620. VersionFlags &= NETLOGON_NT_VERSION_1;
  2621. }
  2622. //
  2623. // Compare the domain SID specified with the one for this domain.
  2624. //
  2625. if( DomainSid != NULL &&
  2626. !RtlEqualSid( DomainInfo->DomAccountDomainId, DomainSid ) ) {
  2627. LPWSTR AlertStrings[4];
  2628. //
  2629. // alert admin.
  2630. //
  2631. AlertStrings[0] = (LPWSTR)UnicodeWorkstationName;
  2632. AlertStrings[1] = DomainInfo->DomUncUnicodeComputerName;
  2633. AlertStrings[2] = DomainInfo->DomUnicodeDomainName;
  2634. AlertStrings[3] = NULL; // Needed for RAISE_ALERT_TOO
  2635. //
  2636. // Save the info in the eventlog
  2637. //
  2638. NlpWriteEventlog(
  2639. ALERT_NetLogonUntrustedClient,
  2640. EVENTLOG_ERROR_TYPE,
  2641. NULL,
  2642. 0,
  2643. AlertStrings,
  2644. 3 | NETP_RAISE_ALERT_TOO );
  2645. return FALSE;
  2646. }
  2647. //
  2648. // Logons are not processed if the service is paused
  2649. //
  2650. // Even though we're "PartialDisabled",
  2651. // we'll respond to queries that originated from this machine.
  2652. // That ensures apps on this machine find this machine even though we're booting.
  2653. //
  2654. // Also, if this a PDC discovery and we are the PDC,
  2655. // respond to it even if netlogon is paused since we are the only one who can respond.
  2656. //
  2657. if ( NlGlobalServiceStatus.dwCurrentState == SERVICE_PAUSED &&
  2658. !((VersionFlags & NETLOGON_NT_VERSION_PDC) != 0 && DomainInfo->DomRole == RolePrimary) ) {
  2659. NetlogonPaused = TRUE;
  2660. NetlogonPausedReason = "Netlogon Service Paused";
  2661. } else if ( NlGlobalDsPaused ) {
  2662. NetlogonPaused = TRUE;
  2663. NetlogonPausedReason = "DS paused";
  2664. } else if ( NlGlobalPartialDisable && (VersionFlags & NETLOGON_NT_VERSION_LOCAL) == 0 ) {
  2665. NetlogonPaused = TRUE;
  2666. NetlogonPausedReason = "Waiting for RPCSS";
  2667. } else if ( !NlGlobalParameters.SysVolReady ) {
  2668. NetlogonPaused = TRUE;
  2669. NetlogonPausedReason = "SysVol not ready";
  2670. }
  2671. if ( NetlogonPaused ) {
  2672. if ( Version == LMNT_MESSAGE ) {
  2673. Response = LOGON_SAM_PAUSE_RESPONSE;
  2674. } else {
  2675. //
  2676. // Don't respond immediately to non-nt clients. They treat
  2677. // "paused" responses as fatal. That's just not so.
  2678. // There may be many other DCs that are able to process the logon.
  2679. //
  2680. if ( RequestCount >= MAX_LOGONREQ_COUNT &&
  2681. NlGlobalServiceStatus.dwCurrentState == SERVICE_PAUSED ) {
  2682. Response = LOGON_PAUSE_RESPONSE;
  2683. }
  2684. }
  2685. NlPrintDom((NL_MAILSLOT, DomainInfo,
  2686. "Returning paused to '%ws' since: %s\n",
  2687. UnicodeWorkstationName,
  2688. NetlogonPausedReason ));
  2689. goto Cleanup;
  2690. }
  2691. //
  2692. // Check the user name if passed
  2693. //
  2694. if ( UnicodeUserName == NULL || *UnicodeUserName == L'\0' ) {
  2695. //
  2696. // NT 5 does queries with a null account name.
  2697. // Bypass the SAM lookup for efficiencies sake.
  2698. //
  2699. if ( Version == LMNT_MESSAGE ) {
  2700. Response = LOGON_SAM_LOGON_RESPONSE;
  2701. goto Cleanup;
  2702. } else {
  2703. Status = STATUS_NO_SUCH_USER;
  2704. }
  2705. //
  2706. // If this user does not have an account in SAM,
  2707. // immediately return a response indicating so.
  2708. //
  2709. // All we are trying to do here is ensuring that this guy
  2710. // has a valid account except that we are not checking the
  2711. // password
  2712. //
  2713. // This is done so that STANDALONE logons for non existent
  2714. // users can be done in very first try, speeding up the response
  2715. // to user and reducing processing on DCs/BCs.
  2716. //
  2717. //
  2718. // Disallow use of disabled accounts.
  2719. //
  2720. // We use this message to determine if a trusted domain has a
  2721. // particular account. Since the UI recommends disabling an account
  2722. // rather than deleting it (conservation of rids and all that),
  2723. // we shouldn't respond that we have the account if we really don't.
  2724. //
  2725. // We don't check the disabled bit in the Lanmax 2.x/WFW/WIN 95 case. Downlevel
  2726. // interactive logons are directed at a single particular domain.
  2727. // It is better here that we indicate we have the account so later
  2728. // he'll get a better error code indicating that the account is
  2729. // disabled, rather than allowing him to logon standalone.
  2730. //
  2731. } else if ( !NlGlobalParameters.AvoidLocatorAccountLookup ) {
  2732. //
  2733. // If the account is interdomain trust account,
  2734. // we need to look it up in LSA
  2735. //
  2736. if ( AllowableAccountControlBits == USER_DNS_DOMAIN_TRUST_ACCOUNT ||
  2737. AllowableAccountControlBits == USER_INTERDOMAIN_TRUST_ACCOUNT ) {
  2738. Status = NlGetIncomingPassword(
  2739. DomainInfo,
  2740. UnicodeUserName,
  2741. NullSecureChannel, // Don't know the secure channel type
  2742. AllowableAccountControlBits,
  2743. Version == LMNT_MESSAGE,
  2744. NULL, // Don't return the password
  2745. NULL, // Don't return the previous password
  2746. NULL, // Don't return the account RID
  2747. NULL, // Don't return the trust attributes
  2748. &IsDnsDomainTrustAccount );
  2749. //
  2750. // Otherwise the account is a SAM user account and
  2751. // we can use a quick SAM lookup
  2752. //
  2753. } else {
  2754. Status = NlSamVerifyUserAccountEnabled( DomainInfo,
  2755. UnicodeUserName,
  2756. AllowableAccountControlBits,
  2757. Version == LMNT_MESSAGE );
  2758. }
  2759. } else {
  2760. NlPrintDom(( NL_MAILSLOT, DomainInfo,
  2761. "LogonRequestHandler: Avoiding user '%ws' lookup for '%ws'\n",
  2762. UnicodeUserName,
  2763. UnicodeWorkstationName ));
  2764. }
  2765. if ( !NT_SUCCESS(Status) ) {
  2766. if ( Status == STATUS_NO_SUCH_USER ) {
  2767. if ( Version == LMNT_MESSAGE ) {
  2768. Response = LOGON_SAM_USER_UNKNOWN;
  2769. } else if ( Version == LM20_MESSAGE ) {
  2770. Response = LOGON_USER_UNKNOWN;
  2771. }
  2772. }
  2773. goto Cleanup;
  2774. }
  2775. //
  2776. // For SAM clients, respond immediately.
  2777. //
  2778. if ( Version == LMNT_MESSAGE ) {
  2779. Response = LOGON_SAM_LOGON_RESPONSE;
  2780. goto Cleanup;
  2781. //
  2782. // For LM 2.0 clients, respond immediately.
  2783. //
  2784. } else if ( Version == LM20_MESSAGE ) {
  2785. Response = LOGON_RESPONSE2;
  2786. goto Cleanup;
  2787. //
  2788. // For LM 1.0 clients,
  2789. // don't support the request.
  2790. //
  2791. } else {
  2792. Response = LOGON_USER_UNKNOWN;
  2793. goto Cleanup;
  2794. }
  2795. Cleanup:
  2796. //
  2797. // If we should respond to the caller, do so now.
  2798. //
  2799. switch (Response) {
  2800. case LOGON_SAM_PAUSE_RESPONSE:
  2801. case LOGON_SAM_USER_UNKNOWN:
  2802. case LOGON_SAM_LOGON_RESPONSE:
  2803. if (VersionFlags & (NETLOGON_NT_VERSION_5EX|NETLOGON_NT_VERSION_5EX_WITH_IP)) {
  2804. NetStatus = BuildSamLogonResponseEx(
  2805. DomainInfo,
  2806. UseNameAliases,
  2807. Response,
  2808. UnicodeUserName,
  2809. IsDnsDomainTrustAccount,
  2810. TransportName,
  2811. UnicodeWorkstationName,
  2812. ClientSockAddr,
  2813. VersionFlags,
  2814. OurIpAddress,
  2815. ResponseBuffer,
  2816. ResponseBufferSize );
  2817. } else {
  2818. NetStatus = BuildSamLogonResponse(
  2819. DomainInfo,
  2820. UseNameAliases,
  2821. Response,
  2822. UnicodeUserName,
  2823. TransportName,
  2824. UnicodeWorkstationName,
  2825. (VersionFlags & NETLOGON_NT_VERSION_5) != 0,
  2826. OurIpAddress,
  2827. ResponseBuffer,
  2828. ResponseBufferSize );
  2829. }
  2830. if ( NetStatus != NO_ERROR ) {
  2831. goto Done;
  2832. }
  2833. MessageBuilt = TRUE;
  2834. break;
  2835. case LOGON_RESPONSE2:
  2836. case LOGON_USER_UNKNOWN:
  2837. case LOGON_PAUSE_RESPONSE: {
  2838. PNETLOGON_LOGON_RESPONSE2 Response2 = (PNETLOGON_LOGON_RESPONSE2)ResponseBuffer;
  2839. Response2->Opcode = Response;
  2840. Where = Response2->LogonServer;
  2841. (VOID) strcpy( Where, "\\\\");
  2842. Where += 2;
  2843. NetpLogonPutOemString( DomainInfo->DomOemComputerName,
  2844. sizeof(Response2->LogonServer) - 2,
  2845. &Where );
  2846. NetpLogonPutLM20Token( &Where );
  2847. *ResponseBufferSize = (DWORD)(Where - (PCHAR)Response2);
  2848. MessageBuilt = TRUE;
  2849. //
  2850. // Always good to debug
  2851. //
  2852. NlPrintDom((NL_MAILSLOT, DomainInfo,
  2853. "%s logon mailslot message for %ws from \\\\%ws. Response '%s' on %ws\n",
  2854. Version == LMNT_MESSAGE ? "Sam" : "Uas",
  2855. UnicodeUserName,
  2856. UnicodeWorkstationName,
  2857. NlMailslotOpcode(Response),
  2858. TransportName ));
  2859. break;
  2860. }
  2861. }
  2862. //
  2863. // Free up any locally used resources.
  2864. //
  2865. Done:
  2866. return MessageBuilt;
  2867. }
  2868. VOID
  2869. I_NetLogonFree(
  2870. IN PVOID Buffer
  2871. )
  2872. /*++
  2873. Routine Description:
  2874. Free any buffer allocated by Netlogon and returned to an in-process caller.
  2875. Arguments:
  2876. Buffer - Buffer to deallocate.
  2877. Return Value:
  2878. None.
  2879. --*/
  2880. {
  2881. NetpMemoryFree( Buffer );
  2882. }
  2883. BOOLEAN
  2884. PrimaryQueryHandler(
  2885. IN LPCWSTR TransportName,
  2886. IN PDOMAIN_INFO DomainInfo,
  2887. IN BOOLEAN UseNameAliases,
  2888. IN DWORD Version,
  2889. IN DWORD VersionFlags,
  2890. IN LPCWSTR UnicodeWorkstationName,
  2891. IN DWORD OurIpAddress,
  2892. IN PSOCKADDR ClientSockAddr OPTIONAL,
  2893. OUT BYTE ResponseBuffer[NETLOGON_MAX_MS_SIZE],
  2894. OUT LPDWORD ResponseBufferSize
  2895. )
  2896. /*++
  2897. Routine Description:
  2898. Respond appropriately to a primary query request.
  2899. Arguments:
  2900. TransportName - Name of the tranport the request came in on
  2901. DomainInfo - Hosted Domain message came from
  2902. UseNameAliases - TRUE if domain and forest name aliases (not active names)
  2903. should be returned in the response message.
  2904. Version - The version of the input message.
  2905. VersionFlags - The version flag bit from the input messge
  2906. UnicodeWorkstationName - The name of the workstation doing the query.
  2907. OurIpAddress - IP Address of the transport this message was received on.
  2908. 0: Not an IP transport
  2909. ClientSockAddr - Socket Address of the client this request came in on.
  2910. If NULL, the client is this machine.
  2911. ResponseBuffer - Buffer to build the response in
  2912. ResponseBufferSize - Size (in bytes) of the returned message.
  2913. Return Value:
  2914. TRUE if this primary query should be responded to (the ResponseBuffer was filled in)
  2915. --*/
  2916. {
  2917. //
  2918. // If we are emulating NT4.0 domain and the client
  2919. // didn't indicate to neutralize the emulation,
  2920. // treat the client as NT4.0 client. That way we
  2921. // won't leak NT5.0 specific info to the client.
  2922. //
  2923. if ( NlGlobalParameters.Nt4Emulator &&
  2924. (VersionFlags & NETLOGON_NT_VERSION_AVOID_NT4EMUL) == 0 ) {
  2925. //
  2926. // Pick up the only bit that existed in NT4.0
  2927. //
  2928. VersionFlags &= NETLOGON_NT_VERSION_1;
  2929. }
  2930. //
  2931. // Don't respond if the TCP transport isn't yet enabled.
  2932. //
  2933. // This might be a BDC wanting to find its PDC to setup a secure channel.
  2934. // We don't want it to fall back to named pipes.
  2935. //
  2936. if ( NlGlobalDsPaused || NlGlobalPartialDisable ) {
  2937. goto Cleanup;
  2938. }
  2939. //
  2940. // Only respond if we're a PDC.
  2941. //
  2942. if ( DomainInfo->DomRole != RolePrimary ) {
  2943. goto Cleanup;
  2944. }
  2945. //
  2946. // Respond to the query
  2947. //
  2948. //
  2949. // If the caller is an NT5.0 client,
  2950. // respond with a SamLogonResponse.
  2951. //
  2952. if (VersionFlags & (NETLOGON_NT_VERSION_5EX|NETLOGON_NT_VERSION_5EX_WITH_IP)) {
  2953. NET_API_STATUS NetStatus;
  2954. NetStatus = BuildSamLogonResponseEx(
  2955. DomainInfo,
  2956. UseNameAliases,
  2957. LOGON_SAM_LOGON_RESPONSE_EX,
  2958. NULL, // No user name in response
  2959. FALSE, // Not a DNS trust account name
  2960. TransportName,
  2961. UnicodeWorkstationName,
  2962. ClientSockAddr,
  2963. VersionFlags,
  2964. OurIpAddress,
  2965. ResponseBuffer,
  2966. ResponseBufferSize );
  2967. if ( NetStatus != NO_ERROR ) {
  2968. goto Cleanup;
  2969. }
  2970. } else if ( VersionFlags & NETLOGON_NT_VERSION_5 ) {
  2971. NET_API_STATUS NetStatus;
  2972. NetStatus = BuildSamLogonResponse(
  2973. DomainInfo,
  2974. UseNameAliases,
  2975. LOGON_SAM_LOGON_RESPONSE,
  2976. NULL, // No user name in response
  2977. TransportName,
  2978. UnicodeWorkstationName,
  2979. TRUE, // Supply NT 5.0 specific response
  2980. OurIpAddress,
  2981. ResponseBuffer,
  2982. ResponseBufferSize );
  2983. if ( NetStatus != NO_ERROR ) {
  2984. goto Cleanup;
  2985. }
  2986. } else {
  2987. PNETLOGON_PRIMARY Response = (PNETLOGON_PRIMARY)ResponseBuffer;
  2988. PCHAR Where;
  2989. //
  2990. // Build the response
  2991. //
  2992. // If we are the Primary DC, tell the caller our computername.
  2993. // If we are a backup DC,
  2994. // tell the downlevel PDC who we think the primary is.
  2995. //
  2996. Response->Opcode = LOGON_PRIMARY_RESPONSE;
  2997. Where = Response->PrimaryDCName;
  2998. NetpLogonPutOemString(
  2999. DomainInfo->DomOemComputerName,
  3000. sizeof( Response->PrimaryDCName),
  3001. &Where );
  3002. //
  3003. // If this is an NT query,
  3004. // add the NT specific response.
  3005. //
  3006. if ( Version == LMNT_MESSAGE ) {
  3007. NetpLogonPutUnicodeString(
  3008. DomainInfo->DomUnicodeComputerNameString.Buffer,
  3009. sizeof(Response->UnicodePrimaryDCName),
  3010. &Where );
  3011. NetpLogonPutUnicodeString(
  3012. DomainInfo->DomUnicodeDomainName,
  3013. sizeof(Response->UnicodeDomainName),
  3014. &Where );
  3015. NetpLogonPutNtToken( &Where, 0 );
  3016. }
  3017. *ResponseBufferSize = (DWORD)(Where - (PCHAR)Response);
  3018. NlPrintDom((NL_MAILSLOT, DomainInfo,
  3019. "%s Primary Query mailslot message from %ws. Response %ws on %ws\n",
  3020. Version == LMNT_MESSAGE ? "Sam" : "Uas",
  3021. UnicodeWorkstationName,
  3022. DomainInfo->DomUncUnicodeComputerName,
  3023. TransportName ));
  3024. }
  3025. return TRUE;
  3026. //
  3027. // Free Locally used resources
  3028. //
  3029. Cleanup:
  3030. return FALSE;
  3031. }
  3032. NET_API_STATUS
  3033. NlGetLocalPingResponse(
  3034. IN LPCWSTR TransportName,
  3035. IN BOOL LdapPing,
  3036. IN LPCWSTR NetbiosDomainName OPTIONAL,
  3037. IN LPCSTR DnsDomainName OPTIONAL,
  3038. IN GUID *DomainGuid OPTIONAL,
  3039. IN PSID DomainSid OPTIONAL,
  3040. IN BOOL PdcOnly,
  3041. IN LPCWSTR UnicodeComputerName,
  3042. IN LPCWSTR UnicodeUserName OPTIONAL,
  3043. IN ULONG AllowableAccountControlBits,
  3044. IN ULONG NtVersion,
  3045. IN ULONG NtVersionFlags,
  3046. IN PSOCKADDR ClientSockAddr OPTIONAL,
  3047. OUT PVOID *Message,
  3048. OUT PULONG MessageSize
  3049. )
  3050. /*++
  3051. Routine Description:
  3052. Build the message response message to for a DC ping.
  3053. Arguments:
  3054. TransportName - Name of the transport the message came in on
  3055. LdapPing - TRUE iff the ping from client came over LDAP
  3056. NetbiosDomainName - Netbios Domain Name of the domain to query.
  3057. DnsDomainName - UTF-8 DNS Domain Name of the domain to query.
  3058. DomainGuid - GUID of the domain being located.
  3059. If all three of the above are NULL, the primary domain is used.
  3060. DomainSid - If specified, must match the DomainSid of the domain referenced.
  3061. PdcOnly - True if only the PDC should respond.
  3062. UnicodeComputerName - Netbios computer name of the machine to respond to.
  3063. UnicodeUserName - Account name of the user being pinged.
  3064. If NULL, DC will always respond affirmatively.
  3065. AllowableAccountControlBits - Mask of allowable account types for UnicodeUserName.
  3066. NtVersion - Version of the message
  3067. NtVersionFlags - Version of the message.
  3068. 0: For backward compatibility.
  3069. NETLOGON_NT_VERSION_5: for NT 5.0 message.
  3070. ClientSockAddr - Socket Address of the client this request came in on.
  3071. If NULL, the client is this machine.
  3072. Message - Returns the message to be sent to the DC in question.
  3073. Buffer must be free using NetpMemoryFree().
  3074. MessageSize - Returns the size (in bytes) of the returned message
  3075. Return Value:
  3076. NO_ERROR - Operation completed successfully;
  3077. ERROR_NO_SUCH_DOMAIN - If the machine isn't a DC for the requested domain.
  3078. ERROR_NOT_ENOUGH_MEMORY - The message could not be allocated.
  3079. --*/
  3080. {
  3081. NET_API_STATUS NetStatus;
  3082. PDOMAIN_INFO DomainInfo = NULL;
  3083. DWORD ResponseBufferSize;
  3084. BYTE ResponseBuffer[NETLOGON_MAX_MS_SIZE]; // Buffer to build response in
  3085. DWORD OurIpAddress;
  3086. PLIST_ENTRY ListEntry;
  3087. BOOLEAN AliasNameMatched = FALSE;
  3088. //
  3089. // Ignore this call on a workstation.
  3090. //
  3091. if ( NlGlobalMemberWorkstation ) {
  3092. return ERROR_NO_SUCH_DOMAIN;
  3093. }
  3094. //
  3095. // If we are emulating NT4.0 domain and this ping came from LDAP
  3096. // and the client didn't indicate to neutralize the emulation,
  3097. // ignore this ping
  3098. //
  3099. if ( NlGlobalParameters.Nt4Emulator &&
  3100. LdapPing &&
  3101. (NtVersionFlags & NETLOGON_NT_VERSION_AVOID_NT4EMUL) == 0 ) {
  3102. return ERROR_NO_SUCH_DOMAIN;
  3103. }
  3104. //
  3105. // Be Verbose
  3106. //
  3107. NlPrint((NL_MAILSLOT,
  3108. "Received ping from %ws %s %ws on %ws\n",
  3109. UnicodeComputerName,
  3110. DnsDomainName,
  3111. UnicodeUserName,
  3112. TransportName ));
  3113. //
  3114. // The first time this is called, wait for the DS service to start.
  3115. //
  3116. if ( NlGlobalDsRunningUnknown ) {
  3117. DWORD WaitStatus;
  3118. #define NL_NTDS_HANDLE 0
  3119. #define NL_SHUTDOWN_HANDLE 1
  3120. #define NL_DS_HANDLE_COUNT 2
  3121. HANDLE EventHandles[NL_DS_HANDLE_COUNT];
  3122. //
  3123. // Create an event to wait on.
  3124. //
  3125. EventHandles[NL_NTDS_HANDLE] = OpenEvent(
  3126. SYNCHRONIZE,
  3127. FALSE,
  3128. NTDS_DELAYED_STARTUP_COMPLETED_EVENT );
  3129. if ( EventHandles[NL_NTDS_HANDLE] == NULL ) {
  3130. NetStatus = GetLastError();
  3131. NlPrint((NL_CRITICAL,
  3132. "NlGetLocalPingResponse: Cannot OpenEvent %ws %ld\n",
  3133. NTDS_DELAYED_STARTUP_COMPLETED_EVENT,
  3134. NetStatus ));
  3135. goto Cleanup;
  3136. }
  3137. EventHandles[NL_SHUTDOWN_HANDLE] = NlGlobalTerminateEvent;
  3138. //
  3139. // Wait for the DS to start
  3140. //
  3141. WaitStatus = WaitForMultipleObjects( NL_DS_HANDLE_COUNT,
  3142. EventHandles,
  3143. FALSE,
  3144. 20*60*1000 ); // Twenty minutes maximum
  3145. CloseHandle( EventHandles[NL_NTDS_HANDLE] );
  3146. switch ( WaitStatus ) {
  3147. case WAIT_OBJECT_0 + NL_NTDS_HANDLE:
  3148. break;
  3149. case WAIT_OBJECT_0 + NL_SHUTDOWN_HANDLE:
  3150. NlPrint((NL_CRITICAL,
  3151. "NlGetLocalPingResponse: Netlogon shut down.\n" ));
  3152. NetStatus = ERROR_NO_SUCH_DOMAIN;
  3153. goto Cleanup;
  3154. case WAIT_TIMEOUT:
  3155. NlPrint((NL_CRITICAL,
  3156. "NlGetLocalPingResponse: DS took too long to start.\n" ));
  3157. NetStatus = ERROR_NO_SUCH_DOMAIN;
  3158. goto Cleanup;
  3159. case WAIT_FAILED:
  3160. NetStatus = GetLastError();
  3161. NlPrint((NL_CRITICAL,
  3162. "NlGetLocalPingResponse: Wait for DS failed %ld.\n", NetStatus ));
  3163. goto Cleanup;
  3164. default:
  3165. NlPrint((NL_CRITICAL,
  3166. "NlGetLocalPingResponse: Unknown status from Wait %ld.\n", WaitStatus ));
  3167. NetStatus = ERROR_NO_SUCH_DOMAIN;
  3168. goto Cleanup;
  3169. }
  3170. //
  3171. // Never wait again.
  3172. //
  3173. NlGlobalDsRunningUnknown = FALSE;
  3174. }
  3175. //
  3176. // If no specific domain is needed,
  3177. // use the default.
  3178. //
  3179. if ( DnsDomainName == NULL && DomainGuid == NULL && NetbiosDomainName == NULL ) {
  3180. DomainInfo = NlFindNetbiosDomain(
  3181. NULL,
  3182. TRUE );
  3183. //
  3184. // See if the requested domain/NDNC is supported.
  3185. //
  3186. } else if ( DnsDomainName != NULL || DomainGuid != NULL ) {
  3187. //
  3188. // Lookup an emulated domain/NDNC using the passed DNS name.
  3189. //
  3190. // If the DNS domain name alias matches the query, the alias
  3191. // may change by the time we build the response. That's OK,
  3192. // the client will disregard our response which is proper
  3193. // since we will no longer have that alias.
  3194. //
  3195. DomainInfo = NlFindDnsDomain(
  3196. DnsDomainName,
  3197. DomainGuid,
  3198. TRUE, // look up NDNCs too
  3199. TRUE, // check domain name aliase
  3200. &AliasNameMatched );
  3201. //
  3202. // If that didn't find the emulated domain,
  3203. // and the caller is looking for a GC,
  3204. // and this is a query that doesn't need a specific domain,
  3205. // check if the DNS domain name specified is that of our tree,
  3206. // we can respond to this request.
  3207. //
  3208. // Simply use the primary emulated domain.
  3209. //
  3210. if ( DomainInfo == NULL &&
  3211. ( NtVersionFlags & NETLOGON_NT_VERSION_GC ) != 0 &&
  3212. DomainSid == NULL &&
  3213. UnicodeUserName == NULL &&
  3214. AllowableAccountControlBits == 0 &&
  3215. DnsDomainName != NULL ) {
  3216. BOOL ForestNameSame = FALSE;
  3217. EnterCriticalSection( &NlGlobalDnsForestNameCritSect );
  3218. if ( NlGlobalUtf8DnsForestName != NULL &&
  3219. NlEqualDnsNameUtf8( DnsDomainName, NlGlobalUtf8DnsForestName ) ) {
  3220. ForestNameSame = TRUE;
  3221. }
  3222. //
  3223. // If this didn't match, check if the forest name alias does
  3224. //
  3225. if ( !ForestNameSame &&
  3226. NlGlobalUtf8DnsForestNameAlias != NULL &&
  3227. NlEqualDnsNameUtf8( DnsDomainName, NlGlobalUtf8DnsForestNameAlias ) ) {
  3228. ForestNameSame = TRUE;
  3229. AliasNameMatched = TRUE;
  3230. }
  3231. LeaveCriticalSection( &NlGlobalDnsForestNameCritSect );
  3232. if ( ForestNameSame ) {
  3233. DomainInfo = NlFindNetbiosDomain( NULL, TRUE );
  3234. }
  3235. }
  3236. }
  3237. if ( DomainInfo == NULL && NetbiosDomainName != NULL ) {
  3238. DomainInfo = NlFindNetbiosDomain(
  3239. NetbiosDomainName,
  3240. FALSE );
  3241. }
  3242. if ( DomainInfo == NULL ) {
  3243. NlPrint((NL_CRITICAL,
  3244. "Ping from %ws for domain %s %ws for %ws on %ws is invalid since we don't host the named domain.\n",
  3245. UnicodeComputerName,
  3246. DnsDomainName,
  3247. NetbiosDomainName,
  3248. UnicodeUserName,
  3249. TransportName ));
  3250. NetStatus = ERROR_NO_SUCH_DOMAIN;
  3251. goto Cleanup;
  3252. }
  3253. //
  3254. // Get the IP address of this machine (any IP address)
  3255. // Loop through the list of addresses learned via winsock.
  3256. //
  3257. // Default to the loopback address (127.0.0.1). Since all DCs require IP
  3258. // to be installed, make sure we always have an IP address even though
  3259. // the net card is currently unplugged.
  3260. //
  3261. OurIpAddress = htonl(0x7f000001);
  3262. EnterCriticalSection( &NlGlobalTransportCritSect );
  3263. if ( NlGlobalWinsockPnpAddresses != NULL ) {
  3264. int i;
  3265. for ( i=0; i<NlGlobalWinsockPnpAddresses->iAddressCount; i++ ) {
  3266. PSOCKADDR SockAddr;
  3267. SockAddr = NlGlobalWinsockPnpAddresses->Address[i].lpSockaddr;
  3268. if ( SockAddr->sa_family == AF_INET ) {
  3269. OurIpAddress = ((PSOCKADDR_IN)SockAddr)->sin_addr.S_un.S_addr;
  3270. break;
  3271. }
  3272. }
  3273. }
  3274. LeaveCriticalSection( &NlGlobalTransportCritSect );
  3275. //
  3276. // If this is a primary query,
  3277. // handle it.
  3278. //
  3279. if ( PdcOnly ) {
  3280. //
  3281. // If we don't have a response,
  3282. // just tell the caller this DC doesn't match.
  3283. //
  3284. if ( !PrimaryQueryHandler(
  3285. TransportName,
  3286. DomainInfo,
  3287. AliasNameMatched,
  3288. NtVersion,
  3289. NtVersionFlags,
  3290. UnicodeComputerName,
  3291. OurIpAddress,
  3292. ClientSockAddr,
  3293. ResponseBuffer,
  3294. &ResponseBufferSize ) ) {
  3295. NetStatus = ERROR_NO_SUCH_DOMAIN;
  3296. goto Cleanup;
  3297. }
  3298. //
  3299. // If this isn't a primary query,
  3300. // handle it.
  3301. //
  3302. } else {
  3303. //
  3304. // If we don't have a response,
  3305. // just tell the caller this DC doesn't match.
  3306. //
  3307. if ( !LogonRequestHandler(
  3308. TransportName,
  3309. DomainInfo,
  3310. AliasNameMatched,
  3311. DomainSid,
  3312. NtVersion,
  3313. NtVersionFlags,
  3314. UnicodeUserName,
  3315. 0, // RequestCount
  3316. UnicodeComputerName,
  3317. AllowableAccountControlBits,
  3318. OurIpAddress,
  3319. ClientSockAddr,
  3320. ResponseBuffer,
  3321. &ResponseBufferSize ) ) {
  3322. NetStatus = ERROR_NO_SUCH_DOMAIN;
  3323. goto Cleanup;
  3324. }
  3325. }
  3326. //
  3327. // Actually allocate a buffer for the response.
  3328. //
  3329. *Message = NetpMemoryAllocate( ResponseBufferSize );
  3330. if ( *Message == NULL ) {
  3331. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  3332. goto Cleanup;
  3333. }
  3334. RtlCopyMemory( *Message, ResponseBuffer, ResponseBufferSize );
  3335. *MessageSize = ResponseBufferSize;
  3336. NetStatus = NO_ERROR;
  3337. Cleanup:
  3338. if ( DomainInfo != NULL ) {
  3339. NlDereferenceDomain( DomainInfo );
  3340. }
  3341. return NetStatus;
  3342. }
  3343. #endif // _DC_NETLOGON
  3344. BOOL
  3345. TimerExpired(
  3346. IN PTIMER Timer,
  3347. IN PLARGE_INTEGER TimeNow,
  3348. IN OUT LPDWORD Timeout
  3349. )
  3350. /*++
  3351. Routine Description:
  3352. Determine whether a timer has expired. If not, adjust the passed in
  3353. timeout value to take this timer into account.
  3354. Arguments:
  3355. Timer - Specifies the timer to check.
  3356. TimeNow - Specifies the current time of day in NT standard time.
  3357. Timeout - Specifies the current amount of time (in milliseconds)
  3358. that the caller intends to wait for a timer to expire.
  3359. If this timer has not expired, this value is adjusted to the
  3360. smaller of the current value and the amount of time remaining
  3361. on the passed in timer.
  3362. Return Value:
  3363. TRUE - if the timer has expired.
  3364. --*/
  3365. {
  3366. LARGE_INTEGER Period;
  3367. LARGE_INTEGER ExpirationTime;
  3368. LARGE_INTEGER ElapsedTime;
  3369. LARGE_INTEGER TimeRemaining;
  3370. LARGE_INTEGER MillisecondsRemaining;
  3371. /*lint -e569 */ /* don't complain about 32-bit to 31-bit initialize */
  3372. LARGE_INTEGER BaseGetTickMagicDivisor = { 0xe219652c, 0xd1b71758 };
  3373. /*lint +e569 */ /* don't complain about 32-bit to 31-bit initialize */
  3374. CCHAR BaseGetTickMagicShiftCount = 13;
  3375. //
  3376. // If the period to too large to handle (i.e., 0xffffffff is forever),
  3377. // just indicate that the timer has not expired.
  3378. //
  3379. if ( Timer->Period > TIMER_MAX_PERIOD ) {
  3380. return FALSE;
  3381. }
  3382. //
  3383. // If time has gone backwards (someone changed the clock),
  3384. // just start the timer over again.
  3385. //
  3386. // The kernel automatically updates the system time to the CMOS clock
  3387. // periodically. If we just expired the timer when time went backwards,
  3388. // we'd risk periodically falsely triggering the timeout.
  3389. //
  3390. ElapsedTime.QuadPart = TimeNow->QuadPart - Timer->StartTime.QuadPart;
  3391. if ( ElapsedTime.QuadPart < 0 ) {
  3392. Timer->StartTime = *TimeNow;
  3393. }
  3394. //
  3395. // Convert the period from milliseconds to 100ns units.
  3396. //
  3397. Period.QuadPart = UInt32x32To64( (LONG) Timer->Period, 10000 );
  3398. //
  3399. // Compute the expiration time.
  3400. //
  3401. ExpirationTime.QuadPart = Timer->StartTime.QuadPart + Period.QuadPart;
  3402. //
  3403. // Compute the Time remaining on the timer.
  3404. //
  3405. TimeRemaining.QuadPart = ExpirationTime.QuadPart - TimeNow->QuadPart;
  3406. //
  3407. // If the timer has expired, tell the caller so.
  3408. //
  3409. if ( TimeRemaining.QuadPart <= 0 ) {
  3410. return TRUE;
  3411. }
  3412. //
  3413. // If the timer hasn't expired, compute the number of milliseconds
  3414. // remaining.
  3415. //
  3416. MillisecondsRemaining = RtlExtendedMagicDivide(
  3417. TimeRemaining,
  3418. BaseGetTickMagicDivisor,
  3419. BaseGetTickMagicShiftCount );
  3420. NlAssert( MillisecondsRemaining.HighPart == 0 );
  3421. NlAssert( MillisecondsRemaining.LowPart <= TIMER_MAX_PERIOD );
  3422. //
  3423. // Adjust the running timeout to be the smaller of the current value
  3424. // and the value computed for this timer.
  3425. //
  3426. if ( *Timeout > MillisecondsRemaining.LowPart ) {
  3427. *Timeout = MillisecondsRemaining.LowPart;
  3428. }
  3429. return FALSE;
  3430. }
  3431. NET_API_STATUS
  3432. NlDomainScavenger(
  3433. IN PDOMAIN_INFO DomainInfo,
  3434. IN PVOID Context
  3435. )
  3436. /*++
  3437. Routine Description:
  3438. Perform the per-domain scavenging.
  3439. Arguments:
  3440. DomainInfo - The domain being scavenged.
  3441. Context - Not Used
  3442. Return Value:
  3443. Success (not used).
  3444. --*/
  3445. {
  3446. DWORD DomFlags;
  3447. //
  3448. // Change password if neccessary
  3449. //
  3450. if ( NlGlobalTerminate ) {
  3451. return NERR_Success;
  3452. }
  3453. if ( !NlGlobalParameters.DisablePasswordChange ) {
  3454. PCLIENT_SESSION ClientSession;
  3455. ClientSession = NlRefDomClientSession( DomainInfo );
  3456. if ( ClientSession != NULL ) {
  3457. (VOID) NlChangePassword( ClientSession, FALSE, NULL );
  3458. NlUnrefClientSession( ClientSession );
  3459. }
  3460. }
  3461. #ifdef _DC_NETLOGON
  3462. //
  3463. // Change the password on each entry in the trust list.
  3464. //
  3465. // Check whether forest trust info needs update.
  3466. //
  3467. if ( NlGlobalTerminate ) {
  3468. return NERR_Success;
  3469. }
  3470. if ( DomainInfo->DomRole == RolePrimary ) {
  3471. PLIST_ENTRY ListEntry;
  3472. PCLIENT_SESSION ClientSession;
  3473. ULONG LocalFtInfoUpdateInterval;
  3474. //
  3475. // Get the forest trust update interval as configured in registry
  3476. //
  3477. LocalFtInfoUpdateInterval = NlGlobalParameters.FtInfoUpdateInterval;
  3478. //
  3479. // If the value converted into milliseconds fits into a ULONG,
  3480. // use it
  3481. //
  3482. if ( LocalFtInfoUpdateInterval <= MAXULONG/1000 ) {
  3483. LocalFtInfoUpdateInterval *= 1000; // convert to milliseconds
  3484. //
  3485. // Otherwise, the interval is infinity
  3486. //
  3487. } else {
  3488. LocalFtInfoUpdateInterval = MAXULONG;
  3489. }
  3490. //
  3491. // Reset all the flags indicating we need to check the password
  3492. // and forest trust info
  3493. //
  3494. LOCK_TRUST_LIST( DomainInfo );
  3495. for ( ListEntry = DomainInfo->DomTrustList.Flink ;
  3496. ListEntry != &DomainInfo->DomTrustList ;
  3497. ListEntry = ListEntry->Flink) {
  3498. ClientSession = CONTAINING_RECORD( ListEntry,
  3499. CLIENT_SESSION,
  3500. CsNext );
  3501. //
  3502. // Only check if there is a direct trust to the domain.
  3503. //
  3504. if ( ClientSession->CsFlags & CS_DIRECT_TRUST ) {
  3505. ClientSession->CsFlags |= CS_CHECK_DIRECT_TRUST;
  3506. }
  3507. }
  3508. for ( ListEntry = DomainInfo->DomTrustList.Flink ;
  3509. ListEntry != &DomainInfo->DomTrustList ;
  3510. ) {
  3511. BOOL RefreshFtInfo = FALSE;
  3512. ClientSession = CONTAINING_RECORD( ListEntry,
  3513. CLIENT_SESSION,
  3514. CsNext );
  3515. if ( (ClientSession->CsFlags & CS_CHECK_DIRECT_TRUST) == 0 ) {
  3516. ListEntry = ListEntry->Flink;
  3517. continue;
  3518. }
  3519. ClientSession->CsFlags &= ~CS_CHECK_DIRECT_TRUST;
  3520. //
  3521. // See if we need to refresh FTInfo for this (direct) trust
  3522. //
  3523. if ( (ClientSession->CsTrustAttributes & TRUST_ATTRIBUTE_FOREST_TRANSITIVE) &&
  3524. (ClientSession->CsLastFtInfoRefreshTime.QuadPart == 0 ||
  3525. NetpLogonTimeHasElapsed(ClientSession->CsLastFtInfoRefreshTime,
  3526. LocalFtInfoUpdateInterval)) ) {
  3527. RefreshFtInfo = TRUE;
  3528. }
  3529. NlRefClientSession( ClientSession );
  3530. UNLOCK_TRUST_LIST( DomainInfo );
  3531. //
  3532. // Change the password for this trusted domain.
  3533. //
  3534. (VOID) NlChangePassword( ClientSession, FALSE, NULL );
  3535. //
  3536. // check to see if we have been asked to leave.
  3537. //
  3538. if ( NlGlobalTerminate ) {
  3539. NlUnrefClientSession( ClientSession );
  3540. return NERR_Success;
  3541. }
  3542. //
  3543. // Refresh FTInfo if needed
  3544. //
  3545. if ( RefreshFtInfo ) {
  3546. //
  3547. // Become a Writer of the ClientSession.
  3548. //
  3549. if ( !NlTimeoutSetWriterClientSession(ClientSession, WRITER_WAIT_PERIOD) ) {
  3550. NlPrintCs(( NL_CRITICAL, ClientSession,
  3551. "NlDomainScavenger: Can't become writer of client session.\n" ));
  3552. } else {
  3553. PLSA_FOREST_TRUST_INFORMATION ForestTrustInfo = NULL;
  3554. NlPrintCs(( NL_MISC, ClientSession,
  3555. "NlDomainScavenger: Updating forest trust info\n" ));
  3556. //
  3557. // Get the FTinfo from the trusted domain and write it to our TDO.
  3558. // Ignore failures.
  3559. //
  3560. NlpGetForestTrustInfoHigher( ClientSession,
  3561. DS_GFTI_UPDATE_TDO,
  3562. FALSE, // Don't impersonate caller
  3563. FALSE, // We didn't set up the session
  3564. &ForestTrustInfo );
  3565. if ( ForestTrustInfo != NULL ) {
  3566. NetApiBufferFree( ForestTrustInfo );
  3567. }
  3568. NlResetWriterClientSession( ClientSession );
  3569. }
  3570. }
  3571. NlUnrefClientSession( ClientSession );
  3572. //
  3573. // check to see if we have been asked to leave.
  3574. //
  3575. if ( NlGlobalTerminate ) {
  3576. return NERR_Success;
  3577. }
  3578. LOCK_TRUST_LIST( DomainInfo );
  3579. // Start again at the beginning.
  3580. ListEntry = DomainInfo->DomTrustList.Flink;
  3581. }
  3582. UNLOCK_TRUST_LIST( DomainInfo );
  3583. }
  3584. //
  3585. // Scavenge the list of failed forwarded user logons
  3586. //
  3587. if ( DomainInfo->DomRole == RoleBackup ) {
  3588. NlScavengeOldFailedLogons( DomainInfo );
  3589. }
  3590. //
  3591. // Scavenge through the server session table.
  3592. //
  3593. if ( DomainInfo->DomRole == RolePrimary || DomainInfo->DomRole == RoleBackup ) {
  3594. if ( NlGlobalTerminate ) {
  3595. return NERR_Success;
  3596. }
  3597. NlServerSessionScavenger( DomainInfo );
  3598. //
  3599. // Pick a DC for each non-authenicated entry in the trust list.
  3600. //
  3601. if ( NlGlobalTerminate ) {
  3602. return NERR_Success;
  3603. }
  3604. NlPickTrustedDcForEntireTrustList( DomainInfo, FALSE );
  3605. }
  3606. //
  3607. // If the role of this machine isn't known,
  3608. // the role update failed (so schedule another one).
  3609. //
  3610. if ( DomainInfo->DomRole == RoleInvalid ) {
  3611. NlPrintDom((NL_MISC, DomainInfo,
  3612. "DomainScavenger: Try again to update the role.\n" ));
  3613. DomFlags = DOM_ROLE_UPDATE_NEEDED;
  3614. NlStartDomainThread( DomainInfo, &DomFlags );
  3615. }
  3616. //
  3617. // If this is a primary domain and the trust info is not up to date,
  3618. // schedule the trust info update now.
  3619. //
  3620. if ( DomainInfo->DomFlags & DOM_PRIMARY_DOMAIN ) {
  3621. if ( WaitForSingleObject( NlGlobalTrustInfoUpToDateEvent, 0 ) == WAIT_TIMEOUT ) {
  3622. NlPrintDom((NL_MISC, DomainInfo,
  3623. "DomainScavenger: Try again to update the trusted domain list.\n" ));
  3624. DomFlags = DOM_TRUST_UPDATE_NEEDED;
  3625. NlStartDomainThread( DomainInfo, &DomFlags );
  3626. }
  3627. }
  3628. #endif // _DC_NETLOGON
  3629. return NERR_Success;
  3630. UNREFERENCED_PARAMETER( Context );
  3631. }
  3632. VOID
  3633. NlDcScavenger(
  3634. IN LPVOID ScavengerParam
  3635. )
  3636. /*++
  3637. Routine Description:
  3638. This function performs the scavenger operation. This function is
  3639. called every 15 mins interval. This function is
  3640. executed on the scavenger thread, thus leaving the main thread to
  3641. process the mailslot messages better.
  3642. This function is specific to domain controllers.
  3643. Arguments:
  3644. None.
  3645. Return Value:
  3646. None.
  3647. --*/
  3648. {
  3649. LPWSTR MsgStrings[4];
  3650. ULONG TimePassed = 0;
  3651. LARGE_INTEGER DuplicateEventlogTimeout_100ns;
  3652. //
  3653. // Reset the scavenger timer to run at the normal interval.
  3654. // Other places (challenge request/response handling) which
  3655. // need more expedient scavenging will reschedule the timer
  3656. // as needed.
  3657. //
  3658. EnterCriticalSection( &NlGlobalScavengerCritSect );
  3659. NlGlobalScavengerTimer.Period = NlGlobalParameters.ScavengeInterval * 1000L;
  3660. LeaveCriticalSection( &NlGlobalScavengerCritSect );
  3661. //
  3662. // Scavenge one domain at a time
  3663. //
  3664. if ( NlGlobalTerminate ) {
  3665. goto Cleanup;
  3666. }
  3667. (VOID) NlEnumerateDomains( FALSE, NlDomainScavenger, NULL );
  3668. //
  3669. // Scavenge expired challenge entries in the
  3670. // global list of outstanding challenges
  3671. //
  3672. NlScavengeOldChallenges();
  3673. //
  3674. // If there were clients with no site, see if it's time
  3675. // to log an event -- avoid poluting the event log.
  3676. //
  3677. // Note that we don't use the duplicate event log mechanism
  3678. // as the message we are logging is likely to be different
  3679. // from previous ones due to the count parameter.
  3680. //
  3681. EnterCriticalSection( &NlGlobalSiteCritSect );
  3682. DuplicateEventlogTimeout_100ns.QuadPart =
  3683. Int32x32To64( NlGlobalParameters.DuplicateEventlogTimeout, 10000000 );
  3684. if ( NlGlobalNoClientSiteCount > 0 &&
  3685. NlTimeHasElapsedEx(&NlGlobalNoClientSiteEventTime,
  3686. &DuplicateEventlogTimeout_100ns,
  3687. &TimePassed) ) {
  3688. // Max ULONG is 4294967295 => 11 chars to store it
  3689. WCHAR ConnectionCountStr[11];
  3690. WCHAR DefaultLogMaxSizeStr[11];
  3691. WCHAR LogMaxSizeStr[11];
  3692. // 20 chars is more than enough: 0xffffffff/3600 = 1193046.47
  3693. WCHAR TimeoutStr[20];
  3694. //
  3695. // Get the time passed since we logged
  3696. // the event last time
  3697. //
  3698. swprintf( TimeoutStr,
  3699. L"%.2f",
  3700. (double) (NlGlobalParameters.DuplicateEventlogTimeout + TimePassed/1000) / 3600 );
  3701. swprintf( ConnectionCountStr, L"%lu", NlGlobalNoClientSiteCount );
  3702. swprintf( DefaultLogMaxSizeStr, L"%lu", DEFAULT_MAXIMUM_LOGFILE_SIZE );
  3703. swprintf( LogMaxSizeStr, L"%lu", NlGlobalParameters.LogFileMaxSize );
  3704. MsgStrings[0] = TimeoutStr;
  3705. MsgStrings[1] = ConnectionCountStr;
  3706. MsgStrings[2] = DefaultLogMaxSizeStr;
  3707. MsgStrings[3] = LogMaxSizeStr;
  3708. NlpWriteEventlog( NELOG_NetlogonNoSiteForClients,
  3709. EVENTLOG_WARNING_TYPE,
  3710. NULL,
  3711. 0,
  3712. MsgStrings,
  3713. 4 );
  3714. //
  3715. // Reset the count
  3716. //
  3717. NlGlobalNoClientSiteCount = 0;
  3718. NlQuerySystemTime( &NlGlobalNoClientSiteEventTime );
  3719. }
  3720. LeaveCriticalSection( &NlGlobalSiteCritSect );
  3721. //
  3722. // It's OK to run the scavenger again.
  3723. //
  3724. Cleanup:
  3725. EnterCriticalSection( &NlGlobalScavengerCritSect );
  3726. NlGlobalDcScavengerIsRunning = FALSE;
  3727. // Reset the StartTime in case this routine takes a long time to process.
  3728. NlQuerySystemTime( &NlGlobalScavengerTimer.StartTime );
  3729. LeaveCriticalSection( &NlGlobalScavengerCritSect );
  3730. UNREFERENCED_PARAMETER( ScavengerParam );
  3731. }
  3732. VOID
  3733. NlWksScavenger(
  3734. VOID
  3735. )
  3736. /*++
  3737. Routine Description:
  3738. This function performs the scavenger operation. This function is
  3739. called every 15 mins interval. This function is executed on the main thread.
  3740. This function is specific to member workstations and member servers
  3741. Arguments:
  3742. None.
  3743. Return Value:
  3744. None.
  3745. --*/
  3746. {
  3747. ULONG CallAgainPeriod = MAILSLOT_WAIT_FOREVER; // Default to not scavenging again.
  3748. ULONG TempPeriod;
  3749. //
  3750. // Change password if neccessary
  3751. //
  3752. if ( !NlGlobalParameters.DisablePasswordChange ) {
  3753. PCLIENT_SESSION ClientSession;
  3754. ClientSession = NlRefDomClientSession( NlGlobalDomainInfo );
  3755. if ( ClientSession != NULL ) {
  3756. (VOID) NlChangePassword( ClientSession, FALSE, &CallAgainPeriod );
  3757. NlUnrefClientSession( ClientSession );
  3758. } else {
  3759. // This can't happen (but try again periodically)
  3760. CallAgainPeriod = 0;
  3761. }
  3762. }
  3763. //
  3764. // Never scavenge more frequently than the configured rate.
  3765. //
  3766. EnterCriticalSection( &NlGlobalScavengerCritSect );
  3767. NlQuerySystemTime( &NlGlobalScavengerTimer.StartTime );
  3768. NlGlobalScavengerTimer.Period = max( (NlGlobalParameters.ScavengeInterval * 1000L),
  3769. CallAgainPeriod );
  3770. NlpDumpPeriod( NL_MISC,
  3771. "NlWksScavenger: Can be called again in",
  3772. NlGlobalScavengerTimer.Period );
  3773. LeaveCriticalSection( &NlGlobalScavengerCritSect );
  3774. }
  3775. VOID
  3776. NlMainLoop(
  3777. VOID
  3778. )
  3779. /*++
  3780. Routine Description:
  3781. Waits for a logon request to arrive at the NETLOGON mailslot.
  3782. This routine, also, processes several periodic events. These events
  3783. are timed by computing a timeout value on the mailslot read which is the
  3784. time needed before the nearest periodic event needs to be processed.
  3785. After such a timeout, this routine processes the event.
  3786. Arguments:
  3787. None.
  3788. Return Value:
  3789. Return iff the service is to exit.
  3790. mail slot error occurred, eg if someone deleted the NETLOGON
  3791. mail slot explicitly or if the logon server share has been deleted
  3792. and cannot be re-shared.
  3793. --*/
  3794. {
  3795. NET_API_STATUS NetStatus;
  3796. DWORD WaitStatus;
  3797. BOOLEAN IgnoreDuplicatesOfThisMessage;
  3798. BOOLEAN RegNotifyNeeded = TRUE;
  3799. HKEY ParmHandle = NULL;
  3800. HANDLE ParmEventHandle = NULL;
  3801. BOOLEAN GpRegNotifyNeeded = TRUE;
  3802. HKEY GpParmHandle = NULL;
  3803. HANDLE GpParmEventHandle = NULL;
  3804. //
  3805. // Variables controlling mailslot read timeout
  3806. //
  3807. DWORD MainLoopTimeout = 0;
  3808. LARGE_INTEGER TimeNow;
  3809. TIMER AnnouncerTimer;
  3810. TIMER SubnetSiteUpdateTimer;
  3811. #define NL_WAIT_TERMINATE 0
  3812. #define NL_WAIT_TIMER 1
  3813. #define NL_WAIT_MAILSLOT 2
  3814. // Optional entries should be at the end.
  3815. ULONG NlWaitWinsock = 0; // 3
  3816. ULONG NlWaitNotify = 0; // 4
  3817. ULONG NlWaitParameters = 0; // 5
  3818. ULONG NlWaitGpParameters = 0; // 6
  3819. #define NL_WAIT_COUNT 7
  3820. HANDLE WaitHandles[ NL_WAIT_COUNT ];
  3821. DWORD WaitCount = 0;
  3822. //
  3823. // Initialize handles to wait on.
  3824. //
  3825. WaitHandles[NL_WAIT_TERMINATE] = NlGlobalTerminateEvent;
  3826. WaitCount++;
  3827. WaitHandles[NL_WAIT_TIMER] = NlGlobalTimerEvent;
  3828. WaitCount++;
  3829. WaitHandles[NL_WAIT_MAILSLOT] = NlGlobalMailslotHandle;
  3830. WaitCount++;
  3831. //
  3832. // In IP-less environments the Winsock event doesn't exist.
  3833. //
  3834. if ( NlGlobalWinsockPnpEvent != NULL ) {
  3835. NlWaitWinsock = WaitCount;
  3836. WaitHandles[NlWaitWinsock] = NlGlobalWinsockPnpEvent;
  3837. WaitCount++;
  3838. }
  3839. //
  3840. // When netlogon is run during retail setup
  3841. // (in an attempt to replicate the databases to a BDC),
  3842. // the role is Workstation at the instant netlogon.dll is loaded,
  3843. // therefore, the ChangeLogEvent won't have been initialized.
  3844. //
  3845. if ( NlGlobalChangeLogEvent != NULL ) {
  3846. NlWaitNotify = WaitCount;
  3847. WaitHandles[NlWaitNotify] = NlGlobalChangeLogEvent;
  3848. WaitCount++;
  3849. }
  3850. //
  3851. // Set up a secure channel to any DC in the domain.
  3852. // Don't fail if setup is impossible.
  3853. //
  3854. // We wait until now since this is a potentially lengthy operation.
  3855. // If the user on the workstation is trying to logon immediately after
  3856. // reboot, we'd rather have him wait in netlogon (where we have more
  3857. // control) than have him waiting in MSV.
  3858. //
  3859. if ( NlGlobalMemberWorkstation ) {
  3860. PDOMAIN_INFO DomainInfo;
  3861. PCLIENT_SESSION ClientSession;
  3862. DomainInfo = NlFindNetbiosDomain( NULL, TRUE ); // Primary domain
  3863. if ( DomainInfo != NULL ) {
  3864. ClientSession = NlRefDomClientSession(DomainInfo);
  3865. if ( ClientSession != NULL ) {
  3866. //
  3867. // Set up a client session if it hasn't been already done
  3868. //
  3869. (VOID) NlTimeoutSetWriterClientSession( ClientSession, 0xFFFFFFFF );
  3870. if ( ClientSession->CsState == CS_IDLE ) {
  3871. (VOID) NlSessionSetup( ClientSession );
  3872. }
  3873. NlResetWriterClientSession( ClientSession );
  3874. NlUnrefClientSession( ClientSession );
  3875. } else {
  3876. NlPrint((NL_CRITICAL,
  3877. "NlMainLoop: Cannot NlRefDomClientSession\n" ));
  3878. }
  3879. NlDereferenceDomain( DomainInfo );
  3880. } else {
  3881. NlPrint((NL_CRITICAL,
  3882. "NlMainLoop: Cannot NlFindNetbiosDomain\n" ));
  3883. }
  3884. }
  3885. //
  3886. // Force the announce to happen immediately.
  3887. //
  3888. // Actually, wait the announcement period. NlInitTcpRpc will force an "immediate"
  3889. // announcement as soon as TCP RPC is enabled.
  3890. //
  3891. NlQuerySystemTime( &TimeNow );
  3892. AnnouncerTimer.StartTime = TimeNow;
  3893. AnnouncerTimer.Period = NlGlobalParameters.Pulse * 1000L;
  3894. SubnetSiteUpdateTimer.StartTime = TimeNow;
  3895. SubnetSiteUpdateTimer.Period = (DWORD) MAILSLOT_WAIT_FOREVER;
  3896. NlGlobalApiTimer.StartTime = TimeNow;
  3897. //
  3898. // It is possible that we missed service notifications to update DNS
  3899. // records on boot because we were not ready to process notifications
  3900. // at that time. So if any of the DNS service bits is set, schedule
  3901. // the DNS scavenger to run immediately to update DNS if it indeed
  3902. // hasn't been done already.
  3903. //
  3904. if ( !NlGlobalMemberWorkstation &&
  3905. (NlGetDomainFlags(NULL) & DS_DNS_SERVICE_BITS) != 0 ) {
  3906. NlGlobalDnsScavengerTimer.StartTime.QuadPart = 0;
  3907. NlGlobalDnsScavengerTimer.Period = 0;
  3908. }
  3909. NlPrint((NL_INIT, "Started successfully\n" ));
  3910. //
  3911. // Loop reading from the Netlogon mailslot
  3912. //
  3913. IgnoreDuplicatesOfThisMessage = FALSE;
  3914. for ( ;; ) {
  3915. DWORD Timeout;
  3916. //
  3917. // Issue a mailslot read request if we are domain controller and
  3918. // there is no outstanding read request pending.
  3919. //
  3920. NlMailslotPostRead( IgnoreDuplicatesOfThisMessage );
  3921. IgnoreDuplicatesOfThisMessage = FALSE;
  3922. //
  3923. // Register for registry change notification
  3924. //
  3925. if ( RegNotifyNeeded || GpRegNotifyNeeded ) {
  3926. ULONG TryCount;
  3927. //
  3928. // Try couple of times to post the registry
  3929. // notification requests
  3930. //
  3931. for ( TryCount = 0; TryCount < 2; TryCount++ ) {
  3932. NetStatus = NO_ERROR;
  3933. // Retry the Netlogon Parameters registration on each iteration for resiliency
  3934. if ( ParmHandle == NULL ) {
  3935. ParmHandle = NlOpenNetlogonKey( NL_PARAM_KEY );
  3936. if (ParmHandle == NULL) {
  3937. NlPrint(( NL_CRITICAL,
  3938. "Cannot NlOpenNetlogonKey (ignored)\n" ));
  3939. }
  3940. }
  3941. if ( ParmEventHandle == NULL ) {
  3942. ParmEventHandle = CreateEvent( NULL, // No security attributes
  3943. TRUE, // Must be manually reset
  3944. FALSE, // Initially not signaled
  3945. NULL ); // No name
  3946. if ( ParmEventHandle == NULL ) {
  3947. NlPrint(( NL_CRITICAL,
  3948. "Cannot Create parameter key event %ld (ignored)\n",
  3949. GetLastError() ));
  3950. } else {
  3951. NlWaitParameters = WaitCount;
  3952. WaitHandles[NlWaitParameters] = ParmEventHandle;
  3953. WaitCount++;
  3954. NlAssert( WaitCount <= NL_WAIT_COUNT );
  3955. }
  3956. }
  3957. if ( RegNotifyNeeded && ParmHandle != NULL && ParmEventHandle != NULL ) {
  3958. NetStatus = RegNotifyChangeKeyValue(
  3959. ParmHandle,
  3960. FALSE, // don't watch subtree
  3961. REG_NOTIFY_CHANGE_LAST_SET,
  3962. ParmEventHandle,
  3963. TRUE ); // Async
  3964. if ( NetStatus == NO_ERROR ) {
  3965. RegNotifyNeeded = FALSE;
  3966. // If the key has been manually deleted,
  3967. // recover from it by just closing ParmHandle
  3968. // to reopen it on the second try
  3969. } else if ( NetStatus == ERROR_KEY_DELETED ) {
  3970. NlPrint(( NL_CRITICAL, "Netlogon Parameters key deleted (recover)\n" ));
  3971. RegCloseKey( ParmHandle );
  3972. ParmHandle = NULL;
  3973. ResetEvent( ParmEventHandle );
  3974. } else {
  3975. NlPrint(( NL_CRITICAL,
  3976. "Cannot RegNotifyChangeKeyValue 0x%lx (ignored)\n",
  3977. NetStatus ));
  3978. }
  3979. }
  3980. // Retry the GP Parameters registration on each iteration for resiliency
  3981. // Note that here we open the Netlogon key (not Netlogon\Parameters key)
  3982. // and we watch for the subtree. We do this for debugging purposes to
  3983. // see whether GP is enabled for Netlogon by checking if the GP created
  3984. // Parameters section exists. See nlparse.c.
  3985. if ( GpParmHandle == NULL ) {
  3986. GpParmHandle = NlOpenNetlogonKey( NL_GP_KEY );
  3987. if (GpParmHandle == NULL) {
  3988. NlPrint(( NL_CRITICAL,
  3989. "Cannot NlOpenNetlogonKey for GP (ignored)\n" ));
  3990. }
  3991. }
  3992. if ( GpParmEventHandle == NULL ) {
  3993. GpParmEventHandle = CreateEvent( NULL, // No security attributes
  3994. TRUE, // Must be manually reset
  3995. FALSE, // Initially not signaled
  3996. NULL ); // No name
  3997. if ( GpParmEventHandle == NULL ) {
  3998. NlPrint(( NL_CRITICAL,
  3999. "Cannot Create GP parameter key event %ld (ignored)\n",
  4000. GetLastError() ));
  4001. } else {
  4002. NlWaitGpParameters = WaitCount;
  4003. WaitHandles[NlWaitGpParameters] = GpParmEventHandle;
  4004. WaitCount++;
  4005. NlAssert( WaitCount <= NL_WAIT_COUNT );
  4006. }
  4007. }
  4008. if ( GpRegNotifyNeeded && GpParmHandle != NULL && GpParmEventHandle != NULL ) {
  4009. NetStatus = RegNotifyChangeKeyValue(
  4010. GpParmHandle,
  4011. TRUE, // watch subtree
  4012. REG_NOTIFY_CHANGE_LAST_SET,
  4013. GpParmEventHandle,
  4014. TRUE ); // Async
  4015. if ( NetStatus == NO_ERROR ) {
  4016. GpRegNotifyNeeded = FALSE;
  4017. // If GP has deleted the key,
  4018. // recover from it by just closing GpParmHandle
  4019. // to reopen it on the second try
  4020. } else if ( NetStatus == ERROR_KEY_DELETED ) {
  4021. NlPrint(( NL_CRITICAL, "Netlogon GP Parameters key deleted (recover)\n" ));
  4022. RegCloseKey( GpParmHandle );
  4023. GpParmHandle = NULL;
  4024. ResetEvent( GpParmEventHandle );
  4025. } else {
  4026. NlPrint(( NL_CRITICAL,
  4027. "Cannot RegNotifyChangeKeyValue for GP 0x%lx (ignored)\n",
  4028. NetStatus ));
  4029. }
  4030. }
  4031. //
  4032. // If no error occured, no need to retry
  4033. //
  4034. if ( NetStatus == NO_ERROR ) {
  4035. break;
  4036. }
  4037. }
  4038. NlReparse();
  4039. //
  4040. // Grab any changed parameters that affect this routine.
  4041. //
  4042. AnnouncerTimer.Period = NlGlobalParameters.Pulse * 1000L;
  4043. }
  4044. //
  4045. // Wait for the next interesting event.
  4046. //
  4047. // On each iteration of the loop,
  4048. // we do an "extra" wait with a timeout of 0 to force mailslot
  4049. // processing to be more important that timeout processing.
  4050. //
  4051. // Since we can only compute a non-zero timeout by processing the
  4052. // timeout events, using a constant 0 allows us to process all
  4053. // non-timeout events before we compute the next true timeout value.
  4054. //
  4055. // This is especially important for handling async discovery.
  4056. // Our mailslot may be full of responses to discovery queries and
  4057. // we only have a 5 second timer before we ask for more responses.
  4058. // We want to avoid asking for additional responses until we finish
  4059. // processing those we have.
  4060. //
  4061. if ( MainLoopTimeout != 0 ) {
  4062. NlPrint((NL_MAILSLOT_TEXT,
  4063. "Going to wait on mailslot. (Timeout: %ld)\n",
  4064. MainLoopTimeout));
  4065. }
  4066. WaitStatus = WaitForMultipleObjects( WaitCount,
  4067. WaitHandles,
  4068. FALSE, // Wait for ANY handle
  4069. MainLoopTimeout );
  4070. MainLoopTimeout = 0; // Set default timeout
  4071. //
  4072. // If we've been asked to terminate,
  4073. // do so immediately
  4074. //
  4075. if ( WaitStatus == NL_WAIT_TERMINATE ) { // service termination
  4076. goto Cleanup;
  4077. //
  4078. // Process timeouts and determine the timeout for the next iteration
  4079. //
  4080. } else if ( WaitStatus == WAIT_TIMEOUT || // timeout
  4081. WaitStatus == NL_WAIT_TIMER ) { // someone changed a timer
  4082. //
  4083. // Assume there is no timeout to do.
  4084. //
  4085. // On each iteration of the loop we only process a single timer.
  4086. // That ensures other events are more important than timers.
  4087. //
  4088. Timeout = (DWORD) -1;
  4089. NlQuerySystemTime( &TimeNow );
  4090. //
  4091. // On the primary, timeout announcements to BDCs
  4092. //
  4093. if ( NlGlobalPdcDoReplication &&
  4094. TimerExpired( &NlGlobalPendingBdcTimer, &TimeNow, &Timeout )) {
  4095. //
  4096. // The work is done in the domain thread.
  4097. //
  4098. // Note that we don't need to worry about serializing
  4099. // this with previous announcement activity in the
  4100. // worker thread because there exists only one worker
  4101. // thread. Also note that we don't run the risk of doing
  4102. // network I/O too often because this will be basically
  4103. // no-op if there are no outstanding BDCs to timeout.
  4104. //
  4105. NlPrimaryAnnouncementTimeout();
  4106. NlGlobalPendingBdcTimer.StartTime = TimeNow;
  4107. //
  4108. // Check the scavenger timer
  4109. //
  4110. } else if ( TimerExpired( &NlGlobalScavengerTimer, &TimeNow, &Timeout ) ) {
  4111. //
  4112. // On workstation run the scavenger on main thread.
  4113. //
  4114. if ( NlGlobalMemberWorkstation ) {
  4115. NlWksScavenger();
  4116. //
  4117. // On domain controller, start scavenger thread if it is not
  4118. // running already.
  4119. //
  4120. } else {
  4121. EnterCriticalSection( &NlGlobalScavengerCritSect );
  4122. if ( !NlGlobalDcScavengerIsRunning ) {
  4123. if ( NlQueueWorkItem( &NlGlobalDcScavengerWorkItem, TRUE, FALSE ) ) {
  4124. NlGlobalDcScavengerIsRunning = TRUE;
  4125. }
  4126. }
  4127. //
  4128. // NlDcScavenger sets the StartTime,too
  4129. // (But we have to reset the timer here to prevent it from
  4130. // going off immediately again. We need to reset the period
  4131. // (as well as the start time) since the period is set in
  4132. // the registry notification processing to zero.)
  4133. //
  4134. NlGlobalScavengerTimer.StartTime = TimeNow;
  4135. NlGlobalScavengerTimer.Period = NlGlobalParameters.ScavengeInterval * 1000L;
  4136. LeaveCriticalSection( &NlGlobalScavengerCritSect );
  4137. }
  4138. //
  4139. // Check the API timer
  4140. //
  4141. } else if ( TimerExpired( &NlGlobalApiTimer, &TimeNow, &Timeout)) {
  4142. //
  4143. // On worktstation, do the work in the main loop
  4144. //
  4145. if ( NlGlobalMemberWorkstation ) {
  4146. NlTimeoutApiClientSession( NlGlobalDomainInfo );
  4147. //
  4148. // On DC, timout APIs on all Hosted domains.
  4149. // Do this in domain threads so that not to block
  4150. // the main thread (which is critical for a DC) as
  4151. // the API timeout involves RPC.
  4152. //
  4153. } else {
  4154. DWORD DomFlags = DOM_API_TIMEOUT_NEEDED;
  4155. NlEnumerateDomains( FALSE, NlStartDomainThread, &DomFlags );
  4156. }
  4157. NlGlobalApiTimer.StartTime = TimeNow;
  4158. //
  4159. // Check the DNS Scavenger timer
  4160. //
  4161. } else if ( TimerExpired( &NlGlobalDnsScavengerTimer, &TimeNow, &Timeout)) {
  4162. //
  4163. // DnsScavenger sets the StartTime,too
  4164. // (But we have to reset the timer here to prevent it from
  4165. // going off immediately again.)
  4166. //
  4167. EnterCriticalSection( &NlGlobalDnsCritSect );
  4168. NlGlobalDnsScavengerTimer.StartTime = TimeNow;
  4169. NlGlobalDnsScavengerTimer.Period = (DWORD) MAILSLOT_WAIT_FOREVER;
  4170. LeaveCriticalSection( &NlGlobalDnsCritSect );
  4171. //
  4172. // DNS scavenger does the work in a worker thread
  4173. //
  4174. NlDnsScavenge( TRUE, // Normal periodic scavenge
  4175. TRUE, // Refresh domain records in global list
  4176. FALSE, // Don't force refresh if site coverage doesn't change
  4177. FALSE ); // Don't force record re-register
  4178. //
  4179. // Check the subnet and site update timer
  4180. //
  4181. } else if ( TimerExpired(&SubnetSiteUpdateTimer, &TimeNow, &Timeout) ) {
  4182. SubnetSiteUpdateTimer.StartTime = TimeNow;
  4183. SubnetSiteUpdateTimer.Period = (DWORD) MAILSLOT_WAIT_FOREVER;
  4184. if ( !NlGlobalMemberWorkstation ) {
  4185. BOOLEAN SiteNameChanged = FALSE;
  4186. (VOID) NlSitesAddSubnetFromDs( &SiteNameChanged );
  4187. //
  4188. // If the Site Name changed,
  4189. // tell DNS to re-register its names.
  4190. //
  4191. if ( SiteNameChanged || NlGlobalParameters.AutoSiteCoverage ) {
  4192. NlDnsForceScavenge( TRUE, // refresh domain entries
  4193. FALSE ); // don't force re-register
  4194. }
  4195. }
  4196. //
  4197. // If we're the primary,
  4198. // periodically do announcements
  4199. //
  4200. } else if (NlGlobalPdcDoReplication &&
  4201. TimerExpired( &AnnouncerTimer, &TimeNow, &Timeout ) ) {
  4202. //
  4203. // The work is done in the domain thread.
  4204. // Even though there is only one domain thread (so
  4205. // periodic announcements are serialized), we want
  4206. // to avoid adding a new work item if there is already
  4207. // one outstanding; otherwise we run a high risk of
  4208. // doing too much of periodic long network I/O.
  4209. //
  4210. LOCK_SERVER_SESSION_TABLE( NlGlobalDomainInfo );
  4211. if ( !NlGlobalPrimaryAnnouncementIsRunning ) {
  4212. DWORD DomFlags = DOM_PRIMARY_ANNOUNCE_NEEDED;
  4213. NlGlobalPrimaryAnnouncementIsRunning = TRUE;
  4214. UNLOCK_SERVER_SESSION_TABLE( NlGlobalDomainInfo );
  4215. NlStartDomainThread( NlGlobalDomainInfo, &DomFlags );
  4216. LOCK_SERVER_SESSION_TABLE( NlGlobalDomainInfo );
  4217. }
  4218. UNLOCK_SERVER_SESSION_TABLE( NlGlobalDomainInfo );
  4219. AnnouncerTimer.StartTime = TimeNow;
  4220. //
  4221. // If we've gotten this far,
  4222. // we know the only thing left to do is to wait for the next event.
  4223. //
  4224. } else {
  4225. MainLoopTimeout = Timeout;
  4226. }
  4227. //
  4228. // Process interesting changelog events.
  4229. //
  4230. } else if ( WaitStatus == NlWaitNotify ) {
  4231. //
  4232. // If a "replicate immediately" event has happened,
  4233. // send a primary announcement.
  4234. //
  4235. LOCK_CHANGELOG();
  4236. if ( NlGlobalChangeLogReplicateImmediately ) {
  4237. NlGlobalChangeLogReplicateImmediately = FALSE;
  4238. NlPrint((NL_MISC,
  4239. "NlMainLoop: Notification to replicate immediately\n" ));
  4240. UNLOCK_CHANGELOG();
  4241. //
  4242. // Ignore this event on BDCs.
  4243. //
  4244. // This event is never set on a BDC. It may have been set
  4245. // prior to the role change while this machine was a PDC.
  4246. //
  4247. // There is only one domain thread, so this will be serialized
  4248. // with other anouncement activity.
  4249. //
  4250. if ( NlGlobalPdcDoReplication ) {
  4251. DWORD DomFlags = DOM_PRIMARY_ANNOUNCE_IMMEDIATE;
  4252. NlStartDomainThread( NlGlobalDomainInfo, &DomFlags );
  4253. }
  4254. LOCK_CHANGELOG();
  4255. }
  4256. //
  4257. // Process any notifications that need processing
  4258. //
  4259. while ( !IsListEmpty( &NlGlobalChangeLogNotifications ) ) {
  4260. PLIST_ENTRY ListEntry;
  4261. PCHANGELOG_NOTIFICATION Notification;
  4262. DWORD DomFlags;
  4263. ListEntry = RemoveHeadList( &NlGlobalChangeLogNotifications );
  4264. UNLOCK_CHANGELOG();
  4265. Notification = CONTAINING_RECORD(
  4266. ListEntry,
  4267. CHANGELOG_NOTIFICATION,
  4268. Next );
  4269. switch ( Notification->EntryType ) {
  4270. case ChangeLogTrustAccountAdded: {
  4271. NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType =
  4272. *(NETLOGON_SECURE_CHANNEL_TYPE*)&Notification->ObjectGuid;
  4273. NlPrint((NL_MISC,
  4274. "NlMainLoop: Notification that trust account added (or changed) %wZ 0x%lx %lx\n",
  4275. &Notification->ObjectName,
  4276. Notification->ObjectRid,
  4277. SecureChannelType ));
  4278. // This event happens on both a PDC and BDC
  4279. (VOID) NlCheckServerSession( Notification->ObjectRid,
  4280. &Notification->ObjectName,
  4281. SecureChannelType );
  4282. break;
  4283. }
  4284. case ChangeLogTrustAccountDeleted:
  4285. NlPrint((NL_MISC,
  4286. "NlMainLoop: Notification that trust account deleted\n" ));
  4287. // This event happens on both a PDC and BDC
  4288. NlFreeServerSessionForAccount( &Notification->ObjectName );
  4289. break;
  4290. case ChangeLogTrustDeleted:
  4291. case ChangeLogTrustAdded:
  4292. //
  4293. // When a TrustedDomainObject is deleted,
  4294. // don't just delete the ClientSession.
  4295. // There still might be an XREF object stating an indirect trust.
  4296. //
  4297. NlPrint((NL_MISC,
  4298. "NlMainLoop: Notification that TDO added or deleted.\n" ));
  4299. DomFlags = DOM_TRUST_UPDATE_NEEDED;
  4300. NlStartDomainThread( NlGlobalDomainInfo, &DomFlags );
  4301. break;
  4302. case ChangeLogRoleChanged:
  4303. NlPrint((NL_MISC,
  4304. "NlMainLoop: Notification that role changed\n" ));
  4305. DomFlags = DOM_ROLE_UPDATE_NEEDED;
  4306. NlStartDomainThread( NlGlobalDomainInfo, &DomFlags );
  4307. break;
  4308. case ChangeDnsNames:
  4309. NlPrint((NL_MISC,
  4310. "NlMainLoop: Notification that registered DNS names should change\n" ));
  4311. //
  4312. // Register any names that need it.
  4313. // (The caller passed TRUE or FALSE in ObjectRid to indicate whether
  4314. // or not to force re-registration.)
  4315. //
  4316. NlDnsForceScavenge( TRUE, // refresh domain entries
  4317. Notification->ObjectRid );
  4318. break;
  4319. case ChangeLogDsChanged: {
  4320. NL_DS_CHANGE_TYPE DsChangeType = (NL_DS_CHANGE_TYPE) Notification->ObjectRid;
  4321. switch ( DsChangeType ) {
  4322. case NlSubnetObjectChanged:
  4323. case NlSiteObjectChanged:
  4324. case NlSiteChanged:
  4325. if ( !NlGlobalMemberWorkstation ) {
  4326. NlPrint(( NL_MISC,
  4327. "NlMainLoop: Notification %lu that DS site info changed\n",
  4328. DsChangeType ));
  4329. //
  4330. // Reset the subnet and site update timer to run in 2 seconds.
  4331. // There are two reasons to delay the processing:
  4332. //
  4333. // * There might be many objects created in one batch. It's better
  4334. // to consolidate all notifications and do one refresh, especially
  4335. // if there are many objects created in which case the DS directory
  4336. // read is going to be expensive.
  4337. // * We don't have a way to sync with ISM for site object changes.
  4338. // So we have to wait a little in the hope that ISM will rebuild
  4339. // its data in the meantime.
  4340. //
  4341. // Note that we don't need to reset the timer event as we will notice
  4342. // this change on the next iteration of the main loop.
  4343. //
  4344. NlQuerySystemTime( &SubnetSiteUpdateTimer.StartTime );
  4345. SubnetSiteUpdateTimer.Period = 2000;
  4346. }
  4347. break;
  4348. case NlNdncChanged:
  4349. if ( !NlGlobalMemberWorkstation ) {
  4350. BOOLEAN ServicedNdncChanged = FALSE;
  4351. NlPrint(( NL_MISC, "NlMainLoop: Notification that NDNC changed\n" ));
  4352. NetStatus = NlUpdateServicedNdncs(
  4353. NlGlobalDomainInfo->DomUnicodeComputerNameString.Buffer,
  4354. NlGlobalDomainInfo->DomUnicodeDnsHostNameString.Buffer,
  4355. FALSE, // Don't call NlExit on failure
  4356. &ServicedNdncChanged );
  4357. if ( NetStatus == NO_ERROR && ServicedNdncChanged ) {
  4358. NlDnsForceScavenge( TRUE, // refresh domain entries
  4359. FALSE ); // don't force re-register
  4360. }
  4361. }
  4362. break;
  4363. case NlDnsRootAliasChanged:
  4364. if ( !NlGlobalMemberWorkstation ) {
  4365. NTSTATUS Status;
  4366. BOOL AliasNamesChanged = FALSE;
  4367. NlPrint(( NL_MISC, "NlMainLoop: Notification that DnsRootAlias changed\n" ));
  4368. Status = NlUpdateDnsRootAlias( NlGlobalDomainInfo,
  4369. &AliasNamesChanged );
  4370. if ( NT_SUCCESS(Status) && AliasNamesChanged ) {
  4371. NlDnsForceScavenge( TRUE, // refresh domain entries
  4372. FALSE ); // don't force re-register
  4373. }
  4374. }
  4375. break;
  4376. case NlOrgChanged:
  4377. NlPrint((NL_MISC,
  4378. "NlMainLoop: Notification that ORG tree changed\n" ));
  4379. DomFlags = DOM_TRUST_UPDATE_NEEDED;
  4380. NlStartDomainThread( NlGlobalDomainInfo, &DomFlags );
  4381. break;
  4382. default:
  4383. NlPrint((NL_CRITICAL,
  4384. "Invalid DsChangeType: %ld\n",
  4385. DsChangeType ));
  4386. }
  4387. break;
  4388. }
  4389. case ChangeLogLsaPolicyChanged: {
  4390. POLICY_NOTIFICATION_INFORMATION_CLASS LsaPolicyChangeType =
  4391. (POLICY_NOTIFICATION_INFORMATION_CLASS) Notification->ObjectRid;
  4392. NlPrint((NL_MISC,
  4393. "NlMainLoop: Notification that LSA Policy changed\n" ));
  4394. switch ( LsaPolicyChangeType ) {
  4395. case PolicyNotifyDnsDomainInformation: {
  4396. LPWSTR DomainName = NULL;
  4397. LPWSTR DnsDomainName = NULL;
  4398. PSID AccountDomainSid = NULL;
  4399. PSID PrimaryDomainSid = NULL;
  4400. GUID *PrimaryDomainGuid = NULL;
  4401. PCLIENT_SESSION ClientSession = NULL;
  4402. BOOLEAN DnsForestNameChanged;
  4403. BOOLEAN DnsDomainNameChanged;
  4404. BOOLEAN NetbiosDomainNameChanged;
  4405. BOOLEAN DomainGuidChanged;
  4406. NTSTATUS Status;
  4407. //
  4408. // Get the updated information from the LSA.
  4409. //
  4410. // (Update the TreeName as a side effect.)
  4411. //
  4412. //
  4413. NetStatus = NlGetDomainName(
  4414. &DomainName,
  4415. &DnsDomainName,
  4416. &AccountDomainSid,
  4417. &PrimaryDomainSid,
  4418. &PrimaryDomainGuid,
  4419. &DnsForestNameChanged );
  4420. if ( NetStatus == NO_ERROR ) {
  4421. PDOMAIN_INFO DomainInfo;
  4422. DomainInfo = NlFindNetbiosDomain( NULL, TRUE ); // Primary domain
  4423. if ( DomainInfo != NULL ) {
  4424. //
  4425. // Set the DomainNames on the domain.
  4426. //
  4427. // ???: retry later on failure
  4428. (VOID) NlSetDomainNameInDomainInfo(
  4429. DomainInfo,
  4430. DnsDomainName,
  4431. DomainName,
  4432. PrimaryDomainGuid,
  4433. &DnsDomainNameChanged,
  4434. &NetbiosDomainNameChanged,
  4435. &DomainGuidChanged );
  4436. //
  4437. // If the Netbios domain name has changed,
  4438. // re-register the <DomainName>[1B] name.
  4439. //
  4440. // Merely flag the fact here that it needs to be renamed.
  4441. // Wait to do the actual rename after the bowser
  4442. // knows about the new emulated domain.
  4443. //
  4444. EnterCriticalSection(&NlGlobalDomainCritSect);
  4445. if ( NetbiosDomainNameChanged && DomainInfo->DomRole == RolePrimary ) {
  4446. DomainInfo->DomFlags |= DOM_RENAMED_1B_NAME;
  4447. }
  4448. LeaveCriticalSection(&NlGlobalDomainCritSect);
  4449. //
  4450. // If there is a client session associated with this domain,
  4451. // set the information there, too.
  4452. //
  4453. ClientSession = NlRefDomClientSession( DomainInfo );
  4454. if ( ClientSession != NULL) {
  4455. //
  4456. // Must be a writer to change
  4457. if ( NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
  4458. UNICODE_STRING NetbiosDomainNameString;
  4459. UNICODE_STRING DnsDomainNameString;
  4460. //
  4461. // Update any names that are on the ClientSession structure.
  4462. //
  4463. // ???: The routine below interprets a NULL parameter as
  4464. // a lack of interest in changing the name. We're calling it
  4465. // as specifying that the name no longer exists.
  4466. // (This only applies to the GUID since the other fields
  4467. // are never passed in as NULL.)
  4468. // But that means this is a NT 4 domain and the GUID won't be used.
  4469. //
  4470. RtlInitUnicodeString( &NetbiosDomainNameString, DomainName );
  4471. RtlInitUnicodeString( &DnsDomainNameString, DnsDomainName );
  4472. // ???: retry later on failure
  4473. LOCK_TRUST_LIST( DomainInfo );
  4474. (VOID ) NlSetNamesClientSession( DomainInfo->DomClientSession,
  4475. &NetbiosDomainNameString,
  4476. &DnsDomainNameString,
  4477. PrimaryDomainSid,
  4478. PrimaryDomainGuid );
  4479. UNLOCK_TRUST_LIST( DomainInfo );
  4480. //
  4481. // If the domain changed,
  4482. // Drop the secure channel since it is to the wrong DC.
  4483. //
  4484. if ( DnsDomainNameChanged ||
  4485. NetbiosDomainNameChanged ||
  4486. DomainGuidChanged ) {
  4487. NlSetStatusClientSession( ClientSession, STATUS_NO_LOGON_SERVERS );
  4488. //
  4489. // Indicate that we no longer know what site we're in.
  4490. //
  4491. NlSetDynamicSiteName( NULL );
  4492. //
  4493. // Grab the trusted domain list from where join left it.
  4494. //
  4495. (VOID) NlReadPersitantTrustedDomainList();
  4496. }
  4497. NlResetWriterClientSession( ClientSession );
  4498. }
  4499. NlUnrefClientSession( ClientSession );
  4500. }
  4501. NlDereferenceDomain( DomainInfo );
  4502. }
  4503. //
  4504. // If one of the names that changed is one of the
  4505. // names registered in DNS,
  4506. // update any DNS names
  4507. //
  4508. if ( (DnsForestNameChanged ||
  4509. DnsDomainNameChanged ||
  4510. DomainGuidChanged ) &&
  4511. !NlGlobalMemberWorkstation ) {
  4512. NlDnsForceScavenge( TRUE, // refresh domain entries
  4513. FALSE ); // don't force re-register
  4514. }
  4515. //
  4516. // Tell the browser about the domain rename
  4517. //
  4518. Status = NlBrowserRenameDomain( NULL, DomainName );
  4519. if ( !NT_SUCCESS(Status) ) {
  4520. NlPrint((NL_CRITICAL,
  4521. "Browser cannot rename domain to: %ws 0x%lx\n",
  4522. DomainName,
  4523. Status ));
  4524. }
  4525. }
  4526. if ( DomainName != NULL ) {
  4527. (VOID)LocalFree( DomainName );
  4528. }
  4529. if ( DnsDomainName != NULL ) {
  4530. (VOID)LocalFree( DnsDomainName );
  4531. }
  4532. if ( AccountDomainSid != NULL ) {
  4533. (VOID)LocalFree( AccountDomainSid );
  4534. }
  4535. if ( PrimaryDomainSid != NULL ) {
  4536. (VOID)LocalFree( PrimaryDomainSid );
  4537. }
  4538. if ( PrimaryDomainGuid != NULL ) {
  4539. (VOID)LocalFree( PrimaryDomainGuid );
  4540. }
  4541. break;
  4542. }
  4543. default:
  4544. NlPrint((NL_CRITICAL,
  4545. "Invalid LsaPolicyChangeType: %ld\n",
  4546. LsaPolicyChangeType ));
  4547. }
  4548. break;
  4549. }
  4550. //
  4551. // NTDS-DSA object deleted
  4552. //
  4553. case ChangeLogNtdsDsaDeleted:
  4554. (VOID) NlDnsNtdsDsaDeletion (
  4555. Notification->DomainName.Buffer,
  4556. &Notification->DomainGuid,
  4557. &Notification->ObjectGuid,
  4558. Notification->ObjectName.Buffer );
  4559. break;
  4560. default:
  4561. NlPrint((NL_CRITICAL,
  4562. "Invalid ChangeLogNotification: %ld %wZ\n",
  4563. Notification->EntryType,
  4564. &Notification->ObjectName ));
  4565. }
  4566. NetpMemoryFree( Notification );
  4567. LOCK_CHANGELOG();
  4568. }
  4569. UNLOCK_CHANGELOG();
  4570. //
  4571. // Process WINSOCK PNP events.
  4572. //
  4573. } else if ( WaitStatus == NlWaitWinsock ) {
  4574. //
  4575. // Get the new list of IP addresses
  4576. //
  4577. if ( NlHandleWsaPnp() ) {
  4578. //
  4579. // The list changed.
  4580. //
  4581. if ( !NlGlobalMemberWorkstation ) {
  4582. NlDnsForceScavenge( TRUE, // refresh domain entries
  4583. TRUE ); // force re-register
  4584. //
  4585. // Flush any caches that aren't valid any more since there
  4586. // is now a new transport
  4587. //
  4588. // ?? Differentiate between adding a transport and removing one
  4589. //
  4590. NlFlushCacheOnPnp();
  4591. }
  4592. }
  4593. //
  4594. // Process mailslot messages.
  4595. //
  4596. } else if ( WaitStatus == NL_WAIT_MAILSLOT ) {
  4597. PDOMAIN_INFO DomainInfo;
  4598. DWORD Version;
  4599. DWORD VersionFlags;
  4600. DWORD BytesRead;
  4601. LPBYTE Message;
  4602. LPWSTR TransportName;
  4603. PSOCKADDR ClientSockAddr;
  4604. PNL_TRANSPORT Transport;
  4605. LPWSTR ServerOrDomainName;
  4606. NETLOGON_PNP_OPCODE NlPnpOpcode;
  4607. //
  4608. // Variables for unmarshalling the message read.
  4609. //
  4610. PCHAR Where;
  4611. LPSTR OemWorkstationName;
  4612. LPSTR OemUserName;
  4613. LPSTR OemMailslotName;
  4614. LPWSTR UnicodeWorkstationName;
  4615. LPWSTR UnicodeUserName;
  4616. LPSTR OemTemp;
  4617. LPWSTR UnicodeTemp;
  4618. DWORD ResponseBufferSize;
  4619. BYTE ResponseBuffer[NETLOGON_MAX_MS_SIZE]; // Buffer to build response in
  4620. if ( !NlMailslotOverlappedResult( &Message,
  4621. &BytesRead,
  4622. &TransportName,
  4623. &Transport,
  4624. &ClientSockAddr,
  4625. &ServerOrDomainName,
  4626. &IgnoreDuplicatesOfThisMessage,
  4627. &NlPnpOpcode )){
  4628. // Just continue if there really isn't a message
  4629. continue;
  4630. }
  4631. //
  4632. // If this is a PNP notification,
  4633. // process it.
  4634. //
  4635. if ( NlPnpOpcode != NlPnpMailslotMessage ) {
  4636. BOOLEAN IpTransportChanged = FALSE;
  4637. switch ( NlPnpOpcode ) {
  4638. case NlPnpTransportBind:
  4639. case NlPnpNewIpAddress:
  4640. if (!NlTransportAddTransportName(TransportName, &IpTransportChanged )) {
  4641. NlPrint((NL_CRITICAL,
  4642. "PNP: %ws: cannot add transport.\n",
  4643. TransportName ));
  4644. }
  4645. //
  4646. // Flush any caches that aren't valid any more since there
  4647. // is now a new transport
  4648. //
  4649. NlFlushCacheOnPnp();
  4650. break;
  4651. case NlPnpTransportUnbind:
  4652. IpTransportChanged = NlTransportDisableTransportName( TransportName );
  4653. break;
  4654. case NlPnpDomainRename:
  4655. NlPrint((NL_DOMAIN,
  4656. "PNP: Bowser says the domain has been renamed\n" ));
  4657. //
  4658. // Now that the hosted domain name in the bowser
  4659. // matches the one in netlogon,
  4660. // Ensure the DomainName<1B> names are properly registered.
  4661. //
  4662. (VOID) NlEnumerateDomains( FALSE, NlBrowserFixAllNames, NULL );
  4663. break;
  4664. case NlPnpNewRole:
  4665. // We don't care that the browser has a new role.
  4666. break;
  4667. default:
  4668. NlPrint((NL_CRITICAL,
  4669. "Unknown PNP opcode 0x%x\n",
  4670. NlPnpOpcode ));
  4671. break;
  4672. }
  4673. // Just continue if there really isn't a message
  4674. continue;
  4675. }
  4676. //
  4677. // Ignore mailslot messages to NETLOGON mailslot on workstation.
  4678. //
  4679. if ( NlGlobalMemberWorkstation ) {
  4680. NlPrint((NL_CRITICAL,"NETLOGON mailslot on workstation (ignored)\n" ));
  4681. continue;
  4682. }
  4683. //
  4684. // ASSERT: Message and BytesRead describe a newly read message
  4685. //
  4686. //
  4687. // Got a message. Check for bad length just in case.
  4688. //
  4689. if (BytesRead < sizeof(unsigned short) ) {
  4690. NlPrint((NL_CRITICAL,"message size bad %ld\n", BytesRead ));
  4691. continue; // Need at least an opcode
  4692. }
  4693. //
  4694. // Here with a request to process in the Message.
  4695. //
  4696. Version = NetpLogonGetMessageVersion( Message, &BytesRead, &VersionFlags );
  4697. if (Version == LMUNKNOWNNT_MESSAGE) {
  4698. //
  4699. // received a non-supported NT message.
  4700. //
  4701. NlPrint((NL_CRITICAL,
  4702. "Received a non-supported NT message, Opcode is 0x%x\n",
  4703. ((PNETLOGON_LOGON_QUERY)Message)->Opcode ));
  4704. continue;
  4705. }
  4706. //
  4707. // Determine which domain this message came in for.
  4708. //
  4709. DomainInfo = NlFindNetbiosDomain( ServerOrDomainName, FALSE );
  4710. if ( DomainInfo == NULL ) {
  4711. DomainInfo = NlFindDomainByServerName( ServerOrDomainName );
  4712. if ( DomainInfo == NULL ) {
  4713. NlPrint((NL_CRITICAL,
  4714. "%ws: Received message for this unsupported domain\n",
  4715. ServerOrDomainName ));
  4716. continue;
  4717. }
  4718. }
  4719. //
  4720. // Handle a logon request from a UAS client
  4721. //
  4722. switch ( ((PNETLOGON_LOGON_QUERY)Message)->Opcode) {
  4723. case LOGON_REQUEST: {
  4724. USHORT RequestCount;
  4725. //
  4726. // Unmarshall the incoming message.
  4727. //
  4728. if ( Version == LMNT_MESSAGE ) {
  4729. break;
  4730. }
  4731. Where = ((PNETLOGON_LOGON_REQUEST)Message)->ComputerName;
  4732. if ( !NetpLogonGetOemString(
  4733. (PNETLOGON_LOGON_REQUEST)Message,
  4734. BytesRead,
  4735. &Where,
  4736. sizeof( ((PNETLOGON_LOGON_REQUEST)Message)->ComputerName),
  4737. &OemWorkstationName )) {
  4738. break;
  4739. }
  4740. if ( !NetpLogonGetOemString(
  4741. (PNETLOGON_LOGON_REQUEST)Message,
  4742. BytesRead,
  4743. &Where,
  4744. sizeof( ((PNETLOGON_LOGON_REQUEST)Message)->UserName),
  4745. &OemUserName )) {
  4746. break;
  4747. }
  4748. if ( !NetpLogonGetOemString(
  4749. (PNETLOGON_LOGON_REQUEST)Message,
  4750. BytesRead,
  4751. &Where,
  4752. sizeof( ((PNETLOGON_LOGON_REQUEST)Message)->MailslotName),
  4753. &OemMailslotName )) {
  4754. break;
  4755. }
  4756. // LM 2.x puts request count right before token
  4757. Where = Message + BytesRead - 2;
  4758. if ( !NetpLogonGetBytes(
  4759. (PNETLOGON_LOGON_REQUEST)Message,
  4760. BytesRead,
  4761. &Where,
  4762. sizeof( ((PNETLOGON_LOGON_REQUEST)Message)->RequestCount),
  4763. &RequestCount )) {
  4764. break;
  4765. }
  4766. //
  4767. // Handle the logon request
  4768. //
  4769. UnicodeUserName = NetpLogonOemToUnicode( OemUserName );
  4770. if ( UnicodeUserName == NULL ) {
  4771. break;
  4772. }
  4773. UnicodeWorkstationName = NetpLogonOemToUnicode( OemWorkstationName );
  4774. if( UnicodeWorkstationName == NULL ) {
  4775. NetpMemoryFree( UnicodeUserName );
  4776. break;
  4777. }
  4778. //
  4779. // Handle the primary query request
  4780. //
  4781. if ( LogonRequestHandler(
  4782. Transport->TransportName,
  4783. DomainInfo,
  4784. FALSE, // don't use name aliases
  4785. NULL, // Domain Sid not known
  4786. Version,
  4787. VersionFlags,
  4788. UnicodeUserName,
  4789. RequestCount,
  4790. UnicodeWorkstationName,
  4791. USER_NORMAL_ACCOUNT,
  4792. Transport->IpAddress,
  4793. ClientSockAddr,
  4794. ResponseBuffer,
  4795. &ResponseBufferSize ) ) {
  4796. NTSTATUS Status;
  4797. Status = NlBrowserSendDatagram( DomainInfo,
  4798. 0,
  4799. UnicodeWorkstationName,
  4800. ComputerName,
  4801. Transport->TransportName,
  4802. OemMailslotName,
  4803. ResponseBuffer,
  4804. ResponseBufferSize,
  4805. FALSE, // Send asynch to avoid blocking main thread
  4806. NULL ); // Don't flush Netbios cache
  4807. if ( NT_SUCCESS(Status) ) {
  4808. IgnoreDuplicatesOfThisMessage = TRUE;
  4809. }
  4810. }
  4811. NetpMemoryFree( UnicodeWorkstationName );
  4812. NetpMemoryFree( UnicodeUserName );
  4813. break;
  4814. }
  4815. //
  4816. // Handle a logon request from a SAM client
  4817. //
  4818. case LOGON_SAM_LOGON_REQUEST: {
  4819. USHORT RequestCount;
  4820. ULONG AllowableAccountControlBits;
  4821. DWORD DomainSidSize;
  4822. PCHAR DomainSid = NULL;
  4823. //
  4824. // Unmarshall the incoming message.
  4825. //
  4826. if ( Version != LMNT_MESSAGE ) {
  4827. break;
  4828. }
  4829. RequestCount = ((PNETLOGON_SAM_LOGON_REQUEST)Message)->RequestCount;
  4830. Where = (PCHAR)
  4831. (((PNETLOGON_SAM_LOGON_REQUEST)Message)->UnicodeComputerName);
  4832. if ( !NetpLogonGetUnicodeString(
  4833. (PNETLOGON_SAM_LOGON_REQUEST)Message,
  4834. BytesRead,
  4835. &Where,
  4836. sizeof( ((PNETLOGON_SAM_LOGON_REQUEST)Message)->
  4837. UnicodeComputerName),
  4838. &UnicodeWorkstationName )) {
  4839. break;
  4840. }
  4841. if ( !NetpLogonGetUnicodeString(
  4842. (PNETLOGON_SAM_LOGON_REQUEST)Message,
  4843. BytesRead,
  4844. &Where,
  4845. sizeof( ((PNETLOGON_SAM_LOGON_REQUEST)Message)->
  4846. UnicodeUserName),
  4847. &UnicodeUserName )) {
  4848. break;
  4849. }
  4850. if ( !NetpLogonGetOemString(
  4851. (PNETLOGON_SAM_LOGON_REQUEST)Message,
  4852. BytesRead,
  4853. &Where,
  4854. sizeof( ((PNETLOGON_SAM_LOGON_REQUEST)Message)->
  4855. MailslotName),
  4856. &OemMailslotName )) {
  4857. break;
  4858. }
  4859. if ( !NetpLogonGetBytes(
  4860. (PNETLOGON_SAM_LOGON_REQUEST)Message,
  4861. BytesRead,
  4862. &Where,
  4863. sizeof( ((PNETLOGON_SAM_LOGON_REQUEST)Message)->
  4864. AllowableAccountControlBits),
  4865. &AllowableAccountControlBits )) {
  4866. break;
  4867. }
  4868. //
  4869. // Get the domain SID.
  4870. //
  4871. // Don't make the following check mandatory. Chicago
  4872. // uses this message type without the SID present. Oct 1993.
  4873. //
  4874. if( Where < ((PCHAR)Message + BytesRead ) ) {
  4875. //
  4876. // Read Domain SID Length
  4877. //
  4878. if ( !NetpLogonGetBytes(
  4879. (PNETLOGON_SAM_LOGON_REQUEST)Message,
  4880. BytesRead,
  4881. &Where,
  4882. sizeof( ((PNETLOGON_SAM_LOGON_REQUEST)Message)->
  4883. DomainSidSize),
  4884. &DomainSidSize )) {
  4885. break;
  4886. }
  4887. //
  4888. // Read the SID itself.
  4889. //
  4890. if( DomainSidSize > 0 ) {
  4891. if ( !NetpLogonGetDomainSID(
  4892. (PNETLOGON_SAM_LOGON_REQUEST)Message,
  4893. BytesRead,
  4894. &Where,
  4895. DomainSidSize,
  4896. &DomainSid )) {
  4897. break;
  4898. }
  4899. }
  4900. }
  4901. //
  4902. // Handle the logon request
  4903. //
  4904. if ( LogonRequestHandler(
  4905. Transport->TransportName,
  4906. DomainInfo,
  4907. FALSE, // don't use name aliases
  4908. DomainSid,
  4909. Version,
  4910. VersionFlags,
  4911. UnicodeUserName,
  4912. RequestCount,
  4913. UnicodeWorkstationName,
  4914. AllowableAccountControlBits,
  4915. Transport->IpAddress,
  4916. ClientSockAddr,
  4917. ResponseBuffer,
  4918. &ResponseBufferSize ) ) {
  4919. NTSTATUS Status;
  4920. Status = NlBrowserSendDatagram( DomainInfo,
  4921. 0,
  4922. UnicodeWorkstationName,
  4923. ComputerName,
  4924. Transport->TransportName,
  4925. OemMailslotName,
  4926. ResponseBuffer,
  4927. ResponseBufferSize,
  4928. FALSE, // Send asynch to avoid blocking main thread
  4929. NULL ); // Don't flush Netbios cache
  4930. if ( NT_SUCCESS(Status) ) {
  4931. IgnoreDuplicatesOfThisMessage = TRUE;
  4932. }
  4933. }
  4934. break;
  4935. }
  4936. //
  4937. // Handle Logon Central query.
  4938. //
  4939. // This query could be sent by either LM1.0, LM 2.0 or LM NT Netlogon
  4940. // services. We ignore LM 2.0 and LM NT queries since they are merely
  4941. // trying
  4942. // to find out if there are any LM1.0 netlogon services in the domain.
  4943. // For LM 1.0 we respond with a LOGON_CENTRAL_RESPONSE to prevent the
  4944. // starting LM1.0 netlogon service from starting.
  4945. //
  4946. case LOGON_CENTRAL_QUERY:
  4947. if ( Version != LMUNKNOWN_MESSAGE ) {
  4948. break;
  4949. }
  4950. //
  4951. // Drop on through to LOGON_DISTRIB_QUERY to send the response
  4952. //
  4953. //
  4954. // Handle a Logon Disrib query
  4955. //
  4956. // LM2.0 NETLOGON server never sends this query hence it
  4957. // must be another LM1.0 NETLOGON server trying to start up
  4958. // in non-centralized mode. LM2.0 NETLOGON server will respond
  4959. // with LOGON_CENTRAL_RESPONSE to prevent this.
  4960. //
  4961. case LOGON_DISTRIB_QUERY:
  4962. //
  4963. // Unmarshall the incoming message.
  4964. //
  4965. Where = ((PNETLOGON_LOGON_QUERY)Message)->ComputerName;
  4966. if ( !NetpLogonGetOemString(
  4967. Message,
  4968. BytesRead,
  4969. &Where,
  4970. sizeof( ((PNETLOGON_LOGON_QUERY)Message)->ComputerName ),
  4971. &OemWorkstationName )) {
  4972. break;
  4973. }
  4974. if ( !NetpLogonGetOemString(
  4975. Message,
  4976. BytesRead,
  4977. &Where,
  4978. sizeof( ((PNETLOGON_LOGON_QUERY)Message)->MailslotName ),
  4979. &OemMailslotName )) {
  4980. break;
  4981. }
  4982. //
  4983. // Build the response
  4984. //
  4985. ((PNETLOGON_LOGON_QUERY)ResponseBuffer)->Opcode = LOGON_CENTRAL_RESPONSE;
  4986. ResponseBufferSize = sizeof( unsigned short); // opcode only
  4987. #if NETLOGONDBG
  4988. NlPrintDom((NL_MAILSLOT, DomainInfo,
  4989. "Sent '%s' message to %s[%s] on %ws.\n",
  4990. NlMailslotOpcode(((PNETLOGON_LOGON_QUERY)ResponseBuffer)->Opcode),
  4991. OemWorkstationName,
  4992. NlDgrNameType(ComputerName),
  4993. TransportName ));
  4994. #endif // NETLOGONDBG
  4995. //
  4996. // NlBrowserSendDatagramA always sends asynchronously,
  4997. // so we are not blocking the main thread here
  4998. //
  4999. (VOID) NlBrowserSendDatagramA( DomainInfo,
  5000. 0,
  5001. OemWorkstationName,
  5002. ComputerName,
  5003. TransportName,
  5004. OemMailslotName,
  5005. ResponseBuffer,
  5006. ResponseBufferSize );
  5007. break;
  5008. //
  5009. // Handle LOGON_PRIMARY_QUERY
  5010. //
  5011. // If we're the PDC, always respond to this message
  5012. // identifying ourselves.
  5013. //
  5014. // Otherwise, only respond to the message if it is from a Lanman 2.x
  5015. // netlogon trying to see if it can start up as a PDC. In that
  5016. // case, pretend we are a PDC to prevent the Lanman 2.x PDC from
  5017. // starting.
  5018. //
  5019. //
  5020. case LOGON_PRIMARY_QUERY:
  5021. //
  5022. // Unmarshall the incoming message.
  5023. //
  5024. Where =((PNETLOGON_LOGON_QUERY)Message)->ComputerName;
  5025. if ( !NetpLogonGetOemString(
  5026. Message,
  5027. BytesRead,
  5028. &Where,
  5029. sizeof( ((PNETLOGON_LOGON_QUERY)Message)->ComputerName ),
  5030. &OemWorkstationName )) {
  5031. break;
  5032. }
  5033. if ( !NetpLogonGetOemString(
  5034. Message,
  5035. BytesRead,
  5036. &Where,
  5037. sizeof( ((PNETLOGON_LOGON_QUERY)Message)->MailslotName ),
  5038. &OemMailslotName )) {
  5039. break;
  5040. }
  5041. UnicodeWorkstationName =
  5042. NetpLogonOemToUnicode( OemWorkstationName );
  5043. if( UnicodeWorkstationName == NULL ) {
  5044. NlPrintDom((NL_CRITICAL, DomainInfo,
  5045. "Out of memory to send logon response\n"));
  5046. break;
  5047. }
  5048. //
  5049. // Handle the primary query request
  5050. //
  5051. if ( PrimaryQueryHandler(Transport->TransportName,
  5052. DomainInfo,
  5053. FALSE, // don't use name aliases
  5054. Version,
  5055. VersionFlags,
  5056. UnicodeWorkstationName,
  5057. Transport->IpAddress,
  5058. ClientSockAddr,
  5059. ResponseBuffer,
  5060. &ResponseBufferSize ) ) {
  5061. NTSTATUS Status;
  5062. Status = NlBrowserSendDatagram( DomainInfo,
  5063. 0,
  5064. UnicodeWorkstationName,
  5065. ComputerName,
  5066. Transport->TransportName,
  5067. OemMailslotName,
  5068. ResponseBuffer,
  5069. ResponseBufferSize,
  5070. FALSE, // Send asynch to avoid blocking main thread
  5071. NULL ); // Don't flush Netbios cache
  5072. if ( NT_SUCCESS(Status) ) {
  5073. IgnoreDuplicatesOfThisMessage = TRUE;
  5074. }
  5075. }
  5076. NetpMemoryFree( UnicodeWorkstationName );
  5077. break;
  5078. //
  5079. // Handle LOGON_FAIL_PRIMARY
  5080. //
  5081. case LOGON_FAIL_PRIMARY:
  5082. //
  5083. // If we are the primary,
  5084. // let everyone know we are really alive.
  5085. //
  5086. if ( NlGlobalPdcDoReplication ) {
  5087. // Send a UAS_CHANGE to everyone.
  5088. // Pretend it's time to do normal announcement
  5089. DWORD DomFlags = DOM_PRIMARY_ANNOUNCE_NEEDED;
  5090. NlStartDomainThread( NlGlobalDomainInfo, &DomFlags );
  5091. break;
  5092. }
  5093. break;
  5094. //
  5095. // Handle LOGON_UAS_CHANGE
  5096. //
  5097. case LOGON_UAS_CHANGE:
  5098. //
  5099. // Only accept messages from an NT PDC.
  5100. //
  5101. if ( Version != LMNT_MESSAGE ) {
  5102. break;
  5103. }
  5104. //
  5105. // Only accepts messages if we're doing replication.
  5106. //
  5107. NlPrint((NL_CRITICAL,
  5108. "UAS Change message ignored since replication not enabled on this BDC.\n" ));
  5109. break;
  5110. //
  5111. // Message not sent since NT3.1.
  5112. // We ingnore this message and wait for the announcement.
  5113. //
  5114. case LOGON_START_PRIMARY:
  5115. break;
  5116. //
  5117. // Messages used for NetLogonEnum support.
  5118. //
  5119. // Simply ignore the messages
  5120. //
  5121. case LOGON_NO_USER:
  5122. case LOGON_RELOGON_RESPONSE:
  5123. case LOGON_WKSTINFO_RESPONSE:
  5124. break;
  5125. //
  5126. // Handle unidentified opcodes
  5127. //
  5128. default:
  5129. //
  5130. // Unknown request, continue for re-issue of read.
  5131. //
  5132. NlPrintDom((NL_CRITICAL, DomainInfo,
  5133. "Unknown op-code in mailslot message 0x%x\n",
  5134. ((PNETLOGON_LOGON_QUERY)Message)->Opcode ));
  5135. break;
  5136. }
  5137. //
  5138. // Dereference the domain.
  5139. //
  5140. if ( DomainInfo != NULL ) {
  5141. NlDereferenceDomain( DomainInfo );
  5142. }
  5143. //
  5144. // Process registry change notifications
  5145. //
  5146. } else if ( WaitStatus == NlWaitParameters ) {
  5147. NlPrint((NL_CRITICAL,
  5148. "NlMainLoop: Registry changed\n" ));
  5149. RegNotifyNeeded = TRUE;
  5150. //
  5151. // Process GP registry change notifications
  5152. //
  5153. } else if ( WaitStatus == NlWaitGpParameters ) {
  5154. NlPrint((NL_CRITICAL,
  5155. "NlMainLoop: GP Registry changed\n" ));
  5156. GpRegNotifyNeeded = TRUE;
  5157. //
  5158. // Handle all other reasons of waking up
  5159. //
  5160. } else {
  5161. NetStatus = GetLastError();
  5162. NlPrint((NL_CRITICAL,
  5163. "NlMainLoop: Invalid wait status %ld %ld\n",
  5164. WaitStatus, NetStatus ));
  5165. NlExit(NELOG_NetlogonSystemError, NetStatus, LogErrorAndNetStatus, NULL);
  5166. goto Cleanup;
  5167. }
  5168. }
  5169. Cleanup:
  5170. if ( ParmEventHandle != NULL ) {
  5171. CloseHandle( ParmEventHandle );
  5172. }
  5173. if ( ParmHandle != NULL ) {
  5174. RegCloseKey( ParmHandle );
  5175. }
  5176. if ( GpParmEventHandle != NULL ) {
  5177. CloseHandle( GpParmEventHandle );
  5178. }
  5179. if ( GpParmHandle != NULL ) {
  5180. RegCloseKey( GpParmHandle );
  5181. }
  5182. return;
  5183. }
  5184. int
  5185. NlNetlogonMain(
  5186. IN DWORD argc,
  5187. IN LPWSTR *argv
  5188. )
  5189. /*++
  5190. Routine Description:
  5191. Main routine for Netlogon service.
  5192. This routine initializes the netlogon service. This thread becomes
  5193. the thread that reads logon mailslot messages.
  5194. Arguments:
  5195. argc, argv - Command line arguments for the service.
  5196. Return Value:
  5197. None.
  5198. --*/
  5199. {
  5200. NET_API_STATUS NetStatus;
  5201. PDB_INFO DBInfo;
  5202. DWORD i;
  5203. LARGE_INTEGER TimeNow;
  5204. //
  5205. // Initialize all global variable.
  5206. //
  5207. // We can't rely on this happening at load time since this address
  5208. // space is shared by other services.
  5209. //
  5210. RtlZeroMemory( &NlGlobalParameters, sizeof(NlGlobalParameters) );
  5211. NlGlobalMailslotHandle = NULL;
  5212. NlGlobalNtDsaHandle = NULL;
  5213. NlGlobalDsApiDllHandle = NULL;
  5214. NlGlobalIsmDllHandle = NULL;
  5215. NlGlobalRpcServerStarted = FALSE;
  5216. NlGlobalServerSupportsAuthRpc = TRUE;
  5217. NlGlobalTcpIpRpcServerStarted = FALSE;
  5218. NlGlobalUnicodeComputerName = NULL;
  5219. NlGlobalNetlogonSecurityDescriptor = NULL;
  5220. try {
  5221. InitializeCriticalSection( &NlGlobalChallengeCritSect );
  5222. } except( EXCEPTION_EXECUTE_HANDLER ) {
  5223. NlPrint(( NL_CRITICAL, "Cannot initialize NlGlobalChallengeCritSect\n" ));
  5224. return (int) ERROR_NOT_ENOUGH_MEMORY;
  5225. }
  5226. InitializeListHead( &NlGlobalChallengeList );
  5227. NlGlobalChallengeCount = 0;
  5228. InitializeListHead( &NlGlobalBdcServerSessionList );
  5229. NlGlobalBdcServerSessionCount = 0;
  5230. NlGlobalPdcDoReplication = FALSE;
  5231. NlGlobalWinSockInitialized = FALSE;
  5232. NlGlobalIpTransportCount = 0;
  5233. InitializeListHead( &NlGlobalTransportList );
  5234. InitializeListHead( &NlGlobalDnsList );
  5235. NlGlobalUnicodeDnsForestName = NULL;
  5236. NlGlobalUnicodeDnsForestNameLen = 0;
  5237. RtlInitUnicodeString( &NlGlobalUnicodeDnsForestNameString, NULL );
  5238. NlGlobalUtf8DnsForestName = NULL;
  5239. NlGlobalUtf8DnsForestNameAlias = NULL;
  5240. NlGlobalWinsockPnpSocket = INVALID_SOCKET;
  5241. NlGlobalWinsockPnpEvent = NULL;
  5242. NlGlobalWinsockPnpAddresses = NULL;
  5243. NlGlobalWinsockPnpAddressSize = 0;
  5244. InitializeListHead( &NlGlobalPendingBdcList );
  5245. NlGlobalPendingBdcCount = 0;
  5246. NlGlobalPendingBdcTimer.Period = (DWORD) MAILSLOT_WAIT_FOREVER;
  5247. NlGlobalPrimaryAnnouncementIsRunning = FALSE;
  5248. NlGlobalTerminateEvent = NULL;
  5249. NlGlobalStartedEvent = NULL;
  5250. NlGlobalTimerEvent = NULL;
  5251. NlGlobalServiceHandle = 0;
  5252. NlGlobalServiceStatus.dwServiceType = SERVICE_WIN32;
  5253. NlGlobalServiceStatus.dwCurrentState = SERVICE_START_PENDING;
  5254. NlGlobalServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP |
  5255. SERVICE_ACCEPT_PAUSE_CONTINUE;
  5256. NlGlobalServiceStatus.dwCheckPoint = 1;
  5257. NlGlobalServiceStatus.dwWaitHint = NETLOGON_INSTALL_WAIT;
  5258. SET_SERVICE_EXITCODE(
  5259. NO_ERROR,
  5260. NlGlobalServiceStatus.dwWin32ExitCode,
  5261. NlGlobalServiceStatus.dwServiceSpecificExitCode
  5262. );
  5263. NlGlobalClientSession = NULL;
  5264. NlGlobalDomainInfo = NULL;
  5265. NlGlobalServicedDomainCount = 0;
  5266. NlGlobalTrustedDomainList = NULL;
  5267. NlGlobalParameters.SiteName = NULL;
  5268. NlGlobalTrustedDomainCount = 0;
  5269. NlGlobalTrustedDomainListTime.QuadPart = 0;
  5270. NlGlobalSiteNameSetTime.QuadPart = 0;
  5271. NlGlobalNoClientSiteCount = 0;
  5272. NlQuerySystemTime( &NlGlobalNoClientSiteEventTime );
  5273. NlGlobalBindingHandleCount = 0;
  5274. NlGlobalApiTimer.Period = (DWORD) MAILSLOT_WAIT_FOREVER;
  5275. NlGlobalDnsScavengerTimer.Period = (DWORD) MAILSLOT_WAIT_FOREVER;
  5276. NlGlobalNetlogonUnloaded = NlGlobalChangeLogDllUnloaded;
  5277. NlGlobalDsRunningUnknown = TRUE;
  5278. RtlZeroMemory( &NlGlobalZeroGuid, sizeof(NlGlobalZeroGuid) );
  5279. NlGlobalJoinLogicDone = FALSE;
  5280. //
  5281. // Force the scavenger to start immediately.
  5282. //
  5283. // We want the password on the trust account to change immediately
  5284. // on the very first boot.
  5285. //
  5286. NlGlobalScavengerTimer.StartTime.QuadPart = 0;
  5287. NlGlobalScavengerTimer.Period = NlGlobalParameters.ScavengeInterval * 1000L;
  5288. #if NETLOGONDBG
  5289. NlGlobalParameters.DbFlag = 0;
  5290. NlGlobalLogFile = INVALID_HANDLE_VALUE;
  5291. NlGlobalDebugSharePath = NULL;
  5292. #endif // NETLOGONDBG
  5293. for( i = 0, DBInfo = &NlGlobalDBInfoArray[0];
  5294. i < NUM_DBS;
  5295. i++, DBInfo++ ) {
  5296. RtlZeroMemory( DBInfo, sizeof(*DBInfo) );
  5297. }
  5298. NlGlobalPartialDisable = FALSE;
  5299. NlGlobalDsPaused = TRUE;
  5300. NlGlobalDsPausedEvent = NULL;
  5301. NlGlobalDsPausedWaitHandle = NULL;
  5302. NlGlobalDcDemotionInProgress = FALSE;
  5303. NlGlobalDcScavengerIsRunning = FALSE;
  5304. NlInitializeWorkItem( &NlGlobalDcScavengerWorkItem, NlDcScavenger, NULL );
  5305. //
  5306. // Setup things needed before NlExit can be called
  5307. //
  5308. NlGlobalTerminate = FALSE;
  5309. #if NETLOGONDBG
  5310. NlGlobalUnloadNetlogon = FALSE;
  5311. #endif // NETLOGONDBG
  5312. NlGlobalTerminateEvent = CreateEvent( NULL, // No security attributes
  5313. TRUE, // Must be manually reset
  5314. FALSE, // Initially not signaled
  5315. NULL ); // No name
  5316. if ( NlGlobalTerminateEvent == NULL ) {
  5317. NetStatus = GetLastError();
  5318. NlPrint((NL_CRITICAL, "Cannot create termination Event %lu\n",
  5319. NetStatus ));
  5320. return (int) NetStatus;
  5321. }
  5322. //
  5323. // Initialize global crit sects
  5324. //
  5325. try {
  5326. InitializeCriticalSection( &NlGlobalReplicatorCritSect );
  5327. InitializeCriticalSection( &NlGlobalDcDiscoveryCritSect );
  5328. InitializeCriticalSection( &NlGlobalScavengerCritSect );
  5329. InitializeCriticalSection( &NlGlobalTransportCritSect );
  5330. InitializeCriticalSection( &NlGlobalDnsCritSect );
  5331. InitializeCriticalSection( &NlGlobalDnsForestNameCritSect );
  5332. } except( EXCEPTION_EXECUTE_HANDLER ) {
  5333. NlPrint((NL_CRITICAL, "Cannot InitializeCriticalSection\n" ));
  5334. return (int) ERROR_NOT_ENOUGH_MEMORY;
  5335. }
  5336. try {
  5337. InitializeCriticalSection( &NlGlobalParametersCritSect );
  5338. } except( EXCEPTION_EXECUTE_HANDLER ) {
  5339. NlPrint((NL_CRITICAL, "Cannot initialize NlGlobalParametersCritSect\n" ));
  5340. return (int) ERROR_NOT_ENOUGH_MEMORY;
  5341. }
  5342. //
  5343. // seed the pseudo random number generator
  5344. //
  5345. NlQuerySystemTime( &TimeNow );
  5346. srand( TimeNow.LowPart );
  5347. //
  5348. // Tell the service controller we've started.
  5349. //
  5350. // ?? - Need to set up security descriptor.
  5351. //
  5352. NlPrint((NL_INIT,"Calling RegisterServiceCtrlHandler\n"));
  5353. NlGlobalServiceHandle =
  5354. RegisterServiceCtrlHandler( SERVICE_NETLOGON, NlControlHandler);
  5355. if (NlGlobalServiceHandle == 0) {
  5356. LPWSTR MsgStrings[1];
  5357. NetStatus = GetLastError();
  5358. NlPrint((NL_CRITICAL, "RegisterServiceCtrlHandler failed %lu\n",
  5359. NetStatus ));
  5360. MsgStrings[0] = (LPWSTR) ULongToPtr( NetStatus );
  5361. NlpWriteEventlog (NELOG_NetlogonFailedToRegisterSC,
  5362. EVENTLOG_ERROR_TYPE,
  5363. (LPBYTE) &NetStatus,
  5364. sizeof(NetStatus),
  5365. MsgStrings,
  5366. 1 | NETP_LAST_MESSAGE_IS_NETSTATUS );
  5367. return (int) NetStatus;
  5368. }
  5369. if ( !GiveInstallHints( FALSE ) ) {
  5370. goto Cleanup;
  5371. }
  5372. //
  5373. // Nlparse the command line (.ini) arguments
  5374. // it will set globals reflecting switch settings
  5375. //
  5376. NlOpenDebugFile( FALSE );
  5377. if (! NlparseAllSections( &NlGlobalParameters, FALSE ) ) {
  5378. goto Cleanup;
  5379. }
  5380. NlPrint((NL_INIT,"Command line parsed successfully ...\n"));
  5381. if ( NlGlobalNetlogonUnloaded ) {
  5382. NlPrint((NL_INIT,"Netlogon.dll has been unloaded (recover from it).\n"));
  5383. }
  5384. #if DBG
  5385. //
  5386. // Enter the debugger.
  5387. //
  5388. // Wait 'til now since we don't want the service controller to time us out.
  5389. //
  5390. IF_NL_DEBUG( BREAKPOINT ) {
  5391. DbgBreakPoint( );
  5392. }
  5393. #endif // DBG
  5394. //
  5395. // Do startup checks, initialize data structs and do prelim setups
  5396. //
  5397. if ( !NlInit() ) {
  5398. goto Cleanup;
  5399. }
  5400. //
  5401. // Loop till the service is to exit.
  5402. //
  5403. NlGlobalNetlogonUnloaded = FALSE;
  5404. NlMainLoop();
  5405. //
  5406. // Common exit point
  5407. //
  5408. Cleanup:
  5409. //
  5410. // Cleanup and return to our caller.
  5411. //
  5412. return (int) NlCleanup();
  5413. UNREFERENCED_PARAMETER( argc );
  5414. UNREFERENCED_PARAMETER( argv );
  5415. }