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.

11365 lines
334 KiB

  1. /*++
  2. Copyright (c) 1987-1996 Microsoft Corporation
  3. Module Name:
  4. trustutl.c
  5. Abstract:
  6. Utilities routine to manage the trusted domain list.
  7. Author:
  8. 30-Jan-92 (cliffv)
  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. --*/
  15. //
  16. // Common include files.
  17. //
  18. #include "logonsrv.h" // Include files common to entire service
  19. #pragma hdrstop
  20. //
  21. // Include files specific to this .c file
  22. //
  23. #include <ntdsapip.h>
  24. #define INDEX_LIST_ALLOCATED_CHUNK_SIZE 50
  25. //
  26. // Assume the domain is mixed mode until proven otherwise
  27. //
  28. BOOL NlGlobalWorkstationMixedModeDomain = TRUE;
  29. //
  30. // Local procedure forwards.
  31. //
  32. VOID
  33. NlDcDiscoveryWorker(
  34. IN PVOID Context
  35. );
  36. //
  37. // Local structures.
  38. //
  39. //
  40. // Context keeping track of the current attempt to build the trust list.
  41. //
  42. typedef struct _NL_INIT_TRUSTLIST_CONTEXT {
  43. //
  44. // Buffer for building Forest trust list into.
  45. //
  46. BUFFER_DESCRIPTOR BufferDescriptor;
  47. //
  48. // Total size (in bytes) of the Forest trust list.
  49. //
  50. ULONG DomForestTrustListSize;
  51. //
  52. // Number of entries in the Forest trust list.
  53. //
  54. ULONG DomForestTrustListCount;
  55. } NL_INIT_TRUSTLIST_CONTEXT, *PNL_INIT_TRUSTLIST_CONTEXT;
  56. NET_API_STATUS
  57. NlpSecureChannelBind(
  58. IN LPWSTR ServerName OPTIONAL,
  59. OUT handle_t *ContextHandle
  60. )
  61. /*++
  62. Routine Description:
  63. Returns a handle to be used for a secure channel to the named DC.
  64. Arguments:
  65. ServerName - The name of the remote server.
  66. ContextHandle - Returns a handle to be used on subsequent calls
  67. Return Value:
  68. NERR_Success: the operation was successful
  69. --*/
  70. {
  71. NET_API_STATUS NetStatus;
  72. //
  73. // Create the RPC binding handle
  74. //
  75. NetStatus = NlRpcpBindRpc (
  76. ServerName,
  77. SERVICE_NETLOGON,
  78. L"Security=Impersonation Dynamic False",
  79. UseTcpIp, // Always use TCP/IP
  80. ContextHandle );
  81. if ( NetStatus != NO_ERROR ) {
  82. *ContextHandle = NULL;
  83. return NetStatus;
  84. }
  85. return NetStatus;
  86. }
  87. VOID
  88. NlpSecureChannelUnbind(
  89. IN PCLIENT_SESSION ClientSession,
  90. IN LPCWSTR ServerName,
  91. IN LPCSTR DebugInfo,
  92. IN ULONG CaIndex,
  93. IN handle_t ContextHandle,
  94. IN NL_RPC_BINDING RpcBindingType
  95. )
  96. /*++
  97. Routine Description:
  98. Unbinds a handle returned from NetLogonSecureChannelBind or
  99. NlBindingAddServerToCache.
  100. Arguments:
  101. ClientSession - Session this binding handle is for
  102. ServerName - Name of server handle is to
  103. DebugInfo - Text string identifying the caller
  104. CaIndex - Index identifying which binding handle
  105. ContextHandle - Specifies handle to be unbound
  106. RpcBindingType - Type of binding
  107. Return Value:
  108. None.
  109. --*/
  110. {
  111. NTSTATUS Status;
  112. NET_API_STATUS NetStatus;
  113. NlPrintCs((NL_SESSION_SETUP, ClientSession,
  114. "%s: Unbind from server %ws (%s) %ld.\n",
  115. DebugInfo,
  116. ServerName,
  117. RpcBindingType == UseTcpIp ? "TCP" : "PIPE",
  118. CaIndex ));
  119. //
  120. // Some RPC handles are unbound via routines in netapi32
  121. //
  122. if ( CaIndex == 0 ) {
  123. Status = NlBindingRemoveServerFromCache(
  124. ContextHandle,
  125. RpcBindingType );
  126. //
  127. // Other RPC handles are handled directly in netlogon
  128. //
  129. } else {
  130. NetStatus = RpcpUnbindRpc( ContextHandle );
  131. Status = NetpApiStatusToNtStatus( NetStatus );
  132. }
  133. if ( Status != STATUS_SUCCESS ) {
  134. NlPrintCs((NL_CRITICAL, ClientSession,
  135. "%s: Unbind from server %ws (%s) %ld failed. 0x%lX\n",
  136. DebugInfo,
  137. ServerName,
  138. RpcBindingType == UseTcpIp ? "TCP" : "PIPE",
  139. CaIndex,
  140. Status ));
  141. }
  142. UNREFERENCED_PARAMETER( DebugInfo );
  143. UNREFERENCED_PARAMETER( ServerName );
  144. }
  145. PCLIENT_SESSION
  146. NlFindNamedClientSession(
  147. IN PDOMAIN_INFO DomainInfo,
  148. IN PUNICODE_STRING DomainName,
  149. IN ULONG Flags,
  150. OUT PBOOLEAN TransitiveUsed OPTIONAL
  151. )
  152. /*++
  153. Routine Description:
  154. Find the specified entry in the Trust List.
  155. Arguments:
  156. DomainInfo - Hosted domain of the client session to find
  157. DomainName - The NetbiosName or Dns Name of the domain to find
  158. Flags - Flags defining which client session to return:
  159. NL_DIRECT_TRUST_REQUIRED: Indicates that NULL should be returned
  160. if DomainName is not directly trusted.
  161. NL_RETURN_CLOSEST_HOP: Indicates that for indirect trust, the "closest hop"
  162. session should be returned rather than the actual session
  163. NL_ROLE_PRIMARY_OK: Indicates that if this is a PDC, it's OK to return
  164. the client session to the primary domain.
  165. NL_REQUIRE_DOMAIN_IN_FOREST - Indicates that DomainName must be a domain in
  166. the forest.
  167. TransitiveUsed - If specified and NL_RETURN_CLOSEST_HOP is specified,
  168. the returned boolean will be TRUE if transitive trust was used.
  169. Return Value:
  170. Returns a pointer to the found entry.
  171. The found entry is returned referenced and must be dereferenced using
  172. NlUnrefClientSession.
  173. If there is no such entry, return NULL.
  174. --*/
  175. {
  176. PCLIENT_SESSION ClientSession = NULL;
  177. PLIST_ENTRY ListEntry;
  178. //
  179. // Lock trust list.
  180. //
  181. LOCK_TRUST_LIST( DomainInfo );
  182. if ( ARGUMENT_PRESENT( TransitiveUsed )) {
  183. *TransitiveUsed = FALSE;
  184. }
  185. #ifdef _DC_NETLOGON
  186. //
  187. // On DC, look up the domain in the trusted domain list.
  188. //
  189. // Lookup the ClientSession with the TrustList locked and reference
  190. // the found entry before dropping the lock.
  191. //
  192. if ( DomainInfo->DomRole == RoleBackup || DomainInfo->DomRole == RolePrimary ) {
  193. for ( ListEntry = DomainInfo->DomTrustList.Flink ;
  194. ListEntry != &DomainInfo->DomTrustList ;
  195. ListEntry = ListEntry->Flink) {
  196. ClientSession =
  197. CONTAINING_RECORD( ListEntry, CLIENT_SESSION, CsNext );
  198. if ( (ClientSession->CsNetbiosDomainName.Buffer != NULL &&
  199. RtlEqualDomainName( &ClientSession->CsNetbiosDomainName,
  200. DomainName ) ) ||
  201. (ClientSession->CsDnsDomainName.Buffer != NULL &&
  202. NlEqualDnsNameU( &ClientSession->CsDnsDomainName,
  203. DomainName ) ) ) {
  204. //
  205. // If the caller requires a domain in the forest,
  206. // Ensure this is one.
  207. //
  208. if ( (Flags & NL_REQUIRE_DOMAIN_IN_FOREST) != 0 &&
  209. (ClientSession->CsFlags & CS_DOMAIN_IN_FOREST) == 0 ) {
  210. ClientSession = NULL;
  211. break;
  212. }
  213. //
  214. // If the found domain is not directly trusted,
  215. // check if that's OK with the caller.
  216. //
  217. if ((ClientSession->CsFlags & CS_DIRECT_TRUST) == 0 ) {
  218. //
  219. // If the caller needs a direct trust,
  220. // simply indicate that the domain isn't trusted.
  221. //
  222. if ( Flags & NL_DIRECT_TRUST_REQUIRED ) {
  223. ClientSession = NULL;
  224. break;
  225. }
  226. //
  227. // If the caller wants the closest Hop,
  228. // return that instead.
  229. //
  230. if ( Flags & NL_RETURN_CLOSEST_HOP ) {
  231. //
  232. // If there isn't a domain that's one hop closer than this one,
  233. // return failure to the caller.
  234. //
  235. if ( ClientSession->CsDirectClientSession == NULL ) {
  236. ClientSession = NULL;
  237. break;
  238. }
  239. //
  240. // Otherwise return the client session that's one hop closer.
  241. //
  242. ClientSession = ClientSession->CsDirectClientSession;
  243. if ( ARGUMENT_PRESENT( TransitiveUsed )) {
  244. *TransitiveUsed = TRUE;
  245. }
  246. }
  247. }
  248. NlRefClientSession( ClientSession );
  249. break;
  250. }
  251. ClientSession = NULL;
  252. }
  253. }
  254. #endif // _DC_NETLOGON
  255. //
  256. // On a workstation or BDC, refer to the Primary domain.
  257. // Also, if this is a PDC and it's OK to return its only
  258. // client session (to itself), refer to the Primary domain.
  259. //
  260. if ( (DomainInfo->DomRole == RoleBackup && ClientSession == NULL) ||
  261. (DomainInfo->DomRole == RolePrimary && ClientSession == NULL &&
  262. (Flags & NL_ROLE_PRIMARY_OK) ) ||
  263. DomainInfo->DomRole == RoleMemberWorkstation ) {
  264. ClientSession = NlRefDomClientSession( DomainInfo );
  265. if ( ClientSession != NULL ) {
  266. if ( RtlEqualDomainName( &DomainInfo->DomUnicodeDomainNameString,
  267. DomainName ) ||
  268. DomainInfo->DomUnicodeDnsDomainNameString.Buffer != NULL &&
  269. NlEqualDnsNameU( &DomainInfo->DomUnicodeDnsDomainNameString,
  270. DomainName ) ) {
  271. /* Drop Through */
  272. } else {
  273. NlUnrefClientSession( ClientSession );
  274. ClientSession = NULL;
  275. }
  276. }
  277. }
  278. UNLOCK_TRUST_LIST( DomainInfo );
  279. return ClientSession;
  280. }
  281. BOOL
  282. NlSetNamesClientSession(
  283. IN PCLIENT_SESSION ClientSession,
  284. IN PUNICODE_STRING DomainName OPTIONAL,
  285. IN PUNICODE_STRING DnsDomainName OPTIONAL,
  286. IN PSID DomainId OPTIONAL,
  287. IN GUID *DomainGuid OPTIONAL
  288. )
  289. /*++
  290. Routine Description:
  291. Set the name of the client session on the ClientSession structure.
  292. Enter with the domain trust list locked.
  293. The caller must be a writer of the trust list entry.
  294. Arguments:
  295. ClientSession - Client session to update
  296. The next four parameters specify the names of the client session.
  297. All of the non-null names are updated on the client session structure.
  298. DomainId -- Domain Id of the domain to do the discovery for.
  299. DomainName -- Specifies the Netbios DomainName of the trusted domain.
  300. DnsDomainName - Specifies the Dns domain name of the trusted domain.
  301. DomainGuid - Specifies the GUID of the trusted domain
  302. Return Value:
  303. TRUE: Names were successfully updated.
  304. FALSE: there was not enough memory available to update the names.
  305. --*/
  306. {
  307. WCHAR AccountNameBuffer[SSI_ACCOUNT_NAME_LENGTH+1];
  308. LPWSTR AccountName = NULL;
  309. NTSTATUS Status;
  310. NlAssert( ClientSession->CsReferenceCount > 0 );
  311. // We're not writer for a newly allocated structure, but it
  312. // doesn't make any difference since it isn't linked anywhere.
  313. // NlAssert( ClientSession->CsFlags & CS_WRITER );
  314. //
  315. // If we now know the domain GUID,
  316. // save it.
  317. //
  318. if ( ARGUMENT_PRESENT( DomainGuid ) ) {
  319. ClientSession->CsDomainGuidBuffer = *DomainGuid;
  320. ClientSession->CsDomainGuid = &ClientSession->CsDomainGuidBuffer;
  321. }
  322. //
  323. // If we now know the domain Sid,
  324. // save it.
  325. //
  326. if ( ARGUMENT_PRESENT( DomainId ) ) {
  327. //
  328. // If the Domain Sid is already known,
  329. // ditch the old sid if it is different that the new one.
  330. if ( ClientSession->CsDomainId != NULL &&
  331. !RtlEqualSid( ClientSession->CsDomainId, DomainId ) ) {
  332. LocalFree( ClientSession->CsDomainId );
  333. ClientSession->CsDomainId = NULL;
  334. }
  335. //
  336. // If the Domain Sid is not alreay known,
  337. // Save the new one.
  338. //
  339. if ( ClientSession->CsDomainId == NULL ) {
  340. ULONG SidSize;
  341. SidSize = RtlLengthSid( DomainId );
  342. ClientSession->CsDomainId = LocalAlloc( 0, SidSize );
  343. if (ClientSession->CsDomainId == NULL ) {
  344. return FALSE;
  345. }
  346. RtlCopyMemory( ClientSession->CsDomainId, DomainId, SidSize );
  347. }
  348. }
  349. //
  350. // If we now know the Netbios domain name,
  351. // save it.
  352. //
  353. if ( ARGUMENT_PRESENT(DomainName) ) {
  354. //
  355. // If the Netbios domain name is already known,
  356. // ditch the old name if it is different than the new name.
  357. //
  358. if ( ClientSession->CsNetbiosDomainName.Length != 0 &&
  359. !RtlEqualDomainName( &ClientSession->CsNetbiosDomainName,
  360. DomainName ) ) {
  361. if ( ClientSession->CsDebugDomainName == ClientSession->CsNetbiosDomainName.Buffer ) {
  362. ClientSession->CsDebugDomainName = NULL;
  363. }
  364. NlFreeUnicodeString( &ClientSession->CsNetbiosDomainName );
  365. ClientSession->CsOemNetbiosDomainNameLength = 0;
  366. ClientSession->CsOemNetbiosDomainName[0] = '\0';
  367. }
  368. //
  369. // If there is no Netbios domain name,
  370. // save the new one.
  371. //
  372. if ( ClientSession->CsNetbiosDomainName.Length == 0 ) {
  373. if ( !NlDuplicateUnicodeString( DomainName,
  374. &ClientSession->CsNetbiosDomainName ) ) {
  375. return FALSE;
  376. }
  377. if ( ClientSession->CsDebugDomainName == NULL ) {
  378. ClientSession->CsDebugDomainName = ClientSession->CsNetbiosDomainName.Buffer;
  379. }
  380. //
  381. // Convert the domain name to OEM for passing it over the wire.
  382. //
  383. Status = RtlUpcaseUnicodeToOemN( ClientSession->CsOemNetbiosDomainName,
  384. sizeof(ClientSession->CsOemNetbiosDomainName),
  385. &ClientSession->CsOemNetbiosDomainNameLength,
  386. DomainName->Buffer,
  387. DomainName->Length );
  388. if (!NT_SUCCESS(Status)) {
  389. NlPrint(( NL_CRITICAL, "%ws: Unable to convert Domain name to OEM 0x%lx\n", DomainName, Status ));
  390. return FALSE;
  391. }
  392. ClientSession->CsOemNetbiosDomainName[ClientSession->CsOemNetbiosDomainNameLength] = '\0';
  393. }
  394. }
  395. //
  396. // If we now know the DNS domain name,
  397. // save it.
  398. //
  399. if ( ARGUMENT_PRESENT(DnsDomainName) ) {
  400. //
  401. // If the DNS domain name is already known,
  402. // ditch the old name if it is different than the new name.
  403. //
  404. if ( ClientSession->CsDnsDomainName.Length != 0 &&
  405. !NlEqualDnsNameU( &ClientSession->CsDnsDomainName,
  406. DnsDomainName ) ) {
  407. if ( ClientSession->CsDebugDomainName == ClientSession->CsDnsDomainName.Buffer ) {
  408. ClientSession->CsDebugDomainName = NULL;
  409. }
  410. NlFreeUnicodeString( &ClientSession->CsDnsDomainName );
  411. if ( ClientSession->CsUtf8DnsDomainName != NULL ) {
  412. NetpMemoryFree( ClientSession->CsUtf8DnsDomainName );
  413. ClientSession->CsUtf8DnsDomainName = NULL;
  414. }
  415. }
  416. //
  417. // If there is no DNS domain name,
  418. // save the new one.
  419. //
  420. if ( ClientSession->CsDnsDomainName.Length == 0 ) {
  421. if ( !NlDuplicateUnicodeString( DnsDomainName,
  422. &ClientSession->CsDnsDomainName ) ) {
  423. return FALSE;
  424. }
  425. if ( ClientSession->CsDebugDomainName == NULL ) {
  426. ClientSession->CsDebugDomainName = ClientSession->CsDnsDomainName.Buffer;
  427. }
  428. if ( ClientSession->CsDnsDomainName.Buffer == NULL ) {
  429. ClientSession->CsUtf8DnsDomainName = NULL;
  430. } else {
  431. ClientSession->CsUtf8DnsDomainName = NetpAllocUtf8StrFromWStr( ClientSession->CsDnsDomainName.Buffer );
  432. if ( ClientSession->CsUtf8DnsDomainName == NULL ) {
  433. return FALSE;
  434. }
  435. }
  436. }
  437. }
  438. //
  439. // If this is a direct trust relationship,
  440. // build the name of the account in the trusted domain.
  441. //
  442. if ( ClientSession->CsFlags & CS_DIRECT_TRUST ) {
  443. //
  444. // Build the account name as a function of the SecureChannelType.
  445. //
  446. switch (ClientSession->CsSecureChannelType) {
  447. case WorkstationSecureChannel:
  448. case ServerSecureChannel:
  449. wcscpy( AccountNameBuffer, ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer );
  450. wcscat( AccountNameBuffer, SSI_ACCOUNT_NAME_POSTFIX);
  451. AccountName = AccountNameBuffer;
  452. break;
  453. case TrustedDomainSecureChannel:
  454. wcscpy( AccountNameBuffer, ClientSession->CsDomainInfo->DomUnicodeDomainName );
  455. wcscat( AccountNameBuffer, SSI_ACCOUNT_NAME_POSTFIX);
  456. AccountName = AccountNameBuffer;
  457. break;
  458. case TrustedDnsDomainSecureChannel:
  459. if ( ClientSession->CsDomainInfo->DomUnicodeDnsDomainName == NULL ) {
  460. NlPrintCs((NL_CRITICAL, ClientSession,
  461. "NlSetNameClientSession: NT 5 DNS trust with no DnsDomainName.\n" ));
  462. return FALSE;
  463. }
  464. AccountName = ClientSession->CsDomainInfo->DomUnicodeDnsDomainName;
  465. break;
  466. default:
  467. return FALSE;
  468. }
  469. //
  470. // If the account name is already known,
  471. // ditch the old name if it is different than the new name.
  472. //
  473. if ( ClientSession->CsAccountName != NULL &&
  474. _wcsicmp( ClientSession->CsAccountName, AccountName ) != 0 ) {
  475. NetApiBufferFree( ClientSession->CsAccountName );
  476. ClientSession->CsAccountName = NULL;
  477. }
  478. //
  479. // If there is no account name,
  480. // save the new one.
  481. //
  482. if ( ClientSession->CsAccountName == NULL ) {
  483. ClientSession->CsAccountName = NetpAllocWStrFromWStr( AccountName );
  484. if ( ClientSession->CsAccountName == NULL ) {
  485. return FALSE;
  486. }
  487. }
  488. }
  489. return TRUE;
  490. }
  491. PCLIENT_SESSION
  492. NlAllocateClientSession(
  493. IN PDOMAIN_INFO DomainInfo,
  494. IN PUNICODE_STRING DomainName,
  495. IN PUNICODE_STRING DnsDomainName OPTIONAL,
  496. IN PSID DomainId,
  497. IN GUID *DomainGuid OPTIONAL,
  498. IN ULONG Flags,
  499. IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
  500. IN ULONG TrustAttributes
  501. )
  502. /*++
  503. Routine Description:
  504. Allocate a ClientSession structure and initialize it.
  505. The allocated entry is returned referenced and must be dereferenced using
  506. NlUnrefClientSession.
  507. Arguments:
  508. DomainInfo - Hosted domain this session is for.
  509. DomainName - Specifies the DomainName of the entry.
  510. DnsDomainName - Specifies the DNS domain name of the trusted domain
  511. DomainId - Specifies the DomainId of the Domain.
  512. DomainGuid - Specifies the GUID of the trusted domain
  513. Flags - Specifies initial flags to set on the session
  514. SecureChannelType -- Type of secure channel this ClientSession structure
  515. will represent.
  516. TrustAttributes - The attributes of the trust corresponding to the
  517. trusted domain
  518. Return Value:
  519. NULL: There's not enough memory to allocate the client session.
  520. --*/
  521. {
  522. PCLIENT_SESSION ClientSession;
  523. ULONG CaIndex;
  524. //
  525. // Validate the arguments
  526. //
  527. if ( DomainName != NULL &&
  528. DomainName->Length > DNLEN * sizeof(WCHAR) ) {
  529. NlPrintDom((NL_CRITICAL, DomainInfo,
  530. "NlAllocateClientSession given too long domain name %wZ\n",
  531. DomainName ));
  532. return NULL;
  533. }
  534. //
  535. // Allocate the Client Session Entry
  536. //
  537. ClientSession = LocalAlloc( LMEM_ZEROINIT,
  538. sizeof(CLIENT_SESSION) +
  539. (NlGlobalMaxConcurrentApi-1) * sizeof(CLIENT_API) );
  540. if (ClientSession == NULL) {
  541. return NULL;
  542. }
  543. //
  544. // Initialize misc. fields.
  545. //
  546. ClientSession->CsSecureChannelType = SecureChannelType;
  547. ClientSession->CsFlags = Flags;
  548. ClientSession->CsState = CS_IDLE;
  549. ClientSession->CsReferenceCount = 1;
  550. ClientSession->CsConnectionStatus = STATUS_NO_LOGON_SERVERS;
  551. ClientSession->CsDomainInfo = DomainInfo;
  552. ClientSession->CsTrustAttributes = TrustAttributes;
  553. InitializeListHead( &ClientSession->CsNext );
  554. NlInitializeWorkItem(&ClientSession->CsAsyncDiscoveryWorkItem, NlDcDiscoveryWorker, ClientSession);
  555. for ( CaIndex=0; CaIndex<NlGlobalMaxConcurrentApi; CaIndex++ ) {
  556. ClientSession->CsClientApi[CaIndex].CaApiTimer.Period = MAILSLOT_WAIT_FOREVER;
  557. }
  558. //
  559. // Set the names of the trusted domain onto the Client session.
  560. //
  561. if ( !NlSetNamesClientSession( ClientSession,
  562. DomainName,
  563. DnsDomainName,
  564. DomainId,
  565. DomainGuid ) ) {
  566. NlUnrefClientSession( ClientSession );
  567. return NULL;
  568. }
  569. //
  570. // Create the writer semaphore.
  571. //
  572. ClientSession->CsWriterSemaphore = CreateSemaphore(
  573. NULL, // No special security
  574. 1, // Initially not locked
  575. 1, // At most 1 unlocker
  576. NULL ); // No name
  577. if ( ClientSession->CsWriterSemaphore == NULL ) {
  578. NlUnrefClientSession( ClientSession );
  579. return NULL;
  580. }
  581. //
  582. // Create the API semaphore
  583. //
  584. if ( NlGlobalMaxConcurrentApi > 1 ) {
  585. ClientSession->CsApiSemaphore = CreateSemaphore(
  586. NULL, // No special security
  587. NlGlobalMaxConcurrentApi-1, // Initially all slots are free
  588. NlGlobalMaxConcurrentApi-1, // And there will never be more slots than that
  589. NULL ); // No name
  590. if ( ClientSession->CsApiSemaphore == NULL ) {
  591. NlUnrefClientSession( ClientSession );
  592. return NULL;
  593. }
  594. }
  595. //
  596. // Create the Discovery event.
  597. //
  598. if ( SecureChannelType != WorkstationSecureChannel ) {
  599. ClientSession->CsDiscoveryEvent = CreateEvent(
  600. NULL, // No special security
  601. TRUE, // Manual Reset
  602. FALSE, // No discovery initially happening
  603. NULL ); // No name
  604. if ( ClientSession->CsDiscoveryEvent == NULL ) {
  605. NlUnrefClientSession( ClientSession );
  606. return NULL;
  607. }
  608. }
  609. return ClientSession;
  610. }
  611. VOID
  612. NlFreeClientSession(
  613. IN PCLIENT_SESSION ClientSession
  614. )
  615. /*++
  616. Routine Description:
  617. This routine prevents any new references to the ClientSession.
  618. It does this by removing it from any global lists.
  619. This routine is called with the Trust List locked.
  620. Arguments:
  621. ClientSession - Specifies a pointer to the trust list entry to delete.
  622. Return Value:
  623. --*/
  624. {
  625. //
  626. // Remove any reference to the directly trusted domain.
  627. // (Do this before checking the reference count since this may
  628. // be a reference to itself.)
  629. //
  630. if ( ClientSession->CsDirectClientSession != NULL ) {
  631. NlUnrefClientSession( ClientSession->CsDirectClientSession );
  632. ClientSession->CsDirectClientSession = NULL;
  633. }
  634. #ifdef _DC_NETLOGON
  635. //
  636. // If this is a trusted domain secure channel,
  637. // Delink the entry from the sequential list.
  638. //
  639. if ( IsDomainSecureChannelType(ClientSession->CsSecureChannelType) &&
  640. !IsListEmpty( &ClientSession->CsNext) ) {
  641. RemoveEntryList( &ClientSession->CsNext );
  642. ClientSession->CsDomainInfo->DomTrustListLength --;
  643. //
  644. // Remove the reference for us being in the list.
  645. //
  646. NlUnrefClientSession( ClientSession );
  647. }
  648. #endif // _DC_NETLOGON
  649. }
  650. VOID
  651. NlRefClientSession(
  652. IN PCLIENT_SESSION ClientSession
  653. )
  654. /*++
  655. Routine Description:
  656. Mark the specified client session as referenced.
  657. On Entry,
  658. The trust list must be locked.
  659. Arguments:
  660. ClientSession - Specifies a pointer to the trust list entry.
  661. Return Value:
  662. None.
  663. --*/
  664. {
  665. //
  666. // Simply increment the reference count.
  667. //
  668. ClientSession->CsReferenceCount ++;
  669. }
  670. VOID
  671. NlUnrefClientSession(
  672. IN PCLIENT_SESSION ClientSession
  673. )
  674. /*++
  675. Routine Description:
  676. Mark the specified client session as unreferenced.
  677. On Entry,
  678. The trust list entry must be referenced by the caller.
  679. The caller must not be a writer of the trust list entry.
  680. The trust list may be locked. But this routine will lock it again to
  681. handle those cases where it isn't already locked.
  682. Arguments:
  683. ClientSession - Specifies a pointer to the trust list entry.
  684. Return Value:
  685. --*/
  686. {
  687. PDOMAIN_INFO DomainInfo = ClientSession->CsDomainInfo;
  688. ULONG CaIndex;
  689. LOCK_TRUST_LIST( DomainInfo );
  690. //
  691. // Dereference the entry.
  692. //
  693. NlAssert( ClientSession->CsReferenceCount > 0 );
  694. ClientSession->CsReferenceCount --;
  695. // NlPrintCs((NL_CRITICAL, ClientSession, "Deref: %ld\n", ClientSession->CsReferenceCount ));
  696. //
  697. // If we're the last reference,
  698. // delete the entry.
  699. //
  700. if ( ClientSession->CsReferenceCount == 0 ) {
  701. //
  702. // Close the discovery event if it exists.
  703. //
  704. if ( ClientSession->CsDiscoveryEvent != NULL ) {
  705. CloseHandle( ClientSession->CsDiscoveryEvent );
  706. }
  707. //
  708. // Close the write synchronization handles.
  709. //
  710. if ( ClientSession->CsWriterSemaphore != NULL ) {
  711. (VOID) CloseHandle( ClientSession->CsWriterSemaphore );
  712. }
  713. //
  714. // Close the API synchronization handles.
  715. //
  716. if ( ClientSession->CsApiSemaphore != NULL ) {
  717. (VOID) CloseHandle( ClientSession->CsApiSemaphore );
  718. }
  719. //
  720. // Clean any outstanding API calls
  721. //
  722. for ( CaIndex=0; CaIndex<NlGlobalMaxConcurrentApi; CaIndex++ ) {
  723. PCLIENT_API ClientApi;
  724. ClientApi = &ClientSession->CsClientApi[CaIndex];
  725. //
  726. // Close the thread handle if it exists.
  727. //
  728. if ( ClientApi->CaThreadHandle != NULL ) {
  729. CloseHandle( ClientApi->CaThreadHandle );
  730. }
  731. //
  732. // If there is an rpc binding handle to this server,
  733. // unbind it.
  734. if ( ClientApi->CaFlags & CA_BINDING_CACHED ) {
  735. //
  736. // Indicate the handle is no longer bound
  737. //
  738. NlGlobalBindingHandleCount --;
  739. //
  740. // Unbind the handle
  741. //
  742. NlAssert( ClientSession->CsUncServerName != NULL );
  743. NlpSecureChannelUnbind(
  744. ClientSession,
  745. ClientSession->CsUncServerName,
  746. "NlFreeClientSession",
  747. CaIndex,
  748. ClientApi->CaRpcHandle,
  749. (ClientApi->CaFlags & CA_TCP_BINDING) ? UseTcpIp : UseNamedPipe);
  750. }
  751. }
  752. //
  753. // Free the credentials handle
  754. //
  755. if ( ClientSession->CsCredHandle.dwUpper != 0 || ClientSession->CsCredHandle.dwLower != 0 ) {
  756. FreeCredentialsHandle( &ClientSession->CsCredHandle );
  757. ClientSession->CsCredHandle.dwUpper = 0;
  758. ClientSession->CsCredHandle.dwLower = 0;
  759. }
  760. //
  761. // If there is authentication data,
  762. // delete it.
  763. if ( ClientSession->ClientAuthData != NULL ) {
  764. NetpMemoryFree( ClientSession->ClientAuthData );
  765. ClientSession->ClientAuthData = NULL;
  766. }
  767. //
  768. // Free the domain Sid
  769. //
  770. if ( ClientSession->CsDomainId != NULL ) {
  771. LocalFree( ClientSession->CsDomainId );
  772. ClientSession->CsDomainId = NULL;
  773. }
  774. //
  775. // Free the Netbios domain name.
  776. //
  777. if ( ClientSession->CsNetbiosDomainName.Buffer != NULL ) {
  778. NlFreeUnicodeString( &ClientSession->CsNetbiosDomainName );
  779. }
  780. ClientSession->CsOemNetbiosDomainNameLength = 0;
  781. ClientSession->CsOemNetbiosDomainName[0] = '\0';
  782. //
  783. // Free the DNS domain name.
  784. //
  785. if ( ClientSession->CsDnsDomainName.Buffer != NULL ) {
  786. NlFreeUnicodeString( &ClientSession->CsDnsDomainName );
  787. }
  788. if ( ClientSession->CsUtf8DnsDomainName != NULL ) {
  789. NetpMemoryFree( ClientSession->CsUtf8DnsDomainName );
  790. ClientSession->CsUtf8DnsDomainName = NULL;
  791. }
  792. //
  793. // Free the DC name.
  794. //
  795. if ( ClientSession->CsUncServerName != NULL ) {
  796. NetApiBufferFree( ClientSession->CsUncServerName );
  797. ClientSession->CsUncServerName = NULL;
  798. }
  799. //
  800. // Delete the entry itself
  801. //
  802. LocalFree( ClientSession );
  803. }
  804. UNLOCK_TRUST_LIST( DomainInfo );
  805. }
  806. PCLIENT_API
  807. NlAllocateClientApi(
  808. IN PCLIENT_SESSION ClientSession,
  809. IN DWORD Timeout
  810. )
  811. /*++
  812. Routine Description:
  813. This routine allocates a ClientApi structure for use by the caller.
  814. Fail the operation if we have to wait more than Timeout milliseconds.
  815. On Entry,
  816. The trust list must NOT be locked.
  817. The trust list entry must be referenced by the caller.
  818. The caller must NOT be a writer of the trust list entry.
  819. Actually, the trust list can be locked if the caller passes in a short
  820. timeout (for instance, zero milliseconds.) Specifying a longer timeout
  821. violates the locking order.
  822. Arguments:
  823. ClientSession - Specifies a pointer to the trust list entry.
  824. Timeout - Maximum time (in milliseconds) to wait for an API slot to become
  825. available.
  826. Return Value:
  827. NULL - The call timed out.
  828. Non-NULL - Return a pointer to a ClientApi structure that should be
  829. freed using NlFreeClientApi.
  830. --*/
  831. {
  832. DWORD WaitStatus;
  833. ULONG CaIndex;
  834. NlAssert( ClientSession->CsReferenceCount > 0 );
  835. //
  836. // If we aren't doing concurrent API calls,
  837. // we're done.
  838. //
  839. if ( NlGlobalMaxConcurrentApi == 1 ||
  840. NlGlobalWinsockPnpAddresses == NULL ) {
  841. return &ClientSession->CsClientApi[0];
  842. }
  843. //
  844. // Wait for an API slot to free up.
  845. //
  846. WaitStatus = WaitForSingleObject( ClientSession->CsApiSemaphore, Timeout );
  847. if ( WaitStatus != WAIT_OBJECT_0 ) {
  848. NlPrintCs(( NL_CRITICAL, ClientSession,
  849. "NlAllocateClientApi timed out: %ld %ld\n",
  850. GetLastError(),
  851. WaitStatus ));
  852. return NULL;
  853. }
  854. //
  855. // Take the next available slot.
  856. //
  857. // Don't use the first slot. It is reserved for non concurrent API calls.
  858. //
  859. LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  860. for ( CaIndex=1; CaIndex < NlGlobalMaxConcurrentApi; CaIndex++ ) {
  861. if ( (ClientSession->CsClientApi[CaIndex].CaFlags & CA_ENTRY_IN_USE) == 0 ) {
  862. ClientSession->CsClientApi[CaIndex].CaFlags |= CA_ENTRY_IN_USE;
  863. UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  864. return &ClientSession->CsClientApi[CaIndex];
  865. }
  866. }
  867. UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  868. NlAssert( FALSE );
  869. NlPrintCs(( NL_CRITICAL, ClientSession,
  870. "NlAllocateClientApi all entries are in use. (This can't happen)\n" ));
  871. return NULL;
  872. }
  873. VOID
  874. NlFreeClientApi(
  875. IN PCLIENT_SESSION ClientSession,
  876. IN PCLIENT_API ClientApi
  877. )
  878. /*++
  879. Routine Description:
  880. This routine frees a ClientApi structure allocated by NlAllocateClientApi
  881. On Entry,
  882. The trust list entry must be referenced by the caller.
  883. The caller must NOT be a writer of the trust list entry.
  884. Arguments:
  885. ClientSession - Specifies a pointer to the trust list entry.
  886. ClientApi - The Client API stucture to free
  887. Return Value:
  888. None.
  889. --*/
  890. {
  891. DWORD WaitStatus;
  892. NlAssert( ClientSession->CsReferenceCount > 0 );
  893. //
  894. // If we aren't doing concurrent API calls,
  895. // we're done.
  896. //
  897. if ( !UseConcurrentRpc( ClientSession, ClientApi) ) {
  898. return;
  899. }
  900. NlAssert( !IsApiActive( ClientApi ) );
  901. //
  902. // Free the entry
  903. //
  904. // The RPC binding is maintained past this free. It is available for
  905. // the next thread to use.
  906. //
  907. LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  908. //
  909. // The entry must be in use
  910. //
  911. NlAssert( ClientApi->CaFlags & CA_ENTRY_IN_USE );
  912. ClientApi->CaFlags &= ~CA_ENTRY_IN_USE;
  913. //
  914. // Close the handle of this thread.
  915. //
  916. if ( ClientApi->CaThreadHandle != NULL ) {
  917. CloseHandle( ClientApi->CaThreadHandle );
  918. ClientApi->CaThreadHandle = NULL;
  919. }
  920. UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  921. //
  922. // Allow someone else to have this slot.
  923. //
  924. if ( !ReleaseSemaphore( ClientSession->CsApiSemaphore, 1, NULL ) ) {
  925. NlAssert( !"ReleaseSemaphore failed" );
  926. NlPrintCs((NL_CRITICAL, ClientSession,
  927. "ReleaseSemaphore CsApiSemaphore returned %ld\n",
  928. GetLastError() ));
  929. }
  930. return;
  931. }
  932. BOOL
  933. NlTimeoutSetWriterClientSession(
  934. IN PCLIENT_SESSION ClientSession,
  935. IN DWORD Timeout
  936. )
  937. /*++
  938. Routine Description:
  939. Become a writer of the specified client session but fail the operation if
  940. we have to wait more than Timeout milliseconds.
  941. A writer can "write" many of the fields in the client session structure.
  942. See the comments in ssiinit.h for details.
  943. On Entry,
  944. The trust list must NOT be locked.
  945. The trust list entry must be referenced by the caller.
  946. The caller must NOT be a writer of the trust list entry.
  947. Actually, the trust list can be locked if the caller passes in a short
  948. timeout (for instance, zero milliseconds.) Specifying a longer timeout
  949. violates the locking order.
  950. Arguments:
  951. ClientSession - Specifies a pointer to the trust list entry.
  952. Timeout - Maximum time (in milliseconds) to wait for a previous writer.
  953. Return Value:
  954. TRUE - The caller is now the writer of the client session.
  955. FALSE - The operation has timed out.
  956. --*/
  957. {
  958. DWORD WaitStatus;
  959. NlAssert( ClientSession->CsReferenceCount > 0 );
  960. //
  961. // Wait for other writers to finish.
  962. //
  963. WaitStatus = WaitForSingleObject( ClientSession->CsWriterSemaphore, Timeout );
  964. if ( WaitStatus != WAIT_OBJECT_0 ) {
  965. NlPrintCs(( NL_CRITICAL, ClientSession,
  966. "NlTimeoutSetWriterClientSession timed out: %ld %ld\n",
  967. GetLastError(),
  968. WaitStatus ));
  969. return FALSE;
  970. }
  971. //
  972. // Become a writer.
  973. //
  974. LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  975. ClientSession->CsFlags |= CS_WRITER;
  976. UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  977. return TRUE;
  978. }
  979. VOID
  980. NlResetWriterClientSession(
  981. IN PCLIENT_SESSION ClientSession
  982. )
  983. /*++
  984. Routine Description:
  985. Stop being a writer of the specified client session.
  986. On Entry,
  987. The trust list must NOT be locked.
  988. The trust list entry must be referenced by the caller.
  989. The caller must be a writer of the trust list entry.
  990. Arguments:
  991. ClientSession - Specifies a pointer to the trust list entry.
  992. Return Value:
  993. --*/
  994. {
  995. NlAssert( ClientSession->CsReferenceCount > 0 );
  996. NlAssert( ClientSession->CsFlags & CS_WRITER );
  997. //
  998. // Stop being a writer.
  999. //
  1000. LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  1001. ClientSession->CsFlags &= ~CS_WRITER;
  1002. //
  1003. // Close the handle of this thread.
  1004. //
  1005. // The zeroeth API slot is reserved for non-concurrent API calls.
  1006. // As such, if the ThreadHandle is set, it had to have been set by this thread.
  1007. //
  1008. if ( ClientSession->CsClientApi[0].CaThreadHandle != NULL ) {
  1009. CloseHandle( ClientSession->CsClientApi[0].CaThreadHandle );
  1010. ClientSession->CsClientApi[0].CaThreadHandle = NULL;
  1011. }
  1012. UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  1013. //
  1014. // Allow writers to try again.
  1015. //
  1016. if ( !ReleaseSemaphore( ClientSession->CsWriterSemaphore, 1, NULL ) ) {
  1017. NlPrintCs((NL_CRITICAL, ClientSession,
  1018. "ReleaseSemaphore CsWriterSemaphore returned %ld\n",
  1019. GetLastError() ));
  1020. }
  1021. }
  1022. VOID
  1023. NlSetStatusClientSession(
  1024. IN PCLIENT_SESSION ClientSession,
  1025. IN NTSTATUS CsConnectionStatus
  1026. )
  1027. /*++
  1028. Routine Description:
  1029. Set the connection state for this client session.
  1030. On Entry,
  1031. The trust list must NOT be locked.
  1032. The trust list entry must be referenced by the caller.
  1033. The caller must be a writer of the trust list entry.
  1034. Arguments:
  1035. ClientSession - Specifies a pointer to the trust list entry.
  1036. CsConnectionStatus - the status of the connection.
  1037. Return Value:
  1038. --*/
  1039. {
  1040. handle_t OldRpcHandle[MAX_MAXCONCURRENTAPI+1];
  1041. NL_RPC_BINDING OldRpcBindingType[MAX_MAXCONCURRENTAPI+1];
  1042. BOOLEAN FreeHandles = FALSE;
  1043. LPWSTR SavedServerName = NULL;
  1044. ULONG CaIndex;
  1045. NlAssert( ClientSession->CsReferenceCount > 0 );
  1046. NlAssert( ClientSession->CsFlags & CS_WRITER );
  1047. NlPrintCs((NL_SESSION_SETUP, ClientSession,
  1048. "NlSetStatusClientSession: Set connection status to %lx\n",
  1049. CsConnectionStatus ));
  1050. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  1051. ClientSession->CsConnectionStatus = CsConnectionStatus;
  1052. if ( NT_SUCCESS(CsConnectionStatus) ) {
  1053. ClientSession->CsState = CS_AUTHENTICATED;
  1054. //
  1055. // Handle setting the connection status to an error condition.
  1056. //
  1057. } else {
  1058. //
  1059. // If there is an rpc binding handle to this server,
  1060. // unbind it.
  1061. LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  1062. for ( CaIndex=0; CaIndex<NlGlobalMaxConcurrentApi; CaIndex++ ) {
  1063. PCLIENT_API ClientApi;
  1064. ClientApi = &ClientSession->CsClientApi[CaIndex];
  1065. if ( ClientApi->CaFlags & CA_BINDING_CACHED ) {
  1066. //
  1067. // If the API call is still active,
  1068. // we can't simply unbind.
  1069. //
  1070. // Rather cancel the call an let the thread doing the call
  1071. // find out that the session was dropped.
  1072. //
  1073. if ( IsApiActive( ClientApi ) ) {
  1074. //
  1075. // Cancel the RPC call.
  1076. //
  1077. // Keep the trust list locked even though this will be a long call
  1078. // since I have to protect the thread handle.
  1079. //
  1080. // RpcCancelThread merely queues a workitem anyway.
  1081. //
  1082. if ( ClientApi->CaThreadHandle != NULL ) {
  1083. NET_API_STATUS NetStatus;
  1084. NlPrintCs(( NL_CRITICAL, ClientSession,
  1085. "NlSetStatusClientSession: Start RpcCancelThread on %ws\n",
  1086. ClientSession->CsUncServerName ));
  1087. NetStatus = RpcCancelThread( ClientApi->CaThreadHandle );
  1088. NlPrintCs(( NL_CRITICAL, ClientSession,
  1089. "NlSetStatusClientSession: Finish RpcCancelThread on %ws %ld\n",
  1090. ClientSession->CsUncServerName,
  1091. NetStatus ));
  1092. } else {
  1093. NlPrintCs(( NL_CRITICAL, ClientSession,
  1094. "NlSetStatusClientSession: No thread handle so can't cancel RPC on %ws\n",
  1095. ClientSession->CsUncServerName ));
  1096. }
  1097. //
  1098. // If there is no active API,
  1099. // just unbind the handle
  1100. //
  1101. } else {
  1102. if ( !FreeHandles ) {
  1103. FreeHandles = TRUE;
  1104. RtlZeroMemory( &OldRpcHandle, sizeof(OldRpcHandle) );
  1105. RtlZeroMemory( &OldRpcBindingType, sizeof(OldRpcBindingType) );
  1106. }
  1107. //
  1108. // Figure out the binding type to unbind.
  1109. //
  1110. OldRpcBindingType[CaIndex] =
  1111. (ClientApi->CaFlags & CA_TCP_BINDING) ? UseTcpIp : UseNamedPipe;
  1112. //
  1113. // Indicate the handle is no longer bound
  1114. //
  1115. ClientApi->CaFlags &= ~(CA_BINDING_CACHED|CA_BINDING_AUTHENTICATED|CA_TCP_BINDING);
  1116. NlGlobalBindingHandleCount --;
  1117. //
  1118. // Save the server name.
  1119. //
  1120. if ( SavedServerName == NULL &&
  1121. ClientSession->CsUncServerName != NULL ) {
  1122. SavedServerName = NetpAllocWStrFromWStr( ClientSession->CsUncServerName );
  1123. NlAssert( SavedServerName != NULL );
  1124. }
  1125. //
  1126. // Some RPC handles are unbound via routines in netapi32
  1127. //
  1128. if ( !UseConcurrentRpc( ClientSession, ClientApi) ) {
  1129. //
  1130. // Capture the ServerName
  1131. //
  1132. NlAssert( ClientSession->CsUncServerName != NULL && SavedServerName != NULL );
  1133. OldRpcHandle[CaIndex] = SavedServerName;
  1134. //
  1135. // Other RPC handles are handled directly in netlogon
  1136. //
  1137. } else {
  1138. OldRpcHandle[CaIndex] = ClientApi->CaRpcHandle;
  1139. }
  1140. ClientApi->CaRpcHandle = NULL;
  1141. }
  1142. }
  1143. }
  1144. UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  1145. //
  1146. // Free the credentials handle
  1147. //
  1148. if ( ClientSession->CsCredHandle.dwUpper != 0 || ClientSession->CsCredHandle.dwLower != 0 ) {
  1149. FreeCredentialsHandle( &ClientSession->CsCredHandle );
  1150. ClientSession->CsCredHandle.dwUpper = 0;
  1151. ClientSession->CsCredHandle.dwLower = 0;
  1152. }
  1153. //
  1154. // If there is authentication data,
  1155. // delete it.
  1156. if ( ClientSession->ClientAuthData != NULL ) {
  1157. NetpMemoryFree( ClientSession->ClientAuthData );
  1158. ClientSession->ClientAuthData = NULL;
  1159. }
  1160. //
  1161. // Indicate discovery is needed (And can be done at any time.)
  1162. //
  1163. ClientSession->CsState = CS_IDLE;
  1164. if ( ClientSession->CsUncServerName != NULL ) {
  1165. NetApiBufferFree( ClientSession->CsUncServerName );
  1166. ClientSession->CsUncServerName = NULL;
  1167. }
  1168. //
  1169. // Zero out the server socket address
  1170. //
  1171. RtlZeroMemory( &ClientSession->CsServerSockAddr,
  1172. sizeof(ClientSession->CsServerSockAddr) );
  1173. RtlZeroMemory( &ClientSession->CsServerSockAddrIn,
  1174. sizeof(ClientSession->CsServerSockAddrIn) );
  1175. #ifdef _DC_NETLOGON
  1176. ClientSession->CsTransport = NULL;
  1177. #endif // _DC_NETLOGON
  1178. ClientSession->CsTimeoutCount = 0;
  1179. ClientSession->CsFastCallCount = 0;
  1180. ClientSession->CsLastAuthenticationTry.QuadPart = 0;
  1181. ClientSession->CsDiscoveryFlags &= ~(CS_DISCOVERY_HAS_DS|
  1182. CS_DISCOVERY_IS_CLOSE|
  1183. CS_DISCOVERY_HAS_IP|
  1184. CS_DISCOVERY_USE_MAILSLOT|
  1185. CS_DISCOVERY_USE_LDAP|
  1186. CS_DISCOVERY_HAS_TIMESERV|
  1187. CS_DISCOVERY_DNS_SERVER|
  1188. CS_DISCOVERY_NO_PWD_MONITOR|
  1189. CS_DISCOVERY_NO_PWD_ATTR_MONITOR);
  1190. ClientSession->CsSessionCount++;
  1191. //
  1192. // Don't be tempted to clear CsAuthenticationSeed and CsSessionKey here.
  1193. // Even though the secure channel is gone, NlFinishApiClientSession may
  1194. // have dropped it. The caller of NlFinishApiClientSession will use
  1195. // the above two fields after the session is dropped in an attempt to
  1196. // complete the final call on the secure channel.
  1197. //
  1198. //
  1199. // Also note that we don't clear CsAccountRid because it doesn't change
  1200. // often, so we can reuse it on failover if we cannot reset the secure
  1201. // channel for some reason.
  1202. //
  1203. }
  1204. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  1205. //
  1206. // Now that I have as many resources unlocked as possible,
  1207. // Unbind from this server.
  1208. //
  1209. if ( FreeHandles ) {
  1210. for ( CaIndex=0; CaIndex<NlGlobalMaxConcurrentApi; CaIndex++ ) {
  1211. //
  1212. // Skip indices that don't have an active API
  1213. //
  1214. if ( OldRpcHandle[CaIndex] == NULL ) {
  1215. continue;
  1216. }
  1217. //
  1218. // Unbind the handle
  1219. //
  1220. NlpSecureChannelUnbind(
  1221. ClientSession,
  1222. SavedServerName,
  1223. "NlSetStatusClientSession",
  1224. CaIndex,
  1225. OldRpcHandle[CaIndex],
  1226. OldRpcBindingType[CaIndex] );
  1227. }
  1228. if ( SavedServerName != NULL ) {
  1229. NetApiBufferFree( SavedServerName );
  1230. }
  1231. }
  1232. }
  1233. #ifdef _DC_NETLOGON
  1234. #ifdef notdef
  1235. PLSAPR_TREE_TRUST_INFO
  1236. NlFindParentInDomainTree(
  1237. IN PDOMAIN_INFO DomainInfo,
  1238. IN PLSAPR_TREE_TRUST_INFO TreeTrustInfo,
  1239. OUT PBOOLEAN ThisNodeIsSelf
  1240. )
  1241. /*++
  1242. Routine Description:
  1243. This routine walks the trust tree and returns a pointer to the entry
  1244. for our parent.
  1245. Arguments:
  1246. DomainInfo - Hosted domain to initialize
  1247. TreeTrustInfo - Structure describing the tree of domains.
  1248. ThisNodeIsSelf - Returns TRUE if the node at TreeTrustInfo is this
  1249. domain. (Return value will be NULL)
  1250. Return Value:
  1251. Returns a pointer to the parent of this domain.
  1252. NULL: Parent is not in the subtree.
  1253. --*/
  1254. {
  1255. NTSTATUS Status;
  1256. // LSAPR_TRUSTED_DOMAIN_INFORMATION_EX TrustInformation;
  1257. // PCLIENT_SESSION ThisDomainClientSession = NULL;
  1258. ULONG Index;
  1259. BOOLEAN ChildIsSelf;
  1260. PLSAPR_TREE_TRUST_INFO LocalTreeTrustInfo;
  1261. //
  1262. // Check if this tree has us as the root
  1263. //
  1264. if ( (TreeTrustInfo->DnsDomainName.Length != 0 &&
  1265. NlEqualDnsNameU( (PUNICODE_STRING)&TreeTrustInfo->DnsDomainName,
  1266. &DomainInfo->DomUnicodeDnsDomainNameString ) ) ||
  1267. RtlEqualDomainName( &DomainInfo->DomUnicodeDomainNameString,
  1268. (PUNICODE_STRING)&TreeTrustInfo->FlatName ) ) {
  1269. *ThisNodeIsSelf = TRUE;
  1270. return NULL;
  1271. }
  1272. //
  1273. // Loop handling each of the children domains.
  1274. //
  1275. for ( Index=0; Index<TreeTrustInfo->Children; Index++ ) {
  1276. //
  1277. // Check the subtree rooted at this domain's children.
  1278. //
  1279. LocalTreeTrustInfo = NlFindParentInDomainTree(
  1280. DomainInfo,
  1281. &TreeTrustInfo->ChildDomains[Index],
  1282. &ChildIsSelf );
  1283. //
  1284. // If our parent has been found,
  1285. // return it to our caller.
  1286. //
  1287. if ( LocalTreeTrustInfo != NULL) {
  1288. *ThisNodeIsSelf = FALSE;
  1289. return LocalTreeTrustInfo;
  1290. }
  1291. //
  1292. // If this child is our domain,
  1293. // then this domain is our domain's parent.
  1294. //
  1295. if ( ChildIsSelf ) {
  1296. *ThisNodeIsSelf = FALSE;
  1297. return TreeTrustInfo;
  1298. }
  1299. }
  1300. //
  1301. // Our domain isn't in this subtree
  1302. //
  1303. *ThisNodeIsSelf = FALSE;
  1304. return NULL;
  1305. }
  1306. #endif // notdef
  1307. VOID
  1308. NlPickTrustedDcForEntireTrustList(
  1309. IN PDOMAIN_INFO DomainInfo,
  1310. IN BOOLEAN OnlyDoNewTrusts
  1311. )
  1312. /*++
  1313. Routine Description:
  1314. For each domain in the trust list where the DC has not been
  1315. available for at least 45 seconds, try to select a new DC.
  1316. Arguments:
  1317. DomainInfo - Hosted domain to handle.
  1318. OnlyDoNewTrusts - True if only new trust relationships are to be done.
  1319. Return Value:
  1320. Status of the operation.
  1321. --*/
  1322. {
  1323. PLIST_ENTRY ListEntry;
  1324. PCLIENT_SESSION ClientSession;
  1325. DISCOVERY_TYPE DiscoveryType;
  1326. //
  1327. // If we're just handling new trusts,
  1328. // Make the discovery a full async discovery.
  1329. //
  1330. if ( OnlyDoNewTrusts ) {
  1331. DiscoveryType = DT_Asynchronous;
  1332. //
  1333. // If we're just scavenging trusts,
  1334. // make the discovery a dead domain discovery.
  1335. //
  1336. } else {
  1337. DiscoveryType = DT_DeadDomain;
  1338. }
  1339. LOCK_TRUST_LIST( DomainInfo );
  1340. //
  1341. // Mark each entry to indicate we need to pick a DC.
  1342. //
  1343. for ( ListEntry = DomainInfo->DomTrustList.Flink ;
  1344. ListEntry != &DomainInfo->DomTrustList ;
  1345. ListEntry = ListEntry->Flink) {
  1346. ClientSession = CONTAINING_RECORD( ListEntry,
  1347. CLIENT_SESSION,
  1348. CsNext );
  1349. ClientSession->CsFlags &= ~CS_PICK_DC;
  1350. //
  1351. // Only pick a DC if the domain is directly trusted.
  1352. // Only pick a DC if the domain isn't in the current forest.
  1353. if ( (ClientSession->CsFlags & (CS_DIRECT_TRUST|CS_DOMAIN_IN_FOREST)) == CS_DIRECT_TRUST ) {
  1354. //
  1355. // Only pick a DC if we're doing ALL trusts, OR
  1356. // if this is a new trust
  1357. //
  1358. if ( !OnlyDoNewTrusts ||
  1359. (ClientSession->CsFlags & CS_NEW_TRUST) != 0 ) {
  1360. ClientSession->CsFlags |= CS_PICK_DC;
  1361. }
  1362. ClientSession->CsFlags &= ~CS_NEW_TRUST;
  1363. }
  1364. }
  1365. //
  1366. // Loop thru the trust list finding secure channels needing the DC
  1367. // to be picked.
  1368. //
  1369. for ( ListEntry = DomainInfo->DomTrustList.Flink ;
  1370. ListEntry != &DomainInfo->DomTrustList ;
  1371. ) {
  1372. ClientSession = CONTAINING_RECORD( ListEntry,
  1373. CLIENT_SESSION,
  1374. CsNext );
  1375. //
  1376. // If we've already done this entry,
  1377. // skip this entry.
  1378. //
  1379. if ( (ClientSession->CsFlags & CS_PICK_DC) == 0 ) {
  1380. ListEntry = ListEntry->Flink;
  1381. continue;
  1382. }
  1383. ClientSession->CsFlags &= ~CS_PICK_DC;
  1384. //
  1385. // If the DC is already picked,
  1386. // skip this entry.
  1387. //
  1388. if ( ClientSession->CsState != CS_IDLE ) {
  1389. ListEntry = ListEntry->Flink;
  1390. continue;
  1391. }
  1392. //
  1393. // Reference this entry while picking the DC.
  1394. //
  1395. NlRefClientSession( ClientSession );
  1396. UNLOCK_TRUST_LIST( DomainInfo );
  1397. //
  1398. // Check if we've tried to authenticate recently.
  1399. // (Don't call NlTimeToReauthenticate with the trust list locked.
  1400. // It locks NlGlobalDcDiscoveryCritSect. That's the wrong locking
  1401. // order.)
  1402. //
  1403. if ( NlTimeToReauthenticate( ClientSession ) ) {
  1404. //
  1405. // Try to pick the DC for the session.
  1406. //
  1407. if ( NlTimeoutSetWriterClientSession( ClientSession, 10*1000 ) ) {
  1408. if ( ClientSession->CsState == CS_IDLE ) {
  1409. //
  1410. // Don't ask for with-account discovery as it's too costly on the
  1411. // server side. If the discovered server doesn't have our account,
  1412. // the session setup logic will attempt with-account discovery.
  1413. //
  1414. (VOID) NlDiscoverDc( ClientSession,
  1415. DiscoveryType,
  1416. FALSE,
  1417. FALSE ); // don't specify account
  1418. }
  1419. NlResetWriterClientSession( ClientSession );
  1420. }
  1421. }
  1422. //
  1423. // Since we dropped the trust list lock,
  1424. // we'll start the search from the front of the list.
  1425. //
  1426. NlUnrefClientSession( ClientSession );
  1427. LOCK_TRUST_LIST( DomainInfo );
  1428. ListEntry = DomainInfo->DomTrustList.Flink ;
  1429. }
  1430. UNLOCK_TRUST_LIST( DomainInfo );
  1431. //
  1432. // On a BDC,
  1433. // ensure we know who the PDC is.
  1434. //
  1435. // In NT 3.1, we relied on the fact that the PDC sent us pulses every 5
  1436. // minutes. For NT 3.5, the PDC backs off after 3 such failed attempts and
  1437. // will only send a pulse every 2 hours. So, we'll take on the
  1438. // responsibility
  1439. //
  1440. if ( DomainInfo->DomRole == RoleBackup ) {
  1441. ClientSession = NlRefDomClientSession( DomainInfo );
  1442. if ( ClientSession != NULL ) {
  1443. if ( ClientSession->CsState == CS_IDLE ) {
  1444. //
  1445. // Check if we've tried to authenticate recently.
  1446. // (Don't call NlTimeToReauthenticate with the trust list locked.
  1447. // It locks NlGlobalDcDiscoveryCritSect. That's the wrong locking
  1448. // order.)
  1449. //
  1450. if ( NlTimeToReauthenticate( ClientSession ) ) {
  1451. //
  1452. // Try to pick the DC for the session.
  1453. //
  1454. if ( NlTimeoutSetWriterClientSession( ClientSession, 10*1000 ) ) {
  1455. if ( ClientSession->CsState == CS_IDLE ) {
  1456. //
  1457. // Don't ask for with-account discovery
  1458. // as there is only one PDC
  1459. //
  1460. (VOID) NlDiscoverDc( ClientSession,
  1461. DT_DeadDomain,
  1462. FALSE,
  1463. FALSE ); // don't specify account
  1464. }
  1465. NlResetWriterClientSession( ClientSession );
  1466. }
  1467. }
  1468. }
  1469. NlUnrefClientSession( ClientSession );
  1470. }
  1471. }
  1472. }
  1473. #endif // _DC_NETLOGON
  1474. BOOL
  1475. NlReadSamLogonResponse (
  1476. IN HANDLE ResponseMailslotHandle,
  1477. IN LPWSTR AccountName,
  1478. OUT LPDWORD Opcode,
  1479. OUT LPWSTR *LogonServer,
  1480. OUT PNL_DC_CACHE_ENTRY *NlDcCacheEntry OPTIONAL
  1481. )
  1482. /*++
  1483. Routine Description:
  1484. Read a response from to a SamLogonRequest.
  1485. Arguments:
  1486. ResponseMailslotHandle - Handle of mailslot to read.
  1487. AccountName - Name of the account the response is for.
  1488. Opcode - Returns the opcode from the message. This will be one of
  1489. LOGON_SAM_LOGON_RESPONSE or LOGON_SAM_USER_UNKNOWN.
  1490. LogonServer - Returns the name of the logon server that responded.
  1491. This buffer is only returned if a valid message was received.
  1492. The buffer returned should be freed via NetpMemoryFree.
  1493. NlDcCacheEntry - Returns the data structure describing the response
  1494. received from the server. Should be freed by calling NetpDcDerefCacheEntry.
  1495. Return Value:
  1496. TRUE: a valid message was received.
  1497. FALSE: a valid message was not received.
  1498. --*/
  1499. {
  1500. NET_API_STATUS NetStatus;
  1501. CHAR ResponseBuffer[MAX_RANDOM_MAILSLOT_RESPONSE];
  1502. DWORD SamLogonResponseSize;
  1503. PNL_DC_CACHE_ENTRY NlLocalDcCacheEntry = NULL;
  1504. PCHAR Where;
  1505. DWORD Version;
  1506. DWORD VersionFlags;
  1507. //
  1508. // Loop ignoring responses which are garbled.
  1509. //
  1510. for ( ;; ) {
  1511. //
  1512. // Read the response from the response mailslot
  1513. // (This mailslot is set up with a 5 second timeout).
  1514. //
  1515. if ( !ReadFile( ResponseMailslotHandle,
  1516. ResponseBuffer,
  1517. sizeof(ResponseBuffer),
  1518. &SamLogonResponseSize,
  1519. NULL ) ) {
  1520. IF_NL_DEBUG( MAILSLOT ) {
  1521. NET_API_STATUS NetStatus;
  1522. NetStatus = GetLastError();
  1523. if ( NetStatus != ERROR_SEM_TIMEOUT ) {
  1524. NlPrint((NL_CRITICAL,
  1525. "NlReadSamLogonResponse: cannot read response mailslot: %ld\n",
  1526. NetStatus ));
  1527. }
  1528. }
  1529. return FALSE;
  1530. }
  1531. NlPrint((NL_MAILSLOT_TEXT, "NlReadSamLogonResponse opcode 0x%x\n",
  1532. ((PNETLOGON_SAM_LOGON_RESPONSE)ResponseBuffer)->Opcode ));
  1533. NlpDumpBuffer(NL_MAILSLOT_TEXT, ResponseBuffer, SamLogonResponseSize);
  1534. //
  1535. // Parse the response
  1536. //
  1537. NetStatus = NetpDcParsePingResponse(
  1538. AccountName,
  1539. ResponseBuffer,
  1540. SamLogonResponseSize,
  1541. &NlLocalDcCacheEntry );
  1542. if ( NetStatus != NO_ERROR ) {
  1543. NlPrint((NL_CRITICAL,
  1544. "NlReadSamLogonResponse: can't parse response. %ld\n",
  1545. NetStatus ));
  1546. continue;
  1547. }
  1548. //
  1549. // Ensure the opcode is expected.
  1550. // (Ignore responses from paused DCs, too.)
  1551. //
  1552. if ( NlLocalDcCacheEntry->Opcode != LOGON_SAM_LOGON_RESPONSE &&
  1553. NlLocalDcCacheEntry->Opcode != LOGON_SAM_USER_UNKNOWN ) {
  1554. NlPrint((NL_CRITICAL,
  1555. "NlReadSamLogonResponse: response opcode not valid. 0x%lx\n",
  1556. NlLocalDcCacheEntry->Opcode ));
  1557. //
  1558. // If the user name is missing,
  1559. // ignore the message.
  1560. //
  1561. } else if ( NlLocalDcCacheEntry->UnicodeUserName == NULL ) {
  1562. NlPrint((NL_CRITICAL,
  1563. "NlReadSamLogonResponse: username missing\n" ));
  1564. //
  1565. // If the server name is missing,
  1566. // ignore the message.
  1567. //
  1568. } else if ( NlLocalDcCacheEntry->UnicodeNetbiosDcName == NULL ) {
  1569. NlPrint((NL_CRITICAL,
  1570. "NlReadSamLogonResponse: severname missing\n" ));
  1571. //
  1572. // If the response is for the wrong account,
  1573. // ignore the response.
  1574. //
  1575. } else if ( NlNameCompare( AccountName, NlLocalDcCacheEntry->UnicodeUserName, NAMETYPE_USER) != 0 ) {
  1576. NlPrint((NL_CRITICAL,
  1577. "NlReadSamLogonResponse: User name %ws s.b. %ws.\n",
  1578. NlLocalDcCacheEntry->UnicodeUserName,
  1579. AccountName ));
  1580. //
  1581. // Otherwise use this response.
  1582. //
  1583. } else {
  1584. break;
  1585. }
  1586. NetpDcDerefCacheEntry( NlLocalDcCacheEntry );
  1587. NlLocalDcCacheEntry = NULL;
  1588. }
  1589. //
  1590. // Return the info to the caller.
  1591. //
  1592. *Opcode = NlLocalDcCacheEntry->Opcode;
  1593. *LogonServer = NetpAllocWStrFromWStr( NlLocalDcCacheEntry->UnicodeNetbiosDcName );
  1594. if ( *LogonServer == NULL ) {
  1595. if ( NlLocalDcCacheEntry != NULL ) {
  1596. NetpDcDerefCacheEntry( NlLocalDcCacheEntry );
  1597. }
  1598. return FALSE;
  1599. }
  1600. if ( NlDcCacheEntry != NULL ) {
  1601. *NlDcCacheEntry = NlLocalDcCacheEntry;
  1602. }
  1603. return TRUE;
  1604. }
  1605. NET_API_STATUS
  1606. NlReadRegTrustedDomainList (
  1607. IN PDOMAIN_INFO DomainInfo,
  1608. IN BOOL DeleteName,
  1609. OUT PDS_DOMAIN_TRUSTSW *RetForestTrustList,
  1610. OUT PULONG RetForestTrustListSize,
  1611. OUT PULONG RetForestTrustListCount
  1612. )
  1613. /*++
  1614. Routine Description:
  1615. Read the list of trusted domains from the registry.
  1616. Arguments:
  1617. DomainInfo - Hosted domain of the primary domain
  1618. DeleteName - TRUE if the name is to be deleted upon successful completion.
  1619. RetForestTrustList - Specifies a list of trusted domains.
  1620. This buffer should be free using NetApiBufferFree().
  1621. RetForestTrustListSize - Size (in bytes) of RetForestTrustList
  1622. RetForestTrustListCount - Number of entries in RetForestTrustList
  1623. Return Value:
  1624. None.
  1625. --*/
  1626. {
  1627. NET_API_STATUS NetStatus;
  1628. NTSTATUS Status;
  1629. LPNET_CONFIG_HANDLE SectionHandle = NULL;
  1630. LPTSTR_ARRAY TStrArray;
  1631. LPTSTR_ARRAY TrustedDomainList = NULL;
  1632. BUFFER_DESCRIPTOR BufferDescriptor;
  1633. PDS_DOMAIN_TRUSTSW TrustedDomain;
  1634. ULONG Size;
  1635. //
  1636. // Initialization
  1637. //
  1638. *RetForestTrustList = NULL;
  1639. *RetForestTrustListCount = 0;
  1640. *RetForestTrustListSize = 0;
  1641. BufferDescriptor.Buffer = NULL;
  1642. //
  1643. // The registry doesn't have the PrimaryDomain. (Add it here).
  1644. //
  1645. Status = NlAllocateForestTrustListEntry (
  1646. &BufferDescriptor,
  1647. &DomainInfo->DomUnicodeDomainNameString,
  1648. &DomainInfo->DomUnicodeDnsDomainNameString,
  1649. DS_DOMAIN_PRIMARY,
  1650. 0, // No ParentIndex
  1651. TRUST_TYPE_DOWNLEVEL,
  1652. 0, // No TrustAttributes
  1653. DomainInfo->DomAccountDomainId,
  1654. DomainInfo->DomDomainGuid,
  1655. &Size,
  1656. &TrustedDomain );
  1657. if ( !NT_SUCCESS(Status) ) {
  1658. NetStatus = NetpNtStatusToApiStatus( Status );
  1659. goto Cleanup;
  1660. }
  1661. *RetForestTrustListSize += Size;
  1662. (*RetForestTrustListCount) ++;
  1663. //
  1664. // Open the NetLogon configuration section.
  1665. //
  1666. NetStatus = NetpOpenConfigData(
  1667. &SectionHandle,
  1668. NULL, // no server name.
  1669. SERVICE_NETLOGON,
  1670. !DeleteName ); // Get Write access if deleting.
  1671. if ( NetStatus != NO_ERROR ) {
  1672. NlPrint((NL_CRITICAL,
  1673. "NlReadRegTrustedDomainList: NetpOpenConfigData failed: %ld\n",
  1674. NetStatus ));
  1675. goto Cleanup;
  1676. }
  1677. //
  1678. // Get the "TrustedDomainList" configured parameter
  1679. //
  1680. NetStatus = NetpGetConfigTStrArray (
  1681. SectionHandle,
  1682. NETLOGON_KEYWORD_TRUSTEDDOMAINLIST,
  1683. &TrustedDomainList ); // Must be freed by NetApiBufferFree().
  1684. //
  1685. // Handle the default
  1686. //
  1687. if (NetStatus == NERR_CfgParamNotFound) {
  1688. NetStatus = NO_ERROR;
  1689. TrustedDomainList = NULL;
  1690. goto Cleanup;
  1691. } else if (NetStatus != NO_ERROR) {
  1692. NlPrint((NL_CRITICAL,
  1693. "NlReadRegTrustedDomainList: NetpGetConfigTStrArray failed: %ld\n",
  1694. NetStatus ));
  1695. goto Cleanup;
  1696. }
  1697. //
  1698. // Delete the key if asked to do so
  1699. //
  1700. if ( DeleteName ) {
  1701. NET_API_STATUS TempNetStatus;
  1702. TempNetStatus = NetpDeleteConfigKeyword ( SectionHandle, NETLOGON_KEYWORD_TRUSTEDDOMAINLIST );
  1703. if ( TempNetStatus != NO_ERROR ) {
  1704. NlPrint((NL_CRITICAL,
  1705. "NlReadRegTrustedDomainList: NetpDeleteConfigKeyword failed: %ld\n",
  1706. TempNetStatus ));
  1707. }
  1708. }
  1709. //
  1710. // Handle each trusted domain.
  1711. //
  1712. TStrArray = TrustedDomainList;
  1713. while (!NetpIsTStrArrayEmpty(TStrArray)) {
  1714. UNICODE_STRING CurrentDomain;
  1715. //
  1716. // Add the domain to the list
  1717. //
  1718. RtlInitUnicodeString( &CurrentDomain, TStrArray );
  1719. Status = NlAllocateForestTrustListEntry (
  1720. &BufferDescriptor,
  1721. &CurrentDomain, // Netbios domain name
  1722. NULL, // No DNS domain name
  1723. DS_DOMAIN_DIRECT_OUTBOUND,
  1724. 0, // No ParentIndex
  1725. TRUST_TYPE_DOWNLEVEL,
  1726. 0, // No TrustAttributes
  1727. NULL, // No Domain Sid
  1728. NULL, // No DomainGuid
  1729. &Size,
  1730. &TrustedDomain );
  1731. if ( !NT_SUCCESS(Status) ) {
  1732. NetStatus = NetpNtStatusToApiStatus( Status );
  1733. goto Cleanup;
  1734. }
  1735. //
  1736. // Account for the newly allocated entry
  1737. //
  1738. *RetForestTrustListSize += Size;
  1739. (*RetForestTrustListCount) ++;
  1740. //
  1741. // Move to the next entry
  1742. //
  1743. TStrArray = NetpNextTStrArrayEntry(TStrArray);
  1744. }
  1745. NetStatus = NO_ERROR;
  1746. Cleanup:
  1747. //
  1748. // Return the buffer to the caller.
  1749. //
  1750. if ( NetStatus == NO_ERROR ) {
  1751. *RetForestTrustList = (PDS_DOMAIN_TRUSTSW)BufferDescriptor.Buffer;
  1752. BufferDescriptor.Buffer = NULL;
  1753. }
  1754. if ( TrustedDomainList != NULL ) {
  1755. NetApiBufferFree( TrustedDomainList );
  1756. }
  1757. if ( SectionHandle != NULL ) {
  1758. (VOID) NetpCloseConfigData( SectionHandle );
  1759. }
  1760. if ( BufferDescriptor.Buffer != NULL ) {
  1761. NetApiBufferFree( BufferDescriptor.Buffer );
  1762. }
  1763. return NetStatus;
  1764. }
  1765. NET_API_STATUS
  1766. NlReadFileTrustedDomainList (
  1767. IN PDOMAIN_INFO DomainInfo OPTIONAL,
  1768. IN LPWSTR FileSuffix,
  1769. IN BOOL DeleteName,
  1770. IN ULONG Flags,
  1771. OUT PDS_DOMAIN_TRUSTSW *ForestTrustList,
  1772. OUT PULONG ForestTrustListSize,
  1773. OUT PULONG ForestTrustListCount
  1774. )
  1775. /*++
  1776. Routine Description:
  1777. Read the list of trusted domains from a binary file.
  1778. Arguments:
  1779. DomainInfo - Hosted domain that this machine is a member of
  1780. If not specified, the check to ensure file is for primary domain isn't done.
  1781. FileSuffix - Specifies the name of the file to write (relative to the
  1782. Windows directory)
  1783. DeleteName - TRUE if the name is to be deleted upon successful completion.
  1784. Flags - Specifies attributes of trusts which should be returned. These are the flags
  1785. of the DS_DOMAIN_TRUSTSW strusture. If an entry has any of the bits specified
  1786. in Flags set, it will be returned.
  1787. ForestTrustList - Specifies a list of trusted domains.
  1788. This buffer should be free using NetApiBufferFree().
  1789. ForestTrustListSize - Size (in bytes) of ForestTrustList
  1790. ForestTrustListCount - Number of entries in ForestTrustList
  1791. Return Value:
  1792. None.
  1793. ERROR_NO_SUCH_DOMAIN: Log file isn't for the primary domain.
  1794. ERROR_INTERNAL_DB_CORRUPTION: Log file is corrupted.
  1795. --*/
  1796. {
  1797. NET_API_STATUS NetStatus;
  1798. NTSTATUS Status;
  1799. ULONG RecordBufferSize;
  1800. PDS_DISK_TRUSTED_DOMAIN_HEADER RecordBuffer = NULL;
  1801. LPBYTE RecordBufferEnd;
  1802. PDS_DISK_TRUSTED_DOMAINS LogEntry;
  1803. ULONG CurrentSize;
  1804. BUFFER_DESCRIPTOR BufferDescriptor;
  1805. BOOLEAN PrimaryDomainHandled = FALSE;
  1806. PULONG IndexInReturnedList = NULL;
  1807. ULONG IndexInReturnedListSize = 0;
  1808. ULONG Index = 0;
  1809. ULONG NumberOfFileEntries;
  1810. LPBYTE Where;
  1811. //
  1812. // Initialization
  1813. //
  1814. *ForestTrustListCount = 0;
  1815. *ForestTrustListSize = 0;
  1816. *ForestTrustList = NULL;
  1817. BufferDescriptor.Buffer = NULL;
  1818. //
  1819. // Read the file into a buffer.
  1820. //
  1821. NetStatus = NlReadBinaryLog(
  1822. FileSuffix,
  1823. DeleteName,
  1824. (LPBYTE *) &RecordBuffer,
  1825. &RecordBufferSize );
  1826. if ( NetStatus != NO_ERROR ) {
  1827. NlPrint(( NL_CRITICAL,
  1828. "NlReadFileForestTrustList: error reading binary log: %ld.\n",
  1829. FileSuffix,
  1830. RecordBufferSize ));
  1831. goto Cleanup;
  1832. }
  1833. if ( RecordBufferSize == 0 ) {
  1834. NetStatus = NO_ERROR;
  1835. goto Cleanup;
  1836. }
  1837. //
  1838. // Validate the returned data.
  1839. //
  1840. if ( RecordBufferSize < sizeof(DS_DISK_TRUSTED_DOMAIN_HEADER) ) {
  1841. NlPrint(( NL_CRITICAL,
  1842. "NlReadFileForestTrustList: %ws: size too small: %ld.\n",
  1843. FileSuffix,
  1844. RecordBufferSize ));
  1845. NetStatus = ERROR_INTERNAL_DB_CORRUPTION;
  1846. goto Cleanup;
  1847. }
  1848. if ( RecordBuffer->Version != DS_DISK_TRUSTED_DOMAIN_VERSION ) {
  1849. NlPrint(( NL_CRITICAL,
  1850. "NlReadFileForestTrustList: %ws: Version wrong: %ld.\n",
  1851. FileSuffix,
  1852. RecordBuffer->Version ));
  1853. NetStatus = ERROR_INTERNAL_DB_CORRUPTION;
  1854. goto Cleanup;
  1855. }
  1856. //
  1857. // If domains in forest were requested,
  1858. // allocate an array of ULONGs that will be used to keep track of the
  1859. // index of a trust entry in the returned list. This is needed to
  1860. // corectly set ParentIndex for entries returned.
  1861. //
  1862. if ( Flags & DS_DOMAIN_IN_FOREST ) {
  1863. IndexInReturnedListSize = INDEX_LIST_ALLOCATED_CHUNK_SIZE;
  1864. IndexInReturnedList = LocalAlloc( LMEM_ZEROINIT,
  1865. IndexInReturnedListSize * sizeof(ULONG) );
  1866. if ( IndexInReturnedList == NULL ) {
  1867. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  1868. goto Cleanup;
  1869. }
  1870. }
  1871. //
  1872. // Loop through each log entry.
  1873. //
  1874. RecordBufferEnd = ((LPBYTE)RecordBuffer) + RecordBufferSize;
  1875. LogEntry = (PDS_DISK_TRUSTED_DOMAINS)ROUND_UP_POINTER( (RecordBuffer + 1), ALIGN_WORST );
  1876. while ( (LPBYTE)(LogEntry+1) <= RecordBufferEnd ) {
  1877. PSID DomainSid;
  1878. UNICODE_STRING NetbiosDomainName;
  1879. UNICODE_STRING DnsDomainName;
  1880. LPBYTE LogEntryEnd;
  1881. ULONG Size;
  1882. PDS_DOMAIN_TRUSTSW TrustedDomain;
  1883. LogEntryEnd = ((LPBYTE)LogEntry) + LogEntry->EntrySize;
  1884. //
  1885. // Ensure this entry is entirely within the allocated buffer.
  1886. //
  1887. if ( LogEntryEnd > RecordBufferEnd || LogEntryEnd <= (LPBYTE)LogEntry ) {
  1888. NlPrint(( NL_CRITICAL,
  1889. "NlReadFileForestTrustList: Entry too big or small: %lx %lx.\n",
  1890. ((LPBYTE)LogEntry)-((LPBYTE)RecordBuffer),
  1891. LogEntry->EntrySize ));
  1892. NetStatus = ERROR_INTERNAL_DB_CORRUPTION;
  1893. goto Cleanup;
  1894. }
  1895. //
  1896. // Validate the entry
  1897. //
  1898. if ( !COUNT_IS_ALIGNED(LogEntry->EntrySize, ALIGN_WORST) ) {
  1899. NlPrint(( NL_CRITICAL,
  1900. "NlReadFileForestTrustList: size not aligned %lx.\n",
  1901. LogEntry->EntrySize ));
  1902. NetStatus = ERROR_INTERNAL_DB_CORRUPTION;
  1903. goto Cleanup;
  1904. }
  1905. //
  1906. // Skip this entry if the caller doesn't need it
  1907. //
  1908. if ( (LogEntry->Flags & Flags) == 0 ) {
  1909. LogEntry = (PDS_DISK_TRUSTED_DOMAINS)LogEntryEnd;
  1910. Index++;
  1911. continue;
  1912. }
  1913. //
  1914. // Grab the Sid from the entry.
  1915. //
  1916. Where = (LPBYTE) (LogEntry+1);
  1917. if ( LogEntry->DomainSidSize ) {
  1918. ULONG DomainSidSize;
  1919. if ( Where + sizeof(SID) > LogEntryEnd ) {
  1920. NlPrint(( NL_CRITICAL,
  1921. "NlReadFileForestTrustList: DomainSid missing (A): %lx\n",
  1922. ((LPBYTE)LogEntry)-((LPBYTE)RecordBuffer) ));
  1923. NetStatus = ERROR_INTERNAL_DB_CORRUPTION;
  1924. goto Cleanup;
  1925. }
  1926. if ( Where + LogEntry->DomainSidSize > LogEntryEnd ) {
  1927. NlPrint(( NL_CRITICAL,
  1928. "NlReadFileForestTrustList: DomainSid missing (B): %lx\n",
  1929. ((LPBYTE)LogEntry)-((LPBYTE)RecordBuffer) ));
  1930. NetStatus = ERROR_INTERNAL_DB_CORRUPTION;
  1931. goto Cleanup;
  1932. }
  1933. DomainSid = Where;
  1934. DomainSidSize = RtlLengthSid( DomainSid );
  1935. if ( LogEntry->DomainSidSize != DomainSidSize ) {
  1936. NlPrint(( NL_CRITICAL,
  1937. "NlReadFileForestTrustList: DomainSidSize mismatch: %ld %ld\n",
  1938. LogEntry->DomainSidSize,
  1939. DomainSidSize ));
  1940. NetStatus = ERROR_INTERNAL_DB_CORRUPTION;
  1941. goto Cleanup;
  1942. }
  1943. Where += DomainSidSize;
  1944. }
  1945. //
  1946. // Grab the NetbiosDomainName from the entry
  1947. //
  1948. if ( LogEntry->NetbiosDomainNameSize ) {
  1949. if ( Where + LogEntry->NetbiosDomainNameSize > LogEntryEnd ) {
  1950. NlPrint(( NL_CRITICAL,
  1951. "NlReadFileForestTrustList: NetbiosDomainName missing: %lx\n",
  1952. ((LPBYTE)LogEntry)-((LPBYTE)RecordBuffer) ));
  1953. NetStatus = ERROR_INTERNAL_DB_CORRUPTION;
  1954. goto Cleanup;
  1955. }
  1956. if ( !COUNT_IS_ALIGNED( LogEntry->NetbiosDomainNameSize, ALIGN_WCHAR) ) {
  1957. NlPrint(( NL_CRITICAL,
  1958. "NlReadFileForestTrustList: NetbiosDomainNameSize not aligned: %ld %lx\n",
  1959. LogEntry->NetbiosDomainNameSize,
  1960. ((LPBYTE)LogEntry)-((LPBYTE)RecordBuffer) ));
  1961. NetStatus = ERROR_INTERNAL_DB_CORRUPTION;
  1962. goto Cleanup;
  1963. }
  1964. NetbiosDomainName.Buffer = (LPWSTR) Where;
  1965. if ( NetbiosDomainName.Buffer[(LogEntry->NetbiosDomainNameSize/sizeof(WCHAR))-1] != L'\0' ) {
  1966. NlPrint(( NL_CRITICAL,
  1967. "NlReadFileForestTrustList: NetbiosDomainName not zero terminated: %lx\n",
  1968. ((LPBYTE)LogEntry)-((LPBYTE)RecordBuffer) ));
  1969. NetStatus = ERROR_INTERNAL_DB_CORRUPTION;
  1970. goto Cleanup;
  1971. }
  1972. Where += LogEntry->NetbiosDomainNameSize;
  1973. }
  1974. //
  1975. // Grab the DnsDomainName from the entry
  1976. //
  1977. if ( LogEntry->DnsDomainNameSize ) {
  1978. if ( Where + LogEntry->DnsDomainNameSize > LogEntryEnd ) {
  1979. NlPrint(( NL_CRITICAL,
  1980. "NlReadFileForestTrustList: DnsDomainName missing: %lx\n",
  1981. ((LPBYTE)LogEntry)-((LPBYTE)RecordBuffer) ));
  1982. NetStatus = ERROR_INTERNAL_DB_CORRUPTION;
  1983. goto Cleanup;
  1984. }
  1985. if ( !COUNT_IS_ALIGNED( LogEntry->DnsDomainNameSize, ALIGN_WCHAR) ) {
  1986. NlPrint(( NL_CRITICAL,
  1987. "NlReadFileForestTrustList: DnsDomainNameSize not aligned: %ld %lx\n",
  1988. LogEntry->DnsDomainNameSize,
  1989. ((LPBYTE)LogEntry)-((LPBYTE)RecordBuffer) ));
  1990. NetStatus = ERROR_INTERNAL_DB_CORRUPTION;
  1991. goto Cleanup;
  1992. }
  1993. DnsDomainName.Buffer = (LPWSTR) Where;
  1994. if ( DnsDomainName.Buffer[(LogEntry->DnsDomainNameSize/sizeof(WCHAR))-1] != L'\0' ) {
  1995. NlPrint(( NL_CRITICAL,
  1996. "NlReadFileForestTrustList: DnsDomainName not zero terminated: %lx\n",
  1997. ((LPBYTE)LogEntry)-((LPBYTE)RecordBuffer) ));
  1998. NetStatus = ERROR_INTERNAL_DB_CORRUPTION;
  1999. goto Cleanup;
  2000. }
  2001. Where += LogEntry->DnsDomainNameSize;
  2002. }
  2003. //
  2004. // Put this entry into the buffer.
  2005. //
  2006. NetbiosDomainName.Length =
  2007. NetbiosDomainName.MaximumLength = (USHORT) LogEntry->NetbiosDomainNameSize;
  2008. DnsDomainName.Length =
  2009. DnsDomainName.MaximumLength = (USHORT) LogEntry->DnsDomainNameSize;
  2010. Status = NlAllocateForestTrustListEntry (
  2011. &BufferDescriptor,
  2012. &NetbiosDomainName,
  2013. &DnsDomainName,
  2014. LogEntry->Flags,
  2015. LogEntry->ParentIndex,
  2016. LogEntry->TrustType,
  2017. LogEntry->TrustAttributes,
  2018. LogEntry->DomainSidSize ?
  2019. DomainSid :
  2020. NULL,
  2021. &LogEntry->DomainGuid,
  2022. &Size,
  2023. &TrustedDomain );
  2024. if ( !NT_SUCCESS(Status) ) {
  2025. NlPrint(( NL_CRITICAL,
  2026. "NlReadFileForestTrustList: Cannot allocate entry %lx\n",
  2027. Status ));
  2028. NetStatus = NetpNtStatusToApiStatus( Status );
  2029. goto Cleanup;
  2030. }
  2031. //
  2032. // If domains in forest were requested,
  2033. // remember the index of this entry in the returned list.
  2034. // Allocate more memory for IndexInReturnedList as needed.
  2035. //
  2036. if ( Flags & DS_DOMAIN_IN_FOREST ) {
  2037. if ( Index >= IndexInReturnedListSize ) {
  2038. PULONG TmpIndexInReturnedList = NULL;
  2039. IndexInReturnedListSize = Index;
  2040. IndexInReturnedListSize += INDEX_LIST_ALLOCATED_CHUNK_SIZE;
  2041. TmpIndexInReturnedList = LocalReAlloc( IndexInReturnedList,
  2042. IndexInReturnedListSize * sizeof(ULONG),
  2043. LMEM_ZEROINIT | LMEM_MOVEABLE );
  2044. if ( TmpIndexInReturnedList == NULL ) {
  2045. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  2046. goto Cleanup;
  2047. }
  2048. IndexInReturnedList = TmpIndexInReturnedList;
  2049. }
  2050. IndexInReturnedList[Index] = *ForestTrustListCount;
  2051. }
  2052. //
  2053. // Account for the newly allocated entry
  2054. //
  2055. *ForestTrustListSize += Size;
  2056. (*ForestTrustListCount) ++;
  2057. //
  2058. // If this entry describes the primary domain,
  2059. // make sure that this log is for the right primary domain.
  2060. //
  2061. if ( TrustedDomain->Flags & DS_DOMAIN_PRIMARY ) {
  2062. //
  2063. // Ensure there is only one primary domain entry.
  2064. //
  2065. if ( PrimaryDomainHandled ) {
  2066. NlPrint(( NL_CRITICAL,
  2067. "NlReadFileForestTrustList: %ws: Duplicate primary domain entry: %ws %ws %lx\n",
  2068. FileSuffix,
  2069. TrustedDomain->NetbiosDomainName,
  2070. TrustedDomain->DnsDomainName,
  2071. ((LPBYTE)LogEntry)-((LPBYTE)RecordBuffer) ));
  2072. }
  2073. PrimaryDomainHandled = TRUE;
  2074. //
  2075. // If the domain names are different,
  2076. // disregard this log file.
  2077. //
  2078. if ( DomainInfo != NULL ) {
  2079. if ( ( TrustedDomain->NetbiosDomainName != NULL &&
  2080. NlNameCompare( TrustedDomain->NetbiosDomainName,
  2081. DomainInfo->DomUnicodeDomainName,
  2082. NAMETYPE_DOMAIN ) != 0 ) ||
  2083. ( TrustedDomain->DnsDomainName != NULL &&
  2084. DomainInfo->DomUnicodeDnsDomainName != NULL &&
  2085. !NlEqualDnsName( TrustedDomain->DnsDomainName,
  2086. DomainInfo->DomUnicodeDnsDomainName ) ) ) {
  2087. NlPrint(( NL_CRITICAL,
  2088. "NlReadFileForestTrustList: %ws: Log file isn't for primary domain: %ws %ws\n",
  2089. FileSuffix,
  2090. TrustedDomain->NetbiosDomainName,
  2091. TrustedDomain->DnsDomainName ));
  2092. NetStatus = ERROR_NO_SUCH_DOMAIN;
  2093. goto Cleanup;
  2094. }
  2095. }
  2096. }
  2097. //
  2098. // Move to the next entry.
  2099. //
  2100. LogEntry = (PDS_DISK_TRUSTED_DOMAINS)LogEntryEnd;
  2101. Index++;
  2102. }
  2103. NumberOfFileEntries = Index;
  2104. if ( !PrimaryDomainHandled ) {
  2105. NlPrint(( NL_CRITICAL,
  2106. "NlReadFileForestTrustList: %ws: No primary domain record in Log file\n",
  2107. FileSuffix ));
  2108. }
  2109. *ForestTrustList = (PDS_DOMAIN_TRUSTSW) BufferDescriptor.Buffer;
  2110. //
  2111. // Fix ParentIndex. If domains in the forest are requested,
  2112. // adjust the index to point to the appropriate entry in the
  2113. // returned list. Otherwise, set the index to 0.
  2114. //
  2115. if ( Flags & DS_DOMAIN_IN_FOREST ) {
  2116. ULONG ParentIndex;
  2117. ULONG ParentIndexInReturnedList;
  2118. for ( Index=0; Index<*ForestTrustListCount; Index++ ) {
  2119. if ( ((*ForestTrustList)[Index].Flags & DS_DOMAIN_IN_FOREST) != 0 &&
  2120. ((*ForestTrustList)[Index].Flags & DS_DOMAIN_TREE_ROOT) == 0 ) {
  2121. ParentIndex = (*ForestTrustList)[Index].ParentIndex;
  2122. //
  2123. // Check if the parent index is out of range. If so, the file is corrupted.
  2124. //
  2125. if ( ParentIndex >= NumberOfFileEntries ||
  2126. ParentIndex >= IndexInReturnedListSize ) {
  2127. NlPrint(( NL_CRITICAL,
  2128. "NlReadFileForestTrustList: ParentIndex %lu is out of range %lu\n",
  2129. ParentIndex, NumberOfFileEntries ));
  2130. NetStatus = ERROR_INTERNAL_DB_CORRUPTION;
  2131. goto Cleanup;
  2132. }
  2133. ParentIndexInReturnedList = IndexInReturnedList[ParentIndex];
  2134. //
  2135. // Check if the returned list entry pointed to by the parent index is
  2136. // in forest. If not, the file is corrupted.
  2137. //
  2138. if ( (*ForestTrustList)[ParentIndexInReturnedList].Flags & DS_DOMAIN_IN_FOREST ) {
  2139. (*ForestTrustList)[Index].ParentIndex = ParentIndexInReturnedList;
  2140. } else {
  2141. NlPrint(( NL_CRITICAL,
  2142. "NlReadFileForestTrustList: ReturnedList entry %lu is not in the forest\n",
  2143. ParentIndexInReturnedList ));
  2144. NetStatus = ERROR_INTERNAL_DB_CORRUPTION;
  2145. goto Cleanup;
  2146. }
  2147. }
  2148. }
  2149. } else {
  2150. for ( Index=0; Index<*ForestTrustListCount; Index++ ) {
  2151. (*ForestTrustList)[Index].ParentIndex = 0;
  2152. }
  2153. }
  2154. BufferDescriptor.Buffer = NULL;
  2155. NetStatus = NO_ERROR;
  2156. //
  2157. // Free any locally used resources.
  2158. //
  2159. Cleanup:
  2160. if ( BufferDescriptor.Buffer != NULL ) {
  2161. NetApiBufferFree( BufferDescriptor.Buffer );
  2162. *ForestTrustListCount = 0;
  2163. *ForestTrustListSize = 0;
  2164. *ForestTrustList = NULL;
  2165. }
  2166. if ( IndexInReturnedList != NULL ) {
  2167. LocalFree( IndexInReturnedList );
  2168. }
  2169. if ( RecordBuffer != NULL ) {
  2170. LocalFree( RecordBuffer );
  2171. }
  2172. if ( *ForestTrustList == NULL ) {
  2173. *ForestTrustListCount = 0;
  2174. *ForestTrustListSize = 0;
  2175. }
  2176. return NetStatus;
  2177. }
  2178. NTSTATUS
  2179. NlUpdatePrimaryDomainInfo(
  2180. IN LSAPR_HANDLE PolicyHandle,
  2181. IN PUNICODE_STRING NetbiosDomainName,
  2182. IN PUNICODE_STRING DnsDomainName,
  2183. IN PUNICODE_STRING DnsForestName,
  2184. IN GUID *DomainGuid
  2185. )
  2186. /*++
  2187. Routine Description:
  2188. This routine sets the DnsDomainName, DnsForestName and DomainGuid in the LSA.
  2189. Arguments:
  2190. PolicyHandle - A trusted policy handle open to the LSA.
  2191. NetbiosDomainName - Specifies the Netbios domain name of the primary domain.
  2192. DnsDomainName - Specifies the DNS domain name of the primary domain.
  2193. DnsForestName - Specifies the DNS tree name the primary domain belongs to.
  2194. DomainGuid - Specifies the GUID of the primary domain.
  2195. Return Value:
  2196. Status of the operation.
  2197. --*/
  2198. {
  2199. NTSTATUS Status;
  2200. PLSAPR_POLICY_INFORMATION OldPrimaryDomainInfo = NULL;
  2201. LSAPR_POLICY_INFORMATION NewPrimaryDomainInfo;
  2202. BOOL SomethingChanged = FALSE;
  2203. //
  2204. // Get the Primary Domain info from the LSA.
  2205. //
  2206. NlPrint((NL_DOMAIN,
  2207. "Setting LSA NetbiosDomain: %wZ DnsDomain: %wZ DnsTree: %wZ DomainGuid:",
  2208. NetbiosDomainName,
  2209. DnsDomainName,
  2210. DnsForestName ));
  2211. NlpDumpGuid( NL_DOMAIN, DomainGuid );
  2212. NlPrint(( NL_DOMAIN, "\n" ));
  2213. Status = LsarQueryInformationPolicy(
  2214. PolicyHandle,
  2215. PolicyDnsDomainInformation,
  2216. &OldPrimaryDomainInfo );
  2217. if ( !NT_SUCCESS(Status) ) {
  2218. goto Cleanup;
  2219. }
  2220. //
  2221. // Initialize the new policy to equal the old policy.
  2222. //
  2223. NewPrimaryDomainInfo.PolicyDnsDomainInfo = OldPrimaryDomainInfo->PolicyDnsDomainInfo;
  2224. //
  2225. // If Netbios domain name changed,
  2226. // update it.
  2227. //
  2228. if ( NetbiosDomainName->Length != 0 ) {
  2229. if ( NewPrimaryDomainInfo.PolicyDnsDomainInfo.Name.Length == 0 ||
  2230. !RtlEqualDomainName(NetbiosDomainName,
  2231. (PUNICODE_STRING)&NewPrimaryDomainInfo.PolicyDnsDomainInfo.Name) ) {
  2232. NlPrint(( NL_DOMAIN,
  2233. " NetbiosDomain changed from %wZ to %wZ\n",
  2234. &NewPrimaryDomainInfo.PolicyDnsDomainInfo.Name,
  2235. NetbiosDomainName ));
  2236. NewPrimaryDomainInfo.PolicyDnsDomainInfo.Name = *((LSAPR_UNICODE_STRING*)NetbiosDomainName);
  2237. SomethingChanged = TRUE;
  2238. }
  2239. }
  2240. //
  2241. // If the DnsDomainName has changed,
  2242. // udpate it.
  2243. //
  2244. if ( !NlEqualDnsNameU(DnsDomainName,
  2245. (PUNICODE_STRING)&NewPrimaryDomainInfo.PolicyDnsDomainInfo.DnsDomainName )) {
  2246. NlPrint((NL_DOMAIN,
  2247. " DnsDomain changed from %wZ to %wZ\n",
  2248. &NewPrimaryDomainInfo.PolicyDnsDomainInfo.DnsDomainName,
  2249. DnsDomainName ));
  2250. NewPrimaryDomainInfo.PolicyDnsDomainInfo.DnsDomainName =
  2251. *((LSAPR_UNICODE_STRING*)DnsDomainName);
  2252. // Truncate the trailing .
  2253. if ( NewPrimaryDomainInfo.PolicyDnsDomainInfo.DnsDomainName.Length > 0 &&
  2254. NewPrimaryDomainInfo.PolicyDnsDomainInfo.DnsDomainName.Buffer[
  2255. NewPrimaryDomainInfo.PolicyDnsDomainInfo.DnsDomainName.Length-1] == L'.' ) {
  2256. NlPrint((NL_DOMAIN,
  2257. " Ditch the dot. DnsDomain changed from %wZ to %wZ\n",
  2258. &NewPrimaryDomainInfo.PolicyDnsDomainInfo.DnsDomainName,
  2259. DnsDomainName ));
  2260. NewPrimaryDomainInfo.PolicyDnsDomainInfo.DnsDomainName.Length --;
  2261. }
  2262. SomethingChanged = TRUE;
  2263. }
  2264. //
  2265. // If the DnsForestName has changed,
  2266. // udpate it.
  2267. //
  2268. if ( !NlEqualDnsNameU( DnsForestName,
  2269. (PUNICODE_STRING)&NewPrimaryDomainInfo.PolicyDnsDomainInfo.DnsForestName ) ) {
  2270. NlPrint((NL_DOMAIN,
  2271. " DnsTree changed from %wZ to %wZ\n",
  2272. &NewPrimaryDomainInfo.PolicyDnsDomainInfo.DnsForestName,
  2273. DnsForestName ));
  2274. NewPrimaryDomainInfo.PolicyDnsDomainInfo.DnsForestName =
  2275. *((LSAPR_UNICODE_STRING*)DnsForestName);
  2276. SomethingChanged = TRUE;
  2277. }
  2278. //
  2279. // If the DomainGuid has changed,
  2280. // udpate it.
  2281. //
  2282. if ( !IsEqualGUID(DomainGuid,
  2283. &NewPrimaryDomainInfo.PolicyDnsDomainInfo.DomainGuid )) {
  2284. NlPrint((NL_DOMAIN,
  2285. " DomainGuid changed from " ));
  2286. NlpDumpGuid( NL_DOMAIN, &NewPrimaryDomainInfo.PolicyDnsDomainInfo.DomainGuid );
  2287. NlPrint((NL_DOMAIN,
  2288. " to " ));
  2289. NlpDumpGuid( NL_DOMAIN, DomainGuid );
  2290. NlPrint((NL_DOMAIN,
  2291. "\n" ));
  2292. NewPrimaryDomainInfo.PolicyDnsDomainInfo.DomainGuid = *DomainGuid;
  2293. SomethingChanged = TRUE;
  2294. }
  2295. //
  2296. // Only update the LSA if something has really changed.
  2297. //
  2298. if ( SomethingChanged ) {
  2299. Status = LsarSetInformationPolicy(
  2300. PolicyHandle,
  2301. PolicyDnsDomainInformation,
  2302. &NewPrimaryDomainInfo );
  2303. if ( !NT_SUCCESS(Status) ) {
  2304. NlPrint((NL_CRITICAL,
  2305. "NlUpdatePrimaryDomainInfo: Cannot LsarSetInformationPolicy 0x%lx\n",
  2306. Status ));
  2307. goto Cleanup;
  2308. }
  2309. }
  2310. Status = STATUS_SUCCESS;
  2311. //
  2312. // Return
  2313. //
  2314. Cleanup:
  2315. if ( OldPrimaryDomainInfo != NULL ) {
  2316. LsaIFree_LSAPR_POLICY_INFORMATION(
  2317. PolicyDnsDomainInformation,
  2318. OldPrimaryDomainInfo );
  2319. }
  2320. return Status;
  2321. }
  2322. NTSTATUS
  2323. NlUpdateDomainInfo(
  2324. IN PCLIENT_SESSION ClientSession
  2325. )
  2326. /*++
  2327. Routine Description:
  2328. Gets the domain information from a DC in the domain and updates that
  2329. information on this workstation.
  2330. Note: this routine is called from NlSessionSetup. When called from outside
  2331. NlSessionSetup, the caller should call this routine directly if the
  2332. session is already setup. Otherwise, the caller should simply setup
  2333. the session and rely on the fact the NlSessionSetup called this routine
  2334. as a side effect.
  2335. Arguments:
  2336. ClientSession - Structure used to define the session.
  2337. The caller must be a writer of the ClientSession.
  2338. Return Value:
  2339. Status of the operation.
  2340. --*/
  2341. {
  2342. NTSTATUS Status;
  2343. NET_API_STATUS NetStatus;
  2344. NETLOGON_AUTHENTICATOR OurAuthenticator;
  2345. NETLOGON_AUTHENTICATOR ReturnAuthenticator;
  2346. PNETLOGON_DOMAIN_INFO NetlogonDomainInfo = NULL;
  2347. NETLOGON_WORKSTATION_INFO NetlogonWorkstationInfo;
  2348. OSVERSIONINFOEXW OsVersionInfoEx;
  2349. WCHAR LocalDnsDomainName[NL_MAX_DNS_LENGTH+1];
  2350. WCHAR LocalNetbiosDomainName[DNLEN+1];
  2351. WCHAR CapturedSiteName[NL_MAX_DNS_LABEL_LENGTH+1];
  2352. SESSION_INFO SessionInfo;
  2353. GUID *NewGuid;
  2354. ULONG i;
  2355. PDS_DOMAIN_TRUSTSW ForestTrustList = NULL;
  2356. ULONG ForestTrustListSize;
  2357. ULONG ForestTrustListCount;
  2358. LPBYTE Where;
  2359. //
  2360. // Initialization.
  2361. //
  2362. NlAssert( ClientSession->CsReferenceCount > 0 );
  2363. NlAssert( ClientSession->CsFlags & CS_WRITER );
  2364. RtlZeroMemory( &NetlogonWorkstationInfo, sizeof(NetlogonWorkstationInfo) );
  2365. SessionInfo.SessionKey = ClientSession->CsSessionKey;
  2366. SessionInfo.NegotiatedFlags = ClientSession->CsNegotiatedFlags;
  2367. //
  2368. // If we are talking to a DC that doesn't support I_NetLogonGetDomainInfo,
  2369. // do things in an NT 4.0 compatible way.
  2370. //
  2371. if (!(SessionInfo.NegotiatedFlags & NETLOGON_SUPPORTS_GET_DOMAIN_INFO )) {
  2372. //
  2373. // Get the trusted domain list from the discovered DC using the NT 4
  2374. // protocol.
  2375. //
  2376. Status = NlGetNt4TrustedDomainList (
  2377. ClientSession->CsUncServerName,
  2378. &ClientSession->CsDomainInfo->DomUnicodeDomainNameString,
  2379. &ClientSession->CsDomainInfo->DomUnicodeDnsDomainNameString,
  2380. ClientSession->CsDomainInfo->DomAccountDomainId,
  2381. ClientSession->CsDomainInfo->DomDomainGuid,
  2382. &ForestTrustList,
  2383. &ForestTrustListSize,
  2384. &ForestTrustListCount );
  2385. //
  2386. // If we failed, error out.
  2387. //
  2388. // Special case the access denied which is probably
  2389. // because LSA ACLs are tightened on the NT4.0 DC.
  2390. // We don't want to fail the secure channel setup
  2391. // in NlSessionSetup because of this. The other
  2392. // place where this routine is called is
  2393. // DsrEnumerateDomainTrusts which will return the
  2394. // trust list cached at the join time and will
  2395. // ignore the failure to update the trust list here.
  2396. //
  2397. if ( !NT_SUCCESS(Status) ) {
  2398. if ( Status == STATUS_ACCESS_DENIED ) {
  2399. Status = STATUS_SUCCESS;
  2400. }
  2401. return Status;
  2402. }
  2403. //
  2404. // Otherwise, fall through and update the
  2405. // forest trust list
  2406. //
  2407. goto Cleanup;
  2408. }
  2409. //
  2410. // Sanity check that the secure channel is really up.
  2411. //
  2412. if ( ClientSession->CsState == CS_IDLE ) {
  2413. Status = ClientSession->CsConnectionStatus;
  2414. NlPrintCs((NL_CRITICAL, ClientSession,
  2415. "NlUpdateDomainInfo: Secure channel is down %lx\n",
  2416. Status ));
  2417. goto Cleanup;
  2418. }
  2419. //
  2420. // Tell the DC that we're not interested in LSA policy.
  2421. // (We only did LSA policy for NT 5.0 beta 1.)
  2422. //
  2423. NetlogonWorkstationInfo.LsaPolicy.LsaPolicySize = 0;
  2424. NetlogonWorkstationInfo.LsaPolicy.LsaPolicy = NULL;
  2425. //
  2426. // Fill in data the DC needs to know about this workstation.
  2427. //
  2428. if ( NlCaptureSiteName( CapturedSiteName ) ) {
  2429. NetlogonWorkstationInfo.SiteName = CapturedSiteName;
  2430. }
  2431. NetlogonWorkstationInfo.DnsHostName =
  2432. ClientSession->CsDomainInfo->DomUnicodeDnsHostNameString.Buffer,
  2433. //
  2434. // Fill in the OS Version of this machine.
  2435. //
  2436. OsVersionInfoEx.dwOSVersionInfoSize = sizeof(OsVersionInfoEx);
  2437. if ( GetVersionEx( (POSVERSIONINFO)&OsVersionInfoEx) ) {
  2438. NetlogonWorkstationInfo.OsVersion.MaximumLength =
  2439. NetlogonWorkstationInfo.OsVersion.Length = sizeof(OsVersionInfoEx);
  2440. NetlogonWorkstationInfo.OsVersion.Buffer = (WCHAR *) &OsVersionInfoEx;
  2441. if ( OsVersionInfoEx.wProductType == VER_NT_WORKSTATION ) {
  2442. RtlInitUnicodeString( &NetlogonWorkstationInfo.OsName,
  2443. L"Windows XP Professional" );
  2444. } else {
  2445. RtlInitUnicodeString( &NetlogonWorkstationInfo.OsName,
  2446. L"Windows .NET Server" );
  2447. }
  2448. } else {
  2449. RtlInitUnicodeString( &NetlogonWorkstationInfo.OsName,
  2450. L"Windows XP" );
  2451. }
  2452. //
  2453. // Ask for both trusted and trusting domains to be returned
  2454. //
  2455. NetlogonWorkstationInfo.WorkstationFlags |= NL_NEED_BIDIRECTIONAL_TRUSTS;
  2456. NetlogonWorkstationInfo.WorkstationFlags |= NL_CLIENT_HANDLES_SPN;
  2457. //
  2458. // Build the Authenticator for this request on the secure channel
  2459. //
  2460. NlBuildAuthenticator(
  2461. &ClientSession->CsAuthenticationSeed,
  2462. &ClientSession->CsSessionKey,
  2463. &OurAuthenticator );
  2464. //
  2465. // Make the request across the secure channel.
  2466. //
  2467. NL_API_START( Status, ClientSession, TRUE ) {
  2468. Status = I_NetLogonGetDomainInfo(
  2469. ClientSession->CsUncServerName,
  2470. ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
  2471. &OurAuthenticator,
  2472. &ReturnAuthenticator,
  2473. NETLOGON_QUERY_DOMAIN_INFO,
  2474. (LPBYTE) &NetlogonWorkstationInfo,
  2475. (LPBYTE *) &NetlogonDomainInfo );
  2476. // NOTE: This call may drop the secure channel behind our back
  2477. } NL_API_ELSE( Status, ClientSession, TRUE ) {
  2478. //
  2479. // We might have been called from NlSessionSetup,
  2480. // So we have to indicate the failure to our caller.
  2481. //
  2482. if ( NT_SUCCESS(Status) ) {
  2483. Status = ClientSession->CsConnectionStatus;
  2484. goto Cleanup;
  2485. }
  2486. } NL_API_END;
  2487. //
  2488. // Verify authenticator of the server on the other side and update our seed.
  2489. //
  2490. // If the server denied access or the server's authenticator is wrong,
  2491. // Force a re-authentication.
  2492. //
  2493. //
  2494. if ( NlpDidDcFail( Status ) ||
  2495. !NlUpdateSeed(
  2496. &ClientSession->CsAuthenticationSeed,
  2497. &ReturnAuthenticator.Credential,
  2498. &ClientSession->CsSessionKey) ) {
  2499. NlPrintCs(( NL_CRITICAL, ClientSession,
  2500. "NlUpdateDomainInfo: denying access after status: 0x%lx\n",
  2501. Status ));
  2502. //
  2503. // Preserve any status indicating a communication error.
  2504. //
  2505. if ( NT_SUCCESS(Status) ) {
  2506. Status = STATUS_ACCESS_DENIED;
  2507. }
  2508. goto Cleanup;
  2509. }
  2510. if ( !NT_SUCCESS(Status) ) {
  2511. goto Cleanup;
  2512. }
  2513. //
  2514. // Normalize the GUID
  2515. //
  2516. if ( !IsEqualGUID( &NetlogonDomainInfo->PrimaryDomain.DomainGuid,
  2517. &NlGlobalZeroGuid ) ) {
  2518. NewGuid = &NetlogonDomainInfo->PrimaryDomain.DomainGuid;
  2519. } else {
  2520. NewGuid = NULL;
  2521. }
  2522. //
  2523. // If the DNS domain name is different than the one we already have,
  2524. // update the one we already have.
  2525. //
  2526. // This allows for DNS domain renaming and for picking up the DNS name from
  2527. // and NT 5 DC once it is upgraded (to NT 5 or to supporting DNS).
  2528. //
  2529. if ( NetlogonDomainInfo->PrimaryDomain.DnsDomainName.Length < sizeof(LocalDnsDomainName) &&
  2530. NetlogonDomainInfo->PrimaryDomain.DomainName.Length < sizeof(LocalNetbiosDomainName) ) {
  2531. BOOLEAN DnsDomainNameWasChanged = FALSE;
  2532. RtlCopyMemory( LocalDnsDomainName,
  2533. NetlogonDomainInfo->PrimaryDomain.DnsDomainName.Buffer,
  2534. NetlogonDomainInfo->PrimaryDomain.DnsDomainName.Length );
  2535. LocalDnsDomainName[
  2536. NetlogonDomainInfo->PrimaryDomain.DnsDomainName.Length/sizeof(WCHAR)] = L'\0';
  2537. RtlCopyMemory( LocalNetbiosDomainName,
  2538. NetlogonDomainInfo->PrimaryDomain.DomainName.Buffer,
  2539. NetlogonDomainInfo->PrimaryDomain.DomainName.Length );
  2540. LocalNetbiosDomainName[
  2541. NetlogonDomainInfo->PrimaryDomain.DomainName.Length/sizeof(WCHAR)] = L'\0';
  2542. NetStatus = NlSetDomainNameInDomainInfo(
  2543. ClientSession->CsDomainInfo,
  2544. LocalDnsDomainName,
  2545. LocalNetbiosDomainName,
  2546. NewGuid,
  2547. &DnsDomainNameWasChanged,
  2548. NULL,
  2549. NULL );
  2550. if ( NetStatus != NO_ERROR ) {
  2551. NlPrintCs((NL_CRITICAL, ClientSession,
  2552. "NlUpdateDomainInfo: Can't NlSetDnsDomainDomainInfo %ld\n",
  2553. NetStatus ));
  2554. Status = NetpApiStatusToNtStatus( NetStatus );
  2555. goto Cleanup;
  2556. }
  2557. //
  2558. // Change the computer name as required. The new name will take effect next time
  2559. // the computer reboots. An error here is not fatal.
  2560. //
  2561. if ( DnsDomainNameWasChanged && LocalDnsDomainName != NULL ) {
  2562. if ( NERR_Success != NetpSetDnsComputerNameAsRequired( LocalDnsDomainName ) ) {
  2563. NlPrintCs((NL_CRITICAL, ClientSession,
  2564. "NlUpdateDomainInfo: Can't NetpSetDnsComputerNameAsRequired %ld\n",
  2565. NetStatus ));
  2566. } else {
  2567. NlPrintCs((NL_MISC, ClientSession,
  2568. "NlUpdateDomainInfo: Successfully set computer name with suffix %ws\n",
  2569. LocalDnsDomainName ));
  2570. }
  2571. }
  2572. }
  2573. //
  2574. // Save the new tree name.
  2575. //
  2576. NetStatus = NlSetDnsForestName( &NetlogonDomainInfo->PrimaryDomain.DnsForestName, NULL );
  2577. if ( NetStatus != NO_ERROR ) {
  2578. NlPrintCs((NL_CRITICAL, ClientSession,
  2579. "NlUpdateDomainInfo: Can't NlSetDnsForestName %ld\n",
  2580. NetStatus ));
  2581. Status = NetpApiStatusToNtStatus( NetStatus );
  2582. goto Cleanup;
  2583. }
  2584. //
  2585. // Update the Dns Domain Name, Dns Tree Name and Domain GUID in the LSA
  2586. //
  2587. Status = NlUpdatePrimaryDomainInfo(
  2588. ClientSession->CsDomainInfo->DomLsaPolicyHandle,
  2589. &NetlogonDomainInfo->PrimaryDomain.DomainName,
  2590. &NetlogonDomainInfo->PrimaryDomain.DnsDomainName,
  2591. &NetlogonDomainInfo->PrimaryDomain.DnsForestName,
  2592. &NetlogonDomainInfo->PrimaryDomain.DomainGuid );
  2593. if ( !NT_SUCCESS(Status) ) {
  2594. NlPrintCs((NL_CRITICAL, ClientSession,
  2595. "NlUpdateDomainInfo: Can't NlUpdatePrimaryDomainInfo 0x%lx\n",
  2596. Status ));
  2597. goto Cleanup;
  2598. }
  2599. //
  2600. // Determine the size of forest trust info returned on this call.
  2601. //
  2602. ForestTrustListSize = 0;
  2603. for ( i=0; i<NetlogonDomainInfo->TrustedDomainCount; i++ ) {
  2604. ForestTrustListSize += sizeof(DS_DOMAIN_TRUSTSW) +
  2605. NetlogonDomainInfo->TrustedDomains[i].DomainName.Length + sizeof(WCHAR) +
  2606. NetlogonDomainInfo->TrustedDomains[i].DnsDomainName.Length + sizeof(WCHAR);
  2607. if ( NetlogonDomainInfo->TrustedDomains[i].DomainSid != NULL ) {
  2608. ForestTrustListSize += RtlLengthSid( NetlogonDomainInfo->TrustedDomains[i].DomainSid );
  2609. }
  2610. ForestTrustListSize = ROUND_UP_COUNT( ForestTrustListSize, ALIGN_DWORD );
  2611. }
  2612. //
  2613. // Allocate the buffer.
  2614. //
  2615. ForestTrustList = NetpMemoryAllocate( ForestTrustListSize );
  2616. if ( ForestTrustList == NULL ) {
  2617. Status = STATUS_NO_MEMORY;
  2618. goto Cleanup;
  2619. }
  2620. ForestTrustListCount = NetlogonDomainInfo->TrustedDomainCount;
  2621. Where = (LPBYTE)(&ForestTrustList[ForestTrustListCount]);
  2622. //
  2623. // Handle each trusted domain.
  2624. //
  2625. for ( i=0; i<NetlogonDomainInfo->TrustedDomainCount; i++ ) {
  2626. NL_TRUST_EXTENSION TrustExtension;
  2627. //
  2628. // See if the caller passed the trust extension to us.
  2629. //
  2630. if ( NetlogonDomainInfo->TrustedDomains[i].TrustExtension.Length >= sizeof(TrustExtension) ) {
  2631. //
  2632. // Copy the extension to get the alignment right
  2633. // (since RPC thinks this is a WCHAR buffer).
  2634. //
  2635. RtlCopyMemory( &TrustExtension,
  2636. NetlogonDomainInfo->TrustedDomains[i].TrustExtension.Buffer,
  2637. sizeof(TrustExtension) );
  2638. ForestTrustList[i].Flags = TrustExtension.Flags;
  2639. ForestTrustList[i].ParentIndex = TrustExtension.ParentIndex;
  2640. ForestTrustList[i].TrustType = TrustExtension.TrustType;
  2641. ForestTrustList[i].TrustAttributes = TrustExtension.TrustAttributes;
  2642. //
  2643. // If not,
  2644. // make something up.
  2645. //
  2646. } else {
  2647. ForestTrustList[i].Flags = DS_DOMAIN_DIRECT_OUTBOUND; // = DS_DOMAIN_DIRECT_TRUST;
  2648. ForestTrustList[i].ParentIndex = 0;
  2649. ForestTrustList[i].TrustType = TRUST_TYPE_DOWNLEVEL;
  2650. ForestTrustList[i].TrustAttributes = 0;
  2651. }
  2652. ForestTrustList[i].DomainGuid = NetlogonDomainInfo->TrustedDomains[i].DomainGuid;
  2653. //
  2654. // Copy the DWORD aligned data
  2655. //
  2656. if ( NetlogonDomainInfo->TrustedDomains[i].DomainSid != NULL ) {
  2657. ULONG SidSize;
  2658. ForestTrustList[i].DomainSid = (PSID) Where;
  2659. SidSize = RtlLengthSid( NetlogonDomainInfo->TrustedDomains[i].DomainSid );
  2660. RtlCopyMemory( Where,
  2661. NetlogonDomainInfo->TrustedDomains[i].DomainSid,
  2662. SidSize );
  2663. Where += SidSize;
  2664. } else {
  2665. ForestTrustList[i].DomainSid = NULL;
  2666. }
  2667. //
  2668. // Copy the WCHAR aligned data
  2669. //
  2670. if ( NetlogonDomainInfo->TrustedDomains[i].DnsDomainName.Length != 0 ) {
  2671. ForestTrustList[i].DnsDomainName = (LPWSTR)Where;
  2672. RtlCopyMemory( Where,
  2673. NetlogonDomainInfo->TrustedDomains[i].DnsDomainName.Buffer,
  2674. NetlogonDomainInfo->TrustedDomains[i].DnsDomainName.Length );
  2675. Where += NetlogonDomainInfo->TrustedDomains[i].DnsDomainName.Length;
  2676. *(PWCHAR)Where = L'\0';
  2677. Where += sizeof(WCHAR);
  2678. } else {
  2679. ForestTrustList[i].DnsDomainName = NULL;
  2680. }
  2681. if ( NetlogonDomainInfo->TrustedDomains[i].DomainName.Length != 0 ) {
  2682. ForestTrustList[i].NetbiosDomainName = (LPWSTR)Where;
  2683. RtlCopyMemory( Where,
  2684. NetlogonDomainInfo->TrustedDomains[i].DomainName.Buffer,
  2685. NetlogonDomainInfo->TrustedDomains[i].DomainName.Length );
  2686. Where += NetlogonDomainInfo->TrustedDomains[i].DomainName.Length;
  2687. *(PWCHAR)Where = L'\0';
  2688. Where += sizeof(WCHAR);
  2689. } else {
  2690. ForestTrustList[i].NetbiosDomainName = NULL;
  2691. }
  2692. Where = ROUND_UP_POINTER( Where, ALIGN_DWORD);
  2693. }
  2694. //
  2695. // Ensure the DC has our latest SPN
  2696. //
  2697. if ( NetlogonDomainInfo->WorkstationFlags & NL_CLIENT_HANDLES_SPN ) {
  2698. LONG WinError;
  2699. HKEY Key;
  2700. //
  2701. // See if we are supposed to set SPN
  2702. //
  2703. WinError = RegOpenKey( HKEY_LOCAL_MACHINE,
  2704. NETSETUPP_NETLOGON_AVOID_SPN_PATH,
  2705. &Key );
  2706. //
  2707. // If the key exists it must have just been set by Netjoin
  2708. // so we should avoid setting it ourselves because we may
  2709. // not know the new machine name until the reboot. The key
  2710. // we just read is volatile, so it won't exist after the
  2711. // reboot when teh new computer name becomes available to us.
  2712. //
  2713. if ( WinError == ERROR_SUCCESS ) {
  2714. RegCloseKey( Key );
  2715. } else {
  2716. BOOLEAN SetSpn = FALSE;
  2717. BOOLEAN SetDnsHostName = FALSE;
  2718. //
  2719. // If the DC doesn't know any DnsHostName at all,
  2720. // set both the SPN and the DNS host name.
  2721. // (This is expected to handle the case where the DC was just upgraded to
  2722. // NT 5. In all other cases, join (etc) is expected to already have set
  2723. // the SPN and DC names.
  2724. //
  2725. if ( NetlogonDomainInfo->DnsHostNameInDs.Buffer == NULL ) {
  2726. SetSpn = TRUE;
  2727. SetDnsHostName = TRUE;
  2728. } else {
  2729. //
  2730. // If the DC simply doesn't know the correct host name,
  2731. // just set that.
  2732. // (The DS will set all of the appropriate SPNs as a side effect of
  2733. // the host name changing.)
  2734. //
  2735. if ( !NlEqualDnsNameU(
  2736. &NetlogonDomainInfo->DnsHostNameInDs,
  2737. &ClientSession->CsDomainInfo->DomUnicodeDnsHostNameString ) ) {
  2738. SetDnsHostName = TRUE;
  2739. }
  2740. }
  2741. (VOID) NlSetDsSPN( TRUE, // Synchronous
  2742. SetSpn,
  2743. SetDnsHostName,
  2744. ClientSession->CsDomainInfo,
  2745. ClientSession->CsUncServerName,
  2746. ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
  2747. ClientSession->CsDomainInfo->DomUnicodeDnsHostNameString.Buffer );
  2748. }
  2749. }
  2750. Status = STATUS_SUCCESS;
  2751. //
  2752. // Free any locally used resources.
  2753. //
  2754. Cleanup:
  2755. //
  2756. // On success,
  2757. // save the trusted domain list.
  2758. //
  2759. if ( NT_SUCCESS(Status) ) {
  2760. //
  2761. // Ensure the SID of the trusted domain isn't the domain sid of the primary
  2762. // domain.
  2763. //
  2764. for ( i=0; i<ForestTrustListCount; i++ ) {
  2765. if ( (ForestTrustList[i].Flags & DS_DOMAIN_PRIMARY) == 0 &&
  2766. ForestTrustList[i].DomainSid != NULL &&
  2767. RtlEqualSid( ForestTrustList[i].DomainSid, ClientSession->CsDomainInfo->DomAccountDomainId )) {
  2768. LPWSTR AlertStrings[3];
  2769. //
  2770. // alert admin.
  2771. //
  2772. AlertStrings[0] = NlGlobalUnicodeComputerName;
  2773. AlertStrings[1] = ForestTrustList[i].DnsDomainName != NULL ?
  2774. ForestTrustList[i].DnsDomainName :
  2775. ForestTrustList[i].NetbiosDomainName;
  2776. AlertStrings[2] = NULL; // Needed for RAISE_ALERT_TOO
  2777. //
  2778. // Save the info in the eventlog
  2779. //
  2780. NlpWriteEventlog(
  2781. ALERT_NetLogonSidConflict,
  2782. EVENTLOG_ERROR_TYPE,
  2783. (LPBYTE)ForestTrustList[i].DomainSid,
  2784. RtlLengthSid(ForestTrustList[i].DomainSid),
  2785. AlertStrings,
  2786. 2 | NETP_RAISE_ALERT_TOO );
  2787. }
  2788. }
  2789. //
  2790. // Save the collected information to the binary file.
  2791. //
  2792. NetStatus = NlWriteFileForestTrustList (
  2793. NL_FOREST_BINARY_LOG_FILE,
  2794. ForestTrustList,
  2795. ForestTrustListCount );
  2796. if ( NetStatus != NO_ERROR ) {
  2797. LPWSTR MsgStrings[2];
  2798. MsgStrings[0] = NL_FOREST_BINARY_LOG_FILE;
  2799. MsgStrings[1] = (LPWSTR) ULongToPtr( NetStatus );
  2800. NlpWriteEventlog (NELOG_NetlogonFailedFileCreate,
  2801. EVENTLOG_ERROR_TYPE,
  2802. (LPBYTE) &NetStatus,
  2803. sizeof(NetStatus),
  2804. MsgStrings,
  2805. 2 | NETP_LAST_MESSAGE_IS_NETSTATUS );
  2806. }
  2807. //
  2808. // Save the list on the DomainInfo
  2809. // (May null out ForestTrustList).
  2810. //
  2811. NlSetForestTrustList ( ClientSession->CsDomainInfo,
  2812. &ForestTrustList,
  2813. ForestTrustListSize,
  2814. ForestTrustListCount );
  2815. }
  2816. if ( NetlogonDomainInfo != NULL ) {
  2817. NetApiBufferFree( NetlogonDomainInfo );
  2818. }
  2819. if ( ForestTrustList != NULL ) {
  2820. NetApiBufferFree( ForestTrustList );
  2821. }
  2822. return Status;
  2823. }
  2824. VOID
  2825. NlSetForestTrustList (
  2826. IN PDOMAIN_INFO DomainInfo,
  2827. IN OUT PDS_DOMAIN_TRUSTSW *ForestTrustList,
  2828. IN ULONG ForestTrustListSize,
  2829. IN ULONG ForestTrustListCount
  2830. )
  2831. /*++
  2832. Routine Description:
  2833. Set the domain list on DomainInfo (on a DC) or globals (on a workstation)
  2834. Arguments:
  2835. DomainInfo - Domain trust list is associated with
  2836. ForestTrustList - Specifies a list of trusted domains.
  2837. This pointer is NULLed if this routine consumes the buffer.
  2838. ForestTrustListSize - Size (in bytes) of ForestTrustList
  2839. ForestTrustListCount - Number of entries in ForestTrustList
  2840. Return Value:
  2841. Status of the operation.
  2842. Upon failure, the previous list remains intact.
  2843. --*/
  2844. {
  2845. PTRUSTED_DOMAIN TempTrustedDomainList = NULL;
  2846. ULONG TempTrustedDomainCount = 0;
  2847. PTRUSTED_DOMAIN LocalTrustedDomainList = NULL;
  2848. DWORD i;
  2849. //
  2850. // On workstations,
  2851. // build a global list that contains the minimum amount of memory possible.
  2852. //
  2853. if ( NlGlobalMemberWorkstation ) {
  2854. DWORD LocalTrustedDomainCount;
  2855. DWORD LocalTrustedDomainSize;
  2856. PTRUSTED_DOMAIN OldList;
  2857. LPBYTE Where;
  2858. //
  2859. // If the new list is zero length,
  2860. // don't bother allocating anything.
  2861. //
  2862. if ( ForestTrustListCount == 0 ) {
  2863. LocalTrustedDomainList = NULL;
  2864. LocalTrustedDomainCount = 0;
  2865. LocalTrustedDomainSize = 0;
  2866. //
  2867. // Otherwise, build a buffer of the trusted domain list
  2868. //
  2869. } else {
  2870. //
  2871. // Allocate a temporary buffer for the new list
  2872. //
  2873. TempTrustedDomainList = NetpMemoryAllocate(
  2874. ForestTrustListCount * sizeof(TRUSTED_DOMAIN) );
  2875. if ( TempTrustedDomainList == NULL ) {
  2876. goto Cleanup;
  2877. }
  2878. RtlZeroMemory( TempTrustedDomainList,
  2879. ForestTrustListCount * sizeof(TRUSTED_DOMAIN ));
  2880. //
  2881. // Copy the Netbios names to the new structure upper casing them and
  2882. // converting to OEM.
  2883. //
  2884. TempTrustedDomainCount = 0;
  2885. LocalTrustedDomainSize = 0;
  2886. EnterCriticalSection( &NlGlobalLogFileCritSect );
  2887. NlPrint((NL_LOGON, "NlSetForestTrustList: New trusted domain list:\n" ));
  2888. for ( i=0; i<ForestTrustListCount; i++ ) {
  2889. NTSTATUS Status;
  2890. NlPrint(( NL_LOGON, " %ld:", i ));
  2891. NlPrintTrustedDomain( &(*ForestTrustList)[i],
  2892. TRUE, // verbose output
  2893. FALSE ); // wide character output
  2894. //
  2895. // Skip entries that represent trusts Netlogon doesn't use
  2896. //
  2897. if ( (*ForestTrustList)[i].TrustType != TRUST_TYPE_DOWNLEVEL &&
  2898. (*ForestTrustList)[i].TrustType != TRUST_TYPE_UPLEVEL ) {
  2899. continue;
  2900. }
  2901. if ( (*ForestTrustList)[i].TrustAttributes & TRUST_ATTRIBUTE_UPLEVEL_ONLY ) {
  2902. continue;
  2903. }
  2904. //
  2905. // On workstation, we keep in memory trusted domains only
  2906. //
  2907. if ( ((*ForestTrustList)[i].Flags & DS_DOMAIN_PRIMARY) == 0 &&
  2908. ((*ForestTrustList)[i].Flags & DS_DOMAIN_IN_FOREST) == 0 &&
  2909. ((*ForestTrustList)[i].Flags & DS_DOMAIN_DIRECT_OUTBOUND) == 0 ) {
  2910. continue;
  2911. }
  2912. //
  2913. // Copy the Netbios names to the new structure.
  2914. //
  2915. if ( (*ForestTrustList)[i].NetbiosDomainName != NULL ) {
  2916. if ( wcslen( (*ForestTrustList)[i].NetbiosDomainName ) > DNLEN ) {
  2917. NlPrint(( NL_CRITICAL,
  2918. "Netbios domain name is too long: %ws\n",
  2919. (*ForestTrustList)[i].NetbiosDomainName ));
  2920. LeaveCriticalSection( &NlGlobalLogFileCritSect );
  2921. goto Cleanup;
  2922. }
  2923. wcscpy( TempTrustedDomainList[TempTrustedDomainCount].UnicodeNetbiosDomainName,
  2924. (*ForestTrustList)[i].NetbiosDomainName );
  2925. }
  2926. //
  2927. // Copy the DNS domain name
  2928. //
  2929. if ( (*ForestTrustList)[i].DnsDomainName != NULL ) {
  2930. TempTrustedDomainList[TempTrustedDomainCount].Utf8DnsDomainName =
  2931. NetpAllocUtf8StrFromWStr( (*ForestTrustList)[i].DnsDomainName );
  2932. if ( TempTrustedDomainList[TempTrustedDomainCount].Utf8DnsDomainName == NULL ) {
  2933. NlPrint(( NL_CRITICAL,
  2934. "Can't convert to UTF-8: %ws\n",
  2935. (*ForestTrustList)[i].DnsDomainName ));
  2936. LeaveCriticalSection( &NlGlobalLogFileCritSect );
  2937. goto Cleanup;
  2938. }
  2939. LocalTrustedDomainSize += strlen(TempTrustedDomainList[TempTrustedDomainCount].Utf8DnsDomainName ) + 1;
  2940. }
  2941. //
  2942. // If this is a primary domain entry,
  2943. // remember whether it's mixed mode
  2944. //
  2945. if ( (*ForestTrustList)[i].Flags & DS_DOMAIN_PRIMARY ) {
  2946. if ( (*ForestTrustList)[i].Flags & DS_DOMAIN_NATIVE_MODE ) {
  2947. NlGlobalWorkstationMixedModeDomain = FALSE;
  2948. } else {
  2949. NlGlobalWorkstationMixedModeDomain = TRUE;
  2950. }
  2951. }
  2952. //
  2953. // Move on to the next entry
  2954. //
  2955. TempTrustedDomainCount ++;
  2956. LocalTrustedDomainSize += sizeof(TRUSTED_DOMAIN);
  2957. }
  2958. LeaveCriticalSection( &NlGlobalLogFileCritSect );
  2959. //
  2960. // Allocate a single buffer to contain the list
  2961. // (to improve locality of reference)
  2962. //
  2963. LocalTrustedDomainList = NetpMemoryAllocate( LocalTrustedDomainSize );
  2964. if ( LocalTrustedDomainList == NULL ) {
  2965. goto Cleanup;
  2966. }
  2967. Where = (LPBYTE)(&LocalTrustedDomainList[TempTrustedDomainCount]);
  2968. LocalTrustedDomainCount = TempTrustedDomainCount;
  2969. //
  2970. // Copy it to the local buffer
  2971. //
  2972. for ( i=0; i<TempTrustedDomainCount; i++ ) {
  2973. //
  2974. // Copy the Netbios domain name
  2975. //
  2976. RtlCopyMemory( LocalTrustedDomainList[i].UnicodeNetbiosDomainName,
  2977. TempTrustedDomainList[i].UnicodeNetbiosDomainName,
  2978. sizeof(LocalTrustedDomainList[i].UnicodeNetbiosDomainName ));
  2979. //
  2980. // Copy the DNS domain name
  2981. //
  2982. if ( TempTrustedDomainList[i].Utf8DnsDomainName != NULL ) {
  2983. ULONG Utf8DnsDomainNameSize;
  2984. Utf8DnsDomainNameSize = strlen(TempTrustedDomainList[i].Utf8DnsDomainName ) + 1;
  2985. LocalTrustedDomainList[i].Utf8DnsDomainName = (LPSTR) Where;
  2986. RtlCopyMemory( Where,
  2987. TempTrustedDomainList[i].Utf8DnsDomainName,
  2988. Utf8DnsDomainNameSize );
  2989. Where += Utf8DnsDomainNameSize;
  2990. } else {
  2991. LocalTrustedDomainList[i].Utf8DnsDomainName = NULL;
  2992. }
  2993. }
  2994. }
  2995. //
  2996. // Swap in the new list
  2997. //
  2998. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  2999. OldList = NlGlobalTrustedDomainList;
  3000. NlGlobalTrustedDomainList = LocalTrustedDomainList;
  3001. LocalTrustedDomainList = NULL;
  3002. NlGlobalTrustedDomainCount = LocalTrustedDomainCount;
  3003. NlQuerySystemTime( &NlGlobalTrustedDomainListTime );
  3004. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3005. //
  3006. // Free the old list.
  3007. //
  3008. if ( OldList != NULL ) {
  3009. NetpMemoryFree( OldList );
  3010. }
  3011. //
  3012. // On a DC,
  3013. // save the exact list we want to return later.
  3014. //
  3015. } else {
  3016. LOCK_TRUST_LIST( DomainInfo );
  3017. if ( DomainInfo->DomForestTrustList != NULL ) {
  3018. MIDL_user_free( DomainInfo->DomForestTrustList );
  3019. DomainInfo->DomForestTrustList = NULL;
  3020. }
  3021. DomainInfo->DomForestTrustList = *ForestTrustList;
  3022. *ForestTrustList = NULL;
  3023. DomainInfo->DomForestTrustListSize = ForestTrustListSize;
  3024. DomainInfo->DomForestTrustListCount = ForestTrustListCount;
  3025. UNLOCK_TRUST_LIST( DomainInfo );
  3026. }
  3027. //
  3028. // Free locally used resources.
  3029. //
  3030. Cleanup:
  3031. if ( TempTrustedDomainList != NULL ) {
  3032. for ( i=0; i<TempTrustedDomainCount; i++ ) {
  3033. if ( TempTrustedDomainList[i].Utf8DnsDomainName != NULL ) {
  3034. NetpMemoryFree( TempTrustedDomainList[i].Utf8DnsDomainName );
  3035. }
  3036. }
  3037. NetpMemoryFree( TempTrustedDomainList );
  3038. }
  3039. if ( LocalTrustedDomainList != NULL ) {
  3040. NetpMemoryFree( LocalTrustedDomainList );
  3041. }
  3042. }
  3043. BOOLEAN
  3044. NlIsDomainTrusted (
  3045. IN PUNICODE_STRING DomainName
  3046. )
  3047. /*++
  3048. Routine Description:
  3049. Determine if the specified domain is trusted.
  3050. Arguments:
  3051. DomainName - Name of the DNS or Netbios domain to query.
  3052. Return Value:
  3053. TRUE - if the domain name specified is a trusted domain.
  3054. --*/
  3055. {
  3056. NTSTATUS Status;
  3057. DWORD i;
  3058. BOOLEAN RetVal;
  3059. LPSTR Utf8String = NULL;
  3060. PDOMAIN_INFO DomainInfo = NULL;
  3061. //
  3062. // If the no domain name was specified,
  3063. // indicate the domain is not trusted.
  3064. //
  3065. if ( DomainName == NULL || DomainName->Length == 0 ) {
  3066. RetVal = FALSE;
  3067. goto Cleanup;
  3068. }
  3069. //
  3070. // Get a pointer to the primary domain info.
  3071. //
  3072. DomainInfo = NlFindNetbiosDomain( NULL, TRUE ); // Primary domain
  3073. if ( DomainInfo == NULL ) {
  3074. RetVal = FALSE;
  3075. goto Cleanup;
  3076. }
  3077. //
  3078. // Convert the input string to UTF-8
  3079. //
  3080. Utf8String = NetpAllocUtf8StrFromUnicodeString( DomainName );
  3081. if ( Utf8String == NULL ) {
  3082. RetVal = FALSE;
  3083. goto Cleanup;
  3084. }
  3085. //
  3086. // Compare the input trusted domain name to each element in the list
  3087. //
  3088. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3089. for ( i=0; i<NlGlobalTrustedDomainCount; i++ ) {
  3090. UNICODE_STRING UnicodeNetbiosDomainName;
  3091. RtlInitUnicodeString( &UnicodeNetbiosDomainName,
  3092. NlGlobalTrustedDomainList[i].UnicodeNetbiosDomainName );
  3093. //
  3094. // Simply compare the bytes (both are already uppercased)
  3095. //
  3096. if ( RtlEqualDomainName( DomainName, &UnicodeNetbiosDomainName ) ||
  3097. ( Utf8String != NULL &&
  3098. NlGlobalTrustedDomainList[i].Utf8DnsDomainName != NULL &&
  3099. NlEqualDnsNameUtf8( Utf8String,
  3100. NlGlobalTrustedDomainList[i].Utf8DnsDomainName ) ) ) {
  3101. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3102. RetVal = TRUE;
  3103. goto Cleanup;
  3104. }
  3105. }
  3106. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3107. //
  3108. // All other domains aren't trusted.
  3109. //
  3110. RetVal = FALSE;
  3111. Cleanup:
  3112. if ( DomainInfo != NULL ) {
  3113. NlDereferenceDomain( DomainInfo );
  3114. }
  3115. if ( Utf8String != NULL ) {
  3116. NetApiBufferFree( Utf8String );
  3117. }
  3118. return RetVal;
  3119. }
  3120. NET_API_STATUS
  3121. NlGetTrustedDomainNames (
  3122. IN PDOMAIN_INFO DomainInfo,
  3123. IN LPWSTR DomainName,
  3124. OUT LPWSTR *TrustedDnsDomainName,
  3125. OUT LPWSTR *TrustedNetbiosDomainName
  3126. )
  3127. /*++
  3128. Routine Description:
  3129. Get a DNS name of a trusted domain given its Netbios name.
  3130. Arguments:
  3131. DomainInfo - Hosted domain info.
  3132. DomainName - Name of the Netbios or DNS domain to query.
  3133. TrustedDnsDomainName - Returns the DnsDomainName of the domain if DomainName is trusted.
  3134. The buffer must be freed using NetApiBufferFree.
  3135. TrustedNetbiosDomainName - Returns the Netbios domain name of the domain if DomainName is trusted.
  3136. The buffer must be freed using NetApiBufferFree.
  3137. Return Value:
  3138. NO_ERROR: The routine functioned properly. The returned domain name may or may not
  3139. be set depending on whether DomainName is trusted.
  3140. --*/
  3141. {
  3142. NET_API_STATUS NetStatus;
  3143. ULONG Index;
  3144. LPSTR Utf8DomainName = NULL;
  3145. //
  3146. // Initialization
  3147. //
  3148. *TrustedDnsDomainName = NULL;
  3149. *TrustedNetbiosDomainName = NULL;
  3150. //
  3151. // On a workstation, look up the global trust list
  3152. //
  3153. if ( NlGlobalMemberWorkstation ) {
  3154. //
  3155. // Convert the input string to UTF-8
  3156. //
  3157. Utf8DomainName = NetpAllocUtf8StrFromWStr( DomainName );
  3158. if ( Utf8DomainName == NULL ) {
  3159. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  3160. goto Cleanup;
  3161. }
  3162. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3163. for ( Index=0; Index<NlGlobalTrustedDomainCount; Index++ ) {
  3164. //
  3165. // If the passed in name is either the Netbios or DNS name of the trusted domain,
  3166. // return both names to the caller.
  3167. //
  3168. if ( (NlGlobalTrustedDomainList[Index].UnicodeNetbiosDomainName != NULL &&
  3169. NlNameCompare( NlGlobalTrustedDomainList[Index].UnicodeNetbiosDomainName,
  3170. DomainName,
  3171. NAMETYPE_DOMAIN ) == 0 ) ||
  3172. (NlGlobalTrustedDomainList[Index].Utf8DnsDomainName != NULL &&
  3173. NlEqualDnsNameUtf8( NlGlobalTrustedDomainList[Index].Utf8DnsDomainName,
  3174. Utf8DomainName ) ) ) {
  3175. if ( NlGlobalTrustedDomainList[Index].UnicodeNetbiosDomainName != NULL ) {
  3176. *TrustedNetbiosDomainName = NetpAllocWStrFromWStr( NlGlobalTrustedDomainList[Index].UnicodeNetbiosDomainName );
  3177. if ( *TrustedNetbiosDomainName == NULL ) {
  3178. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  3179. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3180. goto Cleanup;
  3181. }
  3182. }
  3183. if ( NlGlobalTrustedDomainList[Index].Utf8DnsDomainName != NULL ) {
  3184. *TrustedDnsDomainName = NetpAllocWStrFromUtf8Str( NlGlobalTrustedDomainList[Index].Utf8DnsDomainName );
  3185. if ( *TrustedDnsDomainName == NULL ) {
  3186. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  3187. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3188. goto Cleanup;
  3189. }
  3190. }
  3191. break;
  3192. }
  3193. }
  3194. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3195. //
  3196. // On a DC, search the forest trust list associated with the DomainInfo
  3197. //
  3198. } else {
  3199. LOCK_TRUST_LIST( DomainInfo );
  3200. for ( Index=0; Index<DomainInfo->DomForestTrustListCount; Index++ ) {
  3201. //
  3202. // If the passed in name is either the Netbios or DNS name of the trusted domain,
  3203. // return both names to the caller.
  3204. //
  3205. if ( (DomainInfo->DomForestTrustList[Index].NetbiosDomainName != NULL &&
  3206. NlNameCompare( DomainInfo->DomForestTrustList[Index].NetbiosDomainName,
  3207. DomainName,
  3208. NAMETYPE_DOMAIN ) == 0 ) ||
  3209. (DomainInfo->DomForestTrustList[Index].DnsDomainName != NULL &&
  3210. NlEqualDnsName( DomainInfo->DomForestTrustList[Index].DnsDomainName,
  3211. DomainName ) ) ) {
  3212. if ( DomainInfo->DomForestTrustList[Index].NetbiosDomainName != NULL ) {
  3213. *TrustedNetbiosDomainName = NetpAllocWStrFromWStr( DomainInfo->DomForestTrustList[Index].NetbiosDomainName );
  3214. if ( *TrustedNetbiosDomainName == NULL ) {
  3215. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  3216. UNLOCK_TRUST_LIST( DomainInfo );
  3217. goto Cleanup;
  3218. }
  3219. }
  3220. if ( DomainInfo->DomForestTrustList[Index].DnsDomainName != NULL ) {
  3221. *TrustedDnsDomainName = NetpAllocWStrFromWStr( DomainInfo->DomForestTrustList[Index].DnsDomainName );
  3222. if ( *TrustedDnsDomainName == NULL ) {
  3223. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  3224. UNLOCK_TRUST_LIST( DomainInfo );
  3225. goto Cleanup;
  3226. }
  3227. }
  3228. break;
  3229. }
  3230. }
  3231. UNLOCK_TRUST_LIST( DomainInfo );
  3232. }
  3233. NetStatus = NO_ERROR;
  3234. Cleanup:
  3235. if ( NetStatus != NO_ERROR ) {
  3236. if ( *TrustedDnsDomainName != NULL ) {
  3237. NetApiBufferFree( *TrustedDnsDomainName );
  3238. *TrustedDnsDomainName = NULL;
  3239. }
  3240. if ( *TrustedNetbiosDomainName != NULL ) {
  3241. NetApiBufferFree( *TrustedNetbiosDomainName );
  3242. *TrustedNetbiosDomainName = NULL;
  3243. }
  3244. }
  3245. if ( Utf8DomainName != NULL ) {
  3246. NetpMemoryFree( Utf8DomainName );
  3247. }
  3248. return NetStatus;
  3249. }
  3250. VOID
  3251. NlDcDiscoveryWorker(
  3252. IN PVOID Context
  3253. )
  3254. /*++
  3255. Routine Description:
  3256. Worker routine to asynchronously do DC discovery for a client session.
  3257. Arguments:
  3258. Context - ClientSession to do DC discovery for
  3259. Return Value:
  3260. None
  3261. --*/
  3262. {
  3263. NTSTATUS Status;
  3264. PCLIENT_SESSION ClientSession = (PCLIENT_SESSION) Context;
  3265. NlAssert( ClientSession->CsReferenceCount > 0 );
  3266. NlAssert( ClientSession->CsState == CS_IDLE );
  3267. NlAssert( ClientSession->CsDiscoveryFlags & CS_DISCOVERY_ASYNCHRONOUS );
  3268. //
  3269. // Call to discovery routine again telling it we're now in the worker routine.
  3270. // Avoid discovery if being asked to terminate.
  3271. //
  3272. if ( !NlGlobalTerminate ) {
  3273. (VOID) NlDiscoverDc ( ClientSession,
  3274. (ClientSession->CsDiscoveryFlags & CS_DISCOVERY_DEAD_DOMAIN) ?
  3275. DT_DeadDomain : DT_Asynchronous,
  3276. TRUE,
  3277. FALSE ); // with-account discovery is not performed from the discovery thread
  3278. }
  3279. //
  3280. // This was an async discovery,
  3281. // let everyone know we're done.
  3282. //
  3283. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3284. NlAssert( ClientSession->CsReferenceCount > 0 );
  3285. NlAssert( ClientSession->CsDiscoveryFlags & CS_DISCOVERY_ASYNCHRONOUS );
  3286. ClientSession->CsDiscoveryFlags &= ~CS_DISCOVERY_ASYNCHRONOUS;
  3287. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3288. //
  3289. // Let any other caller know we're done.
  3290. //
  3291. NlAssert( ClientSession->CsDiscoveryEvent != NULL );
  3292. if ( !SetEvent( ClientSession->CsDiscoveryEvent ) ) {
  3293. NlPrintCs(( NL_CRITICAL, ClientSession,
  3294. "NlDiscoverDc: SetEvent failed %ld\n",
  3295. GetLastError() ));
  3296. }
  3297. //
  3298. // We no longer care about the Client session
  3299. //
  3300. NlUnrefClientSession( ClientSession );
  3301. }
  3302. VOID
  3303. NlDcQueueDiscovery (
  3304. IN OUT PCLIENT_SESSION ClientSession,
  3305. IN DISCOVERY_TYPE DiscoveryType
  3306. )
  3307. /*++
  3308. Routine Description:
  3309. This routine queues an async discovery to an async discovery thread.
  3310. On Entry,
  3311. The trust list must NOT be locked.
  3312. The trust list entry must be referenced by the caller.
  3313. The caller must be a writer of the trust list entry.
  3314. NlGlobalDcDiscoveryCritSect must be locked.
  3315. Arguments:
  3316. ClientSession -- Client session structure whose DC is to be picked.
  3317. The Client Session structure must be marked for write.
  3318. The Client Session structure must be idle.
  3319. DiscoveryType -- Indicates Asynchronous, or rediscovery of a "Dead domain".
  3320. Return Value:
  3321. None.
  3322. --*/
  3323. {
  3324. NET_API_STATUS NetStatus;
  3325. BOOL ReturnValue;
  3326. NlAssert( ClientSession->CsReferenceCount > 0 );
  3327. NlAssert( ClientSession->CsState == CS_IDLE );
  3328. //
  3329. // Don't let the session go away during discovery.
  3330. //
  3331. ClientSession->CsDiscoveryFlags |= CS_DISCOVERY_ASYNCHRONOUS;
  3332. LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  3333. NlRefClientSession( ClientSession );
  3334. UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  3335. //
  3336. // Indicate the discovery is in progress.
  3337. //
  3338. NlAssert( ClientSession->CsDiscoveryEvent != NULL );
  3339. if ( !ResetEvent( ClientSession->CsDiscoveryEvent ) ) {
  3340. NlPrintCs(( NL_CRITICAL, ClientSession,
  3341. "NlDcQueueDiscovery: ResetEvent failed %ld\n",
  3342. GetLastError() ));
  3343. }
  3344. //
  3345. // Queue this client session for async discovery.
  3346. //
  3347. if ( DiscoveryType == DT_DeadDomain ) {
  3348. ClientSession->CsDiscoveryFlags |= CS_DISCOVERY_DEAD_DOMAIN;
  3349. } else {
  3350. ClientSession->CsDiscoveryFlags &= ~CS_DISCOVERY_DEAD_DOMAIN;
  3351. }
  3352. ReturnValue = NlQueueWorkItem( &ClientSession->CsAsyncDiscoveryWorkItem, TRUE, FALSE );
  3353. //
  3354. // If we can't queue the entry,
  3355. // undo what we've done above.
  3356. if ( !ReturnValue ) {
  3357. NlAssert( ClientSession->CsReferenceCount > 0 );
  3358. NlAssert( ClientSession->CsDiscoveryFlags & CS_DISCOVERY_ASYNCHRONOUS );
  3359. NlPrintCs(( NL_CRITICAL, ClientSession,
  3360. "NlDcQueueDiscovery: Can't queue it.\n" ));
  3361. ClientSession->CsDiscoveryFlags &= ~CS_DISCOVERY_ASYNCHRONOUS;
  3362. //
  3363. // Let any other caller know we're done.
  3364. //
  3365. NlAssert( ClientSession->CsDiscoveryEvent != NULL );
  3366. if ( !SetEvent( ClientSession->CsDiscoveryEvent ) ) {
  3367. NlPrintCs(( NL_CRITICAL, ClientSession,
  3368. "NlDiscoverDc: SetEvent failed %ld\n",
  3369. GetLastError() ));
  3370. }
  3371. //
  3372. // We no longer care about the Client session
  3373. //
  3374. NlUnrefClientSession( ClientSession );
  3375. }
  3376. return;
  3377. }
  3378. NET_API_STATUS
  3379. NlSetServerClientSession(
  3380. IN OUT PCLIENT_SESSION ClientSession,
  3381. IN PNL_DC_CACHE_ENTRY NlDcCacheEntry,
  3382. IN BOOL DcDiscoveredWithAccount,
  3383. IN BOOL SessionRefresh
  3384. )
  3385. /*++
  3386. Routine Description:
  3387. Sets the name of a discovered DC and optionally its IP address
  3388. and the discovery flags onto a ClientSession.
  3389. On Entry,
  3390. The trust list must NOT be locked.
  3391. The trust list entry must be referenced by the caller.
  3392. Arguments:
  3393. ClientSession -- Client session structure whose DC is to be picked.
  3394. NlDcCacheEntry -- DC cache entry.
  3395. DcDiscoveredWithAccount - If TRUE, the DC was discovered with account.
  3396. SessionRefresh -- TRUE if this is a session refresh. If so, the caller
  3397. must be a writer of the client session. If FALSE, the client session
  3398. must be idle in which case the caller doesn't have to be a writer since
  3399. it is safe to change (atomically) the server name from NULL to non-NULL
  3400. with only NlGlobalDcDiscoveryCritSect locked.
  3401. Return Value:
  3402. NO_ERROR - Success
  3403. ERROR_NOT_ENOUGH_MEMORY - Not enough memory to allocate name
  3404. ERROR_INVALID_COMPUTERNAME - ComputerName is too long
  3405. --*/
  3406. {
  3407. NET_API_STATUS NetStatus;
  3408. LPWSTR TmpUncServerName = NULL;
  3409. ULONG TmpDiscoveryFlags = 0;
  3410. LPWSTR CacheEntryServerName = NULL;
  3411. ULONG OldDiscoveryFlags = 0;
  3412. NlAssert( ClientSession->CsReferenceCount > 0 );
  3413. //
  3414. // If this is a session refresh,
  3415. // the caller must be a writer of the client session
  3416. //
  3417. if ( SessionRefresh ) {
  3418. NlAssert( ClientSession->CsFlags & CS_WRITER );
  3419. //
  3420. // Othewise the client session must be idle
  3421. //
  3422. } else {
  3423. NlAssert( ClientSession->CsState == CS_IDLE);
  3424. NlAssert( ClientSession->CsUncServerName == NULL );
  3425. NlAssert( ClientSession->CsServerSockAddr.iSockaddrLength == 0 );
  3426. NlAssert( ClientSession->ClientAuthData == NULL );
  3427. NlAssert( ClientSession->CsCredHandle.dwUpper == 0 && ClientSession->CsCredHandle.dwLower == 0 );
  3428. }
  3429. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3430. //
  3431. // Choose the server name. If we got the cache entry over ldap,
  3432. // prefer the DNS name. Otherwise, prefer the Netbios name.
  3433. //
  3434. if ( NlDcCacheEntry->CacheEntryFlags & NL_DC_CACHE_LDAP ) {
  3435. if ( NlDcCacheEntry->UnicodeDnsHostName != NULL ) {
  3436. CacheEntryServerName = NlDcCacheEntry->UnicodeDnsHostName;
  3437. TmpDiscoveryFlags |= CS_DISCOVERY_DNS_SERVER;
  3438. } else if ( NlDcCacheEntry->UnicodeNetbiosDcName != NULL ) {
  3439. CacheEntryServerName = NlDcCacheEntry->UnicodeNetbiosDcName;
  3440. }
  3441. //
  3442. // Indicate that we should use ldap to ping this DC
  3443. //
  3444. TmpDiscoveryFlags |= CS_DISCOVERY_USE_LDAP;
  3445. } else if ( NlDcCacheEntry->CacheEntryFlags & NL_DC_CACHE_MAILSLOT ) {
  3446. if ( NlDcCacheEntry->UnicodeNetbiosDcName != NULL ) {
  3447. CacheEntryServerName = NlDcCacheEntry->UnicodeNetbiosDcName;
  3448. } else if ( NlDcCacheEntry->UnicodeDnsHostName != NULL ) {
  3449. CacheEntryServerName = NlDcCacheEntry->UnicodeDnsHostName;
  3450. TmpDiscoveryFlags |= CS_DISCOVERY_DNS_SERVER;
  3451. }
  3452. //
  3453. // Indicate that we should use mailslots to ping this DC
  3454. //
  3455. TmpDiscoveryFlags |= CS_DISCOVERY_USE_MAILSLOT;
  3456. }
  3457. if ( CacheEntryServerName == NULL ) {
  3458. NlPrint(( NL_CRITICAL, "NlSetServerClientSession: Invalid data\n" ));
  3459. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3460. return ERROR_INVALID_DATA;
  3461. }
  3462. NetStatus = NetApiBufferAllocate(
  3463. (wcslen(CacheEntryServerName) + 3) * sizeof(WCHAR),
  3464. &TmpUncServerName );
  3465. if ( NetStatus != NO_ERROR ) {
  3466. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3467. return NetStatus;
  3468. }
  3469. wcscpy( TmpUncServerName, L"\\\\" );
  3470. wcscpy( TmpUncServerName+2, CacheEntryServerName );
  3471. //
  3472. // Indicate whether the server has IP address
  3473. //
  3474. if ( NlDcCacheEntry->SockAddr.iSockaddrLength != 0 ) {
  3475. TmpDiscoveryFlags |= CS_DISCOVERY_HAS_IP;
  3476. }
  3477. //
  3478. // Indicate whether the server is NT5 machine and whether it is in a close site.
  3479. //
  3480. if ( (NlDcCacheEntry->ReturnFlags & DS_DS_FLAG) != 0 ) {
  3481. TmpDiscoveryFlags |= CS_DISCOVERY_HAS_DS;
  3482. NlPrintCs(( NL_SESSION_MORE, ClientSession,
  3483. "NlSetServerClientSession: New DC is an NT 5 DC: %ws\n",
  3484. TmpUncServerName ));
  3485. }
  3486. //
  3487. // If the server or client site is not known, assume closest site
  3488. //
  3489. if ( NlDcCacheEntry->UnicodeDcSiteName == NULL ||
  3490. NlDcCacheEntry->UnicodeClientSiteName == NULL ) {
  3491. TmpDiscoveryFlags |= CS_DISCOVERY_IS_CLOSE;
  3492. NlPrintCs(( NL_SESSION_MORE, ClientSession,
  3493. "NlSetServerClientSession: New DC site isn't known (assume closest site): %ws\n",
  3494. TmpUncServerName ));
  3495. } else if ( (NlDcCacheEntry->ReturnFlags & DS_CLOSEST_FLAG) != 0 ) {
  3496. TmpDiscoveryFlags |= CS_DISCOVERY_IS_CLOSE;
  3497. NlPrintCs(( NL_SESSION_MORE, ClientSession,
  3498. "NlSetServerClientSession: New DC is in closest site: %ws\n",
  3499. TmpUncServerName ));
  3500. }
  3501. //
  3502. // Indicate if the server runs the Windows Time Service
  3503. //
  3504. if ( NlDcCacheEntry->ReturnFlags & DS_TIMESERV_FLAG ) {
  3505. TmpDiscoveryFlags |= CS_DISCOVERY_HAS_TIMESERV;
  3506. NlPrintCs(( NL_SESSION_MORE, ClientSession,
  3507. "NlSetServerClientSession: New DC runs the time service: %ws\n",
  3508. TmpUncServerName ));
  3509. }
  3510. //
  3511. // Free the old server name, if any
  3512. //
  3513. if ( ClientSession->CsUncServerName != NULL ) {
  3514. BOOL FreeCurrentName = FALSE;
  3515. //
  3516. // If the current name is Netbios ...
  3517. //
  3518. if ( (ClientSession->CsDiscoveryFlags & CS_DISCOVERY_DNS_SERVER) == 0 ) {
  3519. //
  3520. // If the new name is DNS, free the current name
  3521. //
  3522. if ( (TmpDiscoveryFlags & CS_DISCOVERY_DNS_SERVER) != 0 ) {
  3523. FreeCurrentName = TRUE;
  3524. //
  3525. // Otherwise, check whether the two Netbios names are different
  3526. // (Skip the UNC prefix in the names)
  3527. //
  3528. } else if ( NlNameCompare(ClientSession->CsUncServerName+2,
  3529. TmpUncServerName+2,
  3530. NAMETYPE_COMPUTER) != 0 ) {
  3531. FreeCurrentName = TRUE;
  3532. }
  3533. //
  3534. // If the current name is DNS ...
  3535. //
  3536. } else {
  3537. //
  3538. // If the new name is Netbios, free the current name
  3539. //
  3540. if ( (TmpDiscoveryFlags & CS_DISCOVERY_DNS_SERVER) == 0 ) {
  3541. FreeCurrentName = TRUE;
  3542. //
  3543. // Otherwise, check whether the two DNS names are the same
  3544. // (Skip the UNC prefix in the names)
  3545. //
  3546. } else if ( !NlEqualDnsName(ClientSession->CsUncServerName+2,
  3547. TmpUncServerName+2) ) {
  3548. FreeCurrentName = TRUE;
  3549. }
  3550. }
  3551. //
  3552. // Free the current name as needed
  3553. //
  3554. if ( FreeCurrentName ) {
  3555. NlPrintCs(( NL_SESSION_SETUP, ClientSession,
  3556. "NlSetServerClientSession: New DC name: %ws; Old DC name: %ws\n",
  3557. TmpUncServerName,
  3558. ClientSession->CsUncServerName ));
  3559. NetApiBufferFree( ClientSession->CsUncServerName );
  3560. ClientSession->CsUncServerName = NULL;
  3561. }
  3562. }
  3563. //
  3564. // Reset the discovery flags
  3565. //
  3566. OldDiscoveryFlags = ClientSession->CsDiscoveryFlags &
  3567. (CS_DISCOVERY_USE_MAILSLOT |
  3568. CS_DISCOVERY_USE_LDAP |
  3569. CS_DISCOVERY_HAS_DS |
  3570. CS_DISCOVERY_IS_CLOSE |
  3571. CS_DISCOVERY_DNS_SERVER |
  3572. CS_DISCOVERY_HAS_TIMESERV |
  3573. CS_DISCOVERY_HAS_IP);
  3574. if ( OldDiscoveryFlags != TmpDiscoveryFlags ) {
  3575. NlPrintCs(( NL_SESSION_MORE, ClientSession,
  3576. "NlSetServerClientSession: New discovery flags: 0x%lx; Old flags: 0x%lx\n",
  3577. TmpDiscoveryFlags,
  3578. OldDiscoveryFlags ));
  3579. ClientSession->CsDiscoveryFlags &= ~OldDiscoveryFlags;
  3580. ClientSession->CsDiscoveryFlags |= TmpDiscoveryFlags;
  3581. }
  3582. //
  3583. // Make the (atomic) pointer assignments here
  3584. //
  3585. if ( ClientSession->CsUncServerName == NULL ) {
  3586. ClientSession->CsUncServerName = TmpUncServerName;
  3587. TmpUncServerName = NULL;
  3588. }
  3589. //
  3590. // If there is a socket address, save it
  3591. //
  3592. if ( NlDcCacheEntry->SockAddr.iSockaddrLength != 0 ) {
  3593. ClientSession->CsServerSockAddr.iSockaddrLength =
  3594. NlDcCacheEntry->SockAddr.iSockaddrLength;
  3595. ClientSession->CsServerSockAddr.lpSockaddr =
  3596. (LPSOCKADDR) &ClientSession->CsServerSockAddrIn;
  3597. RtlCopyMemory( ClientSession->CsServerSockAddr.lpSockaddr,
  3598. NlDcCacheEntry->SockAddr.lpSockaddr,
  3599. NlDcCacheEntry->SockAddr.iSockaddrLength );
  3600. //
  3601. // Otherwise, wipe out the previous socket address in the client session
  3602. //
  3603. } else {
  3604. RtlZeroMemory( &ClientSession->CsServerSockAddr,
  3605. sizeof(ClientSession->CsServerSockAddr) );
  3606. RtlZeroMemory( &ClientSession->CsServerSockAddrIn,
  3607. sizeof(ClientSession->CsServerSockAddrIn) );
  3608. }
  3609. //
  3610. // If this is not just a refresh,
  3611. // Leave CsConnectionStatus with a "failure" status code until the
  3612. // secure channel is set up. Other routines simply return
  3613. // CsConnectionStatus as the state of the secure channel.
  3614. //
  3615. if ( !SessionRefresh) {
  3616. ClientSession->CsLastAuthenticationTry.QuadPart = 0;
  3617. NlQuerySystemTime( &ClientSession->CsLastDiscoveryTime );
  3618. //
  3619. // If the server was discovered with account,
  3620. // update that timestampt, too
  3621. //
  3622. if ( DcDiscoveredWithAccount ) {
  3623. NlQuerySystemTime( &ClientSession->CsLastDiscoveryWithAccountTime );
  3624. }
  3625. ClientSession->CsState = CS_DC_PICKED;
  3626. }
  3627. //
  3628. // Update the refresh time
  3629. //
  3630. NlQuerySystemTime( &ClientSession->CsLastRefreshTime );
  3631. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3632. //
  3633. // Free locally allocated memory
  3634. //
  3635. if ( TmpUncServerName != NULL ) {
  3636. NetApiBufferFree( TmpUncServerName );
  3637. }
  3638. return NO_ERROR;
  3639. }
  3640. NTSTATUS
  3641. NlDiscoverDc (
  3642. IN OUT PCLIENT_SESSION ClientSession,
  3643. IN DISCOVERY_TYPE DiscoveryType,
  3644. IN BOOLEAN InDiscoveryThread,
  3645. IN BOOLEAN DiscoverWithAccount
  3646. )
  3647. /*++
  3648. Routine Description:
  3649. Get the name of a DC in a domain.
  3650. If the ClientSession is not currently IDLE, then this is an attempt to
  3651. discover a "better" DC. In that case, the newly discovered DC will only be
  3652. used if it is indeed "better" than the current DC.
  3653. The current implementation only support synchronous attempts to find a "better"
  3654. DC.
  3655. On Entry,
  3656. The trust list must NOT be locked.
  3657. The trust list entry must be referenced by the caller.
  3658. The caller must be a writer of the trust list entry. (Unless in DiscoveryThread).
  3659. Arguments:
  3660. ClientSession -- Client session structure whose DC is to be picked.
  3661. DiscoveryType -- Indicate synchronous, Asynchronous, or rediscovery of a
  3662. "Dead domain".
  3663. InDiscoveryThread -- TRUE if this is the DiscoveryThread completing an async
  3664. call.
  3665. DiscoverWithAccount - If TRUE and this is not in discovery thread,
  3666. the discovery with account will be performed. Otherwise, no account
  3667. will be specified in the discovery attempt.
  3668. Return Value:
  3669. STATUS_SUCCESS - if DC was found.
  3670. STATUS_PENDING - Operation is still in progress
  3671. STATUS_NO_LOGON_SERVERS - if DC was not found.
  3672. STATUS_NO_TRUST_SAM_ACCOUNT - if DC was found but it does not have
  3673. an account for this machine.
  3674. --*/
  3675. {
  3676. NET_API_STATUS NetStatus;
  3677. NTSTATUS Status;
  3678. ULONG AllowableAccountControlBits;
  3679. LPWSTR TransportName = NULL;
  3680. PNL_DC_CACHE_ENTRY DomainControllerCacheEntry = NULL;
  3681. ULONG Flags = 0;
  3682. ULONG InternalFlags = 0;
  3683. LPWSTR CapturedInfo = NULL;
  3684. LPWSTR CapturedDnsForestName;
  3685. LPWSTR CapturedSiteName;
  3686. LPWSTR LocalSiteName;
  3687. //
  3688. // Allocate a temp buffer
  3689. // (Don't put it on the stack since we don't want to commit a huge stack.)
  3690. //
  3691. CapturedInfo = LocalAlloc( 0,
  3692. (NL_MAX_DNS_LENGTH+1)*sizeof(WCHAR) +
  3693. (NL_MAX_DNS_LABEL_LENGTH+1)*sizeof(WCHAR) );
  3694. if ( CapturedInfo == NULL ) {
  3695. return STATUS_NO_MEMORY;
  3696. }
  3697. CapturedDnsForestName = CapturedInfo;
  3698. CapturedSiteName = &CapturedDnsForestName[NL_MAX_DNS_LENGTH+1];
  3699. //
  3700. // Initialization
  3701. //
  3702. NlAssert( ClientSession->CsReferenceCount > 0 );
  3703. // NlAssert( ClientSession->CsState == CS_IDLE );
  3704. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3705. //
  3706. // Ignore discoveries on indirect trusts.
  3707. //
  3708. if ((ClientSession->CsFlags & CS_DIRECT_TRUST) == 0 ) {
  3709. //
  3710. // If this is a synchronous discovery,
  3711. // the caller is confused,
  3712. // tell him we can't find any DCs.
  3713. //
  3714. if ( DiscoveryType == DT_Synchronous ) {
  3715. NlPrintCs(( NL_CRITICAL, ClientSession,
  3716. "NlDiscoverDc: Synchronous discovery attempt of indirect trust.\n" ));
  3717. // NlAssert( DiscoveryType != DT_Synchronous );
  3718. Status = STATUS_NO_LOGON_SERVERS;
  3719. //
  3720. // For non-synchronous,
  3721. // let the caller think he succeeded.
  3722. //
  3723. } else {
  3724. Status = STATUS_PENDING;
  3725. }
  3726. goto Cleanup;
  3727. }
  3728. //
  3729. // If we're in the discovery thread,
  3730. //
  3731. //
  3732. if ( InDiscoveryThread ) {
  3733. NlAssert( DiscoveryType != DT_Synchronous );
  3734. //
  3735. // If we're not in the discovery thread,
  3736. //
  3737. } else {
  3738. NlAssert( ClientSession->CsFlags & CS_WRITER );
  3739. //
  3740. // Handle synchronous requests.
  3741. //
  3742. if ( DiscoveryType == DT_Synchronous ) {
  3743. //
  3744. // If discovery is already going on asynchronously,
  3745. // just wait for it.
  3746. //
  3747. if ( ClientSession->CsDiscoveryFlags & CS_DISCOVERY_ASYNCHRONOUS ) {
  3748. DWORD WaitStatus;
  3749. //
  3750. // Boost the priority of the asynchronous discovery
  3751. // since we now really need it to complete quickly.
  3752. //
  3753. if ( !NlQueueWorkItem(&ClientSession->CsAsyncDiscoveryWorkItem, FALSE, TRUE) ) {
  3754. NlPrintCs(( NL_CRITICAL, ClientSession,
  3755. "NlDiscoverDc: Failed to boost ASYNC discovery priority\n" ));
  3756. }
  3757. //
  3758. // Wait for the maximum time that a discovery might take.
  3759. // (Unlock the crit sect to allow the async discovery to complete)
  3760. //
  3761. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3762. WaitStatus = WaitForSingleObject(
  3763. ClientSession->CsDiscoveryEvent,
  3764. NL_DC_MAX_TIMEOUT + NlGlobalParameters.ExpectedDialupDelay*1000 + 1000 ); // Add extra second to avoid race
  3765. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3766. if ( WaitStatus == WAIT_OBJECT_0 ) {
  3767. if ( ClientSession->CsState == CS_DC_PICKED ) {
  3768. Status = STATUS_SUCCESS;
  3769. } else {
  3770. Status = ClientSession->CsConnectionStatus;
  3771. NlPrintCs((NL_CRITICAL, ClientSession,
  3772. "NlDiscoverDc: ASYNC discovery failed so we will too 0x%lx.\n",
  3773. Status ));
  3774. }
  3775. } else if ( WaitStatus == WAIT_TIMEOUT ) {
  3776. NlPrintCs((NL_CRITICAL, ClientSession,
  3777. "NlDiscoverDc: ASYNC discovery took too long.\n" ));
  3778. Status = STATUS_NO_LOGON_SERVERS;
  3779. } else {
  3780. NlPrintCs((NL_CRITICAL, ClientSession,
  3781. "NlDiscoverDc: wait error: %ld %ld\n",
  3782. GetLastError(),
  3783. WaitStatus ));
  3784. Status = NetpApiStatusToNtStatus( WaitStatus );
  3785. }
  3786. goto Cleanup;
  3787. }
  3788. //
  3789. // If we're starting an async discovery,
  3790. // mark it so and queue up the discovery.
  3791. //
  3792. } else {
  3793. //
  3794. // If discovery is already going on asynchronously,
  3795. // we're done for now.
  3796. //
  3797. if ( ClientSession->CsDiscoveryFlags & CS_DISCOVERY_ASYNCHRONOUS ) {
  3798. Status = STATUS_PENDING;
  3799. goto Cleanup;
  3800. }
  3801. //
  3802. // Queue the discovery
  3803. //
  3804. NlDcQueueDiscovery ( ClientSession, DiscoveryType );
  3805. Status = STATUS_PENDING;
  3806. goto Cleanup;
  3807. }
  3808. }
  3809. //
  3810. // Determine the Account type we're looking for.
  3811. //
  3812. InternalFlags |= DS_IS_TRUSTED_DOMAIN;
  3813. switch ( ClientSession->CsSecureChannelType ) {
  3814. case WorkstationSecureChannel:
  3815. AllowableAccountControlBits = USER_WORKSTATION_TRUST_ACCOUNT;
  3816. InternalFlags |= DS_IS_PRIMARY_DOMAIN;
  3817. break;
  3818. case TrustedDomainSecureChannel:
  3819. AllowableAccountControlBits = USER_INTERDOMAIN_TRUST_ACCOUNT;
  3820. break;
  3821. case TrustedDnsDomainSecureChannel:
  3822. AllowableAccountControlBits = USER_DNS_DOMAIN_TRUST_ACCOUNT;
  3823. break;
  3824. case ServerSecureChannel:
  3825. AllowableAccountControlBits = USER_SERVER_TRUST_ACCOUNT;
  3826. Flags |= DS_PDC_REQUIRED;
  3827. InternalFlags |= DS_IS_PRIMARY_DOMAIN;
  3828. break;
  3829. default:
  3830. NlPrintCs(( NL_CRITICAL, ClientSession,
  3831. "NlDiscoverDc: invalid SecureChannelType retry %ld\n",
  3832. ClientSession->CsSecureChannelType ));
  3833. Status = STATUS_NO_LOGON_SERVERS;
  3834. NlQuerySystemTime( &ClientSession->CsLastDiscoveryTime );
  3835. if ( ClientSession->CsState == CS_IDLE ) {
  3836. ClientSession->CsLastAuthenticationTry = ClientSession->CsLastDiscoveryTime;
  3837. ClientSession->CsConnectionStatus = Status;
  3838. }
  3839. goto Cleanup;
  3840. }
  3841. NlPrintCs(( NL_SESSION_SETUP, ClientSession,
  3842. "NlDiscoverDc: Start %s Discovery\n",
  3843. DiscoveryType == DT_Synchronous ? "Synchronous" : "Async" ));
  3844. //
  3845. // Capture the name of the site this machine is in.
  3846. //
  3847. if ( NlCaptureSiteName( CapturedSiteName ) ) {
  3848. LocalSiteName = CapturedSiteName;
  3849. InternalFlags |= DS_SITENAME_DEFAULTED;
  3850. } else {
  3851. LocalSiteName = NULL;
  3852. }
  3853. //
  3854. // If the trusted domain is an NT 5 domain,
  3855. // prefer an NT 5 DC.
  3856. //
  3857. if ( ClientSession->CsFlags & CS_NT5_DOMAIN_TRUST ) {
  3858. Flags |= DS_DIRECTORY_SERVICE_PREFERRED;
  3859. }
  3860. //
  3861. // If we picked up a DC at least once, force the rediscovery
  3862. // as there is a reason we are called to get a different DC,
  3863. // so we want to avoid cached data. Otherwise, avoid forcing
  3864. // rediscovery so that we get the same DC as other components
  3865. // potentially discovered (in particular, this is important
  3866. // if other component's discovery resulted in join DC being
  3867. // cached -- we don't want to avoid that cache entry on our
  3868. // first secure channel setup).
  3869. //
  3870. if ( ClientSession->CsFlags & CS_DC_PICKED_ONCE ) {
  3871. Flags |= DS_FORCE_REDISCOVERY;
  3872. }
  3873. //
  3874. // Do the actual discovery of the DC.
  3875. //
  3876. // When NetpDcGetName is called from netlogon,
  3877. // it has both the Netbios and DNS domain name available for the primary
  3878. // domain. That can trick DsGetDcName into returning DNS host name of a
  3879. // DC in the primary domain. However, on IPX only systems, that won't work.
  3880. // Avoid that problem by not passing the DNS domain name of the primary domain
  3881. // if there are no DNS servers.
  3882. //
  3883. // Avoid having anything locked while calling NetpDcGetName.
  3884. // It calls back into Netlogon and locks heaven only knows what.
  3885. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3886. NlCaptureDnsForestName( CapturedDnsForestName );
  3887. NetStatus = NetpDcGetName(
  3888. ClientSession->CsDomainInfo, // SendDatagramContext
  3889. ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
  3890. // #define DONT_REQUIRE_MACHINE_ACCOUNT 1
  3891. #ifdef DONT_REQUIRE_MACHINE_ACCOUNT // useful for number of trust testing
  3892. NULL,
  3893. #else // DONT_REQUIRE_MACHINE_ACCOUNT
  3894. DiscoverWithAccount ? // pass the account name as appropriate
  3895. ClientSession->CsAccountName :
  3896. NULL,
  3897. #endif // DONT_REQUIRE_MACHINE_ACCOUNT
  3898. DiscoverWithAccount ? // pass the account control bits as appropriate
  3899. AllowableAccountControlBits :
  3900. 0,
  3901. ClientSession->CsNetbiosDomainName.Buffer,
  3902. NlDnsHasDnsServers() ? ClientSession->CsDnsDomainName.Buffer : NULL,
  3903. CapturedDnsForestName,
  3904. ClientSession->CsDomainId,
  3905. ClientSession->CsDomainGuid,
  3906. LocalSiteName,
  3907. Flags,
  3908. InternalFlags,
  3909. NL_DC_MAX_TIMEOUT + NlGlobalParameters.ExpectedDialupDelay*1000,
  3910. DiscoveryType == DT_DeadDomain ? 1 : MAX_DC_RETRIES,
  3911. NULL,
  3912. &DomainControllerCacheEntry );
  3913. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3914. if( NetStatus != NO_ERROR ) {
  3915. //
  3916. // Map the status to something more appropriate.
  3917. //
  3918. switch ( NetStatus ) {
  3919. case ERROR_NO_SUCH_DOMAIN:
  3920. NlPrintCs(( NL_CRITICAL, ClientSession,
  3921. "NlDiscoverDc: Cannot find DC.\n" ));
  3922. Status = STATUS_NO_LOGON_SERVERS;
  3923. break;
  3924. case ERROR_NO_SUCH_USER:
  3925. NlPrintCs(( NL_CRITICAL, ClientSession,
  3926. "NlDiscoverDc: DC reports no such account found.\n" ));
  3927. Status = STATUS_NO_TRUST_SAM_ACCOUNT;
  3928. break;
  3929. default:
  3930. NlPrintCs(( NL_CRITICAL, ClientSession,
  3931. "NlDiscoverDc: NetpDcGetName Unknown error %ld.\n",
  3932. NetStatus ));
  3933. // This isn't the real status, but callers handle this status
  3934. Status = STATUS_NO_LOGON_SERVERS;
  3935. break;
  3936. }
  3937. NlQuerySystemTime( &ClientSession->CsLastDiscoveryTime );
  3938. if ( ClientSession->CsState == CS_IDLE ) {
  3939. ClientSession->CsLastAuthenticationTry = ClientSession->CsLastDiscoveryTime;
  3940. ClientSession->CsConnectionStatus = Status;
  3941. }
  3942. //
  3943. // If this discovery was with account, update that timestamp, too
  3944. //
  3945. if ( DiscoverWithAccount ) {
  3946. NlQuerySystemTime( &ClientSession->CsLastDiscoveryWithAccountTime );
  3947. }
  3948. goto Cleanup;
  3949. }
  3950. //
  3951. // Indicate that we at least once successfully
  3952. // discovered a DC for this client session
  3953. //
  3954. ClientSession->CsFlags |= CS_DC_PICKED_ONCE;
  3955. //
  3956. // Handle a non-idle secure channel
  3957. //
  3958. if ( ClientSession->CsState != CS_IDLE ) {
  3959. //
  3960. // If we're in the discovery thread,
  3961. // another thread must have finished the discovery.
  3962. // We're done since we're not the writer of the client session.
  3963. //
  3964. // When we implement doing async discovery while a session is already up,
  3965. // we need to handle the case where someone has the ClientSession
  3966. // write locked. In that case, we should probably just hang the new
  3967. // DCname somewhere off the ClientSession structure and swap in the
  3968. // new DCname when the writer drops the write lock. ??
  3969. //
  3970. if ( InDiscoveryThread ) {
  3971. NlPrintCs(( NL_CRITICAL, ClientSession,
  3972. "NlDiscoverDc: Async discovery completed by another thread (current value ignored).\n" ));
  3973. Status = STATUS_SUCCESS;
  3974. goto Cleanup;
  3975. }
  3976. //
  3977. // If the newly discovered DC is "better" than the old one,
  3978. // use the new one.
  3979. //
  3980. NlAssert( ClientSession->CsFlags & CS_WRITER );
  3981. if ( ((ClientSession->CsDiscoveryFlags & CS_DISCOVERY_HAS_DS) == 0 &&
  3982. (DomainControllerCacheEntry->ReturnFlags & DS_DS_FLAG) != 0) ||
  3983. ((ClientSession->CsDiscoveryFlags & CS_DISCOVERY_IS_CLOSE) == 0 &&
  3984. (DomainControllerCacheEntry->ReturnFlags & DS_CLOSEST_FLAG) != 0) ) {
  3985. //
  3986. // Set the client session to idle.
  3987. //
  3988. // Avoid having the crit sect locked while we unbind.
  3989. //
  3990. //
  3991. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3992. NlSetStatusClientSession( ClientSession, STATUS_NO_LOGON_SERVERS );
  3993. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  3994. } else {
  3995. NlPrintCs(( NL_SESSION_SETUP, ClientSession,
  3996. "NlDiscoverDc: Better DC not found (keeping NT old DC). 0x%lx 0x%lx\n",
  3997. ClientSession->CsDiscoveryFlags,
  3998. DomainControllerCacheEntry->ReturnFlags ));
  3999. NlQuerySystemTime( &ClientSession->CsLastDiscoveryTime );
  4000. //
  4001. // If this discovery was with account, update that timestamp, too
  4002. //
  4003. if ( DiscoverWithAccount ) {
  4004. NlQuerySystemTime( &ClientSession->CsLastDiscoveryWithAccountTime );
  4005. }
  4006. Status = STATUS_SUCCESS;
  4007. goto Cleanup;
  4008. }
  4009. }
  4010. //
  4011. // Set the new DC info in the Client session
  4012. //
  4013. NlSetServerClientSession( ClientSession,
  4014. DomainControllerCacheEntry,
  4015. DiscoverWithAccount ? // was it discovery with account?
  4016. TRUE :
  4017. FALSE,
  4018. FALSE ); // not the session refresh
  4019. //
  4020. // Save the transport this discovery came in on.
  4021. //
  4022. // ?? NetpDcGetName should really return the TransportName as a parameter.
  4023. // ?? I can't do this since it just does a mailslot "ReadFile" which doesn't
  4024. // return transport information. So, I guess I'll just have to send UAS change
  4025. // datagrams on all transports.
  4026. //
  4027. if ( TransportName == NULL ) {
  4028. NlPrintCs(( NL_SESSION_SETUP, ClientSession,
  4029. "NlDiscoverDc: Found DC %ws\n",
  4030. ClientSession->CsUncServerName ));
  4031. } else {
  4032. NlPrintCs(( NL_SESSION_SETUP, ClientSession,
  4033. "NlDiscoverDc: Found DC %ws on transport %ws\n",
  4034. ClientSession->CsUncServerName,
  4035. TransportName ));
  4036. ClientSession->CsTransport =
  4037. NlTransportLookupTransportName( TransportName );
  4038. if ( ClientSession->CsTransport == NULL ) {
  4039. NlPrintCs(( NL_CRITICAL, ClientSession,
  4040. "NlDiscoverDc: %ws: Transport not found\n",
  4041. TransportName ));
  4042. }
  4043. }
  4044. Status = STATUS_SUCCESS;
  4045. //
  4046. // Cleanup locally used resources.
  4047. //
  4048. Cleanup:
  4049. //
  4050. // Unlock the crit sect and return.
  4051. //
  4052. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  4053. if ( DomainControllerCacheEntry != NULL ) {
  4054. NetpDcDerefCacheEntry( DomainControllerCacheEntry );
  4055. }
  4056. if ( CapturedInfo != NULL ) {
  4057. LocalFree( CapturedInfo );
  4058. }
  4059. return Status;
  4060. }
  4061. NET_API_STATUS
  4062. NlFlushCacheOnPnpWorker(
  4063. IN PDOMAIN_INFO DomainInfo,
  4064. IN PVOID Context
  4065. )
  4066. /*++
  4067. Routine Description:
  4068. Flush any caches that need to be flush when a new transport comes online
  4069. This worker routine handles on hosted domain.
  4070. Arguments:
  4071. DomainInfo - Domain the cache is to be flushed for
  4072. Context - Not used.
  4073. Return Value:
  4074. NO_ERROR: The cache was flushed.
  4075. --*/
  4076. {
  4077. PCLIENT_SESSION ClientSession;
  4078. PLIST_ENTRY ListEntry;
  4079. //
  4080. // Mark the global entry to indicate we've not tried to authenticate recently
  4081. //
  4082. ClientSession = NlRefDomClientSession( DomainInfo );
  4083. if ( ClientSession != NULL ) {
  4084. //
  4085. // Become a writer to ensure that another thread won't set the
  4086. // last auth time because it just finished a failed discovery.
  4087. //
  4088. if ( NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
  4089. if ( ClientSession->CsState != CS_AUTHENTICATED ) {
  4090. NlPrintCs(( NL_SESSION_SETUP, ClientSession,
  4091. " Zero LastAuth\n" ));
  4092. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  4093. ClientSession->CsLastAuthenticationTry.QuadPart = 0;
  4094. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  4095. }
  4096. NlResetWriterClientSession( ClientSession );
  4097. } else {
  4098. NlPrintCs(( NL_CRITICAL, ClientSession,
  4099. " Cannot Zero LastAuth since cannot become writer.\n" ));
  4100. }
  4101. NlUnrefClientSession( ClientSession );
  4102. }
  4103. //
  4104. // Mark each entry to indicate we've not tried to authenticate recently
  4105. //
  4106. LOCK_TRUST_LIST( DomainInfo );
  4107. for ( ListEntry = DomainInfo->DomTrustList.Flink ;
  4108. ListEntry != &DomainInfo->DomTrustList ;
  4109. ListEntry = ListEntry->Flink) {
  4110. ClientSession = CONTAINING_RECORD( ListEntry,
  4111. CLIENT_SESSION,
  4112. CsNext );
  4113. //
  4114. // Flag each entry to indicate it needs to be processed
  4115. //
  4116. // There may be multiple threads in this routine simultaneously.
  4117. // Each thread will set CS_ZERO_LAST_AUTH. Only one thread needs
  4118. // to do the work.
  4119. //
  4120. ClientSession->CsFlags |= CS_ZERO_LAST_AUTH;
  4121. }
  4122. for ( ListEntry = DomainInfo->DomTrustList.Flink ;
  4123. ListEntry != &DomainInfo->DomTrustList ;
  4124. ) {
  4125. ClientSession = CONTAINING_RECORD( ListEntry,
  4126. CLIENT_SESSION,
  4127. CsNext );
  4128. //
  4129. // If we've already done this entry,
  4130. // skip this entry.
  4131. //
  4132. if ( (ClientSession->CsFlags & CS_ZERO_LAST_AUTH) == 0 ) {
  4133. ListEntry = ListEntry->Flink;
  4134. continue;
  4135. }
  4136. ClientSession->CsFlags &= ~CS_ZERO_LAST_AUTH;
  4137. //
  4138. // Reference this entry while doing the work.
  4139. // Unlock the trust list to keep the locking order right.
  4140. //
  4141. NlRefClientSession( ClientSession );
  4142. UNLOCK_TRUST_LIST( DomainInfo );
  4143. //
  4144. // Become a writer to ensure that another thread won't set the
  4145. // last auth time because it just finished a failed discovery.
  4146. //
  4147. if ( NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
  4148. if ( ClientSession->CsState != CS_AUTHENTICATED ) {
  4149. NlPrintCs(( NL_SESSION_SETUP, ClientSession,
  4150. " Zero LastAuth\n" ));
  4151. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  4152. ClientSession->CsLastAuthenticationTry.QuadPart = 0;
  4153. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  4154. }
  4155. NlResetWriterClientSession( ClientSession );
  4156. } else {
  4157. NlPrintCs(( NL_CRITICAL, ClientSession,
  4158. " Cannot Zero LastAuth since cannot become writer.\n" ));
  4159. }
  4160. //
  4161. // Since we dropped the trust list lock,
  4162. // we'll start the search from the front of the list.
  4163. //
  4164. NlUnrefClientSession( ClientSession );
  4165. LOCK_TRUST_LIST( DomainInfo );
  4166. ListEntry = DomainInfo->DomTrustList.Flink ;
  4167. }
  4168. UNLOCK_TRUST_LIST( DomainInfo );
  4169. return NO_ERROR;
  4170. UNREFERENCED_PARAMETER( Context );
  4171. }
  4172. VOID
  4173. NlFlushCacheOnPnp (
  4174. VOID
  4175. )
  4176. /*++
  4177. Routine Description:
  4178. Flush any caches that need to be flush when a new transport comes online
  4179. Arguments:
  4180. None.
  4181. Return Value:
  4182. None
  4183. --*/
  4184. {
  4185. //
  4186. // Flush caches specific to a trusted domain.
  4187. //
  4188. NlEnumerateDomains( FALSE, NlFlushCacheOnPnpWorker, NULL );
  4189. //
  4190. // Flush the failure to find a DC.
  4191. //
  4192. NetpDcFlushNegativeCache();
  4193. }
  4194. #ifdef _DC_NETLOGON
  4195. NTSTATUS
  4196. NlUpdateForestTrustList (
  4197. IN PNL_INIT_TRUSTLIST_CONTEXT InitTrustListContext,
  4198. IN PCLIENT_SESSION ClientSession OPTIONAL,
  4199. IN PLSAPR_TRUSTED_DOMAIN_INFORMATION_EX CurrentTrust,
  4200. IN ULONG CsFlags,
  4201. IN ULONG TdFlags,
  4202. IN ULONG ParentIndex,
  4203. IN GUID *DomainGuid OPTIONAL,
  4204. OUT PULONG MyIndex OPTIONAL
  4205. )
  4206. /*++
  4207. Routine Description:
  4208. Update a single in-memory trust list entry to match the LSA.
  4209. Do async discovery on a domain.
  4210. Enter with the domain trust list locked.
  4211. Arguments:
  4212. InitTrustListContext - Context describing the current trust list enumeration
  4213. DomainInfo - Hosted domain to update the trust list for.
  4214. ClientSession - Netlogon trust entry
  4215. NULL implies netlogon isn't interested in this trust object
  4216. CurrentTrust - Description of the trusted domain.
  4217. CsFlags - Flags from the client session structure describing the trust.
  4218. These are CS_ flags.
  4219. TdFlags - Flags from the Trusted domains structure describing the trust.
  4220. These are DS_DOMAIN_ flags.
  4221. ParentIndex - Passes in the Index of the domain that is the parent of this domain
  4222. DomainGuid - GUID of the trusted domain
  4223. MyIndex - Returns the index of this domain
  4224. Return Value:
  4225. Status of the operation.
  4226. --*/
  4227. {
  4228. NTSTATUS Status;
  4229. NET_API_STATUS NetStatus;
  4230. PDS_DOMAIN_TRUSTSW TrustedDomain = NULL;
  4231. ULONG Size;
  4232. ULONG VariableSize;
  4233. UNICODE_STRING NetbiosDomainName;
  4234. UNICODE_STRING DnsDomainName;
  4235. PSID DomainSid;
  4236. ULONG Index;
  4237. //
  4238. // Grab the names that we're actually going to store
  4239. //
  4240. if ( ClientSession == NULL ) {
  4241. if ( CurrentTrust->TrustType == TRUST_TYPE_UPLEVEL ) {
  4242. DnsDomainName = *((PUNICODE_STRING)&CurrentTrust->Name);
  4243. } else {
  4244. RtlInitUnicodeString( &DnsDomainName, NULL );
  4245. }
  4246. NetbiosDomainName = *((PUNICODE_STRING)&CurrentTrust->FlatName);
  4247. DomainSid = CurrentTrust->Sid;
  4248. } else {
  4249. DnsDomainName = ClientSession->CsDnsDomainName;
  4250. NetbiosDomainName = ClientSession->CsNetbiosDomainName;
  4251. DomainSid = ClientSession->CsDomainId;
  4252. }
  4253. //
  4254. // Determine if there is already an entry for this domain.
  4255. //
  4256. for ( Index=0; Index<InitTrustListContext->DomForestTrustListCount; Index++ ) {
  4257. ULONG ThisIsIt;
  4258. TrustedDomain = &((PDS_DOMAIN_TRUSTSW)(InitTrustListContext->BufferDescriptor.Buffer))[Index];
  4259. //
  4260. // Compare against each of the specified parameters.
  4261. // This avoids cases where two domains have similar names. That
  4262. // will most likely happen if two netbios names collide after netbios
  4263. // is turned off.
  4264. //
  4265. ThisIsIt = FALSE;
  4266. if ( DomainSid != NULL &&
  4267. TrustedDomain->DomainSid != NULL ) {
  4268. if ( RtlEqualSid( TrustedDomain->DomainSid, DomainSid ) ) {
  4269. ThisIsIt = TRUE;
  4270. }
  4271. }
  4272. if ( NetbiosDomainName.Length != 0 &&
  4273. TrustedDomain->NetbiosDomainName != NULL ) {
  4274. UNICODE_STRING LocalUnicodeString;
  4275. RtlInitUnicodeString( &LocalUnicodeString, TrustedDomain->NetbiosDomainName );
  4276. if ( RtlEqualDomainName( &NetbiosDomainName,
  4277. &LocalUnicodeString )) {
  4278. ThisIsIt = TRUE;
  4279. } else {
  4280. if ( ThisIsIt ) {
  4281. NlPrintCs((NL_CRITICAL, ClientSession,
  4282. "NlUpdateForestTrustList: Similar trusts have different netbios names: %wZ %wZ\n",
  4283. &NetbiosDomainName,
  4284. &LocalUnicodeString ));
  4285. TrustedDomain = NULL;
  4286. continue;
  4287. }
  4288. }
  4289. }
  4290. if ( DnsDomainName.Length != 0 &&
  4291. TrustedDomain->DnsDomainName != NULL ) {
  4292. UNICODE_STRING LocalUnicodeString;
  4293. RtlInitUnicodeString( &LocalUnicodeString, TrustedDomain->DnsDomainName );
  4294. if ( NlEqualDnsNameU( &DnsDomainName,
  4295. &LocalUnicodeString ) ) {
  4296. ThisIsIt = TRUE;
  4297. } else {
  4298. if ( ThisIsIt ) {
  4299. NlPrintCs((NL_CRITICAL, ClientSession,
  4300. "NlUpdateForestTrustList: Similar trusts have different DNS names: %wZ %wZ\n",
  4301. &DnsDomainName,
  4302. &LocalUnicodeString ));
  4303. TrustedDomain = NULL;
  4304. continue;
  4305. }
  4306. }
  4307. }
  4308. //
  4309. // If we found a match,
  4310. // we're done.
  4311. //
  4312. if ( ThisIsIt ) {
  4313. if ( ARGUMENT_PRESENT( MyIndex )) {
  4314. *MyIndex = Index;
  4315. }
  4316. break;
  4317. }
  4318. TrustedDomain = NULL;
  4319. }
  4320. //
  4321. // If no entry was found,
  4322. // allocate one.
  4323. //
  4324. if ( TrustedDomain == NULL ) {
  4325. Status = NlAllocateForestTrustListEntry (
  4326. &InitTrustListContext->BufferDescriptor,
  4327. &NetbiosDomainName,
  4328. &DnsDomainName,
  4329. 0,
  4330. 0, // Start with no parent index
  4331. CurrentTrust->TrustType,
  4332. 0, // Start with no trust attributes
  4333. DomainSid,
  4334. NULL, // Start with no GUID
  4335. &Size,
  4336. &TrustedDomain );
  4337. if ( !NT_SUCCESS(Status) ) {
  4338. goto Cleanup;
  4339. }
  4340. //
  4341. // Update our context to account for the new entry
  4342. //
  4343. InitTrustListContext->DomForestTrustListSize += Size;
  4344. if ( ARGUMENT_PRESENT( MyIndex )) {
  4345. *MyIndex = InitTrustListContext->DomForestTrustListCount;
  4346. }
  4347. InitTrustListContext->DomForestTrustListCount ++;
  4348. }
  4349. //
  4350. // Update any existing information.
  4351. //
  4352. TrustedDomain->Flags |= TdFlags;
  4353. if ( CsFlags & CS_DOMAIN_IN_FOREST ) {
  4354. TrustedDomain->Flags |= DS_DOMAIN_IN_FOREST;
  4355. }
  4356. if ( (CsFlags & CS_DIRECT_TRUST) &&
  4357. (CurrentTrust->TrustDirection & TRUST_DIRECTION_OUTBOUND) ) {
  4358. TrustedDomain->Flags |= DS_DOMAIN_DIRECT_OUTBOUND;
  4359. }
  4360. if ( (CsFlags & CS_DIRECT_TRUST) &&
  4361. (CurrentTrust->TrustDirection & TRUST_DIRECTION_INBOUND) ) {
  4362. TrustedDomain->Flags |= DS_DOMAIN_DIRECT_INBOUND;
  4363. }
  4364. if ( ParentIndex != 0 ) {
  4365. NlAssert( TrustedDomain->ParentIndex == 0 || TrustedDomain->ParentIndex == ParentIndex );
  4366. TrustedDomain->ParentIndex = ParentIndex;
  4367. }
  4368. TrustedDomain->TrustType = CurrentTrust->TrustType;
  4369. TrustedDomain->TrustAttributes |= CurrentTrust->TrustAttributes;
  4370. if ( DomainGuid != NULL ) {
  4371. TrustedDomain->DomainGuid = *DomainGuid;
  4372. }
  4373. //
  4374. // If this node is at the root of a tree, set its ParentIndex to 0
  4375. //
  4376. //
  4377. if ( (TrustedDomain->Flags & DS_DOMAIN_TREE_ROOT) != 0 &&
  4378. (TrustedDomain->Flags & DS_DOMAIN_IN_FOREST) != 0 ) {
  4379. TrustedDomain->ParentIndex = 0;
  4380. }
  4381. Status = STATUS_SUCCESS;
  4382. Cleanup:
  4383. return Status;
  4384. }
  4385. NTSTATUS
  4386. NlUpdateTrustList (
  4387. IN PNL_INIT_TRUSTLIST_CONTEXT InitTrustListContext,
  4388. IN PDOMAIN_INFO DomainInfo,
  4389. IN PLSAPR_TRUSTED_DOMAIN_INFORMATION_EX CurrentTrust,
  4390. IN BOOLEAN IsTdo,
  4391. IN ULONG Flags,
  4392. IN ULONG ParentIndex,
  4393. IN ULONG TdFlags OPTIONAL,
  4394. IN GUID *DomGuid OPTIONAL,
  4395. OUT PULONG MyIndex OPTIONAL,
  4396. OUT PCLIENT_SESSION *RetClientSession OPTIONAL
  4397. )
  4398. /*++
  4399. Routine Description:
  4400. Update a single in-memory trust list entry to match the LSA.
  4401. Enter with the domain trust list locked.
  4402. Arguments:
  4403. InitTrustListContext - Context describing the current trust list enumeration
  4404. DomainInfo - Hosted domain to update the trust list for.
  4405. CurrentTrust - Description of the trusted domain.
  4406. IsTdo - TRUE if CurrentTrust specifies the information from the TDO itself.
  4407. FALSE if CurrentTrust specifies information crafted from a cross ref object.
  4408. Flags - Flags describing the trust.
  4409. ParentIndex - Passes in the Index of the domain that is the parent of this domain
  4410. MyIndex - Returns the index of this domain
  4411. TdFlags - Flags from the Trusted domains structure describing the trust.
  4412. These are DS_DOMAIN_ flags.
  4413. DomGuid - GUID of the trusted domain
  4414. RetClientSession - If specified and the client session could be found or
  4415. created, a pointer to the client session is returned here.
  4416. ClientSession should be dereferenced using NlUnrefClientSession().
  4417. Return Value:
  4418. Status of the operation.
  4419. --*/
  4420. {
  4421. NTSTATUS Status;
  4422. PLIST_ENTRY ListEntry;
  4423. PCLIENT_SESSION ClientSession = NULL;
  4424. BOOLEAN DeleteTrust = FALSE;
  4425. PUNICODE_STRING DomainName = NULL;
  4426. PUNICODE_STRING DnsDomainName = NULL;
  4427. PSID DomainId = NULL;
  4428. NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType;
  4429. //
  4430. // Initialization
  4431. //
  4432. if ( ARGUMENT_PRESENT( RetClientSession )) {
  4433. *RetClientSession = NULL;
  4434. }
  4435. //
  4436. // Get the individual fields from the trust description.
  4437. //
  4438. DomainName = (PUNICODE_STRING)&CurrentTrust->FlatName;
  4439. if ( DomainName->Length == 0 ) {
  4440. DomainName = NULL;
  4441. }
  4442. if ( CurrentTrust->TrustType == TRUST_TYPE_UPLEVEL ) {
  4443. DnsDomainName = (PUNICODE_STRING)&CurrentTrust->Name;
  4444. if ( DnsDomainName->Length == 0 ) {
  4445. DnsDomainName = NULL;
  4446. }
  4447. Flags |= CS_NT5_DOMAIN_TRUST;
  4448. }
  4449. DomainId = CurrentTrust->Sid;
  4450. //
  4451. // No Client session needs to be establish for direct trust unless it is outbound.
  4452. //
  4453. if ( (Flags & CS_DIRECT_TRUST) &&
  4454. (CurrentTrust->TrustDirection & TRUST_DIRECTION_OUTBOUND) == 0 ) {
  4455. NlPrintDom((NL_MISC, DomainInfo,
  4456. "NlUpdateTrustList: %wZ: trust is not outbound (ignored)\n",
  4457. DomainName ));
  4458. DeleteTrust = TRUE;
  4459. }
  4460. //
  4461. // Ensure we have a domain SID for directly trusted domains
  4462. //
  4463. if ( (Flags & CS_DIRECT_TRUST) != 0 &&
  4464. DomainId == NULL ) {
  4465. NlPrintDom((NL_CRITICAL, DomainInfo,
  4466. "NlUpdateTrustList: %wZ: trust has no SID (ignored)\n",
  4467. DomainName ));
  4468. DeleteTrust = TRUE;
  4469. }
  4470. if ( CurrentTrust->TrustType == TRUST_TYPE_DOWNLEVEL ) {
  4471. SecureChannelType = TrustedDomainSecureChannel;
  4472. } else if ( CurrentTrust->TrustType == TRUST_TYPE_UPLEVEL ) {
  4473. SecureChannelType = TrustedDnsDomainSecureChannel;
  4474. } else {
  4475. NlPrintDom((NL_CRITICAL, DomainInfo,
  4476. "NlUpdateTrustList: %wZ: trust type is neither NT4 nor NT 5 (%ld) (ignored)\n",
  4477. DomainName,
  4478. CurrentTrust->TrustType ));
  4479. DeleteTrust = TRUE;
  4480. }
  4481. if ( CurrentTrust->TrustAttributes & TRUST_ATTRIBUTE_UPLEVEL_ONLY ) {
  4482. NlPrintDom((NL_MISC, DomainInfo,
  4483. "NlUpdateTrustList: %wZ: trust is KERB only (ignored)\n",
  4484. DomainName ));
  4485. DeleteTrust = TRUE;
  4486. }
  4487. //
  4488. // Ensure all of the lengths are within spec. Do this after checking
  4489. // the type so we don't validate trusts we don't use.
  4490. //
  4491. if (!DeleteTrust) {
  4492. BOOLEAN NameBad = FALSE;
  4493. UNICODE_STRING BadName;
  4494. if ( DomainName != NULL &&
  4495. DomainName->Length > DNLEN * sizeof(WCHAR) ) {
  4496. NlPrintDom((NL_CRITICAL, DomainInfo,
  4497. "NlUpdateTrustList: %wZ: Netbios domain name is too long.\n",
  4498. DomainName ));
  4499. BadName = *DomainName;
  4500. NameBad = TRUE;
  4501. }
  4502. if ( DnsDomainName != NULL &&
  4503. DnsDomainName->Length > NL_MAX_DNS_LENGTH * sizeof(WCHAR) ) {
  4504. NlPrintDom((NL_CRITICAL, DomainInfo,
  4505. "NlUpdateTrustList: %wZ: DNS domain name is too long (ignored)\n",
  4506. DnsDomainName ));
  4507. BadName = *DnsDomainName;
  4508. NameBad = TRUE;
  4509. }
  4510. if ( NameBad ) {
  4511. LPWSTR AlertStrings[3];
  4512. //
  4513. // alert admin.
  4514. //
  4515. AlertStrings[0] = DomainInfo->DomUnicodeDomainName;
  4516. AlertStrings[1] = LocalAlloc( 0, BadName.Length + sizeof(WCHAR) );
  4517. if ( AlertStrings[1] != NULL ) {
  4518. RtlCopyMemory( AlertStrings[1],
  4519. BadName.Buffer,
  4520. BadName.Length );
  4521. AlertStrings[1][BadName.Length/sizeof(WCHAR)] = L'\0';
  4522. }
  4523. AlertStrings[2] = NULL; // Needed for RAISE_ALERT_TOO
  4524. //
  4525. // Save the info in the eventlog
  4526. //
  4527. NlpWriteEventlog(
  4528. ALERT_NetLogonTrustNameBad,
  4529. EVENTLOG_ERROR_TYPE,
  4530. DomainId,
  4531. DomainId != NULL ? RtlLengthSid( DomainId ) : 0,
  4532. AlertStrings,
  4533. 2 | NETP_RAISE_ALERT_TOO );
  4534. //
  4535. // For consistency, ensure this trust is deleted.
  4536. //
  4537. DeleteTrust = TRUE;
  4538. }
  4539. }
  4540. //
  4541. // Ensure the SID of the trusted domain isn't the domain sid of this
  4542. // machine.
  4543. //
  4544. if ( DomainId != NULL &&
  4545. RtlEqualSid( DomainId, DomainInfo->DomAccountDomainId )) {
  4546. LPWSTR AlertStrings[3];
  4547. WCHAR AlertDomainName[DNLEN+1];
  4548. //
  4549. // alert admin.
  4550. //
  4551. if ( DomainName == NULL ||
  4552. DomainName->Length > sizeof(AlertDomainName) ) {
  4553. AlertDomainName[0] = L'\0';
  4554. } else {
  4555. RtlCopyMemory( AlertDomainName, DomainName->Buffer, DomainName->Length );
  4556. AlertDomainName[ DomainName->Length / sizeof(WCHAR) ] = L'\0';
  4557. }
  4558. AlertStrings[0] = DomainInfo->DomUnicodeDomainName;
  4559. AlertStrings[1] = AlertDomainName;
  4560. AlertStrings[2] = NULL; // Needed for RAISE_ALERT_TOO
  4561. //
  4562. // Save the info in the eventlog
  4563. //
  4564. NlpWriteEventlog(
  4565. ALERT_NetLogonSidConflict,
  4566. EVENTLOG_ERROR_TYPE,
  4567. DomainId,
  4568. RtlLengthSid( DomainId ),
  4569. AlertStrings,
  4570. 2 | NETP_RAISE_ALERT_TOO );
  4571. }
  4572. //
  4573. // Ensure we have at least some search parameters.
  4574. //
  4575. if ( DomainId == NULL &&
  4576. DomainName == NULL &&
  4577. DnsDomainName == NULL ) {
  4578. //
  4579. // This isn't a fatal error.
  4580. //
  4581. // If DeleteTrust was set above, we have no interest in this TDO.
  4582. // Otherwise, we'll get notified when the TDO gets named.
  4583. //
  4584. // In either case, press on.
  4585. //
  4586. NlPrintDom(( NL_CRITICAL, DomainInfo,
  4587. "NlUpdateTrustList: All parameters are NULL (ignored)\n" ));
  4588. Status = STATUS_SUCCESS;
  4589. goto Cleanup;
  4590. }
  4591. //
  4592. // Loop through the trust list finding the right entry.
  4593. //
  4594. // LOCK_TRUST_LIST( DomainInfo );
  4595. for ( ListEntry = DomainInfo->DomTrustList.Flink ;
  4596. ListEntry != &DomainInfo->DomTrustList ;
  4597. ListEntry = ListEntry->Flink) {
  4598. ULONG ThisIsIt;
  4599. ClientSession = CONTAINING_RECORD( ListEntry, CLIENT_SESSION, CsNext );
  4600. //
  4601. // Compare against each of the specified parameters.
  4602. // This avoids cases where two domains have similar names. That
  4603. // will most likely happen if two netbios names collide after netbios
  4604. // is turned off.
  4605. //
  4606. ThisIsIt = FALSE;
  4607. if ( DomainId != NULL &&
  4608. ClientSession->CsDomainId != NULL ) {
  4609. if ( RtlEqualSid( ClientSession->CsDomainId, DomainId ) ) {
  4610. ThisIsIt = TRUE;
  4611. }
  4612. }
  4613. if ( DomainName != NULL &&
  4614. ClientSession->CsNetbiosDomainName.Length != 0 ) {
  4615. if ( RtlEqualDomainName( DomainName,
  4616. &ClientSession->CsNetbiosDomainName )) {
  4617. ThisIsIt = TRUE;
  4618. } else {
  4619. if ( ThisIsIt ) {
  4620. NlPrintCs((NL_CRITICAL, ClientSession,
  4621. "NlUpdateTrustList: Similar trusts have different netbios names: %wZ %wZ\n",
  4622. DomainName,
  4623. &ClientSession->CsNetbiosDomainName ));
  4624. ClientSession = NULL;
  4625. continue;
  4626. }
  4627. }
  4628. }
  4629. if ( DnsDomainName != NULL &&
  4630. ClientSession->CsDnsDomainName.Length != 0 ) {
  4631. if ( NlEqualDnsNameU( DnsDomainName,
  4632. &ClientSession->CsDnsDomainName ) ) {
  4633. ThisIsIt = TRUE;
  4634. } else {
  4635. if ( ThisIsIt ) {
  4636. NlPrintCs((NL_CRITICAL, ClientSession,
  4637. "NlUpdateTrustList: Similar trusts have different DNS names: %wZ %wZ\n",
  4638. DnsDomainName,
  4639. &ClientSession->CsDnsDomainName ));
  4640. ClientSession = NULL;
  4641. continue;
  4642. }
  4643. }
  4644. }
  4645. //
  4646. // If we found a match,
  4647. // we're done.
  4648. //
  4649. if ( ThisIsIt ) {
  4650. break;
  4651. }
  4652. ClientSession = NULL;
  4653. }
  4654. //
  4655. // At this point,
  4656. // DeleteTrust is TRUE if the trust relationship doesn't exist in LSA
  4657. // ClientSession is NULL if the trust relationship doesn't exist in memory
  4658. //
  4659. //
  4660. // If the Trust exists in neither place,
  4661. // ignore this request.
  4662. //
  4663. if ( DeleteTrust && ClientSession == NULL ) {
  4664. // UNLOCK_TRUST_LIST( DomainInfo );
  4665. Status = STATUS_SUCCESS;
  4666. goto Cleanup;
  4667. //
  4668. // If the trust exists in the LSA but not in memory,
  4669. // add the trust entry.
  4670. //
  4671. } else if ( !DeleteTrust && ClientSession == NULL ) {
  4672. ClientSession = NlAllocateClientSession(
  4673. DomainInfo,
  4674. DomainName,
  4675. DnsDomainName,
  4676. DomainId,
  4677. NULL, // No domain GUID
  4678. Flags | CS_NEW_TRUST,
  4679. SecureChannelType,
  4680. CurrentTrust->TrustAttributes );
  4681. if (ClientSession == NULL) {
  4682. // UNLOCK_TRUST_LIST( DomainInfo );
  4683. Status = STATUS_NO_MEMORY;
  4684. goto Cleanup;
  4685. }
  4686. //
  4687. // Link this entry onto the tail of the TrustList.
  4688. // Add reference for us being on the list.
  4689. //
  4690. InsertTailList( &DomainInfo->DomTrustList, &ClientSession->CsNext );
  4691. DomainInfo->DomTrustListLength ++;
  4692. NlRefClientSession( ClientSession );
  4693. NlPrintCs((NL_SESSION_SETUP, ClientSession,
  4694. "NlUpdateTrustList: Added to local trust list\n" ));
  4695. //
  4696. // If the trust exists in memory but not in the LSA,
  4697. // delete the entry.
  4698. //
  4699. } else if ( DeleteTrust && ClientSession != NULL ) {
  4700. NlPrintCs((NL_SESSION_SETUP, ClientSession,
  4701. "NlUpdateTrustList: Deleted from local trust list\n" ));
  4702. NlFreeClientSession( ClientSession );
  4703. ClientSession = NULL;
  4704. //
  4705. // If the trust exists in both places,
  4706. // Mark that the account really is in the LSA.
  4707. //
  4708. } else if ( !DeleteTrust && ClientSession != NULL ) {
  4709. //
  4710. // Update any names that are on the ClientSession structure.
  4711. //
  4712. if ( !NlSetNamesClientSession( ClientSession,
  4713. DomainName,
  4714. DnsDomainName,
  4715. DomainId,
  4716. NULL )) { // No domain GUID
  4717. // UNLOCK_TRUST_LIST( DomainInfo );
  4718. Status = STATUS_NO_MEMORY;
  4719. goto Cleanup;
  4720. }
  4721. ClientSession->CsFlags &= ~CS_NOT_IN_LSA;
  4722. ClientSession->CsFlags |= Flags;
  4723. if ( IsTdo ) {
  4724. ClientSession->CsTrustAttributes = CurrentTrust->TrustAttributes;
  4725. }
  4726. NlRefClientSession( ClientSession );
  4727. NlPrintCs((NL_SESSION_SETUP, ClientSession,
  4728. "NlUpdateTrustList: Already in trust list\n" ));
  4729. }
  4730. //
  4731. // If there is a client session,
  4732. // update it.
  4733. //
  4734. if ( ClientSession != NULL ) {
  4735. //
  4736. // If this is a direct trust,
  4737. // and the directly trusted domain hasn't yet been saved.
  4738. // Save it now.
  4739. //
  4740. if ( (ClientSession->CsFlags & CS_DIRECT_TRUST) != 0 &&
  4741. ClientSession->CsDirectClientSession == NULL ) {
  4742. ClientSession->CsDirectClientSession = ClientSession;
  4743. NlRefClientSession( ClientSession );
  4744. }
  4745. //
  4746. // Save the name of the trusted domain object.
  4747. //
  4748. if ( CurrentTrust->TrustType == TRUST_TYPE_UPLEVEL ) {
  4749. ClientSession->CsTrustName = &ClientSession->CsDnsDomainName;
  4750. } else {
  4751. ClientSession->CsTrustName = &ClientSession->CsNetbiosDomainName;
  4752. }
  4753. }
  4754. // UNLOCK_TRUST_LIST( DomainInfo );
  4755. Status = STATUS_SUCCESS;
  4756. //
  4757. // Cleanup locally used resources.
  4758. //
  4759. Cleanup:
  4760. //
  4761. // Update the ForestTrustList
  4762. //
  4763. Status = NlUpdateForestTrustList(
  4764. InitTrustListContext,
  4765. ClientSession,
  4766. CurrentTrust,
  4767. Flags, // CsFlags
  4768. TdFlags, // TdFlags
  4769. ParentIndex,
  4770. DomGuid,
  4771. MyIndex );
  4772. //
  4773. // Return the client session to the caller (if he wants it)
  4774. //
  4775. if ( ClientSession != NULL ) {
  4776. if ( ARGUMENT_PRESENT( RetClientSession )) {
  4777. *RetClientSession = ClientSession;
  4778. } else {
  4779. NlUnrefClientSession( ClientSession );
  4780. }
  4781. }
  4782. return Status;
  4783. }
  4784. #endif //_DC_NETLOGON
  4785. NTSTATUS
  4786. NlAddDomainTreeToTrustList(
  4787. IN PNL_INIT_TRUSTLIST_CONTEXT InitTrustListContext,
  4788. IN PDOMAIN_INFO DomainInfo,
  4789. IN PLSAPR_TREE_TRUST_INFO TreeTrustInfo,
  4790. IN PCLIENT_SESSION ClientSession OPTIONAL,
  4791. IN ULONG ParentIndex
  4792. )
  4793. /*++
  4794. Routine Description:
  4795. Adds each domain in a tree of domains to the in-memory trust list.
  4796. This routine is implemented recursively. It adds the domain at the
  4797. root of the tree then calls itself to each child domain.
  4798. Enter with the domain trust list locked.
  4799. Arguments:
  4800. InitTrustListContext - Context describing the current trust list enumeration
  4801. DomainInfo - Hosted domain to initialize
  4802. TreeTrustInfo - Structure describing the tree of domains to add
  4803. ClientSession - Pointer an existing session. Attempts to pass through
  4804. to domain at the root of TreeTrustInfo should be routed to the
  4805. ClientSession domain (unless we later find that the domain itself has
  4806. a direct trust).
  4807. This parameter may be NULL if the information isn't yet known.
  4808. ParentIndex - Passes in the Index of the domain that is the parent of this domain
  4809. Return Value:
  4810. Status of the operation.
  4811. This routine will add as much of the tree as possible regardless of the
  4812. returned status.
  4813. --*/
  4814. {
  4815. NTSTATUS Status = STATUS_SUCCESS;
  4816. LSAPR_TRUSTED_DOMAIN_INFORMATION_EX TrustInformation;
  4817. UNICODE_STRING PrintableName;
  4818. PCLIENT_SESSION ThisDomainClientSession = NULL;
  4819. ULONG Index;
  4820. ULONG MyIndex;
  4821. //
  4822. // Initialization
  4823. //
  4824. if ( TreeTrustInfo->DnsDomainName.Length != 0 ) {
  4825. PrintableName = *((PUNICODE_STRING)&TreeTrustInfo->DnsDomainName);
  4826. } else {
  4827. PrintableName = *((PUNICODE_STRING)&TreeTrustInfo->FlatName);
  4828. }
  4829. RtlZeroMemory( &TrustInformation, sizeof(TrustInformation) );
  4830. TrustInformation.Name = *((PLSAPR_UNICODE_STRING)&TreeTrustInfo->DnsDomainName);
  4831. TrustInformation.FlatName = *((PLSAPR_UNICODE_STRING)&TreeTrustInfo->FlatName);
  4832. // ?? Big assumption here that bidirectional trust really exists
  4833. // TrustInformation.TrustDirection = TRUST_DIRECTION_BIDIRECTIONAL;
  4834. TrustInformation.TrustType = TRUST_TYPE_UPLEVEL;
  4835. TrustInformation.TrustAttributes = 0;
  4836. TrustInformation.Sid = TreeTrustInfo->DomainSid;
  4837. //
  4838. // Avoid adding a name for ourself
  4839. //
  4840. if ( (TreeTrustInfo->DnsDomainName.Length != 0 &&
  4841. NlEqualDnsNameU( (PUNICODE_STRING)&TreeTrustInfo->DnsDomainName,
  4842. &DomainInfo->DomUnicodeDnsDomainNameString ) ) ||
  4843. RtlEqualDomainName( &DomainInfo->DomUnicodeDomainNameString,
  4844. (PUNICODE_STRING)&TreeTrustInfo->FlatName ) ) {
  4845. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  4846. "NlAddDomainTreeToTrustList: %wZ ignoring enterprise tree entry for ourself\n",
  4847. &PrintableName ));
  4848. TrustInformation.Sid = DomainInfo->DomAccountDomainId;
  4849. //
  4850. // At least add this domain to the forest trust list
  4851. //
  4852. Status = NlUpdateForestTrustList (
  4853. InitTrustListContext,
  4854. NULL, // There is no client session for ourself.
  4855. &TrustInformation,
  4856. CS_DOMAIN_IN_FOREST, // Indicate this domain is in the forest
  4857. DS_DOMAIN_PRIMARY |
  4858. ( (TreeTrustInfo->Flags & LSAI_FOREST_ROOT_TRUST) ?
  4859. DS_DOMAIN_TREE_ROOT :
  4860. 0),
  4861. ParentIndex,
  4862. ( (TreeTrustInfo->Flags & LSAI_FOREST_DOMAIN_GUID_PRESENT) ?
  4863. &TreeTrustInfo->DomainGuid :
  4864. NULL),
  4865. &MyIndex );
  4866. if ( !NT_SUCCESS(Status) ) {
  4867. goto Cleanup;
  4868. }
  4869. //
  4870. // Build a trust entry describing the domain at the root of the tree.
  4871. //
  4872. } else {
  4873. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  4874. "%wZ: Added from enterprise tree in LSA\n",
  4875. &PrintableName ));
  4876. //
  4877. // Ensure there is a ClientSession for this domain.
  4878. //
  4879. Status = NlUpdateTrustList(
  4880. InitTrustListContext,
  4881. DomainInfo,
  4882. &TrustInformation,
  4883. FALSE, // TrustInformation built from XREF object
  4884. CS_DOMAIN_IN_FOREST, // Indicate this domain is in the forest
  4885. ParentIndex,
  4886. ( (TreeTrustInfo->Flags & LSAI_FOREST_ROOT_TRUST) ?
  4887. DS_DOMAIN_TREE_ROOT :
  4888. 0),
  4889. ( (TreeTrustInfo->Flags & LSAI_FOREST_DOMAIN_GUID_PRESENT) ?
  4890. &TreeTrustInfo->DomainGuid :
  4891. NULL),
  4892. &MyIndex,
  4893. &ThisDomainClientSession );
  4894. if ( !NT_SUCCESS(Status) ) {
  4895. goto Cleanup;
  4896. //
  4897. // Handle sucessfully creating the ClientSession.
  4898. //
  4899. } else if ( ThisDomainClientSession != NULL ) {
  4900. //
  4901. // If we've been told a direct route to this domain,
  4902. // and a more direct route hasn't yet been determined,
  4903. // save the direct route.
  4904. //
  4905. if ( ClientSession != NULL &&
  4906. ThisDomainClientSession->CsDirectClientSession == NULL ) {
  4907. ThisDomainClientSession->CsDirectClientSession = ClientSession;
  4908. NlRefClientSession( ClientSession );
  4909. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  4910. "NlAddDomainTreeToTrustList: Closest path to %wZ is via %ws.\n",
  4911. &PrintableName,
  4912. ClientSession->CsDebugDomainName ));
  4913. }
  4914. //
  4915. // If we have a direct trust to this domain,
  4916. // all children of this domain can be reached through this domain.
  4917. //
  4918. if ( ThisDomainClientSession->CsFlags & CS_DIRECT_TRUST ) {
  4919. ClientSession = ThisDomainClientSession;
  4920. }
  4921. }
  4922. }
  4923. //
  4924. // Loop handling each of the children domains.
  4925. //
  4926. for ( Index=0; Index<TreeTrustInfo->Children; Index++ ) {
  4927. //
  4928. // Add a trust entry for each domain in the tree.
  4929. //
  4930. Status = NlAddDomainTreeToTrustList(
  4931. InitTrustListContext,
  4932. DomainInfo,
  4933. &TreeTrustInfo->ChildDomains[Index],
  4934. ClientSession,
  4935. MyIndex ); // This domain is the parent of its children
  4936. if ( !NT_SUCCESS(Status) ) {
  4937. goto Cleanup;
  4938. }
  4939. }
  4940. Cleanup:
  4941. if ( ThisDomainClientSession != NULL ) {
  4942. NlUnrefClientSession( ThisDomainClientSession );
  4943. }
  4944. return Status;
  4945. }
  4946. // #define DBG_BUILD_FOREST 1
  4947. #ifdef DBG_BUILD_FOREST
  4948. NTSTATUS
  4949. KerbDuplicateString(
  4950. OUT PUNICODE_STRING DestinationString,
  4951. IN OPTIONAL PUNICODE_STRING SourceString
  4952. )
  4953. {
  4954. if ((SourceString == NULL) || (SourceString->Buffer == NULL))
  4955. {
  4956. DestinationString->Buffer = NULL;
  4957. DestinationString->Length = DestinationString->MaximumLength = 0;
  4958. return(STATUS_SUCCESS);
  4959. }
  4960. DestinationString->Buffer = (LPWSTR) MIDL_user_allocate(SourceString->Length + sizeof(WCHAR));
  4961. if (DestinationString->Buffer == NULL)
  4962. {
  4963. return(STATUS_INSUFFICIENT_RESOURCES);
  4964. }
  4965. DestinationString->Length = SourceString->Length;
  4966. DestinationString->MaximumLength = SourceString->Length + sizeof(WCHAR);
  4967. RtlCopyMemory(
  4968. DestinationString->Buffer,
  4969. SourceString->Buffer,
  4970. SourceString->Length
  4971. );
  4972. DestinationString->Buffer[SourceString->Length/sizeof(WCHAR)] = L'\0';
  4973. return(STATUS_SUCCESS);
  4974. }
  4975. PLSAPR_TREE_TRUST_INFO
  4976. DebugBuildNode(
  4977. IN LPWSTR DnsName
  4978. )
  4979. {
  4980. PLSAPR_TREE_TRUST_INFO TreeTrust;
  4981. UNICODE_STRING TempString;
  4982. //
  4983. // Allocate this node and enough space for several children
  4984. //
  4985. TreeTrust = (PLSAPR_TREE_TRUST_INFO) LocalAlloc( LMEM_ZEROINIT, 10 * sizeof(LSAPR_TREE_TRUST_INFO));
  4986. TreeTrust->ChildDomains = (TreeTrust + 1 );
  4987. RtlInitUnicodeString( &TempString, DnsName );
  4988. KerbDuplicateString( (PUNICODE_STRING)
  4989. &TreeTrust->DnsDomainName,
  4990. &TempString );
  4991. // if ( TempString.Length > DNLEN*sizeof(WCHAR)) {
  4992. TempString.Length = (wcschr( TempString.Buffer, L'.' ) - TempString.Buffer) * sizeof(WCHAR);
  4993. // }
  4994. KerbDuplicateString( (PUNICODE_STRING)
  4995. &TreeTrust->FlatName,
  4996. &TempString );
  4997. return TreeTrust;
  4998. }
  4999. PLSAPR_TREE_TRUST_INFO
  5000. DebugAddChild(
  5001. IN PLSAPR_TREE_TRUST_INFO ParentNode,
  5002. IN LPWSTR DnsName
  5003. )
  5004. {
  5005. PLSAPR_TREE_TRUST_INFO TreeTrust;
  5006. TreeTrust = DebugBuildNode( DnsName );
  5007. ParentNode->ChildDomains[ParentNode->Children] = *TreeTrust;
  5008. ParentNode->Children ++;
  5009. return &ParentNode->ChildDomains[ParentNode->Children-1];
  5010. }
  5011. VOID
  5012. DebugBuildDomainForest(
  5013. OUT PLSAPR_FOREST_TRUST_INFO * ForestInfo
  5014. )
  5015. {
  5016. PLSAPR_TREE_TRUST_INFO RootTrust;
  5017. PLSAPR_TREE_TRUST_INFO NovTrust;
  5018. PLSAPR_TREE_TRUST_INFO IbmTrust;
  5019. PLSAPR_TREE_TRUST_INFO Trust1;
  5020. PLSAPR_TREE_TRUST_INFO Trust2;
  5021. PLSAPR_TREE_TRUST_INFO Trust3;
  5022. PLSAPR_TREE_TRUST_INFO Trust4;
  5023. PLSAPR_TREE_TRUST_INFO Trust5;
  5024. PLSAPR_FOREST_TRUST_INFO ForestTrustInfo = NULL;
  5025. PLSAPR_TREE_TRUST_INFO ChildDomains = NULL;
  5026. PLSAPR_TREE_TRUST_INFO ChildRoot = NULL;
  5027. UNICODE_STRING TempString;
  5028. ULONG Index;
  5029. ForestTrustInfo = (PLSAPR_FOREST_TRUST_INFO) MIDL_user_allocate(sizeof(LSAPR_FOREST_TRUST_INFO));
  5030. //
  5031. // Node at root of tree
  5032. //
  5033. RootTrust = DebugBuildNode( L"microsoft.com" );
  5034. ForestTrustInfo->RootTrust = *RootTrust;
  5035. RootTrust = &ForestTrustInfo->RootTrust;
  5036. //
  5037. // Build Novell
  5038. //
  5039. NovTrust = DebugAddChild( RootTrust, L"novell.com" );
  5040. Trust1 = DebugAddChild( NovTrust, L"a.novell.com" );
  5041. DebugAddChild( Trust1, L"c.a.novell.com" );
  5042. Trust2 = DebugAddChild( NovTrust, L"b.novell.com" );
  5043. DebugAddChild( Trust2, L"d.b.novell.com" );
  5044. //
  5045. // Build IBM
  5046. //
  5047. IbmTrust = DebugAddChild( RootTrust, L"ibm.com" );
  5048. DebugAddChild( IbmTrust, L"sub.ibm.com" );
  5049. //
  5050. // Build Microsoft
  5051. //
  5052. Trust1 = DebugAddChild( RootTrust, L"ntdev.microsoft.com" );
  5053. ForestTrustInfo->ParentDomainReference = Trust1;
  5054. Trust2 = DebugAddChild( Trust1, L"cliffvdom.ntdev.microsoft.com" );
  5055. Trust3 = DebugAddChild( Trust2, L"cliffvchild.cliffvdom.ntdev.microsoft.com" );
  5056. Trust4 = DebugAddChild( Trust3, L"cliffvgrand.cliffvchild.cliffvdom.ntdev.microsoft.com" );
  5057. Trust2 = DebugAddChild( Trust1, L"cliffvsib.ntdev.microsoft.com" );
  5058. Trust3 = DebugAddChild( Trust2, L"cliffvsibchild.cliffvsib.ntdev.microsoft.com" );
  5059. //
  5060. // Build Compaq
  5061. Trust1 = DebugAddChild( RootTrust, L"compaq.com" );
  5062. *ForestInfo = ForestTrustInfo;
  5063. }
  5064. VOID
  5065. DebugFillInTrust(
  5066. PLSAPR_TRUSTED_DOMAIN_INFORMATION_EX TrustEntry,
  5067. LPWSTR DnsName
  5068. )
  5069. {
  5070. UNICODE_STRING TempString;
  5071. RtlInitUnicodeString( &TempString, DnsName );
  5072. KerbDuplicateString( (PUNICODE_STRING)
  5073. &TrustEntry->Name,
  5074. &TempString );
  5075. if ( TempString.Length > DNLEN*sizeof(WCHAR)) {
  5076. TempString.Length = (wcschr( TempString.Buffer, L'.' ) - TempString.Buffer) * sizeof(WCHAR);
  5077. }
  5078. KerbDuplicateString( (PUNICODE_STRING)
  5079. &TrustEntry->FlatName,
  5080. &TempString );
  5081. // ?? Big assumption here that bidirectional trust really exists
  5082. TrustEntry->TrustDirection = TRUST_DIRECTION_BIDIRECTIONAL;
  5083. TrustEntry->TrustType = TRUST_TYPE_UPLEVEL;
  5084. TrustEntry->TrustAttributes = 0;
  5085. }
  5086. VOID
  5087. DebugBuildDomainTrust(
  5088. PLSAPR_TRUSTED_ENUM_BUFFER_EX TrustInfo
  5089. )
  5090. {
  5091. PLSAPR_TRUSTED_DOMAIN_INFORMATION_EX TrustEntry;
  5092. TrustInfo->EntriesRead = 0;
  5093. TrustEntry = TrustInfo->EnumerationBuffer =
  5094. LocalAlloc( LMEM_ZEROINIT, 10*sizeof(LSAPR_TRUSTED_DOMAIN_INFORMATION_EX));
  5095. DebugFillInTrust( TrustEntry, L"ntdev.microsoft.com" );
  5096. TrustInfo->EntriesRead++;
  5097. TrustEntry++;
  5098. DebugFillInTrust( TrustEntry, L"cliffvchild.cliffvdom.ntdev.microsoft.com" );
  5099. TrustInfo->EntriesRead++;
  5100. TrustEntry++;
  5101. // Build a downlevel trust
  5102. DebugFillInTrust( TrustEntry, L"redmond.cliffvdom.ntdev.microsoft.com" );
  5103. TrustInfo->EntriesRead++;
  5104. TrustEntry->TrustType = TRUST_TYPE_DOWNLEVEL;
  5105. TrustEntry++;
  5106. return;
  5107. }
  5108. #endif // DBG_BUILD_FOREST
  5109. NTSTATUS
  5110. NlInitTrustList(
  5111. IN PDOMAIN_INFO DomainInfo
  5112. )
  5113. /*++
  5114. Routine Description:
  5115. Initialize the in-memory trust list to match LSA's version.
  5116. Arguments:
  5117. DomainInfo - Hosted domain to initialize
  5118. Return Value:
  5119. Status of the operation.
  5120. --*/
  5121. {
  5122. NTSTATUS Status = STATUS_SUCCESS;
  5123. LSA_ENUMERATION_HANDLE EnumerationContext = 0;
  5124. LSAPR_TRUSTED_ENUM_BUFFER_EX LsaTrustList;
  5125. PLSAPR_TRUSTED_DOMAIN_INFORMATION_EX CurrentTrust;
  5126. PLSAPR_FOREST_TRUST_INFO ForestInfo = NULL;
  5127. ULONG Index;
  5128. PCLIENT_SESSION ClientSession;
  5129. PCLIENT_SESSION ParentClientSession = NULL;
  5130. NL_INIT_TRUSTLIST_CONTEXT InitTrustListContext;
  5131. PLIST_ENTRY ListEntry;
  5132. //
  5133. // Avoid initializing the trust list in the setup mode.
  5134. // We may not fully function as a DC as in the case of
  5135. // a NT4 to NT5 DC upgrade.
  5136. //
  5137. if ( NlDoingSetup() ) {
  5138. NlPrint(( NL_MISC, "NlInitTrustList: avoid trust init in setup mode\n" ));
  5139. return STATUS_SUCCESS;
  5140. }
  5141. //
  5142. // Initialization
  5143. //
  5144. RtlZeroMemory( &LsaTrustList, sizeof(LsaTrustList) );
  5145. InitTrustListContext.BufferDescriptor.Buffer = NULL;
  5146. InitTrustListContext.DomForestTrustListSize = 0;
  5147. InitTrustListContext.DomForestTrustListCount = 0;
  5148. //
  5149. // Mark each entry in the trust list for deletion
  5150. // Keep the trust list locked for the duration since I temporarily
  5151. // clear several fields.
  5152. //
  5153. LOCK_TRUST_LIST( DomainInfo );
  5154. //
  5155. // Set the NlGlobalTrustInfoUpToDateEvent event so that any waiting
  5156. // thread that was waiting to access the trust info will be waked up.
  5157. //
  5158. if ( !SetEvent( NlGlobalTrustInfoUpToDateEvent ) ) {
  5159. NlPrint((NL_CRITICAL,
  5160. "Cannot set NlGlobalTrustInfoUpToDateEvent event: %lu\n",
  5161. GetLastError() ));
  5162. }
  5163. //
  5164. // In the following loop we are clearing the fields in all client sessions
  5165. // which (fields) pertain to the structure of the forest:
  5166. //
  5167. // * The CS_DIRECT_TRUST and CS_DOMAIN_IN_FOREST bits which specify
  5168. // the relation of the trust (represented by the client session
  5169. // in question) to the forest we are in.
  5170. // * The CsDirectClientSession field that specifies the client session
  5171. // to use to pass a logon destined to the domain represented by the
  5172. // client session in question.
  5173. //
  5174. // We will reset these fields as we rebuild the trust info below. However,
  5175. // it's possible that we may fail to reset the fields for some of the client
  5176. // sessions due a critical error (no memory) encountered in the process of
  5177. // rebuilding. In such case we will end up with some client session without
  5178. // these fields set. While this may result in failures to pass logons to
  5179. // the affected domains, it will not result in inconsistent forest structure
  5180. // (that could be quite harmful in case pass-through loops are created (due
  5181. // to wrong values for CsDirectClientSession links) leading to potentially
  5182. // infinite looping of logons within the loops). The CsDirectClientSession
  5183. // link will either represent the right pass-through direction or no direction
  5184. // at all. In case of critical error we will reset the event to rebuild the
  5185. // trust info later so that we hopefully completely recover at that time.
  5186. //
  5187. for ( ListEntry = DomainInfo->DomTrustList.Flink ;
  5188. ListEntry != &DomainInfo->DomTrustList ;
  5189. ListEntry = ListEntry->Flink) {
  5190. ClientSession = CONTAINING_RECORD( ListEntry, CLIENT_SESSION, CsNext );
  5191. ClientSession->CsFlags |= CS_NOT_IN_LSA;
  5192. //
  5193. // Remove the direct trust bit.
  5194. // We'll or it back in below as we enumerate trusts.
  5195. ClientSession->CsFlags &= ~CS_DIRECT_TRUST|CS_DOMAIN_IN_FOREST;
  5196. //
  5197. // Forget all of the directly trusted domains.
  5198. // We'll fill it in again later.
  5199. //
  5200. if ( ClientSession->CsDirectClientSession != NULL ) {
  5201. NlUnrefClientSession( ClientSession->CsDirectClientSession );
  5202. ClientSession->CsDirectClientSession = NULL;
  5203. }
  5204. }
  5205. //
  5206. // Loop through the LSA's list of trusted domains
  5207. //
  5208. // For each entry found,
  5209. // If the entry already exits in the trust list,
  5210. // remove the mark for deletion.
  5211. // else
  5212. // allocate a new entry.
  5213. //
  5214. for (;;) {
  5215. //
  5216. // Free any previous buffer returned from LSA.
  5217. //
  5218. #ifndef DBG_BUILD_FOREST
  5219. if ( LsaTrustList.EnumerationBuffer != NULL ) {
  5220. LsaIFree_LSAPR_TRUSTED_ENUM_BUFFER_EX( &LsaTrustList );
  5221. LsaTrustList.EnumerationBuffer = NULL;
  5222. }
  5223. #endif
  5224. //
  5225. // Do the actual enumeration
  5226. //
  5227. GiveInstallHints( FALSE );
  5228. NlPrintDom((NL_SESSION_MORE, DomainInfo,
  5229. "NlInitTrustList: Calling LsarEnumerateTrustedDomainsEx Context=%ld\n",
  5230. EnumerationContext ));
  5231. #ifndef DBG_BUILD_FOREST
  5232. Status = LsarEnumerateTrustedDomainsEx(
  5233. DomainInfo->DomLsaPolicyHandle,
  5234. &EnumerationContext,
  5235. &LsaTrustList,
  5236. 4096);
  5237. #else
  5238. if ( EnumerationContext == 0 ) {
  5239. DebugBuildDomainTrust( &LsaTrustList);
  5240. Status = STATUS_SUCCESS;
  5241. EnumerationContext = 1;
  5242. } else {
  5243. Status = STATUS_NO_MORE_ENTRIES;
  5244. }
  5245. #endif
  5246. NlPrintDom((NL_SESSION_MORE, DomainInfo,
  5247. "NlInitTrustList: returning from LsarEnumerateTrustedDomainsEx Context=%ld %lX\n",
  5248. EnumerationContext,
  5249. Status ));
  5250. //
  5251. // If Lsa says he's returned all of the information,
  5252. // we're done.
  5253. //
  5254. if ( Status == STATUS_NO_MORE_ENTRIES ) {
  5255. break;
  5256. } else if ( !NT_SUCCESS(Status) ) {
  5257. NlPrintDom(( NL_CRITICAL, DomainInfo,
  5258. "NlInitTrustList: Cannot LsarEnumerateTrustedDomainsEx 0x%lX\n",
  5259. Status ));
  5260. goto Cleanup;
  5261. }
  5262. //
  5263. // Ensure the LSA made some progress.
  5264. //
  5265. if ( LsaTrustList.EntriesRead == 0 ) {
  5266. NlPrintDom(( NL_CRITICAL, DomainInfo,
  5267. "NlInitTrustList: LsarEnumerateTrustedDomainsEx returned zero entries\n" ));
  5268. break; // proceed with X-ref enumeration
  5269. }
  5270. //
  5271. // Handle each of the returned trusted domains.
  5272. //
  5273. for ( Index=0; Index< LsaTrustList.EntriesRead; Index++ ) {
  5274. PUNICODE_STRING DnsDomainName;
  5275. PUNICODE_STRING DomainName;
  5276. //
  5277. // Validate the current entry.
  5278. //
  5279. CurrentTrust = &LsaTrustList.EnumerationBuffer[Index];
  5280. DnsDomainName = (PUNICODE_STRING) &(CurrentTrust->Name);
  5281. DomainName = (PUNICODE_STRING) &(CurrentTrust->FlatName);
  5282. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  5283. "%wZ is directly trusted according to LSA.\n",
  5284. DnsDomainName->Length != 0 ? DnsDomainName : DomainName ));
  5285. if ( RtlEqualDomainName( &DomainInfo->DomUnicodeDomainNameString,
  5286. DomainName ) ) {
  5287. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  5288. "NlInitTrustList: %wZ ignoring trust relationship to our own domain\n",
  5289. DomainName ));
  5290. continue;
  5291. }
  5292. //
  5293. // Update the in-memory trust list to match the LSA.
  5294. //
  5295. Status = NlUpdateTrustList(
  5296. &InitTrustListContext,
  5297. DomainInfo,
  5298. CurrentTrust,
  5299. TRUE, // TrustInformation built from TDO object
  5300. CS_DIRECT_TRUST, // We directly trust this domain
  5301. 0, // Don't know the index of my parent
  5302. 0, // No TdFlags
  5303. NULL, // No DomainGuid
  5304. NULL, // Don't care what my index is
  5305. NULL ); // No need to return client session pointer
  5306. if ( !NT_SUCCESS(Status) ) {
  5307. NlPrintDom(( NL_CRITICAL, DomainInfo,
  5308. "NlInitTrustList: %wZ NlUpdateTrustList failed 0x%lx\n",
  5309. DomainName,
  5310. Status ));
  5311. goto Cleanup;
  5312. }
  5313. //
  5314. // If this is an uplevel inbound trust,
  5315. // update the attributes on any existing inbound server session.
  5316. //
  5317. if ( CurrentTrust->TrustType == TRUST_TYPE_UPLEVEL &&
  5318. (CurrentTrust->TrustDirection & TRUST_DIRECTION_INBOUND) != 0 ) {
  5319. //
  5320. // Set the trust attributes on all of the inbound server sessions
  5321. // from this domain.
  5322. //
  5323. NlSetServerSessionAttributesByTdoName( DomainInfo,
  5324. DnsDomainName,
  5325. CurrentTrust->TrustAttributes );
  5326. }
  5327. }
  5328. }
  5329. //
  5330. // Enumerate all the domains in the enterprise.
  5331. // We indirectly trust all of these domains.
  5332. //
  5333. #ifndef DBG_BUILD_FOREST
  5334. Status = LsaIQueryForestTrustInfo(
  5335. DomainInfo->DomLsaPolicyHandle,
  5336. &ForestInfo );
  5337. #else
  5338. DebugBuildDomainForest(&ForestInfo);
  5339. Status = STATUS_SUCCESS;
  5340. #endif
  5341. if (!NT_SUCCESS(Status)) {
  5342. ForestInfo = NULL;
  5343. NlPrintDom(( NL_CRITICAL, DomainInfo,
  5344. "NlInitTrustList: Cannot LsaIQueryForestTrustInfo 0x%lX\n",
  5345. Status ));
  5346. // We aren't part of a tree, all ok
  5347. if (Status == STATUS_OBJECT_NAME_NOT_FOUND) {
  5348. NlPrint(( NL_INIT,
  5349. "This domain is not part of a tree so domain tree ignored\n" ));
  5350. Status = STATUS_SUCCESS;
  5351. // We're running the non-DS version of LSA
  5352. } else if (Status == STATUS_INVALID_DOMAIN_STATE) {
  5353. NlPrint(( NL_INIT,
  5354. "DS isn't running so domain tree ignored\n" ));
  5355. Status = STATUS_SUCCESS;
  5356. }
  5357. goto Cleanup;
  5358. }
  5359. //
  5360. // Process the tree of trusts that makes up the forest.
  5361. //
  5362. // The LSA identifies the domain that is the parent of this domain.
  5363. //
  5364. // All domains starting at all roots can be reached via our parent domain.
  5365. //
  5366. if ( ForestInfo->ParentDomainReference == NULL ) {
  5367. NlPrintDom((NL_SESSION_SETUP, DomainInfo,
  5368. "NlInitTrustList: This domain has no parent in forest.\n" ));
  5369. ParentClientSession = NULL;
  5370. } else {
  5371. PUNICODE_STRING ParentName;
  5372. if ( ForestInfo->ParentDomainReference->DnsDomainName.Length != 0 ) {
  5373. ParentName = ((PUNICODE_STRING)&ForestInfo->ParentDomainReference->DnsDomainName);
  5374. } else {
  5375. ParentName = ((PUNICODE_STRING)&ForestInfo->ParentDomainReference->FlatName);
  5376. }
  5377. //
  5378. // Find the directly trusted session for the parent.
  5379. //
  5380. ParentClientSession = NlFindNamedClientSession(
  5381. DomainInfo,
  5382. ParentName,
  5383. NL_DIRECT_TRUST_REQUIRED,
  5384. NULL );
  5385. if ( ParentClientSession == NULL ) {
  5386. NlPrintDom(( NL_CRITICAL, DomainInfo,
  5387. "NlInitTrustList: Cannot find trust to my parent domain %wZ.\n",
  5388. ParentName ));
  5389. }
  5390. }
  5391. //
  5392. // Remember the parent client session.
  5393. //
  5394. if ( DomainInfo->DomParentClientSession != NULL ) {
  5395. NlUnrefClientSession( DomainInfo->DomParentClientSession );
  5396. DomainInfo->DomParentClientSession = NULL;
  5397. }
  5398. if ( ParentClientSession != NULL ) {
  5399. NlRefClientSession( ParentClientSession );
  5400. DomainInfo->DomParentClientSession = ParentClientSession;
  5401. }
  5402. //
  5403. // Add the domain tree to the trust list.
  5404. //
  5405. Status = NlAddDomainTreeToTrustList(
  5406. &InitTrustListContext,
  5407. DomainInfo,
  5408. &ForestInfo->RootTrust,
  5409. ParentClientSession,
  5410. 0 ); // The Forest root has no parent
  5411. if ( !NT_SUCCESS(Status) ) {
  5412. NlPrintDom(( NL_CRITICAL, DomainInfo,
  5413. "NlInitTrustList: NlAddDomainTreeToTrustList failed 0x%lx\n",
  5414. Status ));
  5415. goto Cleanup;
  5416. }
  5417. //
  5418. // Delete any trust list entry that no longer exists in LSA.
  5419. //
  5420. for ( ListEntry = DomainInfo->DomTrustList.Flink ;
  5421. ListEntry != &DomainInfo->DomTrustList ;
  5422. ) {
  5423. PCLIENT_SESSION ClientSession;
  5424. ClientSession = CONTAINING_RECORD( ListEntry, CLIENT_SESSION, CsNext );
  5425. ListEntry = ListEntry->Flink;
  5426. if ( ClientSession->CsFlags & CS_NOT_IN_LSA ) {
  5427. NlPrintCs((NL_SESSION_SETUP, ClientSession,
  5428. "NlInitTrustList: Deleted from local trust list\n" ));
  5429. NlFreeClientSession( ClientSession );
  5430. }
  5431. }
  5432. //
  5433. // Swap in the new forest trust list
  5434. // (May null out ForestTrustList).
  5435. //
  5436. NlSetForestTrustList ( DomainInfo,
  5437. (PDS_DOMAIN_TRUSTSW *) &InitTrustListContext.BufferDescriptor.Buffer,
  5438. InitTrustListContext.DomForestTrustListSize,
  5439. InitTrustListContext.DomForestTrustListCount );
  5440. //
  5441. // We have successfully initilized the trust list
  5442. //
  5443. Status = STATUS_SUCCESS;
  5444. Cleanup:
  5445. //
  5446. // If there was an error, reset the TrustInfoUpToDate event so that the
  5447. // scavenger (that checks if the event is set) will call this function
  5448. // again to redo the work. It is possible that the scavenger can call
  5449. // this function unapproprietly when the event was just set by LSA and
  5450. // we didn't dispatch this work item yet in which case this function
  5451. // will be called twice performing the same task. We'll live with that
  5452. // since chances of this happening are very small and doing the task
  5453. // twice does not cause any real error (just a perfomance hit).
  5454. //
  5455. if ( !NT_SUCCESS(Status) ) {
  5456. NlPrint((NL_MISC,
  5457. "NlInitTrustList: Reseting NlGlobalTrustInfoUpToDateEvent on error.\n"));
  5458. if ( !ResetEvent( NlGlobalTrustInfoUpToDateEvent ) ) {
  5459. NlPrint((NL_CRITICAL,
  5460. "Cannot reset NlGlobalTrustInfoUpToDateEvent event: %lu\n",
  5461. GetLastError() ));
  5462. }
  5463. }
  5464. UNLOCK_TRUST_LIST( DomainInfo );
  5465. //
  5466. // Find a DC for all of the newly added trusts.
  5467. //
  5468. NlPickTrustedDcForEntireTrustList( DomainInfo, TRUE );
  5469. //
  5470. // Free locally used resources.
  5471. //
  5472. if ( ParentClientSession != NULL ) {
  5473. NlUnrefClientSession( ParentClientSession );
  5474. }
  5475. #ifndef DBG_BUILD_FOREST
  5476. if ( ForestInfo != NULL ) {
  5477. LsaIFreeForestTrustInfo( ForestInfo );
  5478. }
  5479. LsaIFree_LSAPR_TRUSTED_ENUM_BUFFER_EX( &LsaTrustList );
  5480. #endif // DBG_BUILD_FOREST
  5481. if ( InitTrustListContext.BufferDescriptor.Buffer != NULL ) {
  5482. NetApiBufferFree( InitTrustListContext.BufferDescriptor.Buffer );
  5483. }
  5484. return Status;
  5485. }
  5486. NTSTATUS
  5487. NlCaptureNetbiosServerClientSession (
  5488. IN PCLIENT_SESSION ClientSession,
  5489. OUT WCHAR NetbiosUncServerName[UNCLEN+1]
  5490. )
  5491. /*++
  5492. Routine Description:
  5493. Captures a copy of the Netbios UNC server name for the client session.
  5494. NOTE: This routine isn't currently used.
  5495. On Entry,
  5496. The trust list must NOT be locked.
  5497. The trust list entry must be referenced by the caller.
  5498. The caller must NOT be a writer of the trust list entry.
  5499. Arguments:
  5500. ClientSession - Specifies a pointer to the trust list entry to use.
  5501. UncServerName - Returns the UNC name of the server for this client session.
  5502. If there is none, NULL is returned.
  5503. Returned string should be free using NetApiBufferFree.
  5504. Return Value:
  5505. STATUS_SUCCESS - Server name was successfully copied.
  5506. Otherwise - Status of the secure channel
  5507. --*/
  5508. {
  5509. NTSTATUS Status;
  5510. LPWSTR UncServerName = NULL;
  5511. DWORD NetbiosUncServerNameLength;
  5512. //
  5513. // Grab the DNS or netbios name
  5514. //
  5515. Status = NlCaptureServerClientSession( ClientSession, &UncServerName, NULL );
  5516. if ( !NT_SUCCESS(Status) ) {
  5517. goto Cleanup;
  5518. }
  5519. //
  5520. // Convert the DNS hostname to Netbios Computername
  5521. //
  5522. NetbiosUncServerName[0] = '\\';
  5523. NetbiosUncServerName[1] = '\\';
  5524. NetbiosUncServerNameLength = CNLEN+1;
  5525. if ( !DnsHostnameToComputerNameW( UncServerName+2,
  5526. NetbiosUncServerName+2,
  5527. &NetbiosUncServerNameLength ) ) {
  5528. Status = NetpApiStatusToNtStatus( GetLastError() );
  5529. NlPrintCs(( NL_CRITICAL, ClientSession,
  5530. "Cannot convert DNS to Netbios %ws 0x%lx\n",
  5531. UncServerName+2,
  5532. Status ));
  5533. goto Cleanup;
  5534. }
  5535. Status = STATUS_SUCCESS;
  5536. Cleanup:
  5537. if ( UncServerName != NULL ) {
  5538. NetApiBufferFree( UncServerName );
  5539. }
  5540. return Status;
  5541. }
  5542. NTSTATUS
  5543. NlCaptureServerClientSession (
  5544. IN PCLIENT_SESSION ClientSession,
  5545. OUT LPWSTR *UncServerName,
  5546. OUT DWORD *DiscoveryFlags OPTIONAL
  5547. )
  5548. /*++
  5549. Routine Description:
  5550. Captures a copy of the UNC server name for the client session.
  5551. On Entry,
  5552. The trust list must NOT be locked.
  5553. The trust list entry must be referenced by the caller.
  5554. The caller must NOT be a writer of the trust list entry.
  5555. Arguments:
  5556. ClientSession - Specifies a pointer to the trust list entry to use.
  5557. UncServerName - Returns the UNC name of the server for this client session.
  5558. If there is none, NULL is returned.
  5559. Returned string should be free using NetApiBufferFree.
  5560. DiscoveryFlags - Returns discovery flags
  5561. Return Value:
  5562. STATUS_SUCCESS - Server name was successfully copied.
  5563. Otherwise - Status of the secure channel
  5564. --*/
  5565. {
  5566. NTSTATUS Status;
  5567. NlAssert( ClientSession->CsReferenceCount > 0 );
  5568. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  5569. if ( ClientSession->CsState == CS_IDLE ) {
  5570. Status = ClientSession->CsConnectionStatus;
  5571. *UncServerName = NULL;
  5572. goto Cleanup;
  5573. }
  5574. Status = STATUS_SUCCESS;
  5575. NlAssert( ClientSession->CsUncServerName != NULL );
  5576. *UncServerName = NetpAllocWStrFromWStr( ClientSession->CsUncServerName );
  5577. if ( *UncServerName == NULL ) {
  5578. Status = STATUS_NO_MEMORY;
  5579. goto Cleanup;
  5580. }
  5581. if ( DiscoveryFlags != NULL ) {
  5582. *DiscoveryFlags = ClientSession->CsDiscoveryFlags;
  5583. }
  5584. Cleanup:
  5585. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  5586. if ( Status != STATUS_SUCCESS && *UncServerName != NULL ) {
  5587. NetApiBufferFree( *UncServerName );
  5588. *UncServerName = NULL;
  5589. }
  5590. return Status;
  5591. }
  5592. #ifdef _DC_NETLOGON
  5593. NET_API_STATUS
  5594. NlPreparePingContext (
  5595. IN PCLIENT_SESSION ClientSession,
  5596. IN LPWSTR AccountName,
  5597. IN ULONG AllowableAccountControlBits,
  5598. OUT LPWSTR *ReturnedQueriedDcName,
  5599. OUT PNL_GETDC_CONTEXT *PingContext
  5600. )
  5601. /*++
  5602. Routine Description:
  5603. Initialize the ping context structure using client session info
  5604. Arguments:
  5605. ClientSession - The client session info.
  5606. AccountName - Name of our user account to find.
  5607. AllowableAccountControlBits - A mask of allowable SAM account types that
  5608. are allowed to satisfy this request.
  5609. ReturnedQueriedDcName - Returns the server name that will be pinged
  5610. using this ping context. Should be deallocated by calling
  5611. NetApiBufferFree.
  5612. PingContext - Returns the Context structure that can be used to perform
  5613. the pings. The returned structure should be freed by calling
  5614. NlFreePingContext.
  5615. Return Value:
  5616. Pointer to referenced ClientSession structure describing the secure channel
  5617. to the domain containing the account.
  5618. The returned ClientSession is referenced and should be unreferenced
  5619. using NlUnrefClientSession.
  5620. NULL - DC was not found.
  5621. --*/
  5622. {
  5623. NTSTATUS Status;
  5624. NET_API_STATUS NetStatus;
  5625. ULONG DiscoveryFlags = 0;
  5626. ULONG InternalFlags = 0;
  5627. ULONG Flags = 0;
  5628. PNL_GETDC_CONTEXT Context = NULL;
  5629. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  5630. Status = NlCaptureServerClientSession(
  5631. ClientSession,
  5632. ReturnedQueriedDcName,
  5633. &DiscoveryFlags );
  5634. if ( !NT_SUCCESS(Status) ) {
  5635. NetStatus = NetpNtStatusToApiStatus( Status );
  5636. goto Cleanup;
  5637. }
  5638. //
  5639. // Set the ping flags based on the type of the account
  5640. //
  5641. if ( DiscoveryFlags & CS_DISCOVERY_DNS_SERVER ) {
  5642. InternalFlags |= DS_PING_DNS_HOST;
  5643. } else {
  5644. InternalFlags |= DS_PING_NETBIOS_HOST;
  5645. }
  5646. if ( DiscoveryFlags & CS_DISCOVERY_USE_LDAP ) {
  5647. InternalFlags |= DS_PING_USING_LDAP;
  5648. }
  5649. if ( DiscoveryFlags & CS_DISCOVERY_USE_MAILSLOT ) {
  5650. InternalFlags |= DS_PING_USING_MAILSLOT;
  5651. }
  5652. if ( AllowableAccountControlBits == USER_WORKSTATION_TRUST_ACCOUNT ) {
  5653. InternalFlags |= DS_IS_PRIMARY_DOMAIN;
  5654. }
  5655. if ( AllowableAccountControlBits == USER_SERVER_TRUST_ACCOUNT ) {
  5656. Flags |= DS_PDC_REQUIRED;
  5657. InternalFlags |= DS_IS_PRIMARY_DOMAIN;
  5658. }
  5659. InternalFlags |= DS_IS_TRUSTED_DOMAIN;
  5660. //
  5661. // Initialize the ping context.
  5662. //
  5663. NetStatus = NetApiBufferAllocate( sizeof(*Context), &Context );
  5664. if ( NetStatus != NO_ERROR ) {
  5665. goto Cleanup;
  5666. }
  5667. NetStatus = NetpDcInitializeContext(
  5668. ClientSession->CsDomainInfo, // SendDatagramContext
  5669. ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
  5670. #ifdef DONT_REQUIRE_MACHINE_ACCOUNT // useful for number of trust testing
  5671. NULL,
  5672. #else // DONT_REQUIRE_MACHINE_ACCOUNT
  5673. AccountName,
  5674. #endif // DONT_REQUIRE_MACHINE_ACCOUNT
  5675. AllowableAccountControlBits,
  5676. ClientSession->CsNetbiosDomainName.Buffer,
  5677. ClientSession->CsDnsDomainName.Buffer,
  5678. NULL,
  5679. ClientSession->CsDomainId,
  5680. ClientSession->CsDomainGuid,
  5681. NULL,
  5682. (*ReturnedQueriedDcName) + 2, // Skip '\\' in the DC name
  5683. (ClientSession->CsServerSockAddr.iSockaddrLength != 0) ? // Socket addresses
  5684. &ClientSession->CsServerSockAddr :
  5685. NULL,
  5686. (ClientSession->CsServerSockAddr.iSockaddrLength != 0) ? // Number of socket addresses
  5687. 1 :
  5688. 0,
  5689. Flags,
  5690. InternalFlags,
  5691. NL_GETDC_CONTEXT_INITIALIZE_FLAGS | NL_GETDC_CONTEXT_INITIALIZE_PING,
  5692. Context );
  5693. if ( NetStatus != NO_ERROR ) {
  5694. NlPrintCs(( NL_CRITICAL, ClientSession,
  5695. "NlPickDomainWithAccountViaPing: Cannot NetpDcInitializeContext 0x%lx\n",
  5696. NetStatus ));
  5697. NlFreePingContext( Context );
  5698. goto Cleanup;
  5699. }
  5700. Cleanup:
  5701. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  5702. if ( NetStatus == NO_ERROR ) {
  5703. *PingContext = Context;
  5704. }
  5705. return NetStatus;
  5706. }
  5707. PCLIENT_SESSION
  5708. NlPickDomainWithAccountViaPing (
  5709. IN PDOMAIN_INFO DomainInfo,
  5710. IN LPWSTR AccountName,
  5711. IN ULONG AllowableAccountControlBits
  5712. )
  5713. /*++
  5714. Routine Description:
  5715. Get the name of a trusted domain that defines a particular account.
  5716. Arguments:
  5717. DomainInfo - Domain account is in
  5718. AccountName - Name of our user account to find.
  5719. AllowableAccountControlBits - A mask of allowable SAM account types that
  5720. are allowed to satisfy this request.
  5721. Return Value:
  5722. Pointer to referenced ClientSession structure describing the secure channel
  5723. to the domain containing the account.
  5724. The returned ClientSession is referenced and should be unreferenced
  5725. using NlUnrefClientSession.
  5726. NULL - DC was not found.
  5727. --*/
  5728. {
  5729. NET_API_STATUS NetStatus;
  5730. PCLIENT_SESSION ClientSession;
  5731. PLIST_ENTRY ListEntry;
  5732. DWORD DomainsPending;
  5733. ULONG WaitStartTime;
  5734. BOOL UsedNetbios;
  5735. ULONG PingContextIndex;
  5736. PNL_DC_CACHE_ENTRY NlDcCacheEntry = NULL;
  5737. PNL_GETDC_CONTEXT TrustEntryPingContext;
  5738. //
  5739. // Define a local list of trusted domains.
  5740. //
  5741. ULONG LocalTrustListLength;
  5742. ULONG Index;
  5743. struct _LOCAL_TRUST_LIST {
  5744. //
  5745. // TRUE if ALL processing is finished on this trusted domain.
  5746. //
  5747. BOOLEAN Done;
  5748. //
  5749. // TRUE if at least one discovery has been done on this trusted domain.
  5750. //
  5751. BOOLEAN DiscoveryDone;
  5752. //
  5753. // TRUE if discovery is in progress on this trusted domain.
  5754. //
  5755. BOOLEAN DoingDiscovery;
  5756. //
  5757. // Number of times we need to repeat the current domain discovery
  5758. // or finduser datagram for this current domain.
  5759. //
  5760. DWORD RetriesLeft;
  5761. //
  5762. // Pointer to referenced ClientSession structure for the domain.
  5763. //
  5764. PCLIENT_SESSION ClientSession;
  5765. //
  5766. // Server name for the domain.
  5767. //
  5768. LPWSTR UncServerName;
  5769. //
  5770. // Second server name for the domain.
  5771. //
  5772. LPWSTR UncServerName2;
  5773. //
  5774. // Ping Context for the domain.
  5775. //
  5776. PNL_GETDC_CONTEXT PingContext;
  5777. //
  5778. // Second ping Context for the domain.
  5779. //
  5780. PNL_GETDC_CONTEXT PingContext2;
  5781. } *LocalTrustList = NULL;
  5782. //
  5783. // Allocate a local list of trusted domains.
  5784. //
  5785. LOCK_TRUST_LIST( DomainInfo );
  5786. LocalTrustListLength = DomainInfo->DomTrustListLength;
  5787. LocalTrustList = (struct _LOCAL_TRUST_LIST *) NetpMemoryAllocate(
  5788. LocalTrustListLength * sizeof(struct _LOCAL_TRUST_LIST));
  5789. if ( LocalTrustList == NULL ) {
  5790. UNLOCK_TRUST_LIST( DomainInfo );
  5791. ClientSession = NULL;
  5792. NetStatus = NO_ERROR;
  5793. goto Cleanup;
  5794. }
  5795. //
  5796. // Build a local list of trusted domains we know DCs for.
  5797. //
  5798. Index = 0;
  5799. for ( ListEntry = DomainInfo->DomTrustList.Flink ;
  5800. ListEntry != &DomainInfo->DomTrustList ;
  5801. ListEntry = ListEntry->Flink) {
  5802. ClientSession = CONTAINING_RECORD( ListEntry, CLIENT_SESSION, CsNext );
  5803. //
  5804. // Add this Client Session to the list.
  5805. //
  5806. // Don't do domains in the same forest. We've already handled such
  5807. // domains by going to the GC.
  5808. //
  5809. if ( (ClientSession->CsFlags & (CS_DIRECT_TRUST|CS_DOMAIN_IN_FOREST)) == CS_DIRECT_TRUST ) {
  5810. NlRefClientSession( ClientSession );
  5811. LocalTrustList[Index].ClientSession = ClientSession;
  5812. Index++;
  5813. }
  5814. }
  5815. UNLOCK_TRUST_LIST( DomainInfo );
  5816. LocalTrustListLength = Index;
  5817. //
  5818. // If there are no trusted domains to try,
  5819. // we're done.
  5820. if ( Index == 0 ) {
  5821. ClientSession = NULL;
  5822. NetStatus = NO_ERROR;
  5823. goto Cleanup;
  5824. }
  5825. //
  5826. // Initialize the local trust list.
  5827. //
  5828. for ( Index = 0; Index < LocalTrustListLength; Index ++ ) {
  5829. LocalTrustList[Index].UncServerName = NULL;
  5830. LocalTrustList[Index].UncServerName2 = NULL;
  5831. LocalTrustList[Index].PingContext = NULL;
  5832. LocalTrustList[Index].PingContext2 = NULL;
  5833. //
  5834. // Prepare the ping context. This will fail if the
  5835. // client session is idle.
  5836. //
  5837. NetStatus = NlPreparePingContext ( LocalTrustList[Index].ClientSession,
  5838. AccountName,
  5839. AllowableAccountControlBits,
  5840. &LocalTrustList[Index].UncServerName,
  5841. &LocalTrustList[Index].PingContext );
  5842. //
  5843. // If the client session isn't idle,
  5844. // try sending to the current DC before discovering a new one.
  5845. //
  5846. if ( NetStatus == NO_ERROR ) {
  5847. NlPrintCs(( NL_MISC, LocalTrustList[Index].ClientSession,
  5848. "NlPickDomainWithAccountViaPing: Captured DC %ws\n",
  5849. LocalTrustList[Index].UncServerName ));
  5850. LocalTrustList[Index].RetriesLeft = 3;
  5851. LocalTrustList[Index].DoingDiscovery = FALSE;
  5852. LocalTrustList[Index].DiscoveryDone = FALSE;
  5853. //
  5854. // Otherwise don't try very hard to discover one.
  5855. // (Indeed, just one discovery datagram is all we need.)
  5856. //
  5857. } else {
  5858. //
  5859. // If this is a hard error, error out
  5860. //
  5861. if ( NetStatus == ERROR_NOT_ENOUGH_MEMORY ) {
  5862. ClientSession = NULL;
  5863. goto Cleanup;
  5864. }
  5865. NlPrintCs(( NL_CRITICAL, LocalTrustList[Index].ClientSession,
  5866. "NlPickDomainWithAccountViaPing: Cannot NlPreparePingContext 0x%lx\n",
  5867. NetStatus ));
  5868. LocalTrustList[Index].RetriesLeft = 1;
  5869. LocalTrustList[Index].DoingDiscovery = TRUE;
  5870. LocalTrustList[Index].DiscoveryDone = TRUE;
  5871. }
  5872. //
  5873. // We're not done yet.
  5874. //
  5875. LocalTrustList[Index].Done = FALSE;
  5876. }
  5877. //
  5878. // Try multiple times to get a response from each DC.
  5879. //
  5880. for (;; ) {
  5881. //
  5882. // Send a ping to each domain that has not yet responded.
  5883. //
  5884. DomainsPending = 0;
  5885. for ( Index = 0; Index < LocalTrustListLength; Index ++ ) {
  5886. //
  5887. // If this domain has already responded, ignore it.
  5888. //
  5889. if ( LocalTrustList[Index].Done ) {
  5890. continue;
  5891. }
  5892. //
  5893. // If we don't currently know the DC name for this domain,
  5894. // check if any has been discovered since we started the algorithm.
  5895. //
  5896. if ( LocalTrustList[Index].PingContext == NULL ) {
  5897. //
  5898. // Prepare the ping context. This will fail if
  5899. // the client session is idle.
  5900. //
  5901. NetStatus = NlPreparePingContext ( LocalTrustList[Index].ClientSession,
  5902. AccountName,
  5903. AllowableAccountControlBits,
  5904. &LocalTrustList[Index].UncServerName,
  5905. &LocalTrustList[Index].PingContext );
  5906. //
  5907. // If the client session isn't idle,
  5908. // try sending to the current DC before discovering a new one.
  5909. //
  5910. if ( NetStatus == NO_ERROR ) {
  5911. NlPrintDom((NL_LOGON, DomainInfo,
  5912. "NlPickDomainWithAccount: %ws: Noticed domain %ws has discovered a new DC %ws\n",
  5913. AccountName,
  5914. LocalTrustList[Index].ClientSession->CsDebugDomainName,
  5915. LocalTrustList[Index].UncServerName ));
  5916. //
  5917. // If we did the discovery,
  5918. //
  5919. if ( LocalTrustList[Index].DoingDiscovery ) {
  5920. LocalTrustList[Index].DoingDiscovery = FALSE;
  5921. LocalTrustList[Index].RetriesLeft = 3;
  5922. }
  5923. //
  5924. // Error out on the hard error
  5925. //
  5926. } else if ( NetStatus == ERROR_NOT_ENOUGH_MEMORY ) {
  5927. ClientSession = NULL;
  5928. goto Cleanup;
  5929. }
  5930. }
  5931. //
  5932. // If we have a ping context and retries left, ping the DC
  5933. //
  5934. if ( LocalTrustList[Index].PingContext != NULL &&
  5935. LocalTrustList[Index].RetriesLeft > 0 ) {
  5936. NetStatus = NlPingDcNameWithContext(
  5937. LocalTrustList[Index].PingContext,
  5938. 1, // Send 1 ping
  5939. FALSE, // Do not wait for response
  5940. 0, // Timeout
  5941. NULL, // Don't care which domain name matched
  5942. NULL ); // Don't need the DC info
  5943. //
  5944. // If we cannot send the ping, we are done with this DC.
  5945. //
  5946. if ( NetStatus == ERROR_NO_LOGON_SERVERS ) {
  5947. NlPrint(( NL_CRITICAL,
  5948. "NlPickDomainWithAccount: Cannot ping DC %ws 0x%lx\n",
  5949. LocalTrustList[Index].UncServerName,
  5950. NetStatus ));
  5951. LocalTrustList[Index].RetriesLeft = 0;
  5952. NlFreePingContext( LocalTrustList[Index].PingContext );
  5953. LocalTrustList[Index].PingContext = NULL;
  5954. NetApiBufferFree( LocalTrustList[Index].UncServerName );
  5955. LocalTrustList[Index].UncServerName = NULL;
  5956. //
  5957. // Error out on a hard error
  5958. //
  5959. } else if ( NetStatus != NO_ERROR ) {
  5960. NlPrint(( NL_CRITICAL,
  5961. "NlPickDomainWithAccount: Cannot NlPingDcNameWithContext %ws 0x%lx\n",
  5962. LocalTrustList[Index].UncServerName,
  5963. NetStatus ));
  5964. ClientSession = NULL;
  5965. goto Cleanup;
  5966. }
  5967. }
  5968. //
  5969. // If we're done retrying what we were doing,
  5970. // try something else.
  5971. //
  5972. if ( LocalTrustList[Index].RetriesLeft == 0 ) {
  5973. if ( LocalTrustList[Index].DiscoveryDone ) {
  5974. LocalTrustList[Index].Done = TRUE;
  5975. NlPrintDom((NL_LOGON, DomainInfo,
  5976. "NlPickDomainWithAccount: %ws: Can't find DC for domain %ws (ignore this domain).\n",
  5977. AccountName,
  5978. LocalTrustList[Index].ClientSession->CsDebugDomainName ));
  5979. continue;
  5980. } else {
  5981. //
  5982. // Save the previous DC ping context since it might just
  5983. // be very slow in responding. We'll want to be able
  5984. // to recognize responses from the previous DC.
  5985. //
  5986. LocalTrustList[Index].UncServerName2 = LocalTrustList[Index].UncServerName;
  5987. LocalTrustList[Index].UncServerName = NULL;
  5988. LocalTrustList[Index].PingContext2 = LocalTrustList[Index].PingContext;
  5989. LocalTrustList[Index].PingContext = NULL;
  5990. LocalTrustList[Index].DoingDiscovery = TRUE;
  5991. LocalTrustList[Index].DiscoveryDone = TRUE;
  5992. LocalTrustList[Index].RetriesLeft = 3;
  5993. }
  5994. }
  5995. //
  5996. // If its time to discover a DC in the domain,
  5997. // do it.
  5998. //
  5999. if ( LocalTrustList[Index].DoingDiscovery ) {
  6000. //
  6001. // Discover a new server
  6002. //
  6003. if ( NlTimeoutSetWriterClientSession( LocalTrustList[Index].ClientSession,
  6004. 10*1000 ) ) {
  6005. //
  6006. // Only tear down an existing secure channel once.
  6007. //
  6008. if ( LocalTrustList[Index].RetriesLeft == 3 ) {
  6009. NlSetStatusClientSession( LocalTrustList[Index].ClientSession,
  6010. STATUS_NO_LOGON_SERVERS );
  6011. }
  6012. //
  6013. // We can't afford to wait so only send a single
  6014. // discovery datagram.
  6015. //
  6016. if ( LocalTrustList[Index].ClientSession->CsState == CS_IDLE ) {
  6017. (VOID) NlDiscoverDc( LocalTrustList[Index].ClientSession,
  6018. DT_DeadDomain,
  6019. FALSE,
  6020. FALSE ); // don't specify account
  6021. }
  6022. NlResetWriterClientSession( LocalTrustList[Index].ClientSession );
  6023. }
  6024. }
  6025. //
  6026. // Indicate we're trying something.
  6027. //
  6028. LocalTrustList[Index].RetriesLeft --;
  6029. DomainsPending ++;
  6030. }
  6031. //
  6032. // If all of the domains are done,
  6033. // leave the loop.
  6034. //
  6035. if ( DomainsPending == 0 ) {
  6036. break;
  6037. }
  6038. //
  6039. // See if any DC responds within 5 seconds
  6040. //
  6041. NlPrint(( NL_MISC,
  6042. "NlPickDomainWithAccountViaPing: Waiting for responses\n" ));
  6043. WaitStartTime = GetTickCount();
  6044. while ( DomainsPending > 0 &&
  6045. NetpDcElapsedTime(WaitStartTime) < 5000 ) {
  6046. //
  6047. // Find out which DC responded
  6048. //
  6049. for ( Index = 0; Index < LocalTrustListLength; Index ++ ) {
  6050. if ( LocalTrustList[Index].Done ) {
  6051. continue;
  6052. }
  6053. //
  6054. // Check if a DC has become available if we are
  6055. // doing discovery for this domain. If so, ping it.
  6056. //
  6057. if ( LocalTrustList[Index].DoingDiscovery ) {
  6058. //
  6059. // Prepare the ping context. This will fail if
  6060. // the client session is still idle.
  6061. //
  6062. NetStatus = NlPreparePingContext ( LocalTrustList[Index].ClientSession,
  6063. AccountName,
  6064. AllowableAccountControlBits,
  6065. &LocalTrustList[Index].UncServerName,
  6066. &LocalTrustList[Index].PingContext );
  6067. //
  6068. // If the client session isn't idle,
  6069. // try sending to the current DC.
  6070. //
  6071. if ( NetStatus == NO_ERROR ) {
  6072. LocalTrustList[Index].DoingDiscovery = FALSE;
  6073. NlPrintDom((NL_LOGON, DomainInfo,
  6074. "NlPickDomainWithAccount: %ws: Noticed domain %ws has discovered a new DC %ws\n",
  6075. AccountName,
  6076. LocalTrustList[Index].ClientSession->CsDebugDomainName,
  6077. LocalTrustList[Index].UncServerName ));
  6078. NetStatus = NlPingDcNameWithContext(
  6079. LocalTrustList[Index].PingContext,
  6080. 1, // Send 1 ping
  6081. FALSE, // Do not wait for response
  6082. 0, // Timeout
  6083. NULL, // Don't care which domain name matched
  6084. NULL ); // Don't need the DC info
  6085. LocalTrustList[Index].RetriesLeft = 2; // Already sent 1 ping
  6086. //
  6087. // If we cannot send the ping, we are done with this DC.
  6088. //
  6089. if ( NetStatus == ERROR_NO_LOGON_SERVERS ) {
  6090. NlPrint(( NL_CRITICAL,
  6091. "NlPickDomainWithAccount: Cannot ping DC %ws 0x%lx\n",
  6092. LocalTrustList[Index].UncServerName,
  6093. NetStatus ));
  6094. LocalTrustList[Index].RetriesLeft = 0;
  6095. NlFreePingContext( LocalTrustList[Index].PingContext );
  6096. LocalTrustList[Index].PingContext = NULL;
  6097. NetApiBufferFree( LocalTrustList[Index].UncServerName );
  6098. LocalTrustList[Index].UncServerName = NULL;
  6099. //
  6100. // Error out on a hard error
  6101. //
  6102. } else if ( NetStatus != NO_ERROR ) {
  6103. NlPrint(( NL_CRITICAL,
  6104. "NlPickDomainWithAccount: Cannot NlPingDcNameWithContext %ws 0x%lx\n",
  6105. LocalTrustList[Index].UncServerName,
  6106. NetStatus ));
  6107. ClientSession = NULL;
  6108. goto Cleanup;
  6109. }
  6110. //
  6111. // Error out on the hard error
  6112. //
  6113. } else if ( NetStatus == ERROR_NOT_ENOUGH_MEMORY ) {
  6114. ClientSession = NULL;
  6115. goto Cleanup;
  6116. }
  6117. }
  6118. //
  6119. // Check if the response corresponds to either ping context
  6120. // for this trust entry
  6121. //
  6122. for ( PingContextIndex=0; PingContextIndex<2; PingContextIndex++ ) {
  6123. if ( PingContextIndex == 0 ) {
  6124. TrustEntryPingContext = LocalTrustList[Index].PingContext;
  6125. } else {
  6126. TrustEntryPingContext = LocalTrustList[Index].PingContext2;
  6127. }
  6128. if ( TrustEntryPingContext == NULL ) {
  6129. continue;
  6130. }
  6131. if ( NlDcCacheEntry != NULL ) {
  6132. NetpDcDerefCacheEntry( NlDcCacheEntry );
  6133. NlDcCacheEntry = NULL;
  6134. }
  6135. //
  6136. // Get the response. Set timeout to 0 to avoid
  6137. // waiting for a response if it's not available.
  6138. //
  6139. NetStatus = NetpDcGetPingResponse(
  6140. TrustEntryPingContext,
  6141. 0,
  6142. &NlDcCacheEntry,
  6143. &UsedNetbios );
  6144. //
  6145. // If no error, we've found the domain
  6146. //
  6147. if ( NetStatus == NO_ERROR ) {
  6148. NlPrintDom((NL_MISC, DomainInfo,
  6149. "NlPickDomainWithAccount: %ws has account %ws\n",
  6150. LocalTrustList[Index].ClientSession->CsDebugDomainName,
  6151. AccountName ));
  6152. ClientSession = LocalTrustList[Index].ClientSession;
  6153. goto Cleanup;
  6154. //
  6155. // If there is no such user in the domain, we are
  6156. // done with this trust entry
  6157. //
  6158. } else if ( NetStatus == ERROR_NO_SUCH_USER ) {
  6159. NlPrintDom((NL_CRITICAL, DomainInfo,
  6160. "NlPickDomainWithAccount: %ws responded negatively for account %ws\n",
  6161. LocalTrustList[Index].ClientSession->CsDebugDomainName,
  6162. AccountName ));
  6163. LocalTrustList[Index].RetriesLeft = 0;
  6164. LocalTrustList[Index].Done = TRUE;
  6165. break;
  6166. //
  6167. // Any other response other than wait timeout means
  6168. // that the DC responded with invalid data. We are
  6169. // done with this DC then.
  6170. //
  6171. } else if ( NetStatus != ERROR_SEM_TIMEOUT ) {
  6172. NlPrintDom((NL_CRITICAL, DomainInfo,
  6173. "NlPickDomainWithAccount: %ws invalid response for account %ws\n",
  6174. LocalTrustList[Index].ClientSession->CsDebugDomainName,
  6175. AccountName ));
  6176. //
  6177. // If this is the current DC for this domain,
  6178. // indicate that we should stop pinging it.
  6179. //
  6180. if ( PingContextIndex == 0 ) {
  6181. LocalTrustList[Index].RetriesLeft = 0;
  6182. NlFreePingContext( LocalTrustList[Index].PingContext );
  6183. LocalTrustList[Index].PingContext = NULL;
  6184. NetApiBufferFree( LocalTrustList[Index].UncServerName );
  6185. LocalTrustList[Index].UncServerName = NULL;
  6186. } else {
  6187. NlFreePingContext( LocalTrustList[Index].PingContext2 );
  6188. LocalTrustList[Index].PingContext2 = NULL;
  6189. NetApiBufferFree( LocalTrustList[Index].UncServerName2 );
  6190. LocalTrustList[Index].UncServerName2 = NULL;
  6191. }
  6192. }
  6193. }
  6194. //
  6195. // If we have no ping context for this trust entry
  6196. // and we are not doing a DC discovery for it, we
  6197. // are done with it.
  6198. //
  6199. if ( LocalTrustList[Index].PingContext == NULL &&
  6200. LocalTrustList[Index].PingContext2 == NULL &&
  6201. !LocalTrustList[Index].DoingDiscovery ) {
  6202. NlPrintDom((NL_CRITICAL, DomainInfo,
  6203. "NlPickDomainWithAccount: %ws no ping context for account %ws\n",
  6204. LocalTrustList[Index].ClientSession->CsDebugDomainName,
  6205. AccountName ));
  6206. LocalTrustList[Index].Done = TRUE;
  6207. }
  6208. if ( LocalTrustList[Index].Done ) {
  6209. DomainsPending --;
  6210. }
  6211. }
  6212. //
  6213. // Sleep for a little while waiting for replies
  6214. // (In other words, don't go CPU bound)
  6215. //
  6216. Sleep( NL_DC_MIN_PING_TIMEOUT );
  6217. }
  6218. }
  6219. //
  6220. // No DC has the specified account.
  6221. //
  6222. ClientSession = NULL;
  6223. NetStatus = NO_ERROR;
  6224. //
  6225. // Cleanup locally used resources.
  6226. //
  6227. Cleanup:
  6228. if ( NlDcCacheEntry != NULL ) {
  6229. NetpDcDerefCacheEntry( NlDcCacheEntry );
  6230. }
  6231. //
  6232. // Unreference each client session structure and free the local trust list.
  6233. // (Keep the returned ClientSession referenced).
  6234. //
  6235. if ( LocalTrustList != NULL ) {
  6236. for ( Index=0; Index<LocalTrustListLength; Index++ ) {
  6237. if ( LocalTrustList[Index].UncServerName != NULL ) {
  6238. NetApiBufferFree( LocalTrustList[Index].UncServerName );
  6239. }
  6240. if ( LocalTrustList[Index].UncServerName2 != NULL ) {
  6241. NetApiBufferFree( LocalTrustList[Index].UncServerName2 );
  6242. }
  6243. if ( LocalTrustList[Index].PingContext != NULL ) {
  6244. NlFreePingContext( LocalTrustList[Index].PingContext );
  6245. }
  6246. if ( LocalTrustList[Index].PingContext2 != NULL ) {
  6247. NlFreePingContext( LocalTrustList[Index].PingContext2 );
  6248. }
  6249. if ( ClientSession != LocalTrustList[Index].ClientSession ) {
  6250. NlUnrefClientSession( LocalTrustList[Index].ClientSession );
  6251. }
  6252. }
  6253. NetpMemoryFree(LocalTrustList);
  6254. }
  6255. if ( NetStatus != NO_ERROR && ClientSession == NULL ) {
  6256. NlPrint(( NL_CRITICAL,
  6257. "NlPickDomainWithAccountViaPing failed 0x%lx\n",
  6258. NetStatus ));
  6259. }
  6260. return ClientSession;
  6261. }
  6262. NTSTATUS
  6263. NlLoadNtdsaDll(
  6264. VOID
  6265. )
  6266. /*++
  6267. Routine Description:
  6268. This function loads the ntdsa.dll module if it is not loaded
  6269. already.
  6270. Arguments:
  6271. None
  6272. Return Value:
  6273. NT Status code.
  6274. --*/
  6275. {
  6276. static NTSTATUS DllLoadStatus = STATUS_SUCCESS;
  6277. HANDLE DllHandle = NULL;
  6278. //
  6279. // If the DLL is already loaded,
  6280. // we're done.
  6281. //
  6282. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  6283. if ( NlGlobalNtDsaHandle != NULL ) {
  6284. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  6285. return STATUS_SUCCESS;
  6286. }
  6287. //
  6288. // If we've tried to load the DLL before and it failed,
  6289. // return the same error code again.
  6290. //
  6291. if( DllLoadStatus != STATUS_SUCCESS ) {
  6292. goto Cleanup;
  6293. }
  6294. //
  6295. // Load the dll
  6296. //
  6297. DllHandle = LoadLibraryA( "NtDsa" );
  6298. if ( DllHandle == NULL ) {
  6299. DllLoadStatus = STATUS_DLL_NOT_FOUND;
  6300. goto Cleanup;
  6301. }
  6302. //
  6303. // Macro to grab the address of the named procedure from ntdsa.dll
  6304. //
  6305. #define GRAB_ADDRESS( _X ) \
  6306. NlGlobalp##_X = (P##_X) GetProcAddress( DllHandle, #_X ); \
  6307. \
  6308. if ( NlGlobalp##_X == NULL ) { \
  6309. DllLoadStatus = STATUS_PROCEDURE_NOT_FOUND;\
  6310. goto Cleanup; \
  6311. }
  6312. //
  6313. // Get the addresses of the required procedures.
  6314. //
  6315. GRAB_ADDRESS( CrackSingleName );
  6316. GRAB_ADDRESS( GetConfigurationName );
  6317. GRAB_ADDRESS( GetConfigurationNamesList );
  6318. GRAB_ADDRESS( GetDnsRootAlias );
  6319. GRAB_ADDRESS( DsGetServersAndSitesForNetLogon );
  6320. GRAB_ADDRESS( DsFreeServersAndSitesForNetLogon );
  6321. DllLoadStatus = STATUS_SUCCESS;
  6322. Cleanup:
  6323. if (DllLoadStatus == STATUS_SUCCESS) {
  6324. NlGlobalNtDsaHandle = DllHandle;
  6325. } else {
  6326. if ( DllHandle != NULL ) {
  6327. FreeLibrary( DllHandle );
  6328. }
  6329. }
  6330. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  6331. return( DllLoadStatus );
  6332. }
  6333. NTSTATUS
  6334. NlCrackSingleName(
  6335. DWORD formatOffered, // one of DS_NAME_FORMAT in ntdsapi.h
  6336. BOOL fPerformAtGC, // whether to go to GC or not
  6337. WCHAR *pNameIn, // name to crack
  6338. DWORD formatDesired, // one of DS_NAME_FORMAT in ntdsapi.h
  6339. DWORD *pccDnsDomain, // char count of following argument
  6340. WCHAR *pDnsDomain, // buffer for DNS domain name
  6341. DWORD *pccNameOut, // char count of following argument
  6342. WCHAR *pNameOut, // buffer for formatted name
  6343. DWORD *pErr) // one of DS_NAME_ERROR in ntdsapi.h
  6344. /*++
  6345. Routine Description:
  6346. This routine is a thin wrapper that loads NtDsa.dll then calls
  6347. CrackSingleName.
  6348. Arguments:
  6349. Same as CrackSingleName
  6350. Return Value:
  6351. Same as CrackSingleName
  6352. --*/
  6353. {
  6354. NTSTATUS Status;
  6355. //
  6356. // Ensure ntdsa.dll is loaded.
  6357. //
  6358. Status = NlLoadNtdsaDll();
  6359. if ( NT_SUCCESS(Status) ) {
  6360. //
  6361. // Call the actual function.
  6362. //
  6363. Status = (*NlGlobalpCrackSingleName)(
  6364. formatOffered,
  6365. DS_NAME_FLAG_TRUST_REFERRAL | // Tell CrackSingle name that we understand the DS_NAME_ERROR_TRUST_REFERRAL status code
  6366. (fPerformAtGC ?
  6367. DS_NAME_FLAG_GCVERIFY : 0),
  6368. pNameIn,
  6369. formatDesired,
  6370. pccDnsDomain,
  6371. pDnsDomain,
  6372. pccNameOut,
  6373. pNameOut,
  6374. pErr );
  6375. //
  6376. // CrackSingle name sometimes returns DS_NAME_ERROR_DOMAIN_ONLY after syntactically
  6377. // parsing the name.
  6378. //
  6379. if ( Status == STATUS_SUCCESS &&
  6380. *pErr == DS_NAME_ERROR_DOMAIN_ONLY ) {
  6381. *pErr = DS_NAME_ERROR_NOT_FOUND;
  6382. }
  6383. }
  6384. return Status;
  6385. }
  6386. NTSTATUS
  6387. NlCrackSingleNameEx(
  6388. DWORD formatOffered,
  6389. WCHAR *InGcAccountName OPTIONAL,
  6390. WCHAR *InLsaAccountName OPTIONAL,
  6391. DWORD formatDesired,
  6392. DWORD *CrackedDnsDomainNameLength,
  6393. WCHAR *CrackedDnsDomainName,
  6394. DWORD *CrackedUserNameLength,
  6395. WCHAR *CrackedUserName,
  6396. DWORD *CrackError,
  6397. LPSTR *CrackDebugString
  6398. )
  6399. /*++
  6400. Routine Description:
  6401. This routine tries the crack name in the following places in succession:
  6402. * The cross forest trust cache (at the root of the forest)
  6403. * A local DsCrackName
  6404. * A DsCrackName on the GC.
  6405. Arguments:
  6406. Same as CrackSingleName plus the following
  6407. InGcAccountName - Name to crack on GC.
  6408. If NULL, no name is cracked on GC.
  6409. InLsaAccountName - Name to crack using LsaIForestTrustFindMatch
  6410. If NULL, no name is cracked using LsaIForestTrustFindMatch
  6411. Return Value:
  6412. Same as CrackSingleName
  6413. --*/
  6414. {
  6415. NTSTATUS Status = STATUS_SUCCESS;
  6416. //
  6417. // Save the domain name and user name before we overwrite them
  6418. //
  6419. *CrackDebugString = NULL;
  6420. //
  6421. // If we're a DC the root of the forest,
  6422. // ask LSA if the account is in a trusted forest.
  6423. //
  6424. if ( InLsaAccountName ) {
  6425. UNICODE_STRING InAccountNameString;
  6426. LSA_UNICODE_STRING OutForestName;
  6427. //
  6428. // Match the name to the FTinfo list
  6429. //
  6430. RtlInitUnicodeString( &InAccountNameString, InLsaAccountName );
  6431. *CrackDebugString = "via LsaMatch";
  6432. Status = LsaIForestTrustFindMatch(
  6433. formatOffered == DS_USER_PRINCIPAL_NAME ?
  6434. RoutingMatchUpn :
  6435. RoutingMatchDomainName,
  6436. &InAccountNameString,
  6437. &OutForestName );
  6438. if ( NT_SUCCESS(Status) ) {
  6439. if ( OutForestName.Length + sizeof(WCHAR) <= *CrackedDnsDomainNameLength ) {
  6440. RtlCopyMemory( CrackedDnsDomainName,
  6441. OutForestName.Buffer,
  6442. OutForestName.Length );
  6443. CrackedDnsDomainName[OutForestName.Length/sizeof(WCHAR)] = '\0';
  6444. *CrackedDnsDomainNameLength = OutForestName.Length/sizeof(WCHAR);
  6445. *CrackedUserNameLength = 0;
  6446. *CrackError = DS_NAME_ERROR_TRUST_REFERRAL;
  6447. } else {
  6448. Status = STATUS_BUFFER_TOO_SMALL;
  6449. }
  6450. LsaIFree_LSAPR_UNICODE_STRING_BUFFER( (PLSAPR_UNICODE_STRING)&OutForestName );
  6451. return Status;
  6452. } else if ( Status == STATUS_NO_MATCH ) {
  6453. Status = STATUS_SUCCESS;
  6454. *CrackError = DS_NAME_ERROR_NOT_FOUND;
  6455. }
  6456. }
  6457. //
  6458. // We've already tried the local DC, try the GC.
  6459. //
  6460. if ( InGcAccountName ) {
  6461. *CrackDebugString = "on GC";
  6462. Status = NlCrackSingleName(
  6463. formatOffered,
  6464. TRUE, // do it on GC
  6465. InGcAccountName, // Name to crack
  6466. formatDesired,
  6467. CrackedDnsDomainNameLength, // length of domain buffer
  6468. CrackedDnsDomainName, // domain buffer
  6469. CrackedUserNameLength, // length of user name
  6470. CrackedUserName, // name
  6471. CrackError ); // Translation error code
  6472. }
  6473. return Status;
  6474. }
  6475. NTSTATUS
  6476. NlGetConfigurationName(
  6477. DWORD which,
  6478. DWORD *pcbName,
  6479. DSNAME *pName
  6480. )
  6481. /*++
  6482. Routine Description:
  6483. This routine is a thin wrapper that loads NtDsa.dll then calls
  6484. GetConfigurationName.
  6485. Arguments:
  6486. Same as GetConfigurationName
  6487. Return Value:
  6488. Same as GetConfigurationName
  6489. --*/
  6490. {
  6491. NTSTATUS Status;
  6492. //
  6493. // Ensure ntdsa.dll is loaded.
  6494. //
  6495. Status = NlLoadNtdsaDll();
  6496. if ( NT_SUCCESS(Status) ) {
  6497. //
  6498. // Call the actual function.
  6499. //
  6500. Status = (*NlGlobalpGetConfigurationName)(
  6501. which,
  6502. pcbName,
  6503. pName );
  6504. }
  6505. return Status;
  6506. }
  6507. NTSTATUS
  6508. NlGetConfigurationNamesList(
  6509. DWORD which,
  6510. DWORD dwFlags,
  6511. ULONG * pcbNames,
  6512. DSNAME ** padsNames
  6513. )
  6514. /*++
  6515. Routine Description:
  6516. This routine is a thin wrapper that loads NtDsa.dll then calls
  6517. GetConfigurationNamesList.
  6518. Arguments:
  6519. Same as GetConfigurationNamesList
  6520. Return Value:
  6521. Same as GetConfigurationNamesList
  6522. --*/
  6523. {
  6524. NTSTATUS Status;
  6525. //
  6526. // Ensure ntdsa.dll is loaded.
  6527. //
  6528. Status = NlLoadNtdsaDll();
  6529. if ( NT_SUCCESS(Status) ) {
  6530. //
  6531. // Call the actual function.
  6532. //
  6533. Status = (*NlGlobalpGetConfigurationNamesList)(
  6534. which,
  6535. dwFlags,
  6536. pcbNames,
  6537. padsNames );
  6538. }
  6539. return Status;
  6540. }
  6541. NTSTATUS
  6542. NlGetDnsRootAlias(
  6543. WCHAR * pDnsRootAlias,
  6544. WCHAR * pRootDnsRootAlias
  6545. )
  6546. /*++
  6547. Routine Description:
  6548. This routine is a thin wrapper that loads NtDsa.dll then calls
  6549. GetDnsRootAlias.
  6550. Arguments:
  6551. Same as GetDnsRootAlias
  6552. Return Value:
  6553. Same as GetDnsRootAlias
  6554. --*/
  6555. {
  6556. NTSTATUS Status = STATUS_SUCCESS;
  6557. //
  6558. // Ensure ntdsa.dll is loaded.
  6559. //
  6560. Status = NlLoadNtdsaDll();
  6561. if ( NT_SUCCESS(Status) ) {
  6562. //
  6563. // Call the actual function.
  6564. //
  6565. Status = (*NlGlobalpGetDnsRootAlias)(
  6566. pDnsRootAlias,
  6567. pRootDnsRootAlias );
  6568. }
  6569. return Status;
  6570. }
  6571. DWORD
  6572. NlDsGetServersAndSitesForNetLogon(
  6573. WCHAR * pNDNC,
  6574. SERVERSITEPAIR ** ppaRes)
  6575. /*++
  6576. Routine Description:
  6577. This routine is a thin wrapper that loads NtDsa.dll then calls
  6578. DsGetServersAndSitesForNetLogon.
  6579. Arguments:
  6580. Same as DsGetServersAndSitesForNetLogon
  6581. Return Value:
  6582. Same as DsGetServersAndSitesForNetLogon
  6583. --*/
  6584. {
  6585. NTSTATUS Status;
  6586. //
  6587. // Ensure ntdsa.dll is loaded.
  6588. //
  6589. Status = NlLoadNtdsaDll();
  6590. if ( NT_SUCCESS(Status) ) {
  6591. //
  6592. // Call the actual function.
  6593. //
  6594. Status = (*NlGlobalpDsGetServersAndSitesForNetLogon)(
  6595. pNDNC,
  6596. ppaRes );
  6597. }
  6598. return Status;
  6599. }
  6600. VOID
  6601. NlDsFreeServersAndSitesForNetLogon(
  6602. SERVERSITEPAIR * paServerSites
  6603. )
  6604. /*++
  6605. Routine Description:
  6606. This routine is a thin wrapper that loads NtDsa.dll then calls
  6607. DsFreeServersAndSitesForNetLogon.
  6608. Arguments:
  6609. Same as DsFreeServersAndSitesForNetLogon
  6610. Return Value:
  6611. Same as DsFreeServersAndSitesForNetLogon
  6612. --*/
  6613. {
  6614. NTSTATUS Status;
  6615. //
  6616. // Ensure ntdsa.dll is loaded.
  6617. //
  6618. Status = NlLoadNtdsaDll();
  6619. if ( NT_SUCCESS(Status) ) {
  6620. //
  6621. // Call the actual function.
  6622. //
  6623. (*NlGlobalpDsFreeServersAndSitesForNetLogon)( paServerSites );
  6624. }
  6625. }
  6626. NTSTATUS
  6627. NlPickDomainWithAccount (
  6628. IN PDOMAIN_INFO DomainInfo,
  6629. IN PUNICODE_STRING InAccountNameString,
  6630. IN PUNICODE_STRING InDomainNameString OPTIONAL,
  6631. IN ULONG AllowableAccountControlBits,
  6632. IN NETLOGON_SECURE_CHANNEL_TYPE SecureChannelType,
  6633. IN BOOLEAN ExpediteToRoot,
  6634. IN BOOLEAN CrossForestHop,
  6635. OUT LPWSTR *RealSamAccountName,
  6636. OUT LPWSTR *RealDomainName,
  6637. OUT PULONG RealExtraFlags
  6638. )
  6639. /*++
  6640. Routine Description:
  6641. Get the name of a trusted domain that defines a particular account.
  6642. Arguments:
  6643. DomainInfo - Domain account is in
  6644. AccountNameString - Name of our user account to find.
  6645. DomainNameString - Name of the domain to find the account name in.
  6646. If not specified, the domain name is unknown.
  6647. AllowableAccountControlBits - A mask of allowable SAM account types that
  6648. are allowed to satisfy this request.
  6649. SecureChannelType -- Type of secure channel this request was made over.
  6650. ExpediteToRoot = Request was passed expedite to root DC of this forest.
  6651. CrossForestHop = Request is first hop over cross forest trust TDO.
  6652. RealSamAccountName - On success, returns a pointer to the name of the SAM account to use.
  6653. The caller should free this buffer via NetApiBufferFree().
  6654. Returns NULL if NL_EXFLAGS_EXPEDITE_TO_ROOT or NL_EXFLAGS_CROSS_FOREST_HOP is returned.
  6655. RealDomainName - On success, returns a pointer to the name of the Domain the account is in.
  6656. The caller should free this buffer via NetApiBufferFree().
  6657. Returns NULL if NL_EXFLAGS_EXPEDITE_TO_ROOT is returned.
  6658. Returns the name of the trusted forest if NL_EXFLAGS_CROSS_FOREST_HOP is returned.
  6659. RealExtraFlags - On success, returns flags describing the found account.
  6660. NL_EXFLAGS_EXPEDITE_TO_ROOT - Indicates account is in a trusted forest.
  6661. NL_EXFLAGS_CROSS_FOREST_HOP - Indicates account is in a trusted forest and this domain is root of this forest.
  6662. Return Value:
  6663. STATUS_SUCCESS - Domain found. Information about the DC was returned.
  6664. STATUS_NO_SUCH_DOMAIN - Named account doesn't exist in any domain.
  6665. --*/
  6666. {
  6667. NTSTATUS Status;
  6668. // NET_API_STATUS NetStatus;
  6669. DWORD CrackError;
  6670. LPSTR CrackDebugString = NULL;
  6671. ULONG DebugFlag;
  6672. UNICODE_STRING TemplateDomainNameString;
  6673. PCLIENT_SESSION ClientSession = NULL;
  6674. WCHAR *UpnDomainName = NULL;
  6675. ULONG UpnPrefixLength;
  6676. LPWSTR SamAccountNameToReturn;
  6677. BOOLEAN MightBeUpn = FALSE;
  6678. BOOL MightBeSamAccount;
  6679. LPWSTR AllocatedBuffer = NULL;
  6680. LPWSTR InDomainName;
  6681. LPWSTR InPrintableAccountName;
  6682. LPWSTR InAccountName;
  6683. LPWSTR CrackedDnsDomainName;
  6684. UNICODE_STRING DnsDomainNameString;
  6685. DWORD CrackedDnsDomainNameLength;
  6686. DWORD MaxCrackedDnsDomainNameLength;
  6687. LPWSTR CrackedUserName;
  6688. DWORD CrackedUserNameLength;
  6689. DWORD MaxCrackedUserNameLength;
  6690. BOOLEAN CallerIsDc = IsDomainSecureChannelType(SecureChannelType);
  6691. BOOLEAN AtRoot = (DomainInfo->DomFlags & DOM_FOREST_ROOT) != 0;
  6692. BOOLEAN UseLsaMatch = FALSE;
  6693. BOOLEAN UseGc = FALSE;
  6694. BOOLEAN UseReferral = FALSE;
  6695. BOOLEAN UsePing = FALSE;
  6696. //
  6697. // Initialization
  6698. //
  6699. *RealSamAccountName = NULL;
  6700. *RealDomainName = NULL;
  6701. *RealExtraFlags = 0;
  6702. //
  6703. // Canonicalize the passed in domain name
  6704. //
  6705. if ( InDomainNameString == NULL ) {
  6706. InDomainNameString = &TemplateDomainNameString;
  6707. RtlInitUnicodeString( &TemplateDomainNameString, NULL );
  6708. }
  6709. //
  6710. // Allocate a buffer for storage local to this procedure.
  6711. // (Don't put it on the stack since we don't want to commit a huge stack.)
  6712. //
  6713. MaxCrackedDnsDomainNameLength = NL_MAX_DNS_LENGTH+1;
  6714. MaxCrackedUserNameLength = DNLEN + 1 + UNLEN + 1;
  6715. AllocatedBuffer = LocalAlloc( 0,
  6716. sizeof(WCHAR) * (MaxCrackedDnsDomainNameLength + MaxCrackedUserNameLength) +
  6717. InDomainNameString->Length + sizeof(WCHAR) +
  6718. InDomainNameString->Length + sizeof(WCHAR) +
  6719. InAccountNameString->Length + sizeof(WCHAR) );
  6720. if ( AllocatedBuffer == NULL ) {
  6721. Status = STATUS_NO_MEMORY;
  6722. goto Cleanup;
  6723. }
  6724. CrackedDnsDomainName = AllocatedBuffer;
  6725. CrackedUserName = &AllocatedBuffer[MaxCrackedDnsDomainNameLength];
  6726. InDomainName = &CrackedUserName[MaxCrackedUserNameLength];
  6727. InPrintableAccountName = &InDomainName[(InDomainNameString->Length/sizeof(WCHAR))+1];
  6728. InAccountName = &InPrintableAccountName[(InDomainNameString->Length/sizeof(WCHAR))+1];
  6729. //
  6730. // Build a zero terminated version of the input strings
  6731. //
  6732. if ( InDomainNameString->Length != 0 ) {
  6733. RtlCopyMemory( InDomainName, InDomainNameString->Buffer, InDomainNameString->Length );
  6734. InDomainName[InDomainNameString->Length/sizeof(WCHAR)] = '\0';
  6735. RtlCopyMemory( InPrintableAccountName, InDomainNameString->Buffer, InDomainNameString->Length );
  6736. InPrintableAccountName[InDomainNameString->Length/sizeof(WCHAR)] = '\\';
  6737. } else {
  6738. InDomainName = NULL;
  6739. InPrintableAccountName = InAccountName;
  6740. }
  6741. RtlCopyMemory( InAccountName, InAccountNameString->Buffer, InAccountNameString->Length );
  6742. InAccountName[InAccountNameString->Length/sizeof(WCHAR)] = '\0';
  6743. //
  6744. // Classify the input account name.
  6745. //
  6746. // A UPN has the syntax <AccountName>@<DnsDomainName>.
  6747. // If there are multiple @ signs,
  6748. // use the last one since an AccountName can have an @ in it.
  6749. //
  6750. if ( InDomainName == NULL ) {
  6751. UpnDomainName = wcsrchr( InAccountName, L'@' );
  6752. if ( UpnDomainName != NULL ) {
  6753. //
  6754. // Avoid zero length <AccountName>
  6755. //
  6756. UpnPrefixLength = (ULONG)(UpnDomainName - InAccountName);
  6757. if ( UpnPrefixLength ) {
  6758. UpnDomainName++;
  6759. //
  6760. // Avoid zero length <DnsDomainName>
  6761. //
  6762. if ( *UpnDomainName != L'\0') {
  6763. MightBeUpn = TRUE;
  6764. }
  6765. }
  6766. }
  6767. }
  6768. MightBeSamAccount = NetpIsUserNameValid( InAccountName );
  6769. NlPrintDom((NL_LOGON, DomainInfo,
  6770. "NlPickDomainWithAccount: %ws: Algorithm entered. UPN:%ld Sam:%ld Exp:%ld Cross: %ld Root:%ld DC:%ld\n",
  6771. InPrintableAccountName,
  6772. MightBeUpn,
  6773. MightBeSamAccount,
  6774. ExpediteToRoot,
  6775. CrossForestHop,
  6776. AtRoot,
  6777. CallerIsDc ));
  6778. if ( !MightBeSamAccount && !MightBeUpn ) {
  6779. NlPrintDom((NL_CRITICAL, DomainInfo,
  6780. "NlPickDomainWithAccount: %ws: Must be either UPN or SAM account. UPN:%ld Sam:%ld\n",
  6781. InPrintableAccountName,
  6782. MightBeUpn,
  6783. MightBeSamAccount ));
  6784. Status = STATUS_NO_SUCH_DOMAIN;
  6785. goto Cleanup;
  6786. }
  6787. //
  6788. // Some combinations are invalid
  6789. //
  6790. if ( !CallerIsDc && (CrossForestHop || ExpediteToRoot)) {
  6791. NlPrintDom((NL_CRITICAL, DomainInfo,
  6792. "NlPickDomainWithAccount: %ws: Non-DC passed CrossForestHop (%ld) or ExpediteToRoot (%ld)\n",
  6793. InPrintableAccountName,
  6794. CrossForestHop,
  6795. ExpediteToRoot ));
  6796. Status = STATUS_NO_SUCH_DOMAIN;
  6797. goto Cleanup;
  6798. }
  6799. if ( CrossForestHop && ExpediteToRoot ) {
  6800. NlPrintDom((NL_CRITICAL, DomainInfo,
  6801. "NlPickDomainWithAccount: %ws: Both CrossForestHop (%ld) and ExpediteToRoot (%ld)\n",
  6802. InPrintableAccountName,
  6803. CrossForestHop,
  6804. ExpediteToRoot ));
  6805. Status = STATUS_NO_SUCH_DOMAIN;
  6806. goto Cleanup;
  6807. }
  6808. if ( CrossForestHop && !AtRoot ) {
  6809. NlPrintDom((NL_CRITICAL, DomainInfo,
  6810. "NlPickDomainWithAccount: %ws: CrossForestHop (%ld) and not AtRoot (%ld)\n",
  6811. InPrintableAccountName,
  6812. CrossForestHop,
  6813. AtRoot ));
  6814. Status = STATUS_NO_SUCH_DOMAIN;
  6815. goto Cleanup;
  6816. }
  6817. //
  6818. // If this request came from a DC,
  6819. // that DC should have done this call except in two cases:
  6820. // 1) This is a ExpediteToRoot and we're now at the root.
  6821. // 2) This is a CrossForestHop and we're now in the other forest.
  6822. //
  6823. if ( CallerIsDc &&
  6824. !(ExpediteToRoot && AtRoot) &&
  6825. !CrossForestHop ) {
  6826. Status = STATUS_NO_SUCH_DOMAIN;
  6827. goto Cleanup;
  6828. }
  6829. //
  6830. // Finally, mark which lookups are to be performed
  6831. //
  6832. if ( ExpediteToRoot && AtRoot ) {
  6833. UseLsaMatch = TRUE;
  6834. UseReferral = TRUE;
  6835. } else if ( CrossForestHop ) {
  6836. UseGc = TRUE;
  6837. } else {
  6838. UseLsaMatch = TRUE;
  6839. UseGc = TRUE;
  6840. UseReferral = TRUE;
  6841. UsePing = TRUE;
  6842. }
  6843. //
  6844. // If the name might be a UPN,
  6845. // look it up.
  6846. //
  6847. if ( MightBeUpn ) {
  6848. //
  6849. // Crack the UPN.
  6850. //
  6851. CrackedDnsDomainNameLength = MaxCrackedDnsDomainNameLength;
  6852. CrackedUserNameLength = MaxCrackedUserNameLength;
  6853. Status = NlCrackSingleNameEx(
  6854. DS_USER_PRINCIPAL_NAME, // Translate from UPN,
  6855. UseGc ? InAccountName : NULL, // GC Name to crack
  6856. UseLsaMatch ? InAccountName : NULL, // LSA Name to crack
  6857. DS_NT4_ACCOUNT_NAME, // Translate to NT 4 style
  6858. &CrackedDnsDomainNameLength, // length of domain buffer
  6859. CrackedDnsDomainName, // domain buffer
  6860. &CrackedUserNameLength, // length of user name
  6861. CrackedUserName, // name
  6862. &CrackError, // Translation error code
  6863. &CrackDebugString );
  6864. DebugFlag = NL_CRITICAL;
  6865. if ( Status == STATUS_SUCCESS ) {
  6866. if ( CrackError == DS_NAME_ERROR_TRUST_REFERRAL && !UseReferral ) {
  6867. CrackError = DS_NAME_ERROR_NOT_FOUND;
  6868. }
  6869. if ( CrackError == DS_NAME_NO_ERROR ||
  6870. CrackError == DS_NAME_ERROR_TRUST_REFERRAL ) {
  6871. goto CrackNameWorked;
  6872. } else if ( CrackError == DS_NAME_ERROR_NOT_FOUND ) {
  6873. DebugFlag = NL_SESSION_MORE;
  6874. }
  6875. }
  6876. NlPrintDom(( DebugFlag, DomainInfo,
  6877. "NlPickDomainWithAccount: Username %ws can't be cracked %s. 0x%lx %ld\n",
  6878. InPrintableAccountName,
  6879. CrackDebugString,
  6880. Status,
  6881. CrackError ));
  6882. //
  6883. // If the string to the right of the @ is in the forest or a directly trusted domain,
  6884. // convert the UPN to <DnsDomainName>\<UserName> and try the operation again.
  6885. //
  6886. if ( UpnPrefixLength <= UNLEN ) {
  6887. UNICODE_STRING UpnDomainNameString;
  6888. RtlInitUnicodeString( &UpnDomainNameString, UpnDomainName );
  6889. ClientSession = NlFindNamedClientSession(
  6890. DomainInfo,
  6891. &UpnDomainNameString,
  6892. 0, // Indirect trust OK
  6893. NULL );
  6894. if ( ClientSession != NULL ) {
  6895. //
  6896. // We don't need the client session.
  6897. //
  6898. NlUnrefClientSession( ClientSession );
  6899. ClientSession = NULL;
  6900. //
  6901. // The real sam account name is everything before the @
  6902. //
  6903. RtlCopyMemory( CrackedUserName, InAccountName, UpnPrefixLength*sizeof(WCHAR) );
  6904. CrackedUserName[UpnPrefixLength] = L'\0';
  6905. SamAccountNameToReturn = CrackedUserName;
  6906. //
  6907. // The real domain name is everything after the @
  6908. //
  6909. CrackedDnsDomainName = UpnDomainName;
  6910. NlPrintDom((NL_LOGON, DomainInfo,
  6911. "NlPickDomainWithAccount: Username %ws is assumed to be in %ws with account name %ws\n",
  6912. InPrintableAccountName,
  6913. UpnDomainName,
  6914. SamAccountNameToReturn ));
  6915. Status = STATUS_SUCCESS;
  6916. goto Cleanup;
  6917. }
  6918. }
  6919. }
  6920. //
  6921. // See if this is a SAM account name of an account in the enterprise.
  6922. //
  6923. if ( MightBeSamAccount ) {
  6924. CrackedDnsDomainNameLength = MaxCrackedDnsDomainNameLength;
  6925. CrackedUserNameLength = MaxCrackedUserNameLength;
  6926. //
  6927. // If the domain name isn't specified,
  6928. // try the GC to find the domain name.
  6929. //
  6930. if ( InDomainName == NULL ) {
  6931. if ( UseGc ) {
  6932. CrackDebugString = "On GC";
  6933. Status = NlCrackSingleName(
  6934. DS_NT4_ACCOUNT_NAME_SANS_DOMAIN_EX, // Translate from Sam Account Name without domain name
  6935. // The _EX version also avoids disabled accounts
  6936. TRUE, // do it on GC
  6937. InAccountName, // Name to crack
  6938. DS_NT4_ACCOUNT_NAME, // Translate to NT 4 style
  6939. &CrackedDnsDomainNameLength, // length of domain buffer
  6940. CrackedDnsDomainName, // domain buffer
  6941. &CrackedUserNameLength, // length of user name
  6942. CrackedUserName, // name
  6943. &CrackError ); // Translation error code
  6944. } else {
  6945. CrackDebugString = NULL;
  6946. }
  6947. //
  6948. // If the domain name is specified,
  6949. // the caller already determine that the name isn't that of a (transitively) trusted domain,
  6950. // try the GC (or local DS) to determine if the account is in another forest.
  6951. //
  6952. } else {
  6953. Status = NlCrackSingleNameEx(
  6954. DS_NT4_ACCOUNT_NAME, // Translate from NT 4 style
  6955. UseGc ? InPrintableAccountName : NULL,// GC Name to crack
  6956. UseLsaMatch ? InDomainName : NULL, // LSA Name to crack
  6957. DS_NT4_ACCOUNT_NAME, // Translate to NT 4 style
  6958. &CrackedDnsDomainNameLength, // length of domain buffer
  6959. CrackedDnsDomainName, // domain buffer
  6960. &CrackedUserNameLength, // length of user name
  6961. CrackedUserName, // name
  6962. &CrackError, // Translation error code
  6963. &CrackDebugString );
  6964. }
  6965. if ( CrackDebugString != NULL ) {
  6966. DebugFlag = NL_CRITICAL;
  6967. if ( Status == STATUS_SUCCESS ) {
  6968. if ( CrackError == DS_NAME_ERROR_TRUST_REFERRAL && !UseReferral ) {
  6969. CrackError = DS_NAME_ERROR_NOT_FOUND;
  6970. }
  6971. if ( CrackError == DS_NAME_NO_ERROR ||
  6972. CrackError == DS_NAME_ERROR_TRUST_REFERRAL ) {
  6973. goto CrackNameWorked;
  6974. } else if ( CrackError == DS_NAME_ERROR_NOT_FOUND ) {
  6975. DebugFlag = NL_SESSION_MORE;
  6976. }
  6977. }
  6978. NlPrintDom(( DebugFlag, DomainInfo,
  6979. "NlPickDomainWithAccount: Username %ws can't be cracked (%s). 0x%lx %ld\n",
  6980. InPrintableAccountName,
  6981. CrackDebugString,
  6982. Status,
  6983. CrackError ));
  6984. }
  6985. //
  6986. // Finally, use the barbaric "ping" method of finding a DC.
  6987. //
  6988. if ( InDomainName == NULL && UsePing ) {
  6989. ClientSession = NlPickDomainWithAccountViaPing (
  6990. DomainInfo,
  6991. InAccountName,
  6992. AllowableAccountControlBits );
  6993. if ( ClientSession != NULL ) {
  6994. NlPrintDom((NL_CRITICAL, DomainInfo,
  6995. "NlPickDomainWithAccount: Username %ws found via 'pinging'\n",
  6996. InPrintableAccountName ));
  6997. LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  6998. if ( ClientSession->CsDnsDomainName.Length == 0 ) {
  6999. CrackedDnsDomainName = ClientSession->CsDnsDomainName.Buffer;
  7000. } else {
  7001. CrackedDnsDomainName = ClientSession->CsNetbiosDomainName.Buffer;
  7002. }
  7003. UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  7004. SamAccountNameToReturn = InAccountName;
  7005. Status = STATUS_SUCCESS;
  7006. goto Cleanup;
  7007. }
  7008. }
  7009. }
  7010. //
  7011. // No mechanism worked
  7012. //
  7013. Status = STATUS_NO_SUCH_DOMAIN;
  7014. goto Cleanup;
  7015. //
  7016. // If DsCrackName found the account,
  7017. // Lookup the nearest domain to go to.
  7018. //
  7019. CrackNameWorked:
  7020. if ( CrackError == DS_NAME_NO_ERROR ) {
  7021. //
  7022. // Crackname returned the account name in the form:
  7023. // <NetbiosDomain>\<SamAccountName>
  7024. //
  7025. // Parse that and return the SamAccountName
  7026. //
  7027. SamAccountNameToReturn = wcschr( CrackedUserName, L'\\' );
  7028. if ( SamAccountNameToReturn == NULL ) {
  7029. SamAccountNameToReturn = CrackedUserName;
  7030. } else {
  7031. SamAccountNameToReturn++;
  7032. }
  7033. NlPrintDom(( NL_LOGON, DomainInfo,
  7034. "NlPickDomainWithAccount: Username %ws is %ws\\%ws (found %s)\n",
  7035. InPrintableAccountName,
  7036. CrackedDnsDomainName,
  7037. SamAccountNameToReturn,
  7038. CrackDebugString ));
  7039. //
  7040. // If DsCrackName determined this was a cross forest trust,
  7041. // return that info to the caller.
  7042. //
  7043. } else if ( CrackError == DS_NAME_ERROR_TRUST_REFERRAL ) {
  7044. NlPrintDom(( NL_LOGON, DomainInfo,
  7045. "NlPickDomainWithAccount: Username %ws is in forest %ws (found %s)\n",
  7046. InPrintableAccountName,
  7047. CrackedDnsDomainName,
  7048. CrackDebugString ));
  7049. SamAccountNameToReturn = NULL;
  7050. if ( AtRoot ) {
  7051. //
  7052. // If just hopped from another forest,
  7053. // stay within this forest.
  7054. // Cross forest trust isn't transitive.
  7055. //
  7056. if ( CrossForestHop ) {
  7057. Status = STATUS_NO_SUCH_DOMAIN;
  7058. goto Cleanup;
  7059. }
  7060. *RealExtraFlags |= NL_EXFLAGS_CROSS_FOREST_HOP;
  7061. } else {
  7062. *RealExtraFlags |= NL_EXFLAGS_EXPEDITE_TO_ROOT;
  7063. CrackedDnsDomainName = NULL; // No use returning this to the caller since the caller can't use it
  7064. }
  7065. //
  7066. // Internal error.
  7067. //
  7068. } else {
  7069. NlAssert(( "Invalid CrackError" && FALSE ));
  7070. }
  7071. Status = STATUS_SUCCESS;
  7072. //
  7073. // Cleanup locally used resources.
  7074. //
  7075. //
  7076. Cleanup:
  7077. //
  7078. // On Success, SamAccountNameToReturn and CrackedDnsDomainName are pointers to the names to return
  7079. // SamAccountNameToReturn can be null if the account is in another forest.
  7080. //
  7081. if ( NT_SUCCESS(Status) && SamAccountNameToReturn != NULL ) {
  7082. *RealSamAccountName = NetpAllocWStrFromWStr( SamAccountNameToReturn );
  7083. if ( *RealSamAccountName == NULL ) {
  7084. Status = STATUS_NO_MEMORY;
  7085. }
  7086. }
  7087. if ( NT_SUCCESS(Status) && CrackedDnsDomainName != NULL ) {
  7088. *RealDomainName = NetpAllocWStrFromWStr( CrackedDnsDomainName );
  7089. if ( *RealDomainName == NULL ) {
  7090. if ( *RealSamAccountName != NULL ) {
  7091. NetApiBufferFree( *RealSamAccountName );
  7092. *RealSamAccountName = NULL;
  7093. }
  7094. Status = STATUS_NO_MEMORY;
  7095. }
  7096. }
  7097. if ( AllocatedBuffer != NULL ) {
  7098. LocalFree( AllocatedBuffer );
  7099. }
  7100. if ( ClientSession != NULL ) {
  7101. NlUnrefClientSession( ClientSession );
  7102. }
  7103. return Status;
  7104. }
  7105. #endif // _DC_NETLOGON
  7106. NTSTATUS
  7107. NlStartApiClientSession(
  7108. IN PCLIENT_SESSION ClientSession,
  7109. IN BOOLEAN QuickApiCall,
  7110. IN ULONG RetryIndex,
  7111. IN NTSTATUS DefaultStatus,
  7112. IN PCLIENT_API ClientApi
  7113. )
  7114. /*++
  7115. Routine Description:
  7116. Enable the timer for timing out an API call on the secure channel.
  7117. On Entry,
  7118. The trust list must NOT be locked.
  7119. The caller must be a writer of the trust list entry.
  7120. Arguments:
  7121. ClientSession - Structure used to define the session.
  7122. QuickApiCall - True if this API call MUST finish in less than 45 seconds
  7123. and will in reality finish in less than 15 seconds unless something
  7124. is terribly wrong.
  7125. RetryIndex - Index of number of times this call was retried.
  7126. DefaultStatus - Status to return if the binding type isn't supported.
  7127. (This is either a default status or the status from the previous
  7128. iteration. The status from the previous iteration is better than
  7129. anything we could return here.)
  7130. ClientApi - Specifies a pointer to the structure representing
  7131. this API call.
  7132. Return Value:
  7133. Status of the RPC binding to the server
  7134. --*/
  7135. {
  7136. NTSTATUS Status;
  7137. NET_API_STATUS NetStatus;
  7138. BOOLEAN BindingHandleCached;
  7139. BOOLEAN UnbindFromServer = FALSE;
  7140. BOOLEAN DoAuthenticatedRpc;
  7141. LARGE_INTEGER TimeNow;
  7142. NL_RPC_BINDING RpcBindingType;
  7143. NL_RPC_BINDING OldRpcBindingType;
  7144. //
  7145. // Remember the session count of when we started this API call
  7146. //
  7147. ClientApi->CaSessionCount = ClientSession->CsSessionCount;
  7148. //
  7149. // Determine the RPC Binding Type.
  7150. //
  7151. // Try TCP if the connection is to an NT 5 or newer DC and
  7152. // if this machine has TCP addresses
  7153. //
  7154. // Fall back to named pipes
  7155. //
  7156. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  7157. if ( (ClientSession->CsDiscoveryFlags & CS_DISCOVERY_HAS_DS) != 0 &&
  7158. NlGlobalWinsockPnpAddresses != NULL ) {
  7159. if ( RetryIndex == 0 ) {
  7160. RpcBindingType = UseTcpIp;
  7161. } else {
  7162. if ( UseConcurrentRpc( ClientSession, ClientApi) ) {
  7163. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  7164. return DefaultStatus;
  7165. }
  7166. RpcBindingType = UseNamedPipe;
  7167. }
  7168. //
  7169. // Otherwise, only use named pipes.
  7170. //
  7171. } else {
  7172. // NlAssert( !UseConcurrentRpc(, ClientSession, ClientApi) );
  7173. if ( UseConcurrentRpc( ClientSession, ClientApi) ) {
  7174. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  7175. return DefaultStatus;
  7176. }
  7177. if ( RetryIndex == 0 ) {
  7178. RpcBindingType = UseNamedPipe;
  7179. } else {
  7180. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  7181. return DefaultStatus;
  7182. }
  7183. }
  7184. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  7185. NlAssert( ClientSession->CsUncServerName != NULL );
  7186. //
  7187. // Save the current time.
  7188. // Start the timer on the API call.
  7189. //
  7190. LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  7191. NlQuerySystemTime( &TimeNow );
  7192. ClientApi->CaApiTimer.StartTime = TimeNow;
  7193. ClientApi->CaApiTimer.Period =
  7194. QuickApiCall ? NlGlobalParameters.ShortApiCallPeriod : LONG_API_CALL_PERIOD;
  7195. //
  7196. // If the global timer isn't running,
  7197. // start it and tell the main thread that I've changed a timer.
  7198. //
  7199. if ( NlGlobalBindingHandleCount == 0 ) {
  7200. if ( NlGlobalApiTimer.Period != NlGlobalParameters.ShortApiCallPeriod ) {
  7201. NlGlobalApiTimer.Period = NlGlobalParameters.ShortApiCallPeriod;
  7202. NlGlobalApiTimer.StartTime = TimeNow;
  7203. if ( !SetEvent( NlGlobalTimerEvent ) ) {
  7204. NlPrintCs(( NL_CRITICAL, ClientSession,
  7205. "NlStartApiClientSession: SetEvent failed %ld\n",
  7206. GetLastError() ));
  7207. }
  7208. }
  7209. }
  7210. //
  7211. // If we haven't grabbed a thread handle yet,
  7212. // do so now.
  7213. //
  7214. if ( ClientApi->CaThreadHandle == NULL ) {
  7215. if ( !DuplicateHandle( GetCurrentProcess(),
  7216. GetCurrentThread(),
  7217. GetCurrentProcess(),
  7218. &ClientApi->CaThreadHandle,
  7219. 0,
  7220. FALSE,
  7221. DUPLICATE_SAME_ACCESS ) ) {
  7222. NlPrintCs(( NL_CRITICAL, ClientSession,
  7223. "NlStartApiClientSession: DuplicateHandle failed %ld\n",
  7224. GetLastError() ));
  7225. }
  7226. //
  7227. // Set the amount of time this client thread is willing to wait for
  7228. // the server to respond to a cancel.
  7229. //
  7230. NetStatus = RpcMgmtSetCancelTimeout( 1 ); // 1 second
  7231. if ( NetStatus != NO_ERROR ) {
  7232. NlPrintCs((NL_SESSION_MORE, ClientSession,
  7233. "NlStartApiClientSession: Cannot RpcMgmtSetCancelTimeout: %ld (continuing)\n",
  7234. NetStatus ));
  7235. }
  7236. }
  7237. //
  7238. // Remember if the binding handle is cached, then mark it as cached.
  7239. //
  7240. BindingHandleCached = (ClientApi->CaFlags & CA_BINDING_CACHED) != 0;
  7241. ClientApi->CaFlags |= CA_BINDING_CACHED;
  7242. //
  7243. // Count the number of concurrent binding handles cached
  7244. //
  7245. if ( !BindingHandleCached ) {
  7246. NlGlobalBindingHandleCount ++;
  7247. //
  7248. // If we're currently bound using TCP/IP,
  7249. // and the caller wants named pipe.
  7250. // fall back to named pipe.
  7251. //
  7252. } else if ( ClientApi->CaFlags & CA_TCP_BINDING ) {
  7253. if ( RpcBindingType == UseNamedPipe ) {
  7254. OldRpcBindingType = UseTcpIp;
  7255. UnbindFromServer = TRUE;
  7256. ClientApi->CaFlags &= ~CA_TCP_BINDING;
  7257. }
  7258. //
  7259. // If we're currently bound using named pipe,
  7260. // TCP/IP must have failed in the past,
  7261. // continue using named pipe.
  7262. //
  7263. } else {
  7264. RpcBindingType = UseNamedPipe;
  7265. }
  7266. //
  7267. // Remember the RPC binding type.
  7268. //
  7269. if ( RpcBindingType == UseTcpIp ) {
  7270. ClientApi->CaFlags |= CA_TCP_BINDING;
  7271. }
  7272. //
  7273. // If we haven't yet told RPC to do authenticated RPC,
  7274. // the secure channel is already authenticated (from our perspective), and
  7275. // authenticated RPC has been negotiated,
  7276. // do it now.
  7277. //
  7278. DoAuthenticatedRpc =
  7279. (ClientApi->CaFlags & CA_BINDING_AUTHENTICATED) == 0 &&
  7280. ClientSession->CsState == CS_AUTHENTICATED &&
  7281. (ClientSession->CsNegotiatedFlags & NETLOGON_SUPPORTS_AUTH_RPC) != 0;
  7282. UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  7283. //
  7284. // If we're bound to the wrong transport,
  7285. // unbind.
  7286. //
  7287. if ( UnbindFromServer ) {
  7288. NTSTATUS TempStatus;
  7289. //
  7290. // Ensure we rebind below.
  7291. //
  7292. BindingHandleCached = FALSE;
  7293. //
  7294. // Unbind the handle
  7295. //
  7296. NlpSecureChannelUnbind(
  7297. ClientSession,
  7298. ClientSession->CsUncServerName,
  7299. "NlStartApiClientSession",
  7300. 0,
  7301. ClientSession->CsUncServerName,
  7302. OldRpcBindingType );
  7303. }
  7304. //
  7305. // Impersonate the thread token as anonymous if we use named pipes.
  7306. //
  7307. // By default the token is impersonated as a system token since
  7308. // netlogon is a system service. In this case, if we use named
  7309. // pipes, RPC may authenticate this API call through Kerberos
  7310. // that potentially calls us back to discover a DC therby creating
  7311. // a potential deadlock loop. We avoid this by impersonating the
  7312. // token as anonymous if we use named pipes for this API call.
  7313. // We will revert this by setting the token back to the default
  7314. // value when we are done with this API call.
  7315. //
  7316. if ( (ClientApi->CaFlags & CA_TCP_BINDING) == 0 ) {
  7317. Status = NtImpersonateAnonymousToken( NtCurrentThread() );
  7318. if ( !NT_SUCCESS(Status) ) {
  7319. NlPrint(( NL_CRITICAL,
  7320. "NlStartApiClientSession: cannot NtImpersonateAnonymousToken: 0x%lx\n",
  7321. Status ));
  7322. }
  7323. } else {
  7324. Status = STATUS_SUCCESS;
  7325. }
  7326. //
  7327. // If the binding handle isn't already cached,
  7328. // cache it now.
  7329. //
  7330. if ( NT_SUCCESS(Status) && !BindingHandleCached ) {
  7331. NlPrintCs((NL_SESSION_MORE, ClientSession,
  7332. "NlStartApiClientSession: Bind to server %ws (%s) %ld (Retry: %ld).\n",
  7333. ClientSession->CsUncServerName,
  7334. RpcBindingType == UseTcpIp ? "TCP" : "PIPE",
  7335. ClientApiIndex( ClientSession, ClientApi ),
  7336. RetryIndex ));
  7337. NlAssert( ClientSession->CsState != CS_IDLE );
  7338. //
  7339. // If this API use the netapi32 binding handle,
  7340. // bind it.
  7341. //
  7342. if ( !UseConcurrentRpc( ClientSession, ClientApi ) ) {
  7343. //
  7344. // Bind to the server
  7345. //
  7346. Status = NlBindingAddServerToCache ( ClientSession->CsUncServerName,
  7347. RpcBindingType );
  7348. if ( !NT_SUCCESS(Status) ) {
  7349. //
  7350. // If we're binding to TCP,
  7351. // and TCP isn't supported on this machine,
  7352. // simply return as though the server doesn't support TCP
  7353. // so caller will fall back to Named pipe.
  7354. //
  7355. if ( Status == RPC_NT_PROTSEQ_NOT_SUPPORTED &&
  7356. RpcBindingType == UseTcpIp ) {
  7357. NlPrintCs((NL_SESSION_MORE, ClientSession,
  7358. "NlStartApiClientSession: Bind to server %ws (%s) %ld failed 0x%lx (Client doesn't support TCP/IP).\n",
  7359. ClientSession->CsUncServerName,
  7360. RpcBindingType == UseTcpIp ? "TCP" : "PIPE",
  7361. ClientApiIndex( ClientSession, ClientApi ),
  7362. Status ));
  7363. Status = DefaultStatus;
  7364. } else {
  7365. NlPrintCs((NL_CRITICAL, ClientSession,
  7366. "NlStartApiClientSession: Bind to server %ws (%s) %ld failed 0x%lx.\n",
  7367. ClientSession->CsUncServerName,
  7368. RpcBindingType == UseTcpIp ? "TCP" : "PIPE",
  7369. ClientApiIndex( ClientSession, ClientApi ),
  7370. Status ));
  7371. }
  7372. LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  7373. ClientApi->CaFlags &= ~(CA_BINDING_CACHED|CA_BINDING_AUTHENTICATED|CA_TCP_BINDING);
  7374. NlGlobalBindingHandleCount --;
  7375. UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  7376. } else {
  7377. ClientApi->CaRpcHandle = ClientSession->CsUncServerName;
  7378. }
  7379. //
  7380. // If this API call uses a local binding handle,
  7381. // create it.
  7382. //
  7383. } else {
  7384. NetStatus = NlpSecureChannelBind(
  7385. ClientSession->CsUncServerName,
  7386. &ClientApi->CaRpcHandle );
  7387. if ( NetStatus != NO_ERROR ) {
  7388. Status = NetpApiStatusToNtStatus( NetStatus );
  7389. NlPrintCs((NL_CRITICAL, ClientSession,
  7390. "NlStartApiClientSession: Bind to server %ws (%s) %ld failed 0x%lx.\n",
  7391. ClientSession->CsUncServerName,
  7392. RpcBindingType == UseTcpIp ? "TCP" : "PIPE",
  7393. ClientApiIndex( ClientSession, ClientApi ),
  7394. Status ));
  7395. LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  7396. ClientApi->CaFlags &= ~(CA_BINDING_CACHED|CA_BINDING_AUTHENTICATED|CA_TCP_BINDING);
  7397. NlGlobalBindingHandleCount --;
  7398. UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  7399. } else {
  7400. Status = STATUS_SUCCESS;
  7401. }
  7402. }
  7403. }
  7404. //
  7405. // If we need to tell RPC to do authenticated RPC,
  7406. // do so now.
  7407. //
  7408. LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  7409. if ( NT_SUCCESS(Status) && DoAuthenticatedRpc ) {
  7410. NlPrintCs((NL_SESSION_MORE, ClientSession,
  7411. "NlStartApiClientSession: Try to NlBindingSetAuthInfo\n" ));
  7412. //
  7413. // Build a generic client context for the security package
  7414. // if we don't have one already.
  7415. //
  7416. if ( ClientSession->ClientAuthData == NULL ) {
  7417. ClientSession->ClientAuthData = NlBuildAuthData( ClientSession );
  7418. if ( ClientSession->ClientAuthData == NULL ) {
  7419. Status = STATUS_NO_MEMORY;
  7420. } else {
  7421. SECURITY_STATUS SecStatus;
  7422. TimeStamp DummyTimeStamp;
  7423. //
  7424. // Keep a reference count on the credentials handle associated with this
  7425. // auth data (by calling AcquireCredentialsHandle) to ensure that we use
  7426. // the same handle as long as the secure channel is up. This is a performance
  7427. // improvement since the RPC users of netlogon's SSPI will get the same handle
  7428. // for the same auth data thereby avoiding a new secure RPC connection setup.
  7429. //
  7430. SecStatus = AcquireCredentialsHandleW( NULL,
  7431. NULL,
  7432. SECPKG_CRED_OUTBOUND,
  7433. NULL,
  7434. ClientSession->ClientAuthData,
  7435. NULL,
  7436. NULL,
  7437. &ClientSession->CsCredHandle,
  7438. &DummyTimeStamp );
  7439. if ( SecStatus != SEC_E_OK ) {
  7440. NlPrintCs((NL_CRITICAL, ClientSession,
  7441. "NlStartApiClientSession: AcquireCredentialsHandleW failed 0x%lx\n",
  7442. SecStatus ));
  7443. }
  7444. }
  7445. }
  7446. if ( NT_SUCCESS(Status) ) {
  7447. //
  7448. // If this API uses the netapi32 binding handle,
  7449. // set the auth info there.
  7450. //
  7451. if ( !UseConcurrentRpc( ClientSession, ClientApi ) ) {
  7452. Status = NlBindingSetAuthInfo (
  7453. ClientSession->CsUncServerName,
  7454. RpcBindingType,
  7455. NlGlobalParameters.SealSecureChannel,
  7456. ClientSession->ClientAuthData,
  7457. ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer ); // Server context
  7458. if ( NT_SUCCESS(Status) ) {
  7459. ClientApi->CaFlags |= CA_BINDING_AUTHENTICATED;
  7460. } else {
  7461. NlPrintCs((NL_CRITICAL, ClientSession,
  7462. "NlStartApiClientSession: Cannot NlBindingSetAuthInfo: %lx\n",
  7463. Status ));
  7464. }
  7465. //
  7466. // If this API call uses a local binding handle,
  7467. // Simply call RPC directly
  7468. //
  7469. } else {
  7470. //
  7471. // Tell RPC to start doing secure RPC
  7472. //
  7473. NetStatus = RpcBindingSetAuthInfoW(
  7474. ClientApi->CaRpcHandle,
  7475. ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer, // Server context
  7476. NlGlobalParameters.SealSecureChannel ?
  7477. RPC_C_AUTHN_LEVEL_PKT_PRIVACY : RPC_C_AUTHN_LEVEL_PKT_INTEGRITY,
  7478. RPC_C_AUTHN_NETLOGON, // Netlogon's own security package
  7479. ClientSession->ClientAuthData,
  7480. RPC_C_AUTHZ_NAME );
  7481. if ( NetStatus == NO_ERROR ) {
  7482. ClientApi->CaFlags |= CA_BINDING_AUTHENTICATED;
  7483. } else {
  7484. Status = NetpApiStatusToNtStatus( NetStatus );
  7485. NlPrintCs((NL_CRITICAL, ClientSession,
  7486. "NlStartApiClientSession: Cannot RpcBindingSetAuthInfoW: %ld\n",
  7487. NetStatus ));
  7488. }
  7489. }
  7490. }
  7491. }
  7492. UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  7493. return Status;
  7494. }
  7495. BOOLEAN
  7496. NlFinishApiClientSession(
  7497. IN PCLIENT_SESSION ClientSession,
  7498. IN BOOLEAN OkToKillSession,
  7499. IN BOOLEAN AmWriter,
  7500. IN PCLIENT_API ClientApi
  7501. )
  7502. /*++
  7503. Routine Description:
  7504. Disable the timer for timing out the API call.
  7505. Also, determine if it is time to pick a new DC since the current DC is
  7506. reponding so poorly. The decision is made from the number of
  7507. timeouts that happened during the last reauthentication time. If
  7508. timeoutcount is more than the limit, it sets the connection status
  7509. to CS_IDLE so that new DC will be picked up and new session will be
  7510. established.
  7511. On Entry,
  7512. The trust list must NOT be locked.
  7513. The caller must be a writer of the trust list entry.
  7514. Arguments:
  7515. ClientSession - Structure used to define the session.
  7516. OkToKillSession - TRUE if it's OK to actually drop the secure channel.
  7517. Otherwise, this routine will simply return FALSE upon timeout and
  7518. depend on the caller to drop the secure channel.
  7519. AmWriter - TRUE if the caller is the writer of the client session.
  7520. This should only be false for concurrent API calls where the caller
  7521. could not re-establish writership after the API call completed.
  7522. ClientApi - Specifies a pointer to the structure representing
  7523. this API call.
  7524. Return Value:
  7525. TRUE - API finished normally
  7526. FALSE - API timed out AND the ClientSession structure was torn down.
  7527. The caller shouldn't use the ClientSession structure without first
  7528. setting up another session. FALSE will only be return for a "quick"
  7529. API call.
  7530. FALSE does not imply that the API call failed. It should only be used
  7531. as an indication that the secure channel was torn down.
  7532. --*/
  7533. {
  7534. BOOLEAN SessionOk = TRUE;
  7535. TIMER ApiTimer;
  7536. NTSTATUS Status;
  7537. HANDLE NullToken = NULL;
  7538. // NlAssert( ClientSession->CsUncServerName != NULL ); // Not true for concurrent RPC calls
  7539. //
  7540. // Grab a copy of the ApiTimer.
  7541. //
  7542. // Only a copy is needed and we don't want to keep the trust list locked
  7543. // while locking NlGlobalDcDiscoveryCritSect (wrong locking order) nor while
  7544. // freeing the session.
  7545. //
  7546. LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  7547. ApiTimer = ClientApi->CaApiTimer;
  7548. //
  7549. // Turn off the timer for this API call.
  7550. //
  7551. ClientApi->CaApiTimer.Period = MAILSLOT_WAIT_FOREVER;
  7552. //
  7553. // If some other thread dropped the secure channel,
  7554. // it couldn't unbind this binding handle since we were using it.
  7555. //
  7556. // Unbind now.
  7557. //
  7558. NlAssert( ClientApi->CaFlags & CA_BINDING_CACHED );
  7559. if ( !AmWriter ||
  7560. ClientApi->CaSessionCount != ClientSession->CsSessionCount ) {
  7561. if ( ClientApi->CaFlags & CA_BINDING_CACHED ) {
  7562. NL_RPC_BINDING OldRpcBindingType;
  7563. //
  7564. // Indicate the handle is no longer cached.
  7565. //
  7566. OldRpcBindingType =
  7567. (ClientApi->CaFlags & CA_TCP_BINDING) ? UseTcpIp : UseNamedPipe;
  7568. ClientApi->CaFlags &= ~(CA_BINDING_CACHED|CA_BINDING_AUTHENTICATED|CA_TCP_BINDING);
  7569. NlGlobalBindingHandleCount --;
  7570. //
  7571. // Save the server name but drop all our locks.
  7572. //
  7573. UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  7574. //
  7575. // Unbind the handle
  7576. //
  7577. NlpSecureChannelUnbind(
  7578. ClientSession,
  7579. NULL, // Server name not known
  7580. "NlFinishApiClientSession",
  7581. ClientApiIndex( ClientSession, ClientApi),
  7582. ClientApi->CaRpcHandle,
  7583. OldRpcBindingType );
  7584. LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  7585. }
  7586. }
  7587. UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  7588. //
  7589. // If this was a "quick" API call,
  7590. // and the API took too long,
  7591. // increment the count of times it timed out.
  7592. //
  7593. // Do this analysis only if this is not a BDC
  7594. // to PDC secure channel; there is only ONE
  7595. // PDC so don't attemp to find a "better" PDC
  7596. // in this case.
  7597. //
  7598. if ( ClientSession->CsSecureChannelType != ServerSecureChannel &&
  7599. AmWriter &&
  7600. ApiTimer.Period == NlGlobalParameters.ShortApiCallPeriod ) {
  7601. //
  7602. // If the API took really long,
  7603. // increment the count.
  7604. //
  7605. if( NetpLogonTimeHasElapsed(
  7606. ApiTimer.StartTime,
  7607. MAX_DC_API_TIMEOUT + NlGlobalParameters.ExpectedDialupDelay*1000 ) ) {
  7608. //
  7609. // API timeout.
  7610. //
  7611. ClientSession->CsTimeoutCount++;
  7612. ClientSession->CsFastCallCount = 0;
  7613. NlPrintCs((NL_CRITICAL, ClientSession,
  7614. "NlFinishApiClientSession: timeout call to %ws. Count: %lu \n",
  7615. ClientSession->CsUncServerName,
  7616. ClientSession->CsTimeoutCount));
  7617. //
  7618. // If we've had at least one API that took really long in the past,
  7619. // try to determine if the performance it better now.
  7620. //
  7621. } else if ( ClientSession->CsTimeoutCount ) {
  7622. //
  7623. // If this call was really fast,
  7624. // count this call as an indication of better performance.
  7625. //
  7626. if( NetpLogonTimeHasElapsed(
  7627. ApiTimer.StartTime,
  7628. FAST_DC_API_TIMEOUT ) ) {
  7629. //
  7630. // If we've reached the threshold,
  7631. // decrement our timeout count.
  7632. //
  7633. ClientSession->CsFastCallCount++;
  7634. if ( ClientSession->CsFastCallCount == FAST_DC_API_THRESHOLD ) {
  7635. ClientSession->CsTimeoutCount --;
  7636. ClientSession->CsFastCallCount = 0;
  7637. NlPrintCs((NL_CRITICAL, ClientSession,
  7638. "NlFinishApiClientSession: fast call threshold to %ws. Count: %lu \n",
  7639. ClientSession->CsUncServerName,
  7640. ClientSession->CsTimeoutCount));
  7641. } else {
  7642. NlPrintCs((NL_CRITICAL, ClientSession,
  7643. "NlFinishApiClientSession: fast call to %ws. FastCount: %lu \n",
  7644. ClientSession->CsUncServerName,
  7645. ClientSession->CsFastCallCount ));
  7646. }
  7647. }
  7648. }
  7649. //
  7650. // did we hit the limit ?
  7651. //
  7652. if( ClientSession->CsTimeoutCount >= MAX_DC_TIMEOUT_COUNT ) {
  7653. BOOL IsTimeHasElapsed;
  7654. //
  7655. // block CsLastAuthenticationTry access
  7656. //
  7657. EnterCriticalSection( &NlGlobalDcDiscoveryCritSect );
  7658. IsTimeHasElapsed = NetpLogonTimeHasElapsed(
  7659. ClientSession->CsLastAuthenticationTry,
  7660. MAX_DC_REAUTHENTICATION_WAIT );
  7661. LeaveCriticalSection( &NlGlobalDcDiscoveryCritSect );
  7662. if( IsTimeHasElapsed ) {
  7663. NlPrintCs((NL_CRITICAL, ClientSession,
  7664. "NlFinishApiClientSession: dropping the session to %ws\n",
  7665. ClientSession->CsUncServerName ));
  7666. //
  7667. // timeoutcount limit exceeded and it is time to reauth.
  7668. //
  7669. SessionOk = FALSE;
  7670. //
  7671. // Only drop the secure channel if the caller requested it.
  7672. //
  7673. if ( OkToKillSession ) {
  7674. NlSetStatusClientSession( ClientSession, STATUS_NO_LOGON_SERVERS );
  7675. #ifdef _DC_NETLOGON
  7676. //
  7677. // Start asynchronous DC discovery if this is not a workstation.
  7678. //
  7679. if ( !NlGlobalMemberWorkstation ) {
  7680. (VOID) NlDiscoverDc( ClientSession,
  7681. DT_Asynchronous,
  7682. FALSE,
  7683. FALSE ); // don't specify account
  7684. }
  7685. #endif // _DC_NETLOGON
  7686. }
  7687. }
  7688. }
  7689. }
  7690. //
  7691. // If we didn't use concurrent RPC for this API call and the call
  7692. // was made over named pipes, we impersonated this thread's token
  7693. // as anonymous. Revert this impersonation here to the default.
  7694. // We set the impersonation to the default in any case just to be
  7695. // safe.
  7696. //
  7697. //if ( !UseConcurrentRpc( ClientSession, ClientApi ) &&
  7698. // (ClientApi->CaFlags & CA_TCP_BINDING) == 0 ) {
  7699. // NTSTATUS Status;
  7700. // HANDLE NullToken = NULL;
  7701. Status = NtSetInformationThread(
  7702. NtCurrentThread(),
  7703. ThreadImpersonationToken,
  7704. &NullToken,
  7705. sizeof(HANDLE) );
  7706. if ( !NT_SUCCESS( Status)) {
  7707. NlPrint(( NL_CRITICAL,
  7708. "NlFinishApiClientSession: cannot NtSetInformationThread: 0x%lx\n",
  7709. Status ));
  7710. }
  7711. //}
  7712. return SessionOk;
  7713. }
  7714. BOOLEAN
  7715. NlTimeoutOneApiClientSession (
  7716. PCLIENT_SESSION ClientSession
  7717. )
  7718. /*++
  7719. Routine Description:
  7720. Timeout any API calls active specified client session structure
  7721. Arguments:
  7722. ClientSession: Pointer to client session to time out
  7723. Enter with global trust list locked.
  7724. Return Value:
  7725. TRUE - iff this routine temporarily dropped the global trust list lock.
  7726. --*/
  7727. {
  7728. NET_API_STATUS NetStatus;
  7729. BOOLEAN TrustListNowLocked = TRUE;
  7730. BOOLEAN TrustListUnlockedOnce = FALSE;
  7731. ULONG CaIndex;
  7732. //
  7733. // Ignore non-existent sessions.
  7734. //
  7735. if ( ClientSession == NULL ) {
  7736. return FALSE;
  7737. }
  7738. //
  7739. // Loop handling each API call active on this session
  7740. //
  7741. for ( CaIndex=0; CaIndex<NlGlobalMaxConcurrentApi; CaIndex++ ) {
  7742. PCLIENT_API ClientApi;
  7743. ClientApi = &ClientSession->CsClientApi[CaIndex];
  7744. //
  7745. // If an API call is in progress and has taken too long,
  7746. // Timeout the API call.
  7747. //
  7748. if ( NetpLogonTimeHasElapsed( ClientApi->CaApiTimer.StartTime,
  7749. ClientApi->CaApiTimer.Period ) ) {
  7750. //
  7751. // Cancel the RPC call.
  7752. //
  7753. // Keep the trust list locked even though this will be a long call
  7754. // since I have to protect the thread handle.
  7755. //
  7756. // RpcCancelThread merely queues a workitem anyway.
  7757. //
  7758. if ( ClientApi->CaThreadHandle != NULL ) {
  7759. LPWSTR MsgStrings[3];
  7760. NlPrintCs(( NL_CRITICAL, ClientSession,
  7761. "NlTimeoutApiClientSession: Start RpcCancelThread on %ws\n",
  7762. ClientSession->CsUncServerName ));
  7763. MsgStrings[0] = ClientSession->CsUncServerName;
  7764. MsgStrings[1] = ClientSession->CsDebugDomainName;
  7765. MsgStrings[2] = ClientSession->CsDomainInfo->DomUnicodeComputerNameString.Buffer,
  7766. NlpWriteEventlog( NELOG_NetlogonRpcCallCancelled,
  7767. EVENTLOG_ERROR_TYPE,
  7768. NULL,
  7769. 0,
  7770. MsgStrings,
  7771. 3 );
  7772. NetStatus = RpcCancelThread( ClientApi->CaThreadHandle );
  7773. NlPrintCs(( NL_CRITICAL, ClientSession,
  7774. "NlTimeoutApiClientSession: Finish RpcCancelThread on %ws %ld\n",
  7775. ClientSession->CsUncServerName,
  7776. NetStatus ));
  7777. } else {
  7778. NlPrintCs(( NL_CRITICAL, ClientSession,
  7779. "NlTimeoutApiClientSession: No thread handle so can't cancel RPC on %ws\n",
  7780. ClientSession->CsUncServerName ));
  7781. }
  7782. //
  7783. // If the API is not active,
  7784. // and we have an RPC binding handle cached,
  7785. // and it has outlived its usefulness,
  7786. // purge it from the cache.
  7787. //
  7788. } else if ( !IsApiActive(ClientApi) &&
  7789. (ClientApi->CaFlags & CA_BINDING_CACHED) != 0 &&
  7790. NetpLogonTimeHasElapsed( ClientApi->CaApiTimer.StartTime,
  7791. BINDING_CACHE_PERIOD ) ) {
  7792. //
  7793. // We must be a writer of the Client Session to unbind the RPC binding
  7794. // handle.
  7795. //
  7796. // Don't wait to become the writer because:
  7797. // A) We've violated the locking order by trying to become the writer
  7798. // with the trust list locked.
  7799. // B) The writer might be doing a long API call like replication and
  7800. // we're not willing to wait.
  7801. //
  7802. NlRefClientSession( ClientSession );
  7803. if ( NlTimeoutSetWriterClientSession( ClientSession, 0 ) ) {
  7804. //
  7805. // Check again now that we have the lock locked.
  7806. //
  7807. if ( (ClientApi->CaFlags & CA_BINDING_CACHED) != 0 ) {
  7808. NL_RPC_BINDING OldRpcBindingType;
  7809. //
  7810. // Indicate the handle is no longer cached.
  7811. //
  7812. OldRpcBindingType =
  7813. (ClientApi->CaFlags & CA_TCP_BINDING) ? UseTcpIp : UseNamedPipe;
  7814. ClientApi->CaFlags &= ~(CA_BINDING_CACHED|CA_BINDING_AUTHENTICATED|CA_TCP_BINDING);
  7815. NlGlobalBindingHandleCount --;
  7816. //
  7817. // Save the server name but drop all our locks.
  7818. //
  7819. UNLOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  7820. TrustListNowLocked = FALSE;
  7821. TrustListUnlockedOnce = TRUE;
  7822. //
  7823. // Unbind the handle
  7824. //
  7825. NlpSecureChannelUnbind(
  7826. ClientSession,
  7827. ClientSession->CsUncServerName,
  7828. "NlTimeoutApiClientSession",
  7829. CaIndex,
  7830. ClientApi->CaRpcHandle,
  7831. OldRpcBindingType );
  7832. }
  7833. //
  7834. // Done being a writer of the client session
  7835. //
  7836. NlResetWriterClientSession( ClientSession );
  7837. }
  7838. NlUnrefClientSession( ClientSession );
  7839. }
  7840. if ( !TrustListNowLocked ) {
  7841. LOCK_TRUST_LIST( ClientSession->CsDomainInfo );
  7842. TrustListNowLocked = TRUE;
  7843. }
  7844. }
  7845. NlAssert( TrustListNowLocked );
  7846. return TrustListUnlockedOnce;
  7847. }
  7848. VOID
  7849. NlTimeoutApiClientSession(
  7850. IN PDOMAIN_INFO DomainInfo
  7851. )
  7852. /*++
  7853. Routine Description:
  7854. Timeout any API calls active on any of the client session structures
  7855. Arguments:
  7856. DomainInfo - Hosted domain to timeout APIs for
  7857. Return Value:
  7858. None
  7859. --*/
  7860. {
  7861. PCLIENT_SESSION ClientSession;
  7862. PLIST_ENTRY ListEntry;
  7863. //
  7864. // If there are no API calls outstanding,
  7865. // just reset the global timer.
  7866. //
  7867. NlPrintDom(( NL_SESSION_MORE, DomainInfo,
  7868. "NlTimeoutApiClientSession Called\n"));
  7869. LOCK_TRUST_LIST( DomainInfo );
  7870. if ( NlGlobalBindingHandleCount == 0 ) {
  7871. NlGlobalApiTimer.Period = (DWORD) MAILSLOT_WAIT_FOREVER;
  7872. //
  7873. // If there are API calls outstanding,
  7874. // Loop through the trust list making a list of Servers to kill
  7875. //
  7876. } else {
  7877. //
  7878. // Mark each trust list entry indicating it needs to be handled
  7879. //
  7880. for ( ListEntry = DomainInfo->DomTrustList.Flink ;
  7881. ListEntry != &DomainInfo->DomTrustList ;
  7882. ListEntry = ListEntry->Flink) {
  7883. ClientSession = CONTAINING_RECORD( ListEntry,
  7884. CLIENT_SESSION,
  7885. CsNext );
  7886. //
  7887. // APIs are only outstanding to directly trusted domains.
  7888. //
  7889. if ( ClientSession->CsFlags & CS_DIRECT_TRUST ) {
  7890. ClientSession->CsFlags |= CS_HANDLE_API_TIMER;
  7891. }
  7892. }
  7893. //
  7894. // Loop thru the trust list handling API timeout
  7895. //
  7896. for ( ListEntry = DomainInfo->DomTrustList.Flink ;
  7897. ListEntry != &DomainInfo->DomTrustList ;
  7898. ) {
  7899. ClientSession = CONTAINING_RECORD( ListEntry,
  7900. CLIENT_SESSION,
  7901. CsNext );
  7902. //
  7903. // If we've already done this entry,
  7904. // skip this entry.
  7905. //
  7906. if ( (ClientSession->CsFlags & CS_HANDLE_API_TIMER) == 0 ) {
  7907. ListEntry = ListEntry->Flink;
  7908. continue;
  7909. }
  7910. ClientSession->CsFlags &= ~CS_HANDLE_API_TIMER;
  7911. //
  7912. // Handle timing out the API call and the RPC binding handle.
  7913. //
  7914. // If the routine had to drop the TrustList crit sect,
  7915. // start at the very beginning of the list.
  7916. if ( NlTimeoutOneApiClientSession ( ClientSession ) ) {
  7917. ListEntry = DomainInfo->DomTrustList.Flink;
  7918. } else {
  7919. ListEntry = ListEntry->Flink;
  7920. }
  7921. }
  7922. //
  7923. // Do the global client session, too.
  7924. //
  7925. if ( DomainInfo->DomRole != RolePrimary ) {
  7926. ClientSession = NlRefDomClientSession( DomainInfo );
  7927. if ( ClientSession != NULL ) {
  7928. (VOID) NlTimeoutOneApiClientSession ( ClientSession );
  7929. NlUnrefClientSession( ClientSession );
  7930. }
  7931. }
  7932. }
  7933. UNLOCK_TRUST_LIST( DomainInfo );
  7934. }
  7935. NTSTATUS
  7936. NetrEnumerateTrustedDomains (
  7937. IN LPWSTR ServerName OPTIONAL,
  7938. OUT PDOMAIN_NAME_BUFFER DomainNameBuffer
  7939. )
  7940. /*++
  7941. Routine Description:
  7942. This API returns the names of the domains trusted by the domain ServerName is a member of.
  7943. The returned list does not include the domain ServerName is directly a member of.
  7944. Netlogon implements this API by calling LsaEnumerateTrustedDomains on a DC in the
  7945. domain ServerName is a member of. However, Netlogon returns cached information if
  7946. it has been less than 5 minutes since the last call was made or if no DC is available.
  7947. Netlogon's cache of Trusted domain names is maintained in the registry across reboots.
  7948. As such, the list is available upon boot even if no DC is available.
  7949. Arguments:
  7950. ServerName - name of remote server (null for local). ServerName must be an NT workstation
  7951. or NT non-DC server.
  7952. DomainNameBuffer->DomainNames - Returns an allocated buffer containing the list of trusted domains in
  7953. MULTI-SZ format (i.e., each string is terminated by a zero character, the next string
  7954. immediately follows, the sequence is terminated by zero length domain name). The
  7955. buffer should be freed using NetApiBufferFree.
  7956. DomainNameBuffer->DomainNameByteCount - Number of bytes returned in DomainNames
  7957. Return Value:
  7958. ERROR_SUCCESS - Success.
  7959. STATUS_NOT_SUPPORTED - This machine is not an NT workstation or NT non-DC server.
  7960. STATUS_NO_LOGON_SERVERS - No DC could be found and no cached information is available.
  7961. STATUS_NO_TRUST_LSA_SECRET - The client side of the trust relationship is
  7962. broken and no cached information is available.
  7963. STATUS_NO_TRUST_SAM_ACCOUNT - The server side of the trust relationship is
  7964. broken or the password is broken and no cached information is available.
  7965. --*/
  7966. {
  7967. NET_API_STATUS NetStatus;
  7968. NETLOGON_TRUSTED_DOMAIN_ARRAY Domains = {0};
  7969. LPWSTR CurrentLoc;
  7970. ULONG i;
  7971. ULONG BufferLength;
  7972. LPWSTR TrustedDomainList = NULL;
  7973. //
  7974. // Call the new-fangled routine to do the actual work.
  7975. //
  7976. NetStatus = NetrEnumerateTrustedDomainsEx (
  7977. ServerName,
  7978. &Domains );
  7979. if ( NetStatus != NO_ERROR ) {
  7980. goto Cleanup;
  7981. }
  7982. //
  7983. // Walk through the returned list an convert it to the proper form
  7984. //
  7985. BufferLength = sizeof(WCHAR);
  7986. for ( i=0; i<Domains.DomainCount; i++ ) {
  7987. if ( Domains.Domains[i].NetbiosDomainName != NULL ) {
  7988. BufferLength += (wcslen(Domains.Domains[i].NetbiosDomainName)+1) * sizeof(WCHAR);
  7989. }
  7990. }
  7991. TrustedDomainList = (LPWSTR) NetpMemoryAllocate( BufferLength );
  7992. if (TrustedDomainList == NULL) {
  7993. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  7994. goto Cleanup;
  7995. }
  7996. //
  7997. // Now add all the trusted domains onto the string we allocated
  7998. //
  7999. *TrustedDomainList = L'\0';
  8000. CurrentLoc = TrustedDomainList;
  8001. for ( i=0; i<Domains.DomainCount; i++ ) {
  8002. //
  8003. // Skip domains not understood by the old API.
  8004. //
  8005. if ( Domains.Domains[i].NetbiosDomainName != NULL &&
  8006. (Domains.Domains[i].Flags & DS_DOMAIN_PRIMARY) == 0 &&
  8007. (Domains.Domains[i].TrustType == TRUST_TYPE_UPLEVEL ||
  8008. Domains.Domains[i].TrustType == TRUST_TYPE_DOWNLEVEL ) ) {
  8009. ULONG StringLength =
  8010. wcslen(Domains.Domains[i].NetbiosDomainName);
  8011. RtlCopyMemory(
  8012. CurrentLoc,
  8013. Domains.Domains[i].NetbiosDomainName,
  8014. StringLength * sizeof(WCHAR) );
  8015. CurrentLoc += StringLength;
  8016. *(CurrentLoc++) = L'\0';
  8017. *CurrentLoc = L'\0'; // Place double terminator each time
  8018. }
  8019. }
  8020. NetStatus = NO_ERROR;
  8021. //
  8022. // Free any locally used resources.
  8023. //
  8024. Cleanup:
  8025. //
  8026. // Return the DCName to the caller.
  8027. //
  8028. if ( NetStatus == NO_ERROR ) {
  8029. DomainNameBuffer->DomainNameByteCount = NetpTStrArraySize( TrustedDomainList );
  8030. DomainNameBuffer->DomainNames = (LPBYTE) TrustedDomainList;
  8031. } else {
  8032. if ( TrustedDomainList != NULL ) {
  8033. NetApiBufferFree( TrustedDomainList );
  8034. }
  8035. DomainNameBuffer->DomainNameByteCount = 0;
  8036. DomainNameBuffer->DomainNames = NULL;
  8037. }
  8038. if ( Domains.Domains != NULL ) {
  8039. MIDL_user_free( Domains.Domains );
  8040. }
  8041. return NetpApiStatusToNtStatus( NetStatus );
  8042. UNREFERENCED_PARAMETER( ServerName );
  8043. }
  8044. NET_API_STATUS
  8045. NlpEnumerateDomainTrusts (
  8046. IN PDOMAIN_INFO DomainInfo,
  8047. IN ULONG Flags,
  8048. OUT PULONG RetForestTrustListCount,
  8049. OUT PDS_DOMAIN_TRUSTSW *RetForestTrustList
  8050. )
  8051. /*++
  8052. Routine Description:
  8053. This API returns the names of the domains trusting/trusted by the domain ServerName
  8054. is a member of.
  8055. This is the worker routine for getting the cached domain trusts list from a DC.
  8056. Arguments:
  8057. DomainInfo - Hosted domain that this call pertains to
  8058. Flags - Specifies attributes of trusts which should be returned. These are the flags
  8059. of the DS_DOMAIN_TRUSTSW structure. If a trust entry has any of the bits specified
  8060. in Flags set, it will be returned.
  8061. RetForestTrustListCount - Returns the number of entries in RetForestTrustList.
  8062. RetForestTrustList - Returns an array of domains.
  8063. The caller should free this array using MIDL_user_free.
  8064. Return Value:
  8065. NO_ERROR - Success.
  8066. ERROR_NO_LOGON_SERVERS - No DC could be found and no cached information is available.
  8067. ERROR_NO_TRUST_LSA_SECRET - The client side of the trust relationship is
  8068. broken and no cached information is available.
  8069. ERROR_NO_TRUST_SAM_ACCOUNT - The server side of the trust relationship is
  8070. broken or the password is broken and no cached information is available.
  8071. ERROR_INVALID_FLAGS - The Flags parameter has invalid bits set.
  8072. --*/
  8073. {
  8074. NET_API_STATUS NetStatus;
  8075. PDS_DOMAIN_TRUSTSW ForestTrustList = NULL;
  8076. ULONG ForestTrustListCount = 0;
  8077. ULONG ForestTrustListSize;
  8078. LPBYTE Where;
  8079. ULONG Index;
  8080. DWORD WaitResult;
  8081. PULONG IndexInReturnedList = NULL;
  8082. //
  8083. // Wait until the updated trust info is available or we are signaled to
  8084. // terminate.
  8085. //
  8086. HANDLE Waits[2];
  8087. Waits[0] = NlGlobalTrustInfoUpToDateEvent;
  8088. Waits[1] = NlGlobalTerminateEvent;
  8089. for ( ;; ) {
  8090. WaitResult = WaitForMultipleObjects( 2, // # of events to wait for
  8091. Waits, // array of event handles
  8092. FALSE, // wait for all objects ?
  8093. 20000 ); // wait for 20 seconds max
  8094. //
  8095. // The TrustInfoUpToDate event is set before the trust info gets actually
  8096. // updated, so try to lock DomainInfo here -- you will succeed only after
  8097. // the trust info gets updated at which time the lock has been released.
  8098. //
  8099. LOCK_TRUST_LIST( DomainInfo );
  8100. //
  8101. // If we got a timeout or some kind of error occured, we've done our best to
  8102. // get the updated data but the data is still old. We are going to return
  8103. // the old data. Also, break out of the loop if we are said to terminate.
  8104. //
  8105. if ( WaitResult != WAIT_OBJECT_0 ) {
  8106. NlPrint((NL_MISC,
  8107. "NlpEnumerateDomainTrusts: Can't get updated Domain List from cache.\n"));
  8108. break;
  8109. }
  8110. //
  8111. // Check if the event is still set; it may be reset by another LSA call between
  8112. // the time the event was set last time and the trust info got updated or by
  8113. // the NlInitTrustList function itself if there was an error.
  8114. //
  8115. WaitResult = WaitForSingleObject( NlGlobalTrustInfoUpToDateEvent, 0 );
  8116. if ( WaitResult == WAIT_OBJECT_0 || WaitResult == WAIT_FAILED ) {
  8117. break;
  8118. } else {
  8119. NlPrint((NL_MISC,
  8120. "NlpEnumerateDomainTrusts: NlGlobalTrustInfoUpToDateEvent has been reset.\n" ));
  8121. }
  8122. UNLOCK_TRUST_LIST( DomainInfo );
  8123. }
  8124. //
  8125. // Return the information from the cache
  8126. //
  8127. if ( DomainInfo->DomForestTrustListSize ) {
  8128. ULONG VariableSize;
  8129. //
  8130. // Compute the size of the trusted/trusting domain list.
  8131. //
  8132. ForestTrustListSize = 0;
  8133. ForestTrustListCount = 0;
  8134. for ( Index=0; Index<DomainInfo->DomForestTrustListCount; Index++ ) {
  8135. if ( DomainInfo->DomForestTrustList[Index].Flags & Flags ) {
  8136. VariableSize = 0;
  8137. if ( DomainInfo->DomForestTrustList[Index].DnsDomainName != NULL ) {
  8138. VariableSize +=
  8139. (wcslen( DomainInfo->DomForestTrustList[Index].DnsDomainName ) + 1) * sizeof(WCHAR);
  8140. }
  8141. if ( DomainInfo->DomForestTrustList[Index].NetbiosDomainName != NULL ) {
  8142. VariableSize +=
  8143. (wcslen( DomainInfo->DomForestTrustList[Index].NetbiosDomainName ) + 1) * sizeof(WCHAR);
  8144. }
  8145. if ( DomainInfo->DomForestTrustList[Index].DomainSid != NULL ) {
  8146. VariableSize +=
  8147. RtlLengthSid( DomainInfo->DomForestTrustList[Index].DomainSid );
  8148. }
  8149. VariableSize = ROUND_UP_COUNT( VariableSize, ALIGN_DWORD );
  8150. ForestTrustListSize += ( VariableSize + sizeof(DS_DOMAIN_TRUSTSW) );
  8151. ForestTrustListCount++;
  8152. }
  8153. }
  8154. if ( ForestTrustListSize == 0 ) {
  8155. NetStatus = NO_ERROR;
  8156. UNLOCK_TRUST_LIST( DomainInfo );
  8157. goto Cleanup;
  8158. }
  8159. ForestTrustList = (PDS_DOMAIN_TRUSTSW) NetpMemoryAllocate( ForestTrustListSize );
  8160. if (ForestTrustList == NULL) {
  8161. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  8162. UNLOCK_TRUST_LIST( DomainInfo );
  8163. goto Cleanup;
  8164. }
  8165. //
  8166. // If domains in the forest are requested,
  8167. // allocate an array of ULONGs that will be used to keep track of the
  8168. // index of a trust entry in the returned list. This is needed to
  8169. // corectly set ParentIndex for entries returned.
  8170. //
  8171. if ( Flags & DS_DOMAIN_IN_FOREST ) {
  8172. IndexInReturnedList = LocalAlloc( LMEM_ZEROINIT,
  8173. DomainInfo->DomForestTrustListCount * sizeof(ULONG) );
  8174. if ( IndexInReturnedList == NULL ) {
  8175. NetStatus = ERROR_NOT_ENOUGH_MEMORY;
  8176. UNLOCK_TRUST_LIST( DomainInfo );
  8177. goto Cleanup;
  8178. }
  8179. }
  8180. //
  8181. // Now add all the trusted/trusting domains into the buffer we allocated
  8182. //
  8183. Where = (LPBYTE)&ForestTrustList[ForestTrustListCount];
  8184. ForestTrustListCount = 0;
  8185. for ( Index=0; Index<DomainInfo->DomForestTrustListCount; Index++ ) {
  8186. //
  8187. // Skip this entry if the caller doesn't need it
  8188. //
  8189. if ( (DomainInfo->DomForestTrustList[Index].Flags & Flags) == 0 ) {
  8190. continue;
  8191. }
  8192. //
  8193. // If domains in the forest are requested,
  8194. // remember the index of this entry in the returned list
  8195. //
  8196. if ( Flags & DS_DOMAIN_IN_FOREST ) {
  8197. IndexInReturnedList[Index] = ForestTrustListCount;
  8198. }
  8199. //
  8200. // Fill in the fixed length data
  8201. //
  8202. ForestTrustList[ForestTrustListCount].Flags = DomainInfo->DomForestTrustList[Index].Flags;
  8203. ForestTrustList[ForestTrustListCount].ParentIndex = DomainInfo->DomForestTrustList[Index].ParentIndex;
  8204. ForestTrustList[ForestTrustListCount].TrustType = DomainInfo->DomForestTrustList[Index].TrustType;
  8205. ForestTrustList[ForestTrustListCount].TrustAttributes = DomainInfo->DomForestTrustList[Index].TrustAttributes;
  8206. ForestTrustList[ForestTrustListCount].DomainGuid = DomainInfo->DomForestTrustList[Index].DomainGuid;
  8207. //
  8208. // If this is a primary domain entry, determine whether it runs
  8209. // in native or mixed mode
  8210. //
  8211. if ( (DomainInfo->DomForestTrustList[Index].Flags & DS_DOMAIN_PRIMARY) &&
  8212. !SamIMixedDomain( DomainInfo->DomSamServerHandle ) ) {
  8213. ForestTrustList[ForestTrustListCount].Flags |= DS_DOMAIN_NATIVE_MODE;
  8214. }
  8215. //
  8216. // Fill in the variable length data.
  8217. //
  8218. if ( DomainInfo->DomForestTrustList[Index].DomainSid != NULL ) {
  8219. ULONG SidSize;
  8220. ForestTrustList[ForestTrustListCount].DomainSid = (PSID) Where;
  8221. SidSize = RtlLengthSid( DomainInfo->DomForestTrustList[Index].DomainSid );
  8222. RtlCopyMemory( Where,
  8223. DomainInfo->DomForestTrustList[Index].DomainSid,
  8224. SidSize );
  8225. Where += SidSize;
  8226. } else {
  8227. ForestTrustList[ForestTrustListCount].DomainSid = NULL;
  8228. }
  8229. if ( DomainInfo->DomForestTrustList[Index].NetbiosDomainName != NULL ) {
  8230. ULONG StringSize;
  8231. ForestTrustList[ForestTrustListCount].NetbiosDomainName = (LPWSTR)Where;
  8232. StringSize = (wcslen( DomainInfo->DomForestTrustList[Index].NetbiosDomainName ) + 1) * sizeof(WCHAR);
  8233. RtlCopyMemory( Where,
  8234. DomainInfo->DomForestTrustList[Index].NetbiosDomainName,
  8235. StringSize );
  8236. Where += StringSize;
  8237. } else {
  8238. ForestTrustList[ForestTrustListCount].NetbiosDomainName = NULL;
  8239. }
  8240. if ( DomainInfo->DomForestTrustList[Index].DnsDomainName != NULL ) {
  8241. ULONG StringSize;
  8242. ForestTrustList[ForestTrustListCount].DnsDomainName = (LPWSTR)Where;
  8243. StringSize = (wcslen( DomainInfo->DomForestTrustList[Index].DnsDomainName ) + 1) * sizeof(WCHAR);
  8244. RtlCopyMemory( Where,
  8245. DomainInfo->DomForestTrustList[Index].DnsDomainName,
  8246. StringSize );
  8247. Where += StringSize;
  8248. } else {
  8249. ForestTrustList[ForestTrustListCount].DnsDomainName = NULL;
  8250. }
  8251. Where = ROUND_UP_POINTER( Where, ALIGN_DWORD);
  8252. ForestTrustListCount++;
  8253. }
  8254. //
  8255. // Fix ParentIndex. If domains in the forest are requested,
  8256. // adjust the index to point to the appropriate entry in the
  8257. // returned list. Otherwise, set the index to 0.
  8258. //
  8259. if ( Flags & DS_DOMAIN_IN_FOREST ) {
  8260. for ( Index=0; Index<ForestTrustListCount; Index++ ) {
  8261. if ( (ForestTrustList[Index].Flags & DS_DOMAIN_IN_FOREST) != 0 &&
  8262. (ForestTrustList[Index].Flags & DS_DOMAIN_TREE_ROOT) == 0 ) {
  8263. ForestTrustList[Index].ParentIndex =
  8264. IndexInReturnedList[ForestTrustList[Index].ParentIndex];
  8265. }
  8266. }
  8267. } else {
  8268. for ( Index=0; Index<ForestTrustListCount; Index++ ) {
  8269. ForestTrustList[Index].ParentIndex = 0;
  8270. }
  8271. }
  8272. }
  8273. UNLOCK_TRUST_LIST( DomainInfo );
  8274. NetStatus = NO_ERROR;
  8275. //
  8276. // Free any locally used resources.
  8277. //
  8278. Cleanup:
  8279. if ( IndexInReturnedList != NULL ) {
  8280. LocalFree( IndexInReturnedList );
  8281. }
  8282. //
  8283. // Return the info to the caller.
  8284. //
  8285. if ( NetStatus == NO_ERROR ) {
  8286. *RetForestTrustListCount = ForestTrustListCount;
  8287. *RetForestTrustList = ForestTrustList;
  8288. } else {
  8289. if ( ForestTrustList != NULL ) {
  8290. NetApiBufferFree( ForestTrustList );
  8291. }
  8292. *RetForestTrustListCount = 0;
  8293. *RetForestTrustList = NULL;
  8294. }
  8295. return NetStatus;
  8296. }
  8297. NET_API_STATUS
  8298. DsrEnumerateDomainTrusts (
  8299. IN LPWSTR ServerName OPTIONAL,
  8300. IN ULONG Flags,
  8301. OUT PNETLOGON_TRUSTED_DOMAIN_ARRAY Domains
  8302. )
  8303. /*++
  8304. Routine Description:
  8305. This API returns the names of the domains trusting/trusted by the domain ServerName
  8306. is a member of.
  8307. Netlogon's cache of Trusted domain names is maintained in a file across reboots.
  8308. As such, the list is available upon boot even if no DC is available.
  8309. Arguments:
  8310. ServerName - name of remote server (null for local). ServerName must be an NT workstation
  8311. or NT non-DC server.
  8312. Flags - Specifies attributes of trusts which should be returned. These are the flags
  8313. of the DS_DOMAIN_TRUSTSW strusture. If a trust entry has any of the bits specified
  8314. in Flags set, it will be returned.
  8315. Domains - Returns an array of trusted domains.
  8316. Return Value:
  8317. NO_ERROR - Success.
  8318. ERROR_NO_LOGON_SERVERS - No DC could be found and no cached information is available.
  8319. ERROR_NO_TRUST_LSA_SECRET - The client side of the trust relationship is
  8320. broken and no cached information is available.
  8321. ERROR_NO_TRUST_SAM_ACCOUNT - The server side of the trust relationship is
  8322. broken or the password is broken and no cached information is available.
  8323. ERROR_INVALID_FLAGS - The Flags parameter has invalid bits set.
  8324. --*/
  8325. {
  8326. NET_API_STATUS NetStatus;
  8327. NTSTATUS Status;
  8328. PCLIENT_SESSION ClientSession = NULL;
  8329. BOOLEAN FirstTry = TRUE;
  8330. PDOMAIN_INFO DomainInfo = NULL;
  8331. PDS_DOMAIN_TRUSTSW ForestTrustList = NULL;
  8332. ULONG ForestTrustListCount = 0;
  8333. ULONG ForestTrustListSize;
  8334. NlPrint((NL_MISC,
  8335. "DsrEnumerateDomainTrusts: Called, Flags = 0x%lx\n", Flags ));
  8336. //
  8337. // Validate the parameter
  8338. //
  8339. if ( Domains == NULL ) {
  8340. return ERROR_INVALID_PARAMETER;
  8341. }
  8342. //
  8343. // Validate the Flags parameter
  8344. //
  8345. if ( (Flags & DS_DOMAIN_VALID_FLAGS) == 0 ||
  8346. (Flags & ~DS_DOMAIN_VALID_FLAGS) != 0 ) {
  8347. NlPrint((NL_CRITICAL,
  8348. "DsrEnumerateDomainTrusts: Invalid Flags parameter: 0x%lx\n", Flags ));
  8349. NetStatus = ERROR_INVALID_FLAGS;
  8350. goto Cleanup;
  8351. }
  8352. //
  8353. // Find the referenced domain
  8354. DomainInfo = NlFindDomainByServerName( ServerName ); // Primary domain
  8355. if ( DomainInfo == NULL ) {
  8356. // Default to primary domain to handle the case where the ComputerName
  8357. // is an IP address.
  8358. DomainInfo = NlFindNetbiosDomain( NULL, TRUE );
  8359. if ( DomainInfo == NULL ) {
  8360. NetStatus = ERROR_INVALID_COMPUTERNAME;
  8361. goto Cleanup;
  8362. }
  8363. }
  8364. //
  8365. // On workstations,
  8366. // Refresh the cache periodically
  8367. //
  8368. NetStatus = NO_ERROR;
  8369. if ( NlGlobalMemberWorkstation ) {
  8370. ClientSession = NlRefDomClientSession(DomainInfo);
  8371. if ( ClientSession == NULL ) {
  8372. NetStatus = ERROR_INVALID_COMPUTERNAME;
  8373. } else {
  8374. //
  8375. // Become a writer of the client session.
  8376. //
  8377. if ( !NlTimeoutSetWriterClientSession( ClientSession, WRITER_WAIT_PERIOD ) ) {
  8378. NlPrint((NL_CRITICAL, "DsrEnumerateDomainTrusts: Can't become writer of client session.\n" ));
  8379. NetStatus = ERROR_NO_LOGON_SERVERS;
  8380. } else {
  8381. //
  8382. // If the session isn't authenticated,
  8383. // do so now.
  8384. //
  8385. FirstTryFailed:
  8386. Status = NlEnsureSessionAuthenticated( ClientSession, 0 );
  8387. if ( !NT_SUCCESS(Status) ) {
  8388. NetStatus = NetpNtStatusToApiStatus( Status );
  8389. } else {
  8390. //
  8391. // If it has been more than 5 minutes since we've refreshed our cache,
  8392. // get a new list from our primary domain.
  8393. //
  8394. if ( NetpLogonTimeHasElapsed( NlGlobalTrustedDomainListTime, 5 * 60 * 1000 ) ) {
  8395. NlPrintCs((NL_MISC, ClientSession,
  8396. "DsrEnumerateDomainTrusts: Domain List collected from %ws\n",
  8397. ClientSession->CsUncServerName ));
  8398. NlAssert( ClientSession->CsUncServerName != NULL );
  8399. Status = NlUpdateDomainInfo ( ClientSession );
  8400. if ( !NT_SUCCESS(Status) ) {
  8401. NlSetStatusClientSession( ClientSession, Status );
  8402. if ( Status == STATUS_ACCESS_DENIED ) {
  8403. //
  8404. // Perhaps the netlogon service on the server has just restarted.
  8405. // Try just once to set up a session to the server again.
  8406. //
  8407. if ( FirstTry ) {
  8408. FirstTry = FALSE;
  8409. goto FirstTryFailed;
  8410. }
  8411. }
  8412. NetStatus = NetpNtStatusToApiStatus( Status );
  8413. }
  8414. }
  8415. }
  8416. //
  8417. // Read the list from cache even if you failed to get a fresh copy from a DC.
  8418. // Read it while holding the write lock to avoid concurrent reading/writing
  8419. // problems.
  8420. //
  8421. NetStatus = NlReadFileTrustedDomainList (
  8422. DomainInfo,
  8423. NL_FOREST_BINARY_LOG_FILE,
  8424. FALSE, // Don't delete (Save it for the next boot)
  8425. Flags,
  8426. &ForestTrustList,
  8427. &ForestTrustListSize,
  8428. &ForestTrustListCount );
  8429. if ( NetStatus != NO_ERROR ) {
  8430. NlPrint((NL_CRITICAL,
  8431. "DsrEnumerateDomainTrusts: Can't get Domain List from cache: 0x%lX\n",
  8432. NetStatus ));
  8433. NetStatus = ERROR_NO_LOGON_SERVERS;
  8434. }
  8435. NlResetWriterClientSession( ClientSession );
  8436. }
  8437. NlUnrefClientSession( ClientSession );
  8438. }
  8439. //
  8440. // On non-workstations,
  8441. // grab the trusted domain list from the in-memory list.
  8442. //
  8443. } else {
  8444. //
  8445. // Call the worker routine to get the list.
  8446. //
  8447. NetStatus = NlpEnumerateDomainTrusts (
  8448. DomainInfo,
  8449. Flags,
  8450. &ForestTrustListCount,
  8451. &ForestTrustList );
  8452. }
  8453. //
  8454. // Free any locally used resources.
  8455. //
  8456. Cleanup:
  8457. if ( DomainInfo != NULL ) {
  8458. NlDereferenceDomain( DomainInfo );
  8459. }
  8460. //
  8461. // Return the DCName to the caller.
  8462. //
  8463. if ( NetStatus == NO_ERROR ) {
  8464. Domains->DomainCount = ForestTrustListCount;
  8465. Domains->Domains = ForestTrustList;
  8466. } else {
  8467. if ( ForestTrustList != NULL ) {
  8468. NetApiBufferFree( ForestTrustList );
  8469. }
  8470. Domains->DomainCount = 0;
  8471. Domains->Domains = NULL;
  8472. }
  8473. NlPrint((NL_MISC,
  8474. "DsrEnumerateDomainTrusts: returns: %ld\n",
  8475. NetStatus ));
  8476. return NetStatus;
  8477. }
  8478. NET_API_STATUS
  8479. NetrEnumerateTrustedDomainsEx (
  8480. IN LPWSTR ServerName OPTIONAL,
  8481. OUT PNETLOGON_TRUSTED_DOMAIN_ARRAY Domains
  8482. )
  8483. /*++
  8484. Routine Description:
  8485. This API returns the names of the domains trusted by the domain ServerName
  8486. is a member of.
  8487. Netlogon's cache of Trusted domain names is maintained in a file across reboots.
  8488. As such, the list is available upon boot even if no DC is available.
  8489. Arguments:
  8490. ServerName - name of remote server (null for local). ServerName must be an NT workstation
  8491. or NT non-DC server.
  8492. Domains - Returns an array of trusted domains.
  8493. Return Value:
  8494. NO_ERROR - Success.
  8495. ERROR_NO_LOGON_SERVERS - No DC could be found and no cached information is available.
  8496. ERROR_NO_TRUST_LSA_SECRET - The client side of the trust relationship is
  8497. broken and no cached information is available.
  8498. ERROR_NO_TRUST_SAM_ACCOUNT - The server side of the trust relationship is
  8499. broken or the password is broken and no cached information is available.
  8500. --*/
  8501. {
  8502. NET_API_STATUS NetStatus;
  8503. ULONG Index;
  8504. NlPrint((NL_MISC,
  8505. "NetrEnumerateTrustedDomains: Called.\n" ));
  8506. NetStatus = DsrEnumerateDomainTrusts( ServerName,
  8507. DS_DOMAIN_IN_FOREST |
  8508. DS_DOMAIN_DIRECT_OUTBOUND |
  8509. DS_DOMAIN_PRIMARY,
  8510. Domains );
  8511. //
  8512. // Do not leak the new DS_DOMAIN_DIRECT_INBOUND bit to the caller of this old
  8513. // API; the caller can get confused otherwise. The new DS_DOMAIN_DIRECT_OUTBOUND
  8514. // bit is just the renamed old DS_DOMAIN_DIRECT_TRUST, so leave it alone.
  8515. //
  8516. if ( NetStatus == NO_ERROR ) {
  8517. for ( Index = 0; Index < Domains->DomainCount; Index++ ) {
  8518. Domains->Domains[Index].Flags &= ~DS_DOMAIN_DIRECT_INBOUND;
  8519. }
  8520. }
  8521. return NetStatus;
  8522. }
  8523. NTSTATUS
  8524. I_NetLogonMixedDomain(
  8525. OUT PBOOL MixedMode
  8526. )
  8527. /*++
  8528. Routine Description:
  8529. This routine is provided for in-proc callers on workstations
  8530. to determine whether the workstaion's domain is running in mixed
  8531. mode. This is a quick routine that returns the state of a global
  8532. boolean. The boolean is set on boot from the cached domain trust
  8533. info and it's updated on domain trust refreshes.
  8534. If the machine is a DC, this routine returns the authoritative
  8535. answer by calling SamIMixedDomain.
  8536. Arguments:
  8537. MixedMode - Returns TRUE/FALSE if the domain is mixed/native mode
  8538. Return Value:
  8539. STATUS_SUCCESS - The operation was successful
  8540. STATUS_NETLOGON_NOT_STARTED - Netlogon hasn't started yet
  8541. --*/
  8542. {
  8543. //
  8544. // If caller is calling when the netlogon service hasn't started yet,
  8545. // tell it so.
  8546. //
  8547. if ( !NlStartNetlogonCall() ) {
  8548. return STATUS_NETLOGON_NOT_STARTED;
  8549. }
  8550. if ( NlGlobalMemberWorkstation ) {
  8551. *MixedMode = NlGlobalWorkstationMixedModeDomain;
  8552. } else {
  8553. *MixedMode = SamIMixedDomain( NlGlobalDomainInfo->DomSamServerHandle );
  8554. }
  8555. //
  8556. // Indicate that the calling thread has left netlogon.dll
  8557. //
  8558. NlEndNetlogonCall();
  8559. return STATUS_SUCCESS;
  8560. }