Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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