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

9640 lines
274 KiB

  1. /*++
  2. Copyright (c) 1992 Microsoft Corporation
  3. Module Name:
  4. dblookup.c
  5. Abstract:
  6. LSA Database - Lookup Sid and Name routines
  7. NOTE: This module should remain as portable code that is independent
  8. of the implementation of the LSA Database. As such, it is
  9. permitted to use only the exported LSA Database interfaces
  10. contained in db.h and NOT the private implementation
  11. dependent functions in dbp.h.
  12. Author:
  13. Scott Birrell (ScottBi) November 27, 1992
  14. Environment:
  15. Revision History:
  16. --*/
  17. #include <lsapch2.h>
  18. #include "dbp.h"
  19. #include <sidcache.h>
  20. #include <bndcache.h>
  21. #include <alloca.h>
  22. #include <ntdsa.h>
  23. #include <ntdsapi.h>
  24. #include <ntdsapip.h>
  25. #include "lsawmi.h"
  26. #include <sddl.h>
  27. #include <lmapibuf.h>
  28. #include <dsgetdc.h>
  29. #include <windns.h> // DnsNameCompare_W
  30. //////////////////////////////////////////////////////////////////////////
  31. // //
  32. // Lsa Lookup Sid and Name Private Global State Variables //
  33. // //
  34. //////////////////////////////////////////////////////////////////////////
  35. LARGE_INTEGER LsapDbLookupTimeout;
  36. HANDLE LsapDbLookupStartedEvent = NULL;
  37. //
  38. // This global is set to TRUE when a particular registry key is set
  39. // (see the lookup init routine). It means that when a
  40. // downlevel client is making a request return the current features of
  41. // lookup (search by UPN, transitive trust, etc) all of which are
  42. // performed by doing a GC search. By default this feature is
  43. // turned off.
  44. //
  45. BOOLEAN LsapAllowExtendedDownlevelLookup = FALSE;
  46. //
  47. // This variable, settable in the registry, indicates what events
  48. // should be logged.
  49. //
  50. DWORD LsapLookupLogLevel = 0;
  51. //
  52. // This global makes LsarLookupSids return SidTypeDeleted for SID's that
  53. // otherwise would be returned as SidTypeUnknown. This is to prevent NT4
  54. // wksta's from AV'ing. See WinSERaid bug 11298 for more details.
  55. //
  56. BOOLEAN LsapReturnSidTypeDeleted = FALSE;
  57. //
  58. // This global is set to TRUE when a particular registry value is set
  59. // (see the lookup init routine). The chaining of isolated names to
  60. // externally trusted domains depends on this value being FALSE.
  61. //
  62. BOOLEAN LsapLookupRestrictIsolatedNameLevel = FALSE;
  63. //////////////////////////////////////////////////////////////////////////
  64. // //
  65. // Forwards for this module //
  66. // //
  67. //////////////////////////////////////////////////////////////////////////
  68. NTSTATUS
  69. LsapRtlValidateControllerTrustedDomainByHandle(
  70. IN LSA_HANDLE DcPolicyHandle,
  71. IN PLSAPR_TRUST_INFORMATION TrustInformation
  72. );
  73. NTSTATUS
  74. LsapDbLookupInitPolicyCache(
  75. VOID
  76. );
  77. NTSTATUS
  78. LsapDbLookupGetServerConnection(
  79. IN LSAPR_TRUST_INFORMATION_EX *TrustInfo,
  80. IN DWORD Flags,
  81. IN LSAP_LOOKUP_LEVEL LookupLevel,
  82. IN PLARGE_INTEGER FailedSessionSetupTime, OPTIONAL
  83. OUT LPWSTR *ServerName,
  84. OUT NL_OS_VERSION *ServerOsVersion,
  85. OUT LPWSTR *ServerPrincipalName,
  86. OUT PVOID *ClientContext,
  87. OUT ULONG *AuthnLevel,
  88. OUT LSA_HANDLE *PolicyHandle,
  89. OUT PLSAP_BINDING_CACHE_ENTRY * ControllerPolicyEntry,
  90. OUT PLARGE_INTEGER SessionSetupTime
  91. );
  92. NTSTATUS
  93. LsapNullTerminateUnicodeString(
  94. IN PUNICODE_STRING String,
  95. OUT LPWSTR *pBuffer,
  96. OUT BOOLEAN *fFreeBuffer
  97. );
  98. //
  99. // Flags for LsapDomainHasDomainTrust
  100. //
  101. //
  102. // Lookup domains that we trust that are external to our forest
  103. //
  104. #define LSAP_LOOKUP_DOMAIN_TRUST_DIRECT_EXTERNAL 0x00000001
  105. //
  106. // Lookup domains that are within our forest and that we directly trust
  107. //
  108. #define LSAP_LOOKUP_DOMAIN_TRUST_DIRECT_INTRA 0x00000002
  109. //
  110. // Lookup forest trusts
  111. //
  112. #define LSAP_LOOKUP_DOMAIN_TRUST_FOREST 0x00000004
  113. NTSTATUS
  114. LsapDomainHasDomainTrust(
  115. IN ULONG Flags,
  116. IN PUNICODE_STRING DomainName, OPTIONAL
  117. IN PSID DomainSid, OPTIONAL
  118. IN OUT BOOLEAN *fTDLLock, OPTIONAL
  119. OUT PLSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY *TrustEntryOut OPTIONAL
  120. );
  121. VOID
  122. LsapLookupSamAccountNameToUPN(
  123. IN OUT PUNICODE_STRING Name
  124. );
  125. VOID
  126. LsapLookupUPNToSamAccountName(
  127. IN OUT PUNICODE_STRING Name
  128. );
  129. BOOL
  130. LsapLookupIsUPN(
  131. OUT PUNICODE_STRING Name
  132. );
  133. VOID
  134. LsapLookupCrackName(
  135. IN PUNICODE_STRING Prefix,
  136. IN PUNICODE_STRING Suffix,
  137. OUT PUNICODE_STRING SamAccountName,
  138. OUT PUNICODE_STRING DomainName
  139. );
  140. //////////////////////////////////////////////////////////////////////////
  141. // //
  142. // Lsa Lookup Helper routines //
  143. // //
  144. //////////////////////////////////////////////////////////////////////////
  145. ULONG
  146. LsapLookupGetChainingFlags(
  147. IN NL_OS_VERSION ServerOsVersion
  148. )
  149. /*++
  150. Routine Description:
  151. Based on the OsVersion, this routine determines what flags to
  152. pass into LsaIC* to help route the version of the Lsar* routine
  153. to call.
  154. Arguments:
  155. OsVersion -- the version of the secure channel DC
  156. Return Values:
  157. --*/
  158. {
  159. ULONG Flags = 0;
  160. if ( ServerOsVersion == NlWin2000 ) {
  161. Flags |= LSAIC_WIN2K_TARGET;
  162. } else if (ServerOsVersion <= NlNt40) {
  163. Flags |= LSAIC_NT4_TARGET;
  164. }
  165. return Flags;
  166. }
  167. NTSTATUS
  168. LsapDbLookupAddListReferencedDomains(
  169. IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
  170. IN PLSAPR_TRUST_INFORMATION TrustInformation,
  171. OUT PLONG DomainIndex
  172. )
  173. /*++
  174. Routine Description:
  175. This function searches a Referenced Domain List for an entry for a
  176. given domain and, if no entry exists, adds new entry. If an entry
  177. id added, its index into the Referenced Domain List is returned,
  178. otherwise the index of the existing entry is returned. If an entry
  179. needs to be added and there is insufficient room in the list provided
  180. for the new entry, the list will be created or grown as necessary.
  181. Arguments:
  182. ReferencedDomains - Pointer to a structure in which the list of domains
  183. used in the translation is maintained. The entries in this structure
  184. are referenced by the structure returned via the Sids parameter.
  185. Unlike the Sids parameter, which contains an array entry for each
  186. translated name, this structure will only contain one component for
  187. each domain utilized in the translation.
  188. TrustInformation - Points to Trust Information for the domain being
  189. added to the list. On exit, the DomainIndex parameter will be set to the
  190. index of the entry on the Referenced Domain List; a negative
  191. value will be stored in the error case.
  192. DomainIndex - Pointer to location that receives the index of the
  193. newly added or existing entry for the domain within the
  194. Referenced Domain List. In the error case, a negative value
  195. is returned.
  196. Return Values:
  197. NTSTATUS - Standard Nt Result Code
  198. STATUS_SUCCESS - The call completed successfully.
  199. STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources,
  200. such as memory, to complete the call.
  201. --*/
  202. {
  203. NTSTATUS Status;
  204. ULONG NextIndex;
  205. LSAPR_TRUST_INFORMATION OutputTrustInformation;
  206. OutputTrustInformation.Name.Buffer = NULL;
  207. OutputTrustInformation.Sid = NULL;
  208. Status = STATUS_INVALID_PARAMETER;
  209. if (ReferencedDomains == NULL) {
  210. goto AddListReferencedDomainsError;
  211. }
  212. Status = STATUS_SUCCESS;
  213. //
  214. // Search the existing list, trying to match the Domain Sid in the
  215. // provided Trust Information with the Domain Sid in a Referenced Domain
  216. // List entry. If an entry is found with matching Sid, just return
  217. // that entry's index.
  218. //
  219. if (LsapDbLookupListReferencedDomains(
  220. ReferencedDomains,
  221. TrustInformation->Sid,
  222. DomainIndex
  223. )) {
  224. goto AddListReferencedDomainsFinish;
  225. }
  226. //
  227. // Check that there is enough room in the List provided for one more
  228. // entry. If not, grow the list, copying and freeing the old.
  229. //
  230. Status = STATUS_SUCCESS;
  231. if (ReferencedDomains->Entries >= ReferencedDomains->MaxEntries) {
  232. Status = LsapDbLookupGrowListReferencedDomains(
  233. ReferencedDomains,
  234. ReferencedDomains->MaxEntries +
  235. LSAP_DB_REF_DOMAIN_DELTA
  236. );
  237. if (!NT_SUCCESS(Status)) {
  238. goto AddListReferencedDomainsError;
  239. }
  240. }
  241. //
  242. // We now have a Referenced Domain List with room for at least one more
  243. // entry. Copy in the Trust Information.
  244. //
  245. NextIndex = ReferencedDomains->Entries;
  246. Status = LsapRpcCopyUnicodeString(
  247. NULL,
  248. (PUNICODE_STRING) &OutputTrustInformation.Name,
  249. (PUNICODE_STRING) &TrustInformation->Name
  250. );
  251. if (!NT_SUCCESS(Status)) {
  252. goto AddListReferencedDomainsError;
  253. }
  254. if ( TrustInformation->Sid ) {
  255. Status = LsapRpcCopySid(
  256. NULL,
  257. (PSID) &OutputTrustInformation.Sid,
  258. (PSID) TrustInformation->Sid
  259. );
  260. if (!NT_SUCCESS(Status)) {
  261. goto AddListReferencedDomainsError;
  262. }
  263. }
  264. ReferencedDomains->Domains[NextIndex] = OutputTrustInformation;
  265. *DomainIndex = (LONG) NextIndex;
  266. ReferencedDomains->Entries++;
  267. AddListReferencedDomainsFinish:
  268. return(Status);
  269. AddListReferencedDomainsError:
  270. //
  271. // Cleanup buffers allocated for Output Trust Information structure.
  272. //
  273. if (OutputTrustInformation.Name.Buffer != NULL) {
  274. MIDL_user_free( OutputTrustInformation.Name.Buffer );
  275. }
  276. if (OutputTrustInformation.Sid != NULL) {
  277. MIDL_user_free( OutputTrustInformation.Sid );
  278. }
  279. goto AddListReferencedDomainsFinish;
  280. }
  281. NTSTATUS
  282. LsapDbLookupCreateListReferencedDomains(
  283. OUT PLSAPR_REFERENCED_DOMAIN_LIST *ReferencedDomains,
  284. IN ULONG InitialMaxEntries
  285. )
  286. /*++
  287. Routine Description:
  288. This function creates an empty Referenced Domain List. The caller
  289. is responsible for cleaning up this list when no longer required.
  290. Arguments:
  291. ReferencedDomains - Receives a pointer to the newly created empty
  292. Referenced Domain List.
  293. InitialMaxEntries - Initial maximum number of entries desired.
  294. Return Values:
  295. NTSTATUS - Standard Nt Result Code.
  296. STATUS_SUCCESS - The call completed successfully.
  297. STATUS_INSUFFICIENT_RESOURCES - Insufficient System Resources
  298. such as memory, to complete the call.
  299. --*/
  300. {
  301. NTSTATUS Status = STATUS_SUCCESS;
  302. ULONG DomainsLength;
  303. PLSAPR_TRUST_INFORMATION Domains = NULL;
  304. PVOID Buffers[2];
  305. ULONG BufferCount;
  306. ULONG Index;
  307. PLSAPR_REFERENCED_DOMAIN_LIST OutputReferencedDomains = NULL;
  308. //
  309. // Allocate the Referenced Domain List header.
  310. //
  311. BufferCount = 0;
  312. OutputReferencedDomains = MIDL_user_allocate(
  313. sizeof(LSAP_DB_REFERENCED_DOMAIN_LIST)
  314. );
  315. if (OutputReferencedDomains == NULL) {
  316. Status = STATUS_INSUFFICIENT_RESOURCES;
  317. goto CreateListReferencedDomainsError;
  318. }
  319. Buffers[BufferCount] = OutputReferencedDomains;
  320. BufferCount++;
  321. //
  322. // If a non-zero initial entry count, allocate an array of Trust Information
  323. // entries.
  324. //
  325. if (InitialMaxEntries > 0) {
  326. DomainsLength = sizeof(LSA_TRUST_INFORMATION) * InitialMaxEntries;
  327. Domains = MIDL_user_allocate( DomainsLength );
  328. Status = STATUS_INSUFFICIENT_RESOURCES;
  329. if (Domains == NULL) {
  330. goto CreateListReferencedDomainsError;
  331. }
  332. Status = STATUS_SUCCESS;
  333. Buffers[BufferCount] = Domains;
  334. BufferCount++;
  335. RtlZeroMemory( Domains, DomainsLength );
  336. }
  337. //
  338. // Initialize the Referenced Domain List Header
  339. //
  340. OutputReferencedDomains->Entries = 0;
  341. OutputReferencedDomains->MaxEntries = InitialMaxEntries;
  342. OutputReferencedDomains->Domains = Domains;
  343. CreateListReferencedDomainsFinish:
  344. *ReferencedDomains = OutputReferencedDomains;
  345. return(Status);
  346. CreateListReferencedDomainsError:
  347. //
  348. // Free up buffers allocated by this routine.
  349. //
  350. for (Index = 0; Index < BufferCount; Index++) {
  351. MIDL_user_free(Buffers[Index]);
  352. }
  353. OutputReferencedDomains = NULL;
  354. goto CreateListReferencedDomainsFinish;
  355. }
  356. NTSTATUS
  357. LsapDbLookupGrowListReferencedDomains(
  358. IN OUT PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
  359. IN ULONG MaxEntries
  360. )
  361. /*++
  362. Routine Description:
  363. This function expands a Referenced Domain List to contain the
  364. specified maximum number of entries. The memory for the old Domains
  365. array is released.
  366. Arguments:
  367. ReferencedDomains - Pointer to a Referenced Domain List. This
  368. list references an array of zero or more Trust Information
  369. entries describing each of the domains referenced by the names.
  370. This array will be appended to/reallocated if necessary.
  371. MaxEntries - New maximum number of entries.
  372. Return Values:
  373. NTSTATUS - Standard Nt Result Code
  374. STATUS_SUCCESS - The call completed successfully.
  375. STATUS_INSUFFICIENT_RESOURCES - Insufficient system resources to
  376. complete the call.
  377. --*/
  378. {
  379. NTSTATUS Status = STATUS_SUCCESS;
  380. PLSAPR_TRUST_INFORMATION NewDomainsInfo = NULL;
  381. PLSAPR_TRUST_INFORMATION OldDomainsInfo = NULL;
  382. ULONG OldDomainsInfoLength, NewDomainsInfoLength;
  383. if (ReferencedDomains->MaxEntries < MaxEntries) {
  384. NewDomainsInfoLength = MaxEntries * sizeof (LSA_TRUST_INFORMATION);
  385. OldDomainsInfoLength =
  386. ReferencedDomains->MaxEntries * sizeof (LSA_TRUST_INFORMATION);
  387. NewDomainsInfo = MIDL_user_allocate( NewDomainsInfoLength );
  388. Status = STATUS_INSUFFICIENT_RESOURCES;
  389. if (NewDomainsInfo == NULL) {
  390. goto GrowListReferencedDomainsError;
  391. }
  392. Status = STATUS_SUCCESS;
  393. //
  394. // If there was an existing Trust Information Array, copy it
  395. // to the newly allocated one and free it.
  396. //
  397. OldDomainsInfo = ReferencedDomains->Domains;
  398. if (OldDomainsInfo != NULL) {
  399. RtlCopyMemory( NewDomainsInfo, OldDomainsInfo, OldDomainsInfoLength );
  400. MIDL_user_free( OldDomainsInfo );
  401. }
  402. ReferencedDomains->Domains = NewDomainsInfo;
  403. ReferencedDomains->MaxEntries = MaxEntries;
  404. }
  405. GrowListReferencedDomainsFinish:
  406. return(Status);
  407. GrowListReferencedDomainsError:
  408. goto GrowListReferencedDomainsFinish;
  409. }
  410. BOOLEAN
  411. LsapDbLookupListReferencedDomains(
  412. IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
  413. IN PLSAPR_SID DomainSid,
  414. OUT PLONG DomainIndex
  415. )
  416. /*++
  417. Routine Description:
  418. This function searches a Referenced Domain List for a given domain
  419. and, if found, returns the index of the domain's entry in the list.
  420. If the domain is not found an error is returned and a negative value
  421. is returned.
  422. Arguments:
  423. ReferencedDomains - Pointer to a Referenced Domain List. This
  424. list references an array of zero or more Trust Information
  425. entries describing each of the domains referenced by the names.
  426. This array will be appended to/reallocated if necessary.
  427. DomainSid - Information containing the Domain's Sid.
  428. DomainIndex - Pointer to location that receives the index of the domain
  429. in the Referenced Domain List if the domin is found, otherwise
  430. a negative value.
  431. Return Values:
  432. BOOLEAN - TRUE if entry found, else FALSE.
  433. --*/
  434. {
  435. BOOLEAN BooleanStatus = FALSE;
  436. LONG Index;
  437. LONG Entries;
  438. PLSAPR_TRUST_INFORMATION DomainsInfo;
  439. //
  440. // Search the Referenced Domain List by Sid or by Name
  441. //
  442. Entries = (LONG) ReferencedDomains->Entries;
  443. DomainsInfo = ReferencedDomains->Domains;
  444. *DomainIndex = LSA_UNKNOWN_INDEX;
  445. for (Index = 0; Index < (LONG) Entries && DomainSid; Index++) {
  446. if ( DomainsInfo[Index].Sid &&
  447. RtlEqualSid( ( PSID )DomainsInfo[Index].Sid, ( PSID )DomainSid ) ) {
  448. BooleanStatus = TRUE;
  449. *DomainIndex = Index;
  450. break;
  451. }
  452. }
  453. return(BooleanStatus);
  454. }
  455. NTSTATUS
  456. LsapDbLookupMergeDisjointReferencedDomains(
  457. IN OPTIONAL PLSAPR_REFERENCED_DOMAIN_LIST FirstReferencedDomainList,
  458. IN OPTIONAL PLSAPR_REFERENCED_DOMAIN_LIST SecondReferencedDomainList,
  459. OUT PLSAPR_REFERENCED_DOMAIN_LIST *OutputReferencedDomainList,
  460. IN ULONG Options
  461. )
  462. /*++
  463. Routine Description:
  464. This function merges disjoint Referenced Domain Lists, producing a third
  465. list. The output list is always produced in non allocate(all_nodes) form.
  466. Arguments:
  467. FirstReferencedDomainList - Pointer to first mergand.
  468. SecondReferencedDomainList - Pointer to second mergand.
  469. OutputReferencedDomainList - Receives a pointer to the output list.
  470. Options - Specifies optional actions
  471. LSAP_DB_USE_FIRST_MERGAND_GRAPH - Specifies that the resulting
  472. merged Referenced Domain List may reference the graph of
  473. pointers in the first Referenced Domain list. This option
  474. is normally selected, since that graph has been allocated
  475. as individual nodes.
  476. LSAP_DB_USE_SECOND_MERGAND_GRAPH - Specifies that the resulting
  477. merged Referenced Domain List may reference the graph of
  478. pointers in the first Referenced Domain list. This option
  479. is normally not selected, since that graph is usually allocated
  480. as all_nodes.
  481. Return Values:
  482. NTSTATUS - Standard Nt Result Code.
  483. --*/
  484. {
  485. NTSTATUS Status;
  486. ULONG TotalEntries;
  487. ULONG FirstReferencedDomainListLength;
  488. ULONG SecondReferencedDomainListLength;
  489. ULONG FirstEntries, SecondEntries;
  490. LSAP_MM_FREE_LIST FreeList;
  491. ULONG NextEntry;
  492. ULONG MaximumFreeListEntries;
  493. ULONG CleanupFreeListOptions = (ULONG) 0;
  494. //
  495. // Initialize output parmameter
  496. //
  497. *OutputReferencedDomainList = NULL;
  498. //
  499. // Calculate Size of output Referenced Domain List.
  500. //
  501. FirstEntries = (ULONG) 0;
  502. if (FirstReferencedDomainList != NULL) {
  503. FirstEntries = FirstReferencedDomainList->Entries;
  504. }
  505. SecondEntries = (ULONG) 0;
  506. if (SecondReferencedDomainList != NULL) {
  507. SecondEntries = SecondReferencedDomainList->Entries;
  508. }
  509. TotalEntries = FirstEntries + SecondEntries;
  510. //
  511. // Allocate a Free List for error cleanup. We need two entries
  512. // per Referenced Domain List entry, one for the Domain Name buffer
  513. // and one for the Domain Sid.
  514. //
  515. MaximumFreeListEntries = (ULONG) 0;
  516. if (!(Options & LSAP_DB_USE_FIRST_MERGAND_GRAPH)) {
  517. MaximumFreeListEntries += 2*FirstEntries;
  518. }
  519. if (!(Options & LSAP_DB_USE_SECOND_MERGAND_GRAPH)) {
  520. MaximumFreeListEntries += 2*SecondEntries;
  521. }
  522. Status = LsapMmCreateFreeList( &FreeList, MaximumFreeListEntries );
  523. if (!NT_SUCCESS(Status)) {
  524. goto MergeDisjointDomainsError;
  525. }
  526. Status = LsapDbLookupCreateListReferencedDomains(
  527. OutputReferencedDomainList,
  528. TotalEntries
  529. );
  530. if (!NT_SUCCESS(Status)) {
  531. goto MergeDisjointDomainsError;
  532. }
  533. //
  534. // Set the number of entries used. We will use all of the entries,
  535. // so set this value to the Maximum number of Entries.
  536. //
  537. ASSERT(OutputReferencedDomainList);
  538. (*OutputReferencedDomainList)->Entries = TotalEntries;
  539. if ( 0 == TotalEntries ) {
  540. //
  541. // There is not much to do
  542. //
  543. // This ASSERT is to understand conditions underwhich we might hit this
  544. // scenario. There is likely a coding bug else if we are asking
  545. // two empty lists to be merged.
  546. //
  547. ASSERT( 0 == TotalEntries );
  548. ASSERT( NT_SUCCESS(Status) );
  549. goto MergeDisjointDomainsFinish;
  550. }
  551. //
  552. // Copy in the entries (if any) from the first list.
  553. //
  554. FirstReferencedDomainListLength =
  555. FirstEntries * sizeof(LSA_TRUST_INFORMATION);
  556. if (FirstReferencedDomainListLength > (ULONG) 0) {
  557. if (Options & LSAP_DB_USE_FIRST_MERGAND_GRAPH) {
  558. RtlCopyMemory(
  559. (*OutputReferencedDomainList)->Domains,
  560. FirstReferencedDomainList->Domains,
  561. FirstReferencedDomainListLength
  562. );
  563. } else {
  564. //
  565. // The graph of the first Referenced Domain List must be
  566. // copied to separately allocated memory buffers.
  567. // Copy each of the Trust Information entries, allocating
  568. // individual memory buffers for each Domain Name and Sid.
  569. //
  570. for (NextEntry = 0; NextEntry < FirstEntries; NextEntry++) {
  571. Status = LsapRpcCopyUnicodeString(
  572. &FreeList,
  573. (PUNICODE_STRING) &((*OutputReferencedDomainList)->Domains[NextEntry].Name),
  574. (PUNICODE_STRING) &FirstReferencedDomainList->Domains[NextEntry].Name
  575. );
  576. if (!NT_SUCCESS(Status)) {
  577. break;
  578. }
  579. if ( FirstReferencedDomainList->Domains[NextEntry].Sid ) {
  580. Status = LsapRpcCopySid(
  581. &FreeList,
  582. (PSID) &((*OutputReferencedDomainList)->Domains[NextEntry].Sid),
  583. (PSID) FirstReferencedDomainList->Domains[NextEntry].Sid
  584. );
  585. } else {
  586. (*OutputReferencedDomainList)->Domains[NextEntry].Sid = NULL;
  587. }
  588. if (!NT_SUCCESS(Status)) {
  589. break;
  590. }
  591. }
  592. if (!NT_SUCCESS(Status)) {
  593. goto MergeDisjointDomainsError;
  594. }
  595. }
  596. }
  597. //
  598. // Copy in the entries (if any) from the second list.
  599. //
  600. SecondReferencedDomainListLength =
  601. SecondEntries * sizeof(LSA_TRUST_INFORMATION);
  602. if (SecondReferencedDomainListLength > (ULONG) 0) {
  603. if (Options & LSAP_DB_USE_SECOND_MERGAND_GRAPH) {
  604. RtlCopyMemory(
  605. (*OutputReferencedDomainList)->Domains + FirstReferencedDomainList->Entries,
  606. SecondReferencedDomainList->Domains,
  607. SecondReferencedDomainListLength
  608. );
  609. } else {
  610. //
  611. // Copy each of the Trust Information entries, allocating
  612. // individual memory buffers for each Domain Name and Sid.
  613. //
  614. for (NextEntry = 0; NextEntry < SecondEntries; NextEntry++) {
  615. Status = LsapRpcCopyUnicodeString(
  616. &FreeList,
  617. (PUNICODE_STRING) &((*OutputReferencedDomainList)->Domains[FirstEntries +NextEntry].Name),
  618. (PUNICODE_STRING) &SecondReferencedDomainList->Domains[NextEntry].Name
  619. );
  620. if (!NT_SUCCESS(Status)) {
  621. break;
  622. }
  623. Status = LsapRpcCopySid(
  624. &FreeList,
  625. (PSID) &((*OutputReferencedDomainList)->Domains[FirstEntries +NextEntry].Sid),
  626. (PSID) SecondReferencedDomainList->Domains[NextEntry].Sid
  627. );
  628. if (!NT_SUCCESS(Status)) {
  629. break;
  630. }
  631. }
  632. if (!NT_SUCCESS(Status)) {
  633. goto MergeDisjointDomainsError;
  634. }
  635. }
  636. }
  637. MergeDisjointDomainsFinish:
  638. //
  639. // Delete the Free List, freeing buffers on the list if an error
  640. // occurred.
  641. //
  642. LsapMmCleanupFreeList( &FreeList, CleanupFreeListOptions );
  643. return(Status);
  644. MergeDisjointDomainsError:
  645. //
  646. // Delete the output referenced domain list
  647. //
  648. if (*OutputReferencedDomainList) {
  649. MIDL_user_free( *OutputReferencedDomainList );
  650. *OutputReferencedDomainList = NULL;
  651. }
  652. //
  653. // Specify that buffers on Free List are to be freed.
  654. //
  655. CleanupFreeListOptions |= LSAP_MM_FREE_BUFFERS;
  656. goto MergeDisjointDomainsFinish;
  657. }
  658. NTSTATUS
  659. LsapDbLookupDispatchWorkerThreads(
  660. IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
  661. )
  662. /*++
  663. Routine Description:
  664. This function dispatches sufficient worker threads to handle a
  665. Lookup operation. The worker threads can handle work items
  666. on any Lookup's Work List, so the number of existing active
  667. threads is taken into account. Note that the total number of
  668. active Lookup Worker Threads may exceed the guide maximum number of
  669. threads, in situations where an active thread terminates during
  670. the dispatch cycle. This strategy saves having to recheck the
  671. active thread count each time a thread is dispatched.
  672. NOTE: This routine expects the specified pointer to a Work List to be
  673. valid. A Work List pointer always remains valid until its
  674. Primary thread detects completion of the Work List via this
  675. routine and then deletes it via LsapDbLookupDeleteWorkList().
  676. Arguments:
  677. WorkList - Pointer to a Work List describing a Lookup Sid or Lookup Name
  678. operation.
  679. Return Value:
  680. NTSTATUS - Standard Nt Result Code
  681. --*/
  682. {
  683. NTSTATUS Status = STATUS_SUCCESS;
  684. NTSTATUS DispatchThreadStatus = STATUS_SUCCESS;
  685. ULONG AdvisoryChildThreadCount;
  686. ULONG DispatchThreadCount;
  687. ULONG MaximumDispatchChildThreadCount;
  688. ULONG ThreadNumber;
  689. HANDLE Thread = NULL;
  690. DWORD Ignore;
  691. BOOLEAN AcquiredWorkQueueLock = FALSE;
  692. //
  693. // Acquire the Lookup Work Queue Lock.
  694. //
  695. Status = LsapDbLookupAcquireWorkQueueLock();
  696. if (!NT_SUCCESS(Status)) {
  697. goto LookupDispatchWorkerThreadsError;
  698. }
  699. AcquiredWorkQueueLock = TRUE;
  700. //
  701. // Calculate the number of Worker Threads to dispatch (if any). If
  702. // the WorkList has an Advisory Child Thread Count of 0, we will
  703. // not dispatch any threads, but instead will perform this Lookup
  704. // within this thread. If the WorkList has an Advisory Child Thread
  705. // Count > 0, we will dispatch additional threads. The number of
  706. // additional child threads dispatched is given by the formula:
  707. //
  708. // ThreadsToDispatch =
  709. // min (MaximumChildThreadCount - ActiveChildThreadCount,
  710. // AdvisoryChildThreadCount)
  711. //
  712. AdvisoryChildThreadCount = WorkList->AdvisoryChildThreadCount;
  713. if (AdvisoryChildThreadCount > 0) {
  714. MaximumDispatchChildThreadCount =
  715. LookupWorkQueue.MaximumChildThreadCount -
  716. LookupWorkQueue.ActiveChildThreadCount;
  717. if (AdvisoryChildThreadCount <= MaximumDispatchChildThreadCount) {
  718. DispatchThreadCount = AdvisoryChildThreadCount;
  719. } else {
  720. DispatchThreadCount = MaximumDispatchChildThreadCount;
  721. }
  722. //
  723. // Release the Lookup Work Queue Lock
  724. //
  725. LsapDbLookupReleaseWorkQueueLock();
  726. AcquiredWorkQueueLock = FALSE;
  727. //
  728. // Signal the event that indicates that a new Work List is initiated.
  729. //
  730. Status = NtSetEvent( LsapDbLookupStartedEvent, NULL );
  731. if (!NT_SUCCESS(Status)) {
  732. LsapDiagPrint( DB_LOOKUP_WORK_LIST,
  733. ("LsapDbLookupDispatchWorkList... NtSetEvent failed 0x%lx\n",Status));
  734. goto LookupDispatchWorkerThreadsError;
  735. }
  736. //
  737. // Dispatch the computed number of threads.
  738. //
  739. for (ThreadNumber = 0; ThreadNumber < DispatchThreadCount; ThreadNumber++) {
  740. Thread = CreateThread(
  741. NULL,
  742. 0L,
  743. (LPTHREAD_START_ROUTINE) LsapDbLookupWorkerThreadStart,
  744. NULL,
  745. 0L,
  746. &Ignore
  747. );
  748. if (Thread == NULL) {
  749. Status = GetLastError();
  750. KdPrint(("LsapDbLookupDispatchWorkerThreads: CreateThread failed 0x%lx\n"));
  751. break;
  752. } else {
  753. CloseHandle( Thread );
  754. }
  755. }
  756. if (!NT_SUCCESS(Status)) {
  757. DispatchThreadStatus = Status;
  758. }
  759. }
  760. //
  761. // Unlock the queue so this thread doesn't hog it while doing a lookup
  762. //
  763. if (AcquiredWorkQueueLock) {
  764. LsapDbLookupReleaseWorkQueueLock();
  765. AcquiredWorkQueueLock = FALSE;
  766. }
  767. //
  768. // Do some work in the main thread too.
  769. //
  770. LsapDbLookupWorkerThread( TRUE);
  771. LookupDispatchWorkerThreadsFinish:
  772. if (AcquiredWorkQueueLock) {
  773. LsapDbLookupReleaseWorkQueueLock();
  774. AcquiredWorkQueueLock = FALSE;
  775. }
  776. return(Status);
  777. LookupDispatchWorkerThreadsError:
  778. goto LookupDispatchWorkerThreadsFinish;
  779. }
  780. NTSTATUS
  781. LsapDbGetCachedHandleTrustedDomain(
  782. IN PLSAPR_TRUST_INFORMATION TrustInformation,
  783. IN ACCESS_MASK DesiredAccess,
  784. IN OUT LPWSTR *ServerName,
  785. IN OUT LPWSTR *ServerPrincipalName,
  786. IN OUT PVOID *ClientContext,
  787. OUT PLSAP_BINDING_CACHE_ENTRY * ControllerPolicyEntry
  788. )
  789. /*++
  790. Routine Description:
  791. This routine looks for a cached handle to the LSA on a trusted domain.
  792. If one is not present, it will open & cache a new handle. The handle
  793. is opened for POLICY_LOOKUP_NAMES.
  794. N.B. ServerName, ServerPrincipalName, and ClientContext are IN/OUT
  795. parameters -- if a new handle is created, the memory is transferred
  796. to the cache and so the values are NULL'ed on return.
  797. If a value is found in the cache then the values are also freed (and
  798. NULL'ed), so that the interface is consistent (on success, *ServerName,
  799. *ServerPrincipalName, *ClientContext are freed).
  800. Arguments:
  801. TrustInformation - Specifies the Sid and/or Name of the Trusted Domain
  802. whose Policy database is to be opened.
  803. DesiredAccess -- if a new handle is required, what to ask for
  804. ServerName -- in, out; if a new handle is required the server to get one
  805. from
  806. ServerPrincipalName -- in, out; if a new handle is required this is used
  807. to authenticate.
  808. ClientContext -- in, out; if a new handle is required, this is used to
  809. authenticate.
  810. ControllerPolicyEntry - Receives binding cache entry to the LSA Policy
  811. Object for the Lsa Policy database located on some DC for the
  812. specified Trusted Domain.
  813. Return Value:
  814. NTSTATUS - Standard Nt Result Code
  815. STATUS_SUCCESS - The call completed successfully.
  816. STATUS_NO_MORE_ENTRIES - The DC list for the specified domain
  817. is null.
  818. Result codes from called routines.
  819. --*/
  820. {
  821. NTSTATUS Status = STATUS_SUCCESS;
  822. PUNICODE_STRING DomainName = NULL;
  823. PLSAPR_TRUST_INFORMATION OutputTrustInformation = NULL;
  824. LSA_HANDLE PolicyHandle = NULL;
  825. PLSAP_BINDING_CACHE_ENTRY CacheEntry = NULL;
  826. UNICODE_STRING DomainControllerName;
  827. *ControllerPolicyEntry = NULL;
  828. //
  829. // If the caller didn't provide a domain name, look it up now
  830. //
  831. if ((TrustInformation->Name.Length == 0) ||
  832. (TrustInformation->Name.Buffer == NULL)) {
  833. Status = STATUS_INVALID_PARAMETER;
  834. if (TrustInformation->Sid == NULL) {
  835. goto Cleanup;
  836. }
  837. Status = LsapDbLookupSidTrustedDomainList(
  838. TrustInformation->Sid,
  839. &OutputTrustInformation
  840. );
  841. if (!NT_SUCCESS(Status)) {
  842. goto Cleanup;
  843. }
  844. DomainName = (PUNICODE_STRING) &OutputTrustInformation->Name;
  845. TrustInformation = OutputTrustInformation;
  846. } else {
  847. DomainName = (PUNICODE_STRING) &TrustInformation->Name;
  848. }
  849. //
  850. // Look in the cache for a binding handle
  851. //
  852. CacheEntry = LsapLocateBindingCacheEntry(
  853. DomainName,
  854. FALSE // don't remove
  855. );
  856. if (CacheEntry != NULL) {
  857. //
  858. // Validate the handle to make sure the DC is still there.
  859. //
  860. Status = LsapRtlValidateControllerTrustedDomainByHandle(
  861. CacheEntry->PolicyHandle,
  862. TrustInformation
  863. );
  864. if (!NT_SUCCESS(Status)) {
  865. LsapReferenceBindingCacheEntry(
  866. CacheEntry,
  867. TRUE // unlink
  868. );
  869. LsapDereferenceBindingCacheEntry(
  870. CacheEntry
  871. );
  872. LsapDereferenceBindingCacheEntry(
  873. CacheEntry
  874. );
  875. CacheEntry = NULL;
  876. }
  877. else
  878. {
  879. *ControllerPolicyEntry = CacheEntry;
  880. goto Cleanup;
  881. }
  882. }
  883. //
  884. // There was nothing in the cache, so open a new handle
  885. //
  886. RtlInitUnicodeString(&DomainControllerName, *ServerName);
  887. Status = LsapRtlValidateControllerTrustedDomain( (PLSAPR_UNICODE_STRING)&DomainControllerName,
  888. TrustInformation,
  889. POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION,
  890. *ServerPrincipalName,
  891. *ClientContext,
  892. &PolicyHandle
  893. );
  894. if (!NT_SUCCESS(Status)) {
  895. goto Cleanup;
  896. }
  897. //
  898. // Create a binding cache entry from the handle
  899. //
  900. //
  901. // Note: this routine sets ServerName, ServerPrincipalName and
  902. // ClientContext to NULL on success.
  903. //
  904. Status = LsapCacheBinding(
  905. DomainName,
  906. &PolicyHandle,
  907. ServerName,
  908. ServerPrincipalName,
  909. ClientContext,
  910. ControllerPolicyEntry
  911. );
  912. if (!NT_SUCCESS(Status)) {
  913. goto Cleanup;
  914. }
  915. Cleanup:
  916. if (PolicyHandle != NULL) {
  917. LsaClose(PolicyHandle);
  918. }
  919. if (NT_SUCCESS(Status)) {
  920. //
  921. // On success, always free the IN/OUT parameters to provide
  922. // a consistent interface
  923. //
  924. if (*ServerName) {
  925. LocalFree(*ServerName);
  926. *ServerName = NULL;
  927. }
  928. if (*ServerPrincipalName) {
  929. I_NetLogonFree(*ServerPrincipalName);
  930. *ServerPrincipalName = NULL;
  931. }
  932. if (*ClientContext) {
  933. I_NetLogonFree(*ClientContext);
  934. *ClientContext = NULL;
  935. }
  936. //
  937. // We should have a cache entry to return
  938. //
  939. ASSERT(NULL != *ControllerPolicyEntry);
  940. }
  941. return(Status);
  942. }
  943. NTSTATUS
  944. LsapRtlValidateControllerTrustedDomain(
  945. IN PLSAPR_UNICODE_STRING DomainControllerName,
  946. IN PLSAPR_TRUST_INFORMATION TrustInformation,
  947. IN ACCESS_MASK OriginalDesiredAccess,
  948. IN LPWSTR ServerPrincipalName,
  949. IN PVOID ClientContext,
  950. OUT PLSA_HANDLE ControllerPolicyHandle
  951. )
  952. /*++
  953. Routine Description:
  954. This function verifies that a specified computer is a DC for
  955. a specified Domain and opens the LSA Policy Object with the
  956. desired accesses.
  957. Arguments:
  958. DomainControllerName - Pointer to Unicode String computer name.
  959. TrustInformation - Domain Trust Information. Only the Sid is
  960. used.
  961. OriginalDesiredAccess - Specifies the accesses desired to the
  962. target machine's Lsa Policy Database.
  963. ServerPrincipalName - RPC server principal name.
  964. ClientContext - RPC client context information.
  965. PolicyHandle - Receives handle to the Lsa Policy object on the target
  966. machine.
  967. Return Values:
  968. NTSTATUS - Standard Nt Result Code
  969. STATUS_OBJECT_NAME_NOT_FOUND - The specified computer is not a
  970. Domain Controller for the specified Domain.
  971. --*/
  972. {
  973. NTSTATUS Status = STATUS_SUCCESS;
  974. NTSTATUS Status2 = STATUS_SUCCESS;
  975. NTSTATUS SecondaryStatus = STATUS_SUCCESS;
  976. SECURITY_QUALITY_OF_SERVICE SecurityQualityOfService;
  977. OBJECT_ATTRIBUTES ObjectAttributes;
  978. LSA_HANDLE OutputControllerPolicyHandle = NULL;
  979. ACCESS_MASK DesiredAccess;
  980. //
  981. // Open a handle to the Policy Object on the target DC.
  982. //
  983. SecurityQualityOfService.Length = sizeof(SECURITY_QUALITY_OF_SERVICE);
  984. SecurityQualityOfService.ImpersonationLevel = SecurityImpersonation;
  985. SecurityQualityOfService.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING;
  986. SecurityQualityOfService.EffectiveOnly = FALSE;
  987. //
  988. // Set up the object attributes prior to opening the LSA.
  989. //
  990. InitializeObjectAttributes(
  991. &ObjectAttributes,
  992. NULL,
  993. 0L,
  994. NULL,
  995. NULL
  996. );
  997. //
  998. // The InitializeObjectAttributes macro presently stores NULL for
  999. // the SecurityQualityOfService field, so we must manually copy that
  1000. // structure for now.
  1001. //
  1002. ObjectAttributes.SecurityQualityOfService = &SecurityQualityOfService;
  1003. //
  1004. // For authenticated clients, the server adds in the POLICY_LOOKUP_NAMES
  1005. // access
  1006. //
  1007. Retry:
  1008. if (ClientContext != NULL) {
  1009. DesiredAccess = OriginalDesiredAccess & ~POLICY_LOOKUP_NAMES;
  1010. } else {
  1011. DesiredAccess = OriginalDesiredAccess;
  1012. }
  1013. Status = LsaOpenPolicy(
  1014. (PUNICODE_STRING) DomainControllerName,
  1015. &ObjectAttributes,
  1016. DesiredAccess,
  1017. &OutputControllerPolicyHandle
  1018. );
  1019. if (!NT_SUCCESS(Status)) {
  1020. goto ValidateControllerTrustedDomainError;
  1021. }
  1022. if (ClientContext != NULL) {
  1023. ULONG RpcErr;
  1024. RPC_BINDING_HANDLE RpcBindingHandle;
  1025. //
  1026. // Setup the the RPC authenticated channel
  1027. //
  1028. RpcErr = RpcSsGetContextBinding(
  1029. OutputControllerPolicyHandle,
  1030. &RpcBindingHandle
  1031. );
  1032. //
  1033. // Note: the handle returned by RpcSsGetContextBinding is valid for the lifetime
  1034. // of OutputControllerPolicyHandle; it does not need to be closed.
  1035. //
  1036. Status = I_RpcMapWin32Status(RpcErr);
  1037. if (!NT_SUCCESS(Status)) {
  1038. goto ValidateControllerTrustedDomainError;
  1039. }
  1040. RpcErr = RpcBindingSetAuthInfo(
  1041. RpcBindingHandle,
  1042. ServerPrincipalName,
  1043. RPC_C_AUTHN_LEVEL_PKT_INTEGRITY,
  1044. RPC_C_AUTHN_NETLOGON,
  1045. ClientContext,
  1046. RPC_C_AUTHZ_NONE
  1047. );
  1048. Status = I_RpcMapWin32Status(RpcErr);
  1049. if (!NT_SUCCESS(Status)) {
  1050. goto ValidateControllerTrustedDomainError;
  1051. }
  1052. //
  1053. // Perform a validation on the handle to make sure the new auth info
  1054. // is supported on the called server.
  1055. //
  1056. // N.B. The locator will always return a valid DC so this check is not
  1057. // to validate that the server is a DC, rather it is used to validate
  1058. // that the connection we've created is valid. This check is only
  1059. // necessary when setting up secure RPC.
  1060. //
  1061. Status = LsapRtlValidateControllerTrustedDomainByHandle( OutputControllerPolicyHandle,
  1062. TrustInformation );
  1063. if (!NT_SUCCESS(Status)) {
  1064. //
  1065. // If the server didn't recognize the authn service, try again
  1066. // without authenticated RPC
  1067. //
  1068. if ((Status == RPC_NT_UNKNOWN_AUTHN_SERVICE) ||
  1069. (Status == STATUS_ACCESS_DENIED)) {
  1070. Status = STATUS_SUCCESS;
  1071. LsaClose( OutputControllerPolicyHandle );
  1072. OutputControllerPolicyHandle = NULL;
  1073. ClientContext = NULL;
  1074. goto Retry;
  1075. }
  1076. goto ValidateControllerTrustedDomainError;
  1077. }
  1078. }
  1079. Status = STATUS_SUCCESS;
  1080. ValidateControllerTrustedDomainFinish:
  1081. //
  1082. // Return Controller Policy handle or NULL.
  1083. //
  1084. *ControllerPolicyHandle = OutputControllerPolicyHandle;
  1085. return(Status);
  1086. ValidateControllerTrustedDomainError:
  1087. //
  1088. // Close the last Controller's Policy Handle.
  1089. //
  1090. if (OutputControllerPolicyHandle != NULL) {
  1091. SecondaryStatus = LsaClose( OutputControllerPolicyHandle );
  1092. OutputControllerPolicyHandle = NULL;
  1093. }
  1094. goto ValidateControllerTrustedDomainFinish;
  1095. }
  1096. NTSTATUS
  1097. LsapRtlValidateControllerTrustedDomainByHandle(
  1098. IN LSA_HANDLE DcPolicyHandle,
  1099. IN PLSAPR_TRUST_INFORMATION TrustInformation
  1100. )
  1101. /*++
  1102. Routine Description:
  1103. This function validates that the given policy handle refers to a valid domain controller
  1104. Arguments:
  1105. DcPolicyHandle - Handle to the policy on the machine to verify
  1106. TrustInformation - Information regarding this Dc's domain
  1107. Return Value:
  1108. STATUS_SUCCESS - Success
  1109. Otherwise, the handle isn't valid
  1110. --*/
  1111. {
  1112. NTSTATUS Status = STATUS_SUCCESS;
  1113. PLSAPR_POLICY_ACCOUNT_DOM_INFO PolicyAccountDomainInfo = NULL;
  1114. Status = LsaQueryInformationPolicy( DcPolicyHandle,
  1115. PolicyAccountDomainInformation,
  1116. ( PVOID * )&PolicyAccountDomainInfo );
  1117. //
  1118. // Now compare the Domain Name and Sid stored for the Controller's
  1119. // Account Domain with the Domain Name and Sid provided.
  1120. // PolicyAccountDomainInfo->DomainName is always a NetBIOS name, hence
  1121. // the use of RtlEqualDomainName for comparison.
  1122. //
  1123. if ( NT_SUCCESS( Status ) ) {
  1124. if ( !RtlEqualDomainName( (PUNICODE_STRING) &TrustInformation->Name,
  1125. (PUNICODE_STRING) &PolicyAccountDomainInfo->DomainName ) ) {
  1126. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1127. } else if ( TrustInformation->Sid &&
  1128. !RtlEqualSid( ( PSID )TrustInformation->Sid,
  1129. ( PSID ) PolicyAccountDomainInfo->DomainSid ) ) {
  1130. Status = STATUS_OBJECT_NAME_NOT_FOUND;
  1131. }
  1132. }
  1133. //
  1134. // Free up the Policy Account Domain Info.
  1135. //
  1136. if ( PolicyAccountDomainInfo != NULL ) {
  1137. LsaFreeMemory( (PVOID) PolicyAccountDomainInfo );
  1138. }
  1139. return(Status);
  1140. }
  1141. NTSTATUS
  1142. LsapDbLookupAcquireWorkQueueLock(
  1143. )
  1144. /*++
  1145. Routine Description:
  1146. This function acquires the LSA Database Lookup Sids/Names Work Queue Lock.
  1147. This lock serializes additions or deletions of work lists to/from the
  1148. queue, and sign-in/sign-out of work items by worker threads.
  1149. Arguments:
  1150. None.
  1151. Return Value:
  1152. NTSTATUS - Standard Nt Result Code.
  1153. --*/
  1154. {
  1155. NTSTATUS Status;
  1156. Status = SafeEnterCriticalSection(&LookupWorkQueue.Lock);
  1157. return(Status);
  1158. }
  1159. VOID
  1160. LsapDbLookupReleaseWorkQueueLock(
  1161. )
  1162. /*++
  1163. Routine Description:
  1164. This function releases the LSA Database Lookup Sids/Names Work Queue Lock.
  1165. Arguments:
  1166. None.
  1167. Return Value:
  1168. None. Any error occurring within this routine is an internal error.
  1169. --*/
  1170. {
  1171. SafeLeaveCriticalSection(&LookupWorkQueue.Lock);
  1172. }
  1173. NTSTATUS
  1174. LsapDbLookupNamesBuildWorkList(
  1175. IN ULONG LookupOptions,
  1176. IN ULONG Count,
  1177. IN BOOLEAN fIncludeIntraforest,
  1178. IN PLSAPR_UNICODE_STRING Names,
  1179. IN PLSAPR_UNICODE_STRING PrefixNames,
  1180. IN PLSAPR_UNICODE_STRING SuffixNames,
  1181. IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
  1182. IN OUT PLSAPR_TRANSLATED_SIDS_EX2 TranslatedSids,
  1183. IN LSAP_LOOKUP_LEVEL LookupLevel,
  1184. IN OUT PULONG MappedCount,
  1185. IN OUT PULONG CompletelyUnmappedCount,
  1186. OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList
  1187. )
  1188. /*++
  1189. Routine Description:
  1190. This function constructs a Work List for an LsarLookupNames call. The
  1191. Work List contains the parameters of the call, and an array of Work Items.
  1192. Each Work Item specifies either all of the Names to be looked up
  1193. in a given domain.
  1194. Qualified names (i.e. those of the form DomainName\UserName) are
  1195. sorted into different Work Items, one for each DomainName specifed.
  1196. Unqualified names (i.e. those of the form UserName) are added to
  1197. every Work Item.
  1198. Arguments:
  1199. LookupOptions - LSA_LOOKUP_ISOLATED_AS_LOCAL
  1200. Count - Specifies the number of names to be translated.
  1201. fIncludeIntraforest -- if TRUE, trusted domains in our local forest
  1202. are searched.
  1203. Names - Pointer to an array of Count Unicode String structures
  1204. specifying the names to be looked up and mapped to Sids.
  1205. The strings may be names of User, Group or Alias accounts or
  1206. domains.
  1207. PrefixNames - Pointer to an array of Count Unicode String structures
  1208. containing the Prefix portions of the Names. Names having no
  1209. Prefix are called Isolated Names. For these, the Unicode String
  1210. structure is set to contain a zero Length.
  1211. SuffixNames - Pointer to an array of Count Unicode String structures
  1212. containing the Suffix portions of the Names.
  1213. ReferencedDomains - receives a pointer to a structure describing the
  1214. domains used for the translation. The entries in this structure
  1215. are referenced by the structure returned via the Sids parameter.
  1216. Unlike the Sids parameter, which contains an array entry for
  1217. each translated name, this structure will only contain one
  1218. component for each domain utilized in the translation.
  1219. When this information is no longer needed, it must be released
  1220. by passing the returned pointer to LsaFreeMemory().
  1221. TranslatedSids - Pointer to a structure which will (or already) references an array of
  1222. records describing each translated Sid. The nth entry in this array
  1223. provides a translation for the nth element in the Names parameter.
  1224. When this information is no longer needed, it must be released
  1225. by passing the returned pointer to LsaFreeMemory().
  1226. LookupLevel - Specifies the Level of Lookup to be performed on this
  1227. machine. Values of this field are are follows:
  1228. LsapLookupWksta - First Level Lookup performed on a workstation
  1229. normally configured for Windows-Nt. The lookup searches the
  1230. Well-Known Sids/Names, and the Built-in Domain and Account Domain
  1231. in the local SAM Database. If not all Sids or Names are
  1232. identified, performs a "handoff" of a Second level Lookup to the
  1233. LSA running on a Controller for the workstation's Primary Domain
  1234. (if any).
  1235. LsapLookupPDC - Second Level Lookup performed on a Primary Domain
  1236. Controller. The lookup searches the Account Domain of the
  1237. SAM Database on the controller. If not all Sids or Names are
  1238. found, the Trusted Domain List (TDL) is obtained from the
  1239. LSA's Policy Database and Third Level lookups are performed
  1240. via "handoff" to each Trusted Domain in the List.
  1241. LsapLookupTDL - Third Level Lookup performed on a controller
  1242. for a Trusted Domain. The lookup searches the Account Domain of
  1243. the SAM Database on the controller only.
  1244. MappedCount - Pointer to location that contains a count of the Names
  1245. mapped so far. On exit, this will be updated.
  1246. MappedCount - Pointer to location containing the number of Names
  1247. in the Names array that have already been mapped. This number
  1248. will be updated to reflect additional mapping done by this
  1249. routine.
  1250. CompletelyUnmappedCount - Pointer to location containing the
  1251. count of completely unmapped Names. A Name is completely unmapped
  1252. if it is unknown and non-composite, or composite but with an
  1253. unrecognized Domain component. This count is updated on exit, the
  1254. number of completely unmapped Namess whose Domain Prefices are
  1255. identified by this routine being subtracted from the input value.
  1256. WorkList - Receives pointer to completed Work List if successfully built.
  1257. Return Value:
  1258. NTSTATUS - Standard Nt Result Code
  1259. STATUS_NONE_MAPPED - All of the Names specified were composite,
  1260. but none of their Domains appear in the Trusted Domain List.
  1261. No Work List has been generated. Note that this is not a
  1262. fatal error.
  1263. --*/
  1264. {
  1265. NTSTATUS Status = STATUS_SUCCESS, IgnoreStatus = STATUS_SUCCESS;
  1266. PLSAP_DB_LOOKUP_WORK_LIST OutputWorkList = NULL;
  1267. ULONG NameIndex;
  1268. PLSAP_DB_LOOKUP_WORK_ITEM AnchorWorkItem = NULL;
  1269. PLSAP_DB_LOOKUP_WORK_ITEM NextWorkItem = NULL;
  1270. PLSAP_DB_LOOKUP_WORK_ITEM WorkItemToUpdate = NULL;
  1271. PLSAP_DB_LOOKUP_WORK_ITEM NewWorkItem = NULL;
  1272. PLSAPR_TRUST_INFORMATION TrustInformation = NULL;
  1273. LONG DomainIndex = 0;
  1274. PLSAP_DB_LOOKUP_WORK_ITEM IsolatedNamesWorkItem = NULL;
  1275. BOOLEAN AcquiredTrustedDomainListReadLock = FALSE;
  1276. PLSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY TrustedDomainEntry = NULL;
  1277. LSAPR_UNICODE_STRING DomainNameBuffer;
  1278. PLSAPR_UNICODE_STRING DomainName = &DomainNameBuffer;
  1279. LSAPR_UNICODE_STRING TerminalNameBuffer;
  1280. PLSAPR_UNICODE_STRING TerminalName = &TerminalNameBuffer;
  1281. PLSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY TrustEntry = NULL;
  1282. LSAPR_TRUST_INFORMATION TrustInfo;
  1283. LPWSTR ClientNetworkAddress = NULL;
  1284. //
  1285. // Get the client's address for logging purposes
  1286. //
  1287. ClientNetworkAddress = LsapGetClientNetworkAddress();
  1288. //
  1289. // Create an empty Work List.
  1290. //
  1291. Status = LsapDbLookupCreateWorkList(&OutputWorkList);
  1292. if (!NT_SUCCESS(Status)) {
  1293. goto LookupNamesBuildWorkListError;
  1294. }
  1295. //
  1296. // Initialize the Work List Header.
  1297. //
  1298. OutputWorkList->Status = STATUS_SUCCESS;
  1299. OutputWorkList->State = InactiveWorkList;
  1300. OutputWorkList->LookupType = LookupNames;
  1301. OutputWorkList->Count = Count;
  1302. OutputWorkList->LookupLevel = LookupLevel;
  1303. OutputWorkList->ReferencedDomains = ReferencedDomains;
  1304. OutputWorkList->MappedCount = MappedCount;
  1305. OutputWorkList->CompletelyUnmappedCount = CompletelyUnmappedCount;
  1306. OutputWorkList->LookupNamesParams.Names = Names;
  1307. OutputWorkList->LookupNamesParams.TranslatedSids = TranslatedSids;
  1308. //
  1309. // Construct the array of Work Items. Each Work Item will
  1310. // contain all the Names for a given domain, so we will scan
  1311. // all of the Names, sorting them into Work Items as we go.
  1312. // For each Name, follow the steps detailed below.
  1313. //
  1314. for (NameIndex = 0; NameIndex < Count; NameIndex++) {
  1315. //
  1316. // If this Name's Domain is already marked as known, skip.
  1317. //
  1318. if (TranslatedSids->Sids[NameIndex].DomainIndex != LSA_UNKNOWN_INDEX) {
  1319. continue;
  1320. }
  1321. //
  1322. // Name is completely unknown. See if there is already a Work Item
  1323. // for its Domain.
  1324. //
  1325. AnchorWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) OutputWorkList->AnchorWorkItem;
  1326. NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) AnchorWorkItem->Links.Flink;
  1327. WorkItemToUpdate = NULL;
  1328. NewWorkItem = NULL;
  1329. //
  1330. // If this is a qualified name (e.g. "NtDev\ScottBi") proceed
  1331. // to search for its Domain.
  1332. //
  1333. // Also, crack names of the format [email protected]
  1334. //
  1335. LsapLookupCrackName((PUNICODE_STRING)&PrefixNames[ NameIndex ],
  1336. (PUNICODE_STRING)&SuffixNames[ NameIndex ],
  1337. (PUNICODE_STRING)TerminalName,
  1338. (PUNICODE_STRING)DomainName);
  1339. if (DomainName->Length != 0) {
  1340. while (NextWorkItem != AnchorWorkItem) {
  1341. if (LsapCompareDomainNames(
  1342. (PUNICODE_STRING)&NextWorkItem->TrustInformation.Name,
  1343. (PUNICODE_STRING) DomainName,
  1344. NULL)
  1345. ) {
  1346. //
  1347. // A Work Item already exists for the Name's Trusted Domain.
  1348. // Select that Work Item for update.
  1349. //
  1350. WorkItemToUpdate = NextWorkItem;
  1351. break;
  1352. }
  1353. //
  1354. // Name's domain not found among existing Work Items. Skip to
  1355. // next Work Item.
  1356. //
  1357. NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) NextWorkItem->Links.Flink;
  1358. }
  1359. if (WorkItemToUpdate == NULL) {
  1360. //
  1361. // No Work Item exists for the Name's Domain. See if the
  1362. // Name belongs to one of the Trusted Domains. If not, skip
  1363. // to the next Name.
  1364. //
  1365. ULONG Flags = LSAP_LOOKUP_DOMAIN_TRUST_DIRECT_EXTERNAL;
  1366. if (fIncludeIntraforest) {
  1367. Flags |= LSAP_LOOKUP_DOMAIN_TRUST_DIRECT_INTRA;
  1368. }
  1369. Status = LsapDomainHasDomainTrust(Flags,
  1370. (PUNICODE_STRING)DomainName,
  1371. NULL,
  1372. &AcquiredTrustedDomainListReadLock,
  1373. &TrustEntry);
  1374. if (!NT_SUCCESS(Status)) {
  1375. if (Status == STATUS_NO_SUCH_DOMAIN) {
  1376. //
  1377. // No Match!
  1378. //
  1379. Status = STATUS_SUCCESS;
  1380. continue;
  1381. }
  1382. break;
  1383. }
  1384. RtlZeroMemory( &TrustInfo, sizeof(TrustInfo) );
  1385. TrustInfo.Name = TrustEntry->TrustInfoEx.FlatName;
  1386. TrustInfo.Sid = TrustEntry->TrustInfoEx.Sid;
  1387. TrustInformation = &TrustInfo;
  1388. //
  1389. // Name belongs to a Trusted Domain for which there is
  1390. // no Work Item. Add the Domain to the Referenced Domain List
  1391. // and obtain a Domain Index.
  1392. //
  1393. Status = LsapDbLookupAddListReferencedDomains(
  1394. ReferencedDomains,
  1395. TrustInformation,
  1396. &DomainIndex
  1397. );
  1398. if (!NT_SUCCESS(Status)) {
  1399. break;
  1400. }
  1401. //
  1402. // Create a new Work Item for this domain.
  1403. //
  1404. Status = LsapDbLookupCreateWorkItem(
  1405. TrustInformation,
  1406. DomainIndex,
  1407. (ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
  1408. &NewWorkItem
  1409. );
  1410. if (!NT_SUCCESS(Status)) {
  1411. break;
  1412. }
  1413. //
  1414. // Add the Work Item to the List.
  1415. //
  1416. Status = LsapDbAddWorkItemToWorkList( OutputWorkList, NewWorkItem );
  1417. if (!NT_SUCCESS(Status)) {
  1418. break;
  1419. }
  1420. WorkItemToUpdate = NewWorkItem;
  1421. }
  1422. //
  1423. // Add the Name Index to the Work Item.
  1424. //
  1425. Status = LsapDbLookupAddIndicesToWorkItem(
  1426. WorkItemToUpdate,
  1427. (ULONG) 1,
  1428. &NameIndex
  1429. );
  1430. if (!NT_SUCCESS(Status)) {
  1431. break;
  1432. }
  1433. //
  1434. // Store the Domain Index in the Translated Sids array entry for
  1435. // the Name.
  1436. //
  1437. OutputWorkList->LookupNamesParams.TranslatedSids->Sids[NameIndex].DomainIndex = WorkItemToUpdate->DomainIndex;
  1438. if (!NT_SUCCESS(Status)) {
  1439. goto LookupNamesBuildWorkListError;
  1440. }
  1441. } else {
  1442. //
  1443. // This is an Isolated Name. We know that it is not the name
  1444. // of any Domain on the lookup path, because all of these
  1445. // have been translated earlier. We will add the name to a
  1446. // temporary work item and later transfer all the Isolated Names
  1447. // to every Work Item on the Work List. If we don't already
  1448. // have a temporary Work Item for the Isolated Names, create one.
  1449. //
  1450. //
  1451. // N.B. Only build this list if we want to perform isolated
  1452. // name lookup across the directly trusted forest boundary.
  1453. //
  1454. if ( (LookupOptions & LSA_LOOKUP_ISOLATED_AS_LOCAL) == 0 &&
  1455. !LsapLookupRestrictIsolatedNameLevel) {
  1456. UNICODE_STRING Items[2];
  1457. //
  1458. // Event the fact we are looking up this isolated name
  1459. //
  1460. Items[0] = *(PUNICODE_STRING)TerminalName;
  1461. RtlInitUnicodeString(&Items[1], ClientNetworkAddress);
  1462. LsapTraceEventWithData(EVENT_TRACE_TYPE_INFO,
  1463. LsaTraceEvent_LookupIsolatedNameInTrustedDomains,
  1464. 2,
  1465. Items);
  1466. if (IsolatedNamesWorkItem == NULL) {
  1467. //
  1468. // Create a new Work Item for the Isolated Names.
  1469. // This is temporary.
  1470. //
  1471. Status = LsapDbLookupCreateWorkItem(
  1472. NULL,
  1473. DomainIndex,
  1474. (ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
  1475. &IsolatedNamesWorkItem
  1476. );
  1477. if (!NT_SUCCESS(Status)) {
  1478. break;
  1479. }
  1480. }
  1481. //
  1482. // Mark this Work Item as having Isolated Names only.
  1483. //
  1484. IsolatedNamesWorkItem->Properties = LSAP_DB_LOOKUP_WORK_ITEM_ISOL;
  1485. //
  1486. // Add the Name index to the Isolated Names Work Item
  1487. //
  1488. Status = LsapDbLookupAddIndicesToWorkItem(
  1489. IsolatedNamesWorkItem,
  1490. (ULONG) 1,
  1491. &NameIndex
  1492. );
  1493. if (!NT_SUCCESS(Status)) {
  1494. break;
  1495. }
  1496. }
  1497. }
  1498. }
  1499. if (!NT_SUCCESS(Status)) {
  1500. goto LookupNamesBuildWorkListError;
  1501. }
  1502. //
  1503. // If we have any unmapped Isolated Names, we know at this stage that they are
  1504. // not the Names of Trusted Domains. Therefore, we need to arrange
  1505. // for them to be looked up in every Trusted Domain. We need to
  1506. // traverse the list of Trusted Domains and for each Domain, either
  1507. // create a new Work Item to lookup all of the Names in that Domain,
  1508. // or add the Names to the existing Work Item generated for that
  1509. // Domain (because there are some Qualified Names which reference
  1510. // the Domain). We do this Work Item Generation in 2 stages. First,
  1511. // we will scan the Work List, adding the Isolated Names to every Work
  1512. // Item found therein. Second, we will create a Work Item for each
  1513. // of the Trusted Domains that we don't already have a Work Item.
  1514. //
  1515. //
  1516. // Stage (1) - Scan the Work List, adding the Isolated Names to
  1517. // every Work Item found therein
  1518. //
  1519. if (IsolatedNamesWorkItem != NULL) {
  1520. //
  1521. // Stage (1) - Scan the Work List, adding the Isolated Names to
  1522. // every Work Item found therein
  1523. //
  1524. NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM)
  1525. OutputWorkList->AnchorWorkItem->Links.Flink;
  1526. while (NextWorkItem != OutputWorkList->AnchorWorkItem) {
  1527. //
  1528. // Add the Isolated Name indices to this Work Item
  1529. //
  1530. Status = LsapDbLookupAddIndicesToWorkItem(
  1531. NextWorkItem,
  1532. IsolatedNamesWorkItem->UsedCount,
  1533. IsolatedNamesWorkItem->Indices
  1534. );
  1535. if (!NT_SUCCESS(Status)) {
  1536. break;
  1537. }
  1538. NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM)
  1539. NextWorkItem->Links.Flink;
  1540. }
  1541. if (!NT_SUCCESS(Status)) {
  1542. goto LookupNamesBuildWorkListError;
  1543. }
  1544. //
  1545. // Stage (2) - Now create Work Items to look up all the Isolated Names
  1546. // in every Trusted Domain that does not presently have a Work Item.
  1547. // The Domains for all existing Work Items are already present
  1548. // on the Referenced Domains List because they all specify at least one
  1549. // Qualified Name, so we can lookup that list to determine whether a
  1550. // Work Item exists for a Domain.
  1551. //
  1552. for (;;) {
  1553. //
  1554. // Grab the lock on the TrustedDomainList.
  1555. //
  1556. if (!AcquiredTrustedDomainListReadLock) {
  1557. Status = LsapDbAcquireReadLockTrustedDomainList();
  1558. if (!NT_SUCCESS(Status)) {
  1559. break;
  1560. }
  1561. AcquiredTrustedDomainListReadLock = TRUE;
  1562. }
  1563. NewWorkItem = NULL;
  1564. Status = LsapDbTraverseTrustedDomainList(
  1565. &TrustedDomainEntry,
  1566. &TrustInformation
  1567. );
  1568. if (!NT_SUCCESS(Status)) {
  1569. if (Status == STATUS_NO_MORE_ENTRIES) {
  1570. Status = STATUS_SUCCESS;
  1571. }
  1572. break;
  1573. }
  1574. //
  1575. // Use only trusts to trusted domains
  1576. //
  1577. if (!LsapOutboundTrustedDomain(TrustedDomainEntry)) {
  1578. continue;
  1579. }
  1580. //
  1581. // Found Next Trusted Domain. Check if this domain is already
  1582. // present on the Referenced Domain List. If so, skip to the
  1583. // next domain, because we have a Work Item for this domain.
  1584. //
  1585. // If the Domain is not present on the Referenced Domain List,
  1586. // we need to create a Work Item for this Domain and add all
  1587. // of the unmapped Isolated Names indices to it.
  1588. //
  1589. if (LsapDbLookupListReferencedDomains(
  1590. ReferencedDomains,
  1591. TrustInformation->Sid,
  1592. &DomainIndex
  1593. )) {
  1594. continue;
  1595. }
  1596. //
  1597. // We don't have a Work Item for this Trusted Domain. Create
  1598. // one, and add all of the remaining Isolated Names to it.
  1599. // Mark the Domain Index as unknown. This is picked up
  1600. // later when the Work Item is processed. If any Names
  1601. // were translated, the Domain to which the Work Item
  1602. // relates will be added to the Referenced Domain List.
  1603. //
  1604. Status = LsapDbLookupCreateWorkItem(
  1605. TrustInformation,
  1606. LSA_UNKNOWN_INDEX,
  1607. (ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
  1608. &NewWorkItem
  1609. );
  1610. if (!NT_SUCCESS(Status)) {
  1611. break;
  1612. }
  1613. //
  1614. // Mark this Work Item as having Isolated Names only.
  1615. //
  1616. NewWorkItem->Properties = LSAP_DB_LOOKUP_WORK_ITEM_ISOL;
  1617. //
  1618. // Add the Isolated Name indices to this Work Item
  1619. //
  1620. Status = LsapDbLookupAddIndicesToWorkItem(
  1621. NewWorkItem,
  1622. IsolatedNamesWorkItem->UsedCount,
  1623. IsolatedNamesWorkItem->Indices
  1624. );
  1625. if (!NT_SUCCESS(Status)) {
  1626. break;
  1627. }
  1628. //
  1629. // Add the Work Item to the List.
  1630. //
  1631. Status = LsapDbAddWorkItemToWorkList( OutputWorkList, NewWorkItem );
  1632. if (!NT_SUCCESS(Status)) {
  1633. break;
  1634. }
  1635. }
  1636. if (!NT_SUCCESS(Status)) {
  1637. goto LookupNamesBuildWorkListError;
  1638. }
  1639. }
  1640. //
  1641. // If the Work List has no Work Items, this means that all of the
  1642. // Names were composite, but none of the Domain Names specified
  1643. // could be found on the Trusted Domain List. In this case,
  1644. // we discard the Work List.
  1645. //
  1646. Status = STATUS_NONE_MAPPED;
  1647. if (OutputWorkList->WorkItemCount == 0) {
  1648. goto LookupNamesBuildWorkListError;
  1649. }
  1650. //
  1651. // Compute the Advisory Thread Count for this lookup.
  1652. //
  1653. Status = LsapDbLookupComputeAdvisoryChildThreadCount( OutputWorkList );
  1654. if (!NT_SUCCESS(Status)) {
  1655. goto LookupNamesBuildWorkListError;
  1656. }
  1657. Status = LsapDbLookupInsertWorkList(OutputWorkList);
  1658. if (!NT_SUCCESS(Status)) {
  1659. goto LookupNamesBuildWorkListError;
  1660. }
  1661. //
  1662. // Update the Mapped Counts
  1663. //
  1664. LsapDbUpdateMappedCountsWorkList( OutputWorkList );
  1665. LookupNamesBuildWorkListFinish:
  1666. //
  1667. // If necessary, release the Trusted Domain List Read Lock.
  1668. //
  1669. if (AcquiredTrustedDomainListReadLock) {
  1670. LsapDbReleaseLockTrustedDomainList();
  1671. AcquiredTrustedDomainListReadLock = FALSE;
  1672. }
  1673. //
  1674. // If we have an Isolated Names Work Item, free it.
  1675. //
  1676. if (IsolatedNamesWorkItem != NULL) {
  1677. MIDL_user_free( IsolatedNamesWorkItem->Indices);
  1678. IsolatedNamesWorkItem->Indices = NULL;
  1679. MIDL_user_free( IsolatedNamesWorkItem );
  1680. IsolatedNamesWorkItem = NULL;
  1681. }
  1682. if (ClientNetworkAddress) {
  1683. RpcStringFreeW(&ClientNetworkAddress);
  1684. }
  1685. *WorkList = OutputWorkList;
  1686. return(Status);
  1687. LookupNamesBuildWorkListError:
  1688. //
  1689. // Discard the Work List.
  1690. //
  1691. if (OutputWorkList != NULL) {
  1692. IgnoreStatus = LsapDbLookupDeleteWorkList(OutputWorkList);
  1693. OutputWorkList = NULL;
  1694. }
  1695. goto LookupNamesBuildWorkListFinish;
  1696. }
  1697. NTSTATUS
  1698. LsapDbLookupXForestNamesBuildWorkList(
  1699. IN ULONG Count,
  1700. IN PLSAPR_UNICODE_STRING Names,
  1701. IN PLSAPR_UNICODE_STRING PrefixNames,
  1702. IN PLSAPR_UNICODE_STRING SuffixNames,
  1703. IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
  1704. IN OUT PLSAPR_TRANSLATED_SIDS_EX2 TranslatedSids,
  1705. IN LSAP_LOOKUP_LEVEL LookupLevel,
  1706. IN OUT PULONG MappedCount,
  1707. IN OUT PULONG CompletelyUnmappedCount,
  1708. OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList
  1709. )
  1710. /*++
  1711. Routine Description:
  1712. This function constructs a Work List for an LsarLookupNames call. The
  1713. Work List contains the parameters of the call, and an array of Work Items.
  1714. Each Work Item specifies either all of the Names to be looked up
  1715. in a given forest.
  1716. N.B. The trust information of the WorkList is the DNS name of the
  1717. target forest, not the DNS name of domain (we don't know what domain
  1718. the item belongs to yet).
  1719. Arguments:
  1720. Count - Specifies the number of names to be translated.
  1721. Names - Pointer to an array of Count Unicode String structures
  1722. specifying the names to be looked up and mapped to Sids.
  1723. The strings may be names of User, Group or Alias accounts or
  1724. domains.
  1725. PrefixNames - Pointer to an array of Count Unicode String structures
  1726. containing the Prefix portions of the Names. Names having no
  1727. Prefix are called Isolated Names. For these, the Unicode String
  1728. structure is set to contain a zero Length.
  1729. SuffixNames - Pointer to an array of Count Unicode String structures
  1730. containing the Suffix portions of the Names.
  1731. ReferencedDomains - receives a pointer to a structure describing the
  1732. domains used for the translation. The entries in this structure
  1733. are referenced by the structure returned via the Sids parameter.
  1734. Unlike the Sids parameter, which contains an array entry for
  1735. each translated name, this structure will only contain one
  1736. component for each domain utilized in the translation.
  1737. When this information is no longer needed, it must be released
  1738. by passing the returned pointer to LsaFreeMemory().
  1739. TranslatedSids - Pointer to a structure which will (or already) references an array of
  1740. records describing each translated Sid. The nth entry in this array
  1741. provides a translation for the nth element in the Names parameter.
  1742. When this information is no longer needed, it must be released
  1743. by passing the returned pointer to LsaFreeMemory().
  1744. LookupLevel - Specifies the Level of Lookup to be performed on this
  1745. machine. Values of this field are are follows:
  1746. MappedCount - Pointer to location containing the number of Names
  1747. in the Names array that have already been mapped. This number
  1748. will be updated to reflect additional mapping done by this
  1749. routine.
  1750. CompletelyUnmappedCount - Pointer to location containing the
  1751. count of completely unmapped Names. A Name is completely unmapped
  1752. if it is unknown and non-composite, or composite but with an
  1753. unrecognized Domain component. This count is updated on exit, the
  1754. number of completely unmapped Namess whose Domain Prefices are
  1755. identified by this routine being subtracted from the input value.
  1756. WorkList - Receives pointer to completed Work List if successfully built.
  1757. Return Value:
  1758. NTSTATUS - Standard Nt Result Code
  1759. STATUS_NONE_MAPPED - All of the Names specified were composite,
  1760. but none of their Domains appear in the Trusted Domain List.
  1761. No Work List has been generated. Note that this is not a
  1762. fatal error.
  1763. --*/
  1764. {
  1765. NTSTATUS Status = STATUS_SUCCESS, IgnoreStatus = STATUS_SUCCESS;
  1766. PLSAP_DB_LOOKUP_WORK_LIST OutputWorkList = NULL;
  1767. ULONG NameIndex;
  1768. PLSAP_DB_LOOKUP_WORK_ITEM AnchorWorkItem = NULL;
  1769. PLSAP_DB_LOOKUP_WORK_ITEM NextWorkItem = NULL;
  1770. PLSAP_DB_LOOKUP_WORK_ITEM WorkItemToUpdate = NULL;
  1771. PLSAP_DB_LOOKUP_WORK_ITEM NewWorkItem = NULL;
  1772. PLSAPR_TRUST_INFORMATION TrustInformation = NULL;
  1773. LONG DomainIndex = 0;
  1774. PLSAPR_UNICODE_STRING DomainName = NULL;
  1775. PLSAPR_UNICODE_STRING TerminalName = NULL;
  1776. PLSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY TrustEntry = NULL;
  1777. LSAPR_TRUST_INFORMATION TrustInfo;
  1778. UNICODE_STRING XForestName = {0, 0, NULL};
  1779. //
  1780. // Create an empty Work List.
  1781. //
  1782. Status = LsapDbLookupCreateWorkList(&OutputWorkList);
  1783. if (!NT_SUCCESS(Status)) {
  1784. goto LookupNamesBuildWorkListError;
  1785. }
  1786. //
  1787. // Initialize the Work List Header.
  1788. //
  1789. OutputWorkList->Status = STATUS_SUCCESS;
  1790. OutputWorkList->State = InactiveWorkList;
  1791. OutputWorkList->LookupType = LookupNames;
  1792. OutputWorkList->Count = Count;
  1793. OutputWorkList->LookupLevel = LookupLevel;
  1794. OutputWorkList->ReferencedDomains = ReferencedDomains;
  1795. OutputWorkList->MappedCount = MappedCount;
  1796. OutputWorkList->CompletelyUnmappedCount = CompletelyUnmappedCount;
  1797. OutputWorkList->LookupNamesParams.Names = Names;
  1798. OutputWorkList->LookupNamesParams.TranslatedSids = TranslatedSids;
  1799. //
  1800. // Construct the array of Work Items. Each Work Item will
  1801. // contain all the Names for a given domain, so we will scan
  1802. // all of the Names, sorting them into Work Items as we go.
  1803. // For each Name, follow the steps detailed below.
  1804. //
  1805. for (NameIndex = 0; NameIndex < Count; NameIndex++) {
  1806. //
  1807. // Name is completely unknown. See if there is already a Work Item
  1808. // for its Forest.
  1809. //
  1810. AnchorWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) OutputWorkList->AnchorWorkItem;
  1811. NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) AnchorWorkItem->Links.Flink;
  1812. WorkItemToUpdate = NULL;
  1813. NewWorkItem = NULL;
  1814. //
  1815. // If this is a qualified name match by domain name
  1816. //
  1817. DomainName = &PrefixNames[ NameIndex ];
  1818. TerminalName = &SuffixNames[ NameIndex ];
  1819. if (DomainName->Length == 0) {
  1820. //
  1821. // This is a UPN -- get the XForest trust if any
  1822. //
  1823. Status = LsaIForestTrustFindMatch(RoutingMatchUpn,
  1824. (PLSA_UNICODE_STRING)TerminalName,
  1825. &XForestName);
  1826. if (!NT_SUCCESS(Status)) {
  1827. //
  1828. // Perhaps this is an islolated domain name
  1829. //
  1830. Status = LsaIForestTrustFindMatch(RoutingMatchDomainName,
  1831. (PLSA_UNICODE_STRING)TerminalName,
  1832. &XForestName);
  1833. }
  1834. if (!NT_SUCCESS(Status)) {
  1835. //
  1836. // Can't find match? Continue
  1837. //
  1838. Status = STATUS_SUCCESS;
  1839. continue;
  1840. }
  1841. } else {
  1842. //
  1843. // The name has a domain portion -- get the XForest trust if any
  1844. //
  1845. Status = LsaIForestTrustFindMatch(RoutingMatchDomainName,
  1846. (PLSA_UNICODE_STRING)DomainName,
  1847. &XForestName);
  1848. if (!NT_SUCCESS(Status)) {
  1849. //
  1850. // Can't find match? Continue
  1851. //
  1852. Status = STATUS_SUCCESS;
  1853. continue;
  1854. }
  1855. }
  1856. //
  1857. // We must have found a match
  1858. //
  1859. ASSERT(XForestName.Length > 0);
  1860. //
  1861. // See if any entry already exists for us
  1862. //
  1863. while (NextWorkItem != AnchorWorkItem) {
  1864. if (LsapCompareDomainNames(
  1865. (PUNICODE_STRING) &NextWorkItem->TrustInformation.Name,
  1866. (PUNICODE_STRING) &XForestName,
  1867. NULL)
  1868. ) {
  1869. //
  1870. // A Work Item already exists for the Name's Trusted Domain.
  1871. // Select that Work Item for update.
  1872. //
  1873. WorkItemToUpdate = NextWorkItem;
  1874. break;
  1875. }
  1876. //
  1877. // Name's domain not found among existing Work Items. Skip to
  1878. // next Work Item.
  1879. //
  1880. NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) NextWorkItem->Links.Flink;
  1881. }
  1882. if (WorkItemToUpdate == NULL) {
  1883. RtlZeroMemory( &TrustInfo, sizeof(TrustInfo) );
  1884. TrustInfo.Name.Length = XForestName.Length;
  1885. TrustInfo.Name.MaximumLength = XForestName.MaximumLength;
  1886. TrustInfo.Name.Buffer = XForestName.Buffer;
  1887. TrustInfo.Sid = NULL;
  1888. TrustInformation = &TrustInfo;
  1889. //
  1890. // Create a new Work Item for this domain.
  1891. //
  1892. Status = LsapDbLookupCreateWorkItem(
  1893. TrustInformation,
  1894. LSA_UNKNOWN_INDEX,
  1895. (ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
  1896. &NewWorkItem
  1897. );
  1898. if (!NT_SUCCESS(Status)) {
  1899. break;
  1900. }
  1901. //
  1902. // Add the Work Item to the List.
  1903. //
  1904. Status = LsapDbAddWorkItemToWorkList( OutputWorkList, NewWorkItem );
  1905. if (!NT_SUCCESS(Status)) {
  1906. break;
  1907. }
  1908. WorkItemToUpdate = NewWorkItem;
  1909. //
  1910. // Mark the item as a xforest item
  1911. //
  1912. NewWorkItem->Properties |= LSAP_DB_LOOKUP_WORK_ITEM_XFOREST;
  1913. }
  1914. LsaIFree_LSAPR_UNICODE_STRING_BUFFER( (LSAPR_UNICODE_STRING*)&XForestName);
  1915. XForestName.Buffer = NULL;
  1916. if (!NT_SUCCESS(Status)) {
  1917. break;
  1918. }
  1919. //
  1920. // Add the Name Index to the Work Item.
  1921. //
  1922. Status = LsapDbLookupAddIndicesToWorkItem(
  1923. WorkItemToUpdate,
  1924. (ULONG) 1,
  1925. &NameIndex
  1926. );
  1927. if (!NT_SUCCESS(Status)) {
  1928. break;
  1929. }
  1930. }
  1931. if (XForestName.Buffer) {
  1932. LsaIFree_LSAPR_UNICODE_STRING_BUFFER( (LSAPR_UNICODE_STRING*)&XForestName);
  1933. XForestName.Buffer = NULL;
  1934. }
  1935. if (!NT_SUCCESS(Status)) {
  1936. goto LookupNamesBuildWorkListError;
  1937. }
  1938. //
  1939. // If the Work List has no Work Items, bail
  1940. //
  1941. if (OutputWorkList->WorkItemCount == 0) {
  1942. Status = STATUS_NONE_MAPPED;
  1943. goto LookupNamesBuildWorkListError;
  1944. }
  1945. //
  1946. // Compute the Advisory Thread Count for this lookup.
  1947. //
  1948. Status = LsapDbLookupComputeAdvisoryChildThreadCount( OutputWorkList );
  1949. if (!NT_SUCCESS(Status)) {
  1950. goto LookupNamesBuildWorkListError;
  1951. }
  1952. Status = LsapDbLookupInsertWorkList(OutputWorkList);
  1953. if (!NT_SUCCESS(Status)) {
  1954. goto LookupNamesBuildWorkListError;
  1955. }
  1956. //
  1957. // Update the Mapped Counts
  1958. //
  1959. LsapDbUpdateMappedCountsWorkList( OutputWorkList );
  1960. LookupNamesBuildWorkListFinish:
  1961. *WorkList = OutputWorkList;
  1962. return(Status);
  1963. LookupNamesBuildWorkListError:
  1964. //
  1965. // Discard the Work List.
  1966. //
  1967. if (OutputWorkList != NULL) {
  1968. IgnoreStatus = LsapDbLookupDeleteWorkList(OutputWorkList);
  1969. OutputWorkList = NULL;
  1970. }
  1971. goto LookupNamesBuildWorkListFinish;
  1972. }
  1973. NTSTATUS
  1974. LsapDbLookupSidsBuildWorkList(
  1975. IN ULONG Count,
  1976. IN PLSAPR_SID *Sids,
  1977. IN BOOLEAN fIncludeIntraforest,
  1978. IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
  1979. IN PLSAPR_TRANSLATED_NAMES_EX TranslatedNames,
  1980. IN LSAP_LOOKUP_LEVEL LookupLevel,
  1981. IN OUT PULONG MappedCount,
  1982. IN OUT PULONG CompletelyUnmappedCount,
  1983. OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList
  1984. )
  1985. /*++
  1986. Routine Description:
  1987. This function constructs a Work List for an LsarLookupSids call. The
  1988. Work List contains the parameters of the call, and an array of Work Items.
  1989. Each Work Item specifies all of the Sids belonging to a given Domain
  1990. and is the minimal unit of work that a worker thread will undertake.
  1991. Count - Number of Sids in the Sids array, Note that some of these
  1992. may already have been mapped elsewhere, as specified by the
  1993. MappedCount parameter.
  1994. Sids - Pointer to array of pointers to Sids to be translated.
  1995. Zero or all of the Sids may already have been translated
  1996. elsewhere. If any of the Sids have been translated, the
  1997. Names parameter will point to a location containing a non-NULL
  1998. array of Name translation structures corresponding to the
  1999. Sids. If the nth Sid has been translated, the nth name
  2000. translation structure will contain either a non-NULL name
  2001. or a non-negative offset into the Referenced Domain List. If
  2002. the nth Sid has not yet been translated, the nth name
  2003. translation structure will contain a zero-length name string
  2004. and a negative value for the Referenced Domain List index.
  2005. fIncludeIntraforest -- if TRUE, trusted domains in our local forest
  2006. are searched.
  2007. TrustInformation - Pointer to Trust Information specifying a Domain Sid
  2008. and Name.
  2009. ReferencedDomains - Pointer to a Referenced Domain List structure.
  2010. The structure references an array of zero or more Trust Information
  2011. entries, one per referenced domain. This array will be appended to
  2012. or reallocated if necessary.
  2013. TranslatedNames - Pointer to structure that optionally references a list
  2014. of name translations for some of the Sids in the Sids array.
  2015. LookupLevel - Specifies the Level of Lookup to be performed on this
  2016. machine. Values of this field are are follows:
  2017. LsapLookupPDC - Second Level Lookup performed on a Primary Domain
  2018. Controller. The lookup searches the Account Domain of the
  2019. SAM Database on the controller. If not all Sids or Names are
  2020. found, the Trusted Domain List (TDL) is obtained from the
  2021. LSA's Policy Database and Third Level lookups are performed
  2022. via "handoff" to each Trusted Domain in the List.
  2023. LsapLookupTDL - Third Level Lookup performed on a controller
  2024. for a Trusted Domain. The lookup searches the Account Domain of
  2025. the SAM Database on the controller only.
  2026. NOTE: LsapLookupWksta is not valid for this parameter.
  2027. MappedCount - Pointer to location containing the number of Sids
  2028. in the Sids array that have already been mapped. This number
  2029. will be updated to reflect additional mapping done by this
  2030. routine.
  2031. CompletelyUnmappedCount - Pointer to location containing the
  2032. count of completely unmapped Sids. A Sid is completely unmapped
  2033. if it is unknown and also its Domain Prefix Sid is not recognized.
  2034. This count is updated on exit, the number of completely unmapped
  2035. Sids whose Domain Prefices are identified by this routine being
  2036. subtracted from the input value.
  2037. WorkList - Receives pointer to completed Work List if successfully built.
  2038. Return Values:
  2039. NTSTATUS - Standard Nt Result Code.
  2040. STATUS_NONE_MAPPED - None of the Sids specified belong to any of
  2041. the Trusted Domains. No Work List has been generated. Note
  2042. that this is not a fatal error.
  2043. --*/
  2044. {
  2045. NTSTATUS Status = STATUS_SUCCESS;
  2046. NTSTATUS IgnoreStatus = STATUS_SUCCESS;
  2047. PLSAP_DB_LOOKUP_WORK_LIST OutputWorkList = NULL;
  2048. ULONG SidIndex;
  2049. PSID DomainSid = NULL;
  2050. PLSAP_DB_LOOKUP_WORK_ITEM AnchorWorkItem = NULL;
  2051. PLSAP_DB_LOOKUP_WORK_ITEM NextWorkItem = NULL;
  2052. PLSAP_DB_LOOKUP_WORK_ITEM WorkItemToUpdate = NULL;
  2053. PLSAP_DB_LOOKUP_WORK_ITEM NewWorkItem = NULL;
  2054. PLSAPR_TRUST_INFORMATION TrustInformation = NULL;
  2055. PLSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY TrustEntry = NULL;
  2056. LONG DomainIndex;
  2057. PSID Sid = NULL;
  2058. BOOLEAN AcquiredReadLockTrustedDomainList = FALSE;
  2059. //
  2060. // Create an empty Work List
  2061. //
  2062. Status = LsapDbLookupCreateWorkList(&OutputWorkList);
  2063. if (!NT_SUCCESS(Status)) {
  2064. goto LookupSidsBuildWorkListError;
  2065. }
  2066. //
  2067. // Initialize the rest of the Work List Header fields. Some fields
  2068. // were initialized upon creation to fixed values. The ones set here
  2069. // depend on parameter values passed into this routine.
  2070. //
  2071. OutputWorkList->LookupType = LookupSids;
  2072. OutputWorkList->Count = Count;
  2073. OutputWorkList->LookupLevel = LookupLevel;
  2074. OutputWorkList->ReferencedDomains = ReferencedDomains;
  2075. OutputWorkList->MappedCount = MappedCount;
  2076. OutputWorkList->CompletelyUnmappedCount = CompletelyUnmappedCount;
  2077. OutputWorkList->LookupSidsParams.Sids = Sids;
  2078. OutputWorkList->LookupSidsParams.TranslatedNames = TranslatedNames;
  2079. //
  2080. // Construct the array of Work Items. Each Work Item will
  2081. // contain all the Sids for a given domain, so we will scan
  2082. // all of the Sids, sorting them into Work Items as we go.
  2083. // For each Sid, follow the steps detailed below.
  2084. //
  2085. for (SidIndex = 0; SidIndex < Count; SidIndex++) {
  2086. //
  2087. // If this Sid's Domain is already marked as known, skip.
  2088. //
  2089. if (TranslatedNames->Names[SidIndex].DomainIndex != LSA_UNKNOWN_INDEX) {
  2090. continue;
  2091. }
  2092. //
  2093. // Sid is completely unknown. Extract its Domain Sid and see if
  2094. // there is already a Work Item for its Domain.
  2095. //
  2096. Sid = Sids[SidIndex];
  2097. Status = LsapRtlExtractDomainSid( Sid, &DomainSid );
  2098. if (!NT_SUCCESS(Status)) {
  2099. break;
  2100. }
  2101. NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) OutputWorkList->AnchorWorkItem->Links.Flink;
  2102. AnchorWorkItem = OutputWorkList->AnchorWorkItem;
  2103. WorkItemToUpdate = NULL;
  2104. NewWorkItem = NULL;
  2105. while (NextWorkItem != AnchorWorkItem) {
  2106. if (RtlEqualSid((PSID) NextWorkItem->TrustInformation.Sid,DomainSid)) {
  2107. //
  2108. // A Work Item already exists for the Sid's Trusted Domain.
  2109. // Select that Work Item for update.
  2110. //
  2111. MIDL_user_free(DomainSid);
  2112. DomainSid = NULL;
  2113. WorkItemToUpdate = NextWorkItem;
  2114. break;
  2115. }
  2116. //
  2117. // Sid's domain not found among existing Work Items. Skip to
  2118. // next Work Item.
  2119. //
  2120. NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) NextWorkItem->Links.Flink;
  2121. }
  2122. if (WorkItemToUpdate == NULL) {
  2123. //
  2124. // No Work Item exists for the Sid's Domain. See if the
  2125. // Sid belongs to one of the Trusted Domains. If not, skip
  2126. // to the next Sid.
  2127. //
  2128. ULONG Flags = LSAP_LOOKUP_DOMAIN_TRUST_DIRECT_EXTERNAL;
  2129. if (fIncludeIntraforest) {
  2130. Flags |= LSAP_LOOKUP_DOMAIN_TRUST_DIRECT_INTRA;
  2131. }
  2132. Status = LsapDomainHasDomainTrust(Flags,
  2133. NULL,
  2134. DomainSid,
  2135. &AcquiredReadLockTrustedDomainList,
  2136. &TrustEntry);
  2137. MIDL_user_free(DomainSid);
  2138. DomainSid = NULL;
  2139. if (Status == STATUS_NO_SUCH_DOMAIN) {
  2140. Status = STATUS_SUCCESS;
  2141. continue;
  2142. }
  2143. if ( !NT_SUCCESS(Status) ) {
  2144. break;
  2145. }
  2146. //
  2147. // If the trust is not outbound, don't try to lookup
  2148. //
  2149. ASSERT( NULL != TrustEntry );
  2150. if ( !FLAG_ON( TrustEntry->TrustInfoEx.TrustDirection, TRUST_DIRECTION_OUTBOUND ) ) {
  2151. Status = STATUS_SUCCESS;
  2152. continue;
  2153. }
  2154. ASSERT( NULL != TrustEntry->TrustInfoEx.Sid );
  2155. TrustInformation = &TrustEntry->ConstructedTrustInfo;
  2156. //
  2157. // Sid belongs to a Trusted Domain for which there is
  2158. // no Work Item. Add the Domain to the Referenced Domain List
  2159. // and obtain a Domain Index.
  2160. //
  2161. Status = LsapDbLookupAddListReferencedDomains(
  2162. ReferencedDomains,
  2163. TrustInformation,
  2164. &DomainIndex
  2165. );
  2166. if (!NT_SUCCESS(Status)) {
  2167. break;
  2168. }
  2169. //
  2170. // Create a new Work Item for this domain.
  2171. //
  2172. Status = LsapDbLookupCreateWorkItem(
  2173. TrustInformation,
  2174. DomainIndex,
  2175. (ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
  2176. &NewWorkItem
  2177. );
  2178. if (!NT_SUCCESS(Status)) {
  2179. break;
  2180. }
  2181. //
  2182. // Add the Work Item to the List.
  2183. //
  2184. Status = LsapDbAddWorkItemToWorkList( OutputWorkList, NewWorkItem );
  2185. if (!NT_SUCCESS(Status)) {
  2186. break;
  2187. }
  2188. WorkItemToUpdate = NewWorkItem;
  2189. }
  2190. if (!NT_SUCCESS(Status)) {
  2191. break;
  2192. }
  2193. //
  2194. // Add the Sid Index to the Work Item.
  2195. //
  2196. Status = LsapDbLookupAddIndicesToWorkItem(
  2197. WorkItemToUpdate,
  2198. (ULONG) 1,
  2199. &SidIndex
  2200. );
  2201. if (!NT_SUCCESS(Status)) {
  2202. break;
  2203. }
  2204. //
  2205. // Store the Domain Index in the Translated Names array entry for
  2206. // the Sid.
  2207. //
  2208. OutputWorkList->LookupSidsParams.TranslatedNames->Names[SidIndex].DomainIndex = WorkItemToUpdate->DomainIndex;
  2209. }
  2210. if (!NT_SUCCESS(Status)) {
  2211. goto LookupSidsBuildWorkListError;
  2212. }
  2213. //
  2214. // If the Work List has no Work Items, this means that none of the
  2215. // Sids belong to any of the Trusted Domains. In this case,
  2216. // we discard the Work List.
  2217. //
  2218. Status = STATUS_NONE_MAPPED;
  2219. if (OutputWorkList->WorkItemCount == 0) {
  2220. goto LookupSidsBuildWorkListError;
  2221. }
  2222. //
  2223. // Compute the Advisory Thread Count for this lookup.
  2224. //
  2225. Status = LsapDbLookupComputeAdvisoryChildThreadCount( OutputWorkList );
  2226. if (!NT_SUCCESS(Status)) {
  2227. goto LookupSidsBuildWorkListError;
  2228. }
  2229. //
  2230. // Insert the Work List at the end of the Work Queue.
  2231. //
  2232. Status = LsapDbLookupInsertWorkList(OutputWorkList);
  2233. if (!NT_SUCCESS(Status)) {
  2234. goto LookupSidsBuildWorkListError;
  2235. }
  2236. //
  2237. // Update the Mapped Counts
  2238. //
  2239. LsapDbUpdateMappedCountsWorkList( OutputWorkList );
  2240. *WorkList = OutputWorkList;
  2241. LookupSidsBuildWorkListFinish:
  2242. //
  2243. // If necessary, release the Trusted Domain List Read Lock.
  2244. //
  2245. if (DomainSid) {
  2246. midl_user_free(DomainSid);
  2247. }
  2248. if (AcquiredReadLockTrustedDomainList) {
  2249. LsapDbReleaseLockTrustedDomainList();
  2250. AcquiredReadLockTrustedDomainList = FALSE;
  2251. }
  2252. return(Status);
  2253. LookupSidsBuildWorkListError:
  2254. if ( OutputWorkList != NULL ) {
  2255. IgnoreStatus = LsapDbLookupDeleteWorkList( OutputWorkList );
  2256. OutputWorkList = NULL;
  2257. }
  2258. *WorkList = NULL;
  2259. goto LookupSidsBuildWorkListFinish;
  2260. }
  2261. NTSTATUS
  2262. LsapDbLookupXForestSidsBuildWorkList(
  2263. IN ULONG Count,
  2264. IN PLSAPR_SID *Sids,
  2265. IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains,
  2266. IN PLSAPR_TRANSLATED_NAMES_EX TranslatedNames,
  2267. IN LSAP_LOOKUP_LEVEL LookupLevel,
  2268. IN OUT PULONG MappedCount,
  2269. IN OUT PULONG CompletelyUnmappedCount,
  2270. OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList
  2271. )
  2272. /*++
  2273. Routine Description:
  2274. This function constructs a Work List for a Lookup SID request that contains
  2275. names that need to be resolved at cross forest domains. This routine,
  2276. hence is only called on DC's in the root of forest.
  2277. N.B. The trust information is the name of the target trusted forest,
  2278. not the domain since we don't know the domain at this point.
  2279. Parameters:
  2280. Count - Number of Sids in the Sids array, Note that some of these
  2281. may already have been mapped elsewhere, as specified by the
  2282. MappedCount parameter.
  2283. Sids - Pointer to array of pointers to Sids to be translated.
  2284. Zero or all of the Sids may already have been translated
  2285. elsewhere. If any of the Sids have been translated, the
  2286. Names parameter will point to a location containing a non-NULL
  2287. array of Name translation structures corresponding to the
  2288. Sids. If the nth Sid has been translated, the nth name
  2289. translation structure will contain either a non-NULL name
  2290. or a non-negative offset into the Referenced Domain List. If
  2291. the nth Sid has not yet been translated, the nth name
  2292. translation structure will contain a zero-length name string
  2293. and a negative value for the Referenced Domain List index.
  2294. TrustInformation - Pointer to Trust Information specifying a Domain Sid
  2295. and Name.
  2296. ReferencedDomains - Pointer to a Referenced Domain List structure.
  2297. The structure references an array of zero or more Trust Information
  2298. entries, one per referenced domain. This array will be appended to
  2299. or reallocated if necessary.
  2300. TranslatedNames - Pointer to structure that optionally references a list
  2301. of name translations for some of the Sids in the Sids array.
  2302. LookupLevel - Specifies the Level of Lookup to be performed on this
  2303. machine.
  2304. MappedCount - Pointer to location containing the number of Sids
  2305. in the Sids array that have already been mapped. This number
  2306. will be updated to reflect additional mapping done by this
  2307. routine.
  2308. CompletelyUnmappedCount - Pointer to location containing the
  2309. count of completely unmapped Sids. A Sid is completely unmapped
  2310. if it is unknown and also its Domain Prefix Sid is not recognized.
  2311. This count is updated on exit, the number of completely unmapped
  2312. Sids whose Domain Prefices are identified by this routine being
  2313. subtracted from the input value.
  2314. WorkList - Receives pointer to completed Work List if successfully built.
  2315. Return Values:
  2316. NTSTATUS - Standard Nt Result Code.
  2317. STATUS_NONE_MAPPED - None of the Sids specified belong to any of
  2318. the Trusted Domains. No Work List has been generated. Note
  2319. that this is not a fatal error.
  2320. --*/
  2321. {
  2322. NTSTATUS Status = STATUS_SUCCESS;
  2323. NTSTATUS IgnoreStatus = STATUS_SUCCESS;
  2324. PLSAP_DB_LOOKUP_WORK_LIST OutputWorkList = NULL;
  2325. ULONG SidIndex;
  2326. PSID DomainSid = NULL;
  2327. PLSAP_DB_LOOKUP_WORK_ITEM AnchorWorkItem = NULL;
  2328. PLSAP_DB_LOOKUP_WORK_ITEM NextWorkItem = NULL;
  2329. PLSAP_DB_LOOKUP_WORK_ITEM WorkItemToUpdate = NULL;
  2330. PLSAP_DB_LOOKUP_WORK_ITEM NewWorkItem = NULL;
  2331. PLSAPR_TRUST_INFORMATION TrustInformation = NULL;
  2332. LSAPR_TRUST_INFORMATION TrustInfo;
  2333. LONG DomainIndex;
  2334. PSID Sid = NULL;
  2335. UNICODE_STRING XForestName = {0, 0, NULL};
  2336. //
  2337. // Create an empty Work List
  2338. //
  2339. Status = LsapDbLookupCreateWorkList(&OutputWorkList);
  2340. if (!NT_SUCCESS(Status)) {
  2341. goto LookupSidsBuildWorkListError;
  2342. }
  2343. //
  2344. // Initialize the rest of the Work List Header fields. Some fields
  2345. // were initialized upon creation to fixed values. The ones set here
  2346. // depend on parameter values passed into this routine.
  2347. //
  2348. OutputWorkList->LookupType = LookupSids;
  2349. OutputWorkList->Count = Count;
  2350. OutputWorkList->LookupLevel = LookupLevel;
  2351. OutputWorkList->ReferencedDomains = ReferencedDomains;
  2352. OutputWorkList->MappedCount = MappedCount;
  2353. OutputWorkList->CompletelyUnmappedCount = CompletelyUnmappedCount;
  2354. OutputWorkList->LookupSidsParams.Sids = Sids;
  2355. OutputWorkList->LookupSidsParams.TranslatedNames = TranslatedNames;
  2356. //
  2357. // Construct the array of Work Items. Each Work Item will
  2358. // contain all the Sids for a given domain, so we will scan
  2359. // all of the Sids, sorting them into Work Items as we go.
  2360. // For each Sid, follow the steps detailed below.
  2361. //
  2362. for (SidIndex = 0; SidIndex < Count; SidIndex++) {
  2363. ULONG Length;
  2364. ULONG DomainSidBuffer[SECURITY_MAX_SID_SIZE/sizeof( ULONG ) + 1 ];
  2365. //
  2366. // Sid is completely unknown. Extract its Domain Sid and see if
  2367. // there is already a Work Item for its Domain.
  2368. //
  2369. Sid = Sids[SidIndex];
  2370. //
  2371. // Extract the domain portion of the SID and see if it matches
  2372. // one of our cross forest domains.
  2373. //
  2374. Length = sizeof(DomainSidBuffer);
  2375. DomainSid = (PSID)DomainSidBuffer;
  2376. if (!GetWindowsAccountDomainSid(Sid, DomainSid, &Length)) {
  2377. continue;
  2378. }
  2379. Status = LsaIForestTrustFindMatch(RoutingMatchDomainSid,
  2380. (PVOID)DomainSid,
  2381. &XForestName);
  2382. if (!NT_SUCCESS(Status)) {
  2383. //
  2384. // Can't find match? Continue
  2385. //
  2386. Status = STATUS_SUCCESS;
  2387. continue;
  2388. }
  2389. NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) OutputWorkList->AnchorWorkItem->Links.Flink;
  2390. AnchorWorkItem = OutputWorkList->AnchorWorkItem;
  2391. WorkItemToUpdate = NULL;
  2392. NewWorkItem = NULL;
  2393. while (NextWorkItem != AnchorWorkItem) {
  2394. if ( LsapCompareDomainNames(
  2395. (PUNICODE_STRING) &NextWorkItem->TrustInformation.Name,
  2396. (PUNICODE_STRING) &XForestName,
  2397. NULL)
  2398. ) {
  2399. //
  2400. // A Work Item already exists for the Sid's Trusted Domain.
  2401. // Select that Work Item for update.
  2402. //
  2403. WorkItemToUpdate = NextWorkItem;
  2404. break;
  2405. }
  2406. //
  2407. // Sid's domain not found among existing Work Items. Skip to
  2408. // next Work Item.
  2409. //
  2410. NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) NextWorkItem->Links.Flink;
  2411. }
  2412. if (WorkItemToUpdate == NULL) {
  2413. //
  2414. // No Work Item exists for the Sid's Domain. See if the
  2415. // Sid belongs to one of the Trusted Domains. If not, skip
  2416. // to the next Sid.
  2417. //
  2418. RtlZeroMemory( &TrustInfo, sizeof(TrustInfo) );
  2419. TrustInfo.Name.Length = XForestName.Length;
  2420. TrustInfo.Name.MaximumLength = XForestName.MaximumLength;
  2421. TrustInfo.Name.Buffer = XForestName.Buffer;
  2422. TrustInfo.Sid = NULL;
  2423. TrustInformation = &TrustInfo;
  2424. //
  2425. // Create a new Work Item for this domain.
  2426. //
  2427. Status = LsapDbLookupCreateWorkItem(
  2428. TrustInformation,
  2429. LSA_UNKNOWN_INDEX,
  2430. (ULONG) LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY + (ULONG) 1,
  2431. &NewWorkItem
  2432. );
  2433. if (!NT_SUCCESS(Status)) {
  2434. break;
  2435. }
  2436. //
  2437. // Add the Work Item to the List.
  2438. //
  2439. Status = LsapDbAddWorkItemToWorkList( OutputWorkList, NewWorkItem );
  2440. if (!NT_SUCCESS(Status)) {
  2441. break;
  2442. }
  2443. WorkItemToUpdate = NewWorkItem;
  2444. NewWorkItem->Properties |= LSAP_DB_LOOKUP_WORK_ITEM_XFOREST;
  2445. }
  2446. LsaIFree_LSAPR_UNICODE_STRING_BUFFER( (LSAPR_UNICODE_STRING*)&XForestName);
  2447. XForestName.Buffer = NULL;
  2448. //
  2449. // Add the Sid Index to the Work Item.
  2450. //
  2451. Status = LsapDbLookupAddIndicesToWorkItem(
  2452. WorkItemToUpdate,
  2453. (ULONG) 1,
  2454. &SidIndex
  2455. );
  2456. if (!NT_SUCCESS(Status)) {
  2457. break;
  2458. }
  2459. //
  2460. // Store the Domain Index in the Translated Names array entry for
  2461. // the Sid.
  2462. //
  2463. OutputWorkList->LookupSidsParams.TranslatedNames->Names[SidIndex].DomainIndex = WorkItemToUpdate->DomainIndex;
  2464. }
  2465. if (XForestName.Buffer) {
  2466. LsaIFree_LSAPR_UNICODE_STRING_BUFFER( (LSAPR_UNICODE_STRING*)&XForestName);
  2467. XForestName.Buffer = NULL;
  2468. }
  2469. if (!NT_SUCCESS(Status)) {
  2470. goto LookupSidsBuildWorkListError;
  2471. }
  2472. //
  2473. // If the Work List has no Work Items, this means that none of the
  2474. // Sids belong to any of the Trusted Domains. In this case,
  2475. // we discard the Work List.
  2476. //
  2477. Status = STATUS_NONE_MAPPED;
  2478. if (OutputWorkList->WorkItemCount == 0) {
  2479. goto LookupSidsBuildWorkListError;
  2480. }
  2481. //
  2482. // Compute the Advisory Thread Count for this lookup.
  2483. //
  2484. Status = LsapDbLookupComputeAdvisoryChildThreadCount( OutputWorkList );
  2485. if (!NT_SUCCESS(Status)) {
  2486. goto LookupSidsBuildWorkListError;
  2487. }
  2488. //
  2489. // Insert the Work List at the end of the Work Queue.
  2490. //
  2491. Status = LsapDbLookupInsertWorkList(OutputWorkList);
  2492. if (!NT_SUCCESS(Status)) {
  2493. goto LookupSidsBuildWorkListError;
  2494. }
  2495. //
  2496. // Update the Mapped Counts
  2497. //
  2498. LsapDbUpdateMappedCountsWorkList( OutputWorkList );
  2499. *WorkList = OutputWorkList;
  2500. LookupSidsBuildWorkListFinish:
  2501. return(Status);
  2502. LookupSidsBuildWorkListError:
  2503. if ( OutputWorkList != NULL ) {
  2504. IgnoreStatus = LsapDbLookupDeleteWorkList( OutputWorkList );
  2505. OutputWorkList = NULL;
  2506. }
  2507. *WorkList = NULL;
  2508. goto LookupSidsBuildWorkListFinish;
  2509. }
  2510. NTSTATUS
  2511. LsapDbLookupCreateWorkList(
  2512. OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList
  2513. )
  2514. /*++
  2515. Routine Description:
  2516. This function creates a Lookup Operation Work List and
  2517. initializes fixed default fields.
  2518. Arguments:
  2519. WorkList - Receives Pointer to an empty Work List structure.
  2520. Return Value:
  2521. NTSTATUS - Standard Nt Result Code
  2522. --*/
  2523. {
  2524. NTSTATUS Status = STATUS_SUCCESS;
  2525. //
  2526. // Allocate memory for the Work List header.
  2527. //
  2528. *WorkList = LsapAllocateLsaHeap( sizeof(LSAP_DB_LOOKUP_WORK_LIST) );
  2529. if ( *WorkList == NULL ) {
  2530. Status = STATUS_INSUFFICIENT_RESOURCES;
  2531. } else {
  2532. //
  2533. // Initialize the fixed fields in the Work List.
  2534. //
  2535. Status = LsapDbLookupInitializeWorkList(*WorkList);
  2536. if (!NT_SUCCESS(Status)) {
  2537. LsapFreeLsaHeap( (PVOID)*WorkList );
  2538. *WorkList = NULL;
  2539. }
  2540. }
  2541. return(Status);
  2542. }
  2543. NTSTATUS
  2544. LsapDbLookupInsertWorkList(
  2545. IN PLSAP_DB_LOOKUP_WORK_LIST WorkList
  2546. )
  2547. /*++
  2548. Routine Description:
  2549. This function inserts a Lookup Operation Work List in the Work Queue.
  2550. Arguments:
  2551. WorkList - Pointer to a Work List structure describing a Lookup Sids
  2552. or Lookup Names operation.
  2553. Return Value:
  2554. NTSTATUS - Standard Nt Result Code
  2555. --*/
  2556. {
  2557. NTSTATUS Status = STATUS_SUCCESS;
  2558. BOOLEAN AcquiredWorkQueueLock = FALSE;
  2559. //
  2560. // Acquire the Lookup Work Queue Lock.
  2561. //
  2562. Status = LsapDbLookupAcquireWorkQueueLock();
  2563. if (!NT_SUCCESS(Status)) {
  2564. goto LookupInsertWorkListError;
  2565. }
  2566. AcquiredWorkQueueLock = TRUE;
  2567. //
  2568. // Mark the Work List as Active.
  2569. //
  2570. WorkList->State = ActiveWorkList;
  2571. //
  2572. // Link the Work List onto the end of the Work Queue.
  2573. //
  2574. WorkList->WorkLists.Flink =
  2575. (PLIST_ENTRY) LookupWorkQueue.AnchorWorkList;
  2576. WorkList->WorkLists.Blink =
  2577. (PLIST_ENTRY) LookupWorkQueue.AnchorWorkList->WorkLists.Blink;
  2578. WorkList->WorkLists.Flink->Blink = (PLIST_ENTRY) WorkList;
  2579. WorkList->WorkLists.Blink->Flink = (PLIST_ENTRY) WorkList;
  2580. //
  2581. // Update the Currently Assignable Work List and Work Item pointers
  2582. // if there is none.
  2583. //
  2584. if (LookupWorkQueue.CurrentAssignableWorkList == NULL) {
  2585. LookupWorkQueue.CurrentAssignableWorkList = WorkList;
  2586. LookupWorkQueue.CurrentAssignableWorkItem =
  2587. (PLSAP_DB_LOOKUP_WORK_ITEM) WorkList->AnchorWorkItem->Links.Flink;
  2588. }
  2589. //
  2590. // Diagnostic message indicating work list has been inserted
  2591. //
  2592. LsapDiagPrint( DB_LOOKUP_WORK_LIST,
  2593. ("LSA DB: Inserting WorkList: 0x%lx ( Item Count: %ld)\n", WorkList, WorkList->WorkItemCount) );
  2594. LookupInsertWorkListFinish:
  2595. //
  2596. // If necessary, release the Lookup Work Queue Lock.
  2597. //
  2598. if (AcquiredWorkQueueLock) {
  2599. LsapDbLookupReleaseWorkQueueLock();
  2600. AcquiredWorkQueueLock = FALSE;
  2601. }
  2602. return(Status);
  2603. LookupInsertWorkListError:
  2604. goto LookupInsertWorkListFinish;
  2605. }
  2606. NTSTATUS
  2607. LsapDbLookupDeleteWorkList(
  2608. IN PLSAP_DB_LOOKUP_WORK_LIST WorkList
  2609. )
  2610. /*++
  2611. Routine Description:
  2612. This function Deletes a Lookup Operation Work List from the Work Queue
  2613. and frees the Work List structure.
  2614. Arguments:
  2615. WorkList - Pointer to a Work List structure describing a Lookup Sids
  2616. or Lookup Names operation.
  2617. Return Value:
  2618. NTSTATUS - Standard Nt Result Code
  2619. --*/
  2620. {
  2621. NTSTATUS Status = STATUS_SUCCESS;
  2622. PLSAP_DB_LOOKUP_WORK_ITEM ThisWorkItem = NULL;
  2623. PLSAP_DB_LOOKUP_WORK_ITEM NextWorkItem = NULL;
  2624. BOOLEAN AcquiredWorkQueueLock = FALSE;
  2625. //
  2626. // Acquire the Lookup Work Queue Lock.
  2627. //
  2628. Status = LsapDbLookupAcquireWorkQueueLock();
  2629. if (!NT_SUCCESS(Status)) {
  2630. goto LookupDeleteWorkListError;
  2631. }
  2632. AcquiredWorkQueueLock = TRUE;
  2633. //
  2634. // An internal error exists if we are trying to delete an active Work List.
  2635. // Only inactive or completed Work Lists can be removed.
  2636. //
  2637. ASSERT(WorkList->State != ActiveWorkList);
  2638. //
  2639. // If the Work List is on the Work Queue, remove it.
  2640. //
  2641. if ((WorkList->WorkLists.Blink != NULL) &&
  2642. (WorkList->WorkLists.Flink != NULL)) {
  2643. WorkList->WorkLists.Blink->Flink = WorkList->WorkLists.Flink;
  2644. WorkList->WorkLists.Flink->Blink = WorkList->WorkLists.Blink;
  2645. }
  2646. //
  2647. // Release the Lookup Work Queue Lock.
  2648. //
  2649. LsapDbLookupReleaseWorkQueueLock();
  2650. AcquiredWorkQueueLock = FALSE;
  2651. //
  2652. // Free up memory allocated for the Work Items on the List.
  2653. //
  2654. ThisWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) WorkList->AnchorWorkItem->Links.Blink;
  2655. while (ThisWorkItem != WorkList->AnchorWorkItem) {
  2656. NextWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM) ThisWorkItem->Links.Blink;
  2657. if (ThisWorkItem->Indices != NULL) {
  2658. MIDL_user_free( ThisWorkItem->Indices );
  2659. }
  2660. MIDL_user_free( ThisWorkItem->TrustInformation.Sid );
  2661. MIDL_user_free( ThisWorkItem->TrustInformation.Name.Buffer );
  2662. MIDL_user_free( ThisWorkItem );
  2663. ThisWorkItem = NextWorkItem;
  2664. }
  2665. //
  2666. // Release the handle
  2667. //
  2668. if ( WorkList->LookupCompleteEvent ) {
  2669. NtClose( WorkList->LookupCompleteEvent );
  2670. }
  2671. //
  2672. // Free up memory allocated for the Work List structure itself.
  2673. //
  2674. MIDL_user_free( WorkList );
  2675. LookupDeleteWorkListFinish:
  2676. //
  2677. // If necessary, release the Lookup Work Queue Lock.
  2678. //
  2679. if (AcquiredWorkQueueLock) {
  2680. LsapDbLookupReleaseWorkQueueLock();
  2681. AcquiredWorkQueueLock = FALSE;
  2682. }
  2683. return(Status);
  2684. LookupDeleteWorkListError:
  2685. goto LookupDeleteWorkListFinish;
  2686. }
  2687. VOID
  2688. LsapDbUpdateMappedCountsWorkList(
  2689. IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
  2690. )
  2691. /*++
  2692. Routine Decsription:
  2693. This function updates the counts of completely Mapped and completely
  2694. Unmapped Sids or Names in a Work List. A Sid or Name is completely
  2695. mapped if its Use has been identified, partially mapped if its
  2696. Domain is known but its terminal name of relative id is not yet
  2697. known, completely unmapped if its domain is not yet known.
  2698. Arguments:
  2699. WorkList - Pointer to Work List to be updated.
  2700. Return Values:
  2701. None.
  2702. --*/
  2703. {
  2704. NTSTATUS Status = STATUS_SUCCESS;
  2705. BOOLEAN AcquiredWorkQueueLock = FALSE;
  2706. ULONG OutputMappedCount = (ULONG) 0;
  2707. ULONG OutputCompletelyUnmappedCount = WorkList->Count;
  2708. ULONG Index;
  2709. //
  2710. // Acquire the Lookup Work Queue Lock.
  2711. //
  2712. Status = LsapDbLookupAcquireWorkQueueLock();
  2713. if (!NT_SUCCESS(Status)) {
  2714. goto UpdateMappedCountsWorkListError;
  2715. }
  2716. AcquiredWorkQueueLock = TRUE;
  2717. if (WorkList->LookupType == LookupSids) {
  2718. for ( Index = (ULONG) 0; Index < WorkList->Count; Index++ ) {
  2719. if (WorkList->LookupSidsParams.TranslatedNames->Names[ Index].Use
  2720. != SidTypeUnknown) {
  2721. OutputMappedCount++;
  2722. OutputCompletelyUnmappedCount--;
  2723. } else if (WorkList->LookupSidsParams.TranslatedNames->Names[ Index].DomainIndex
  2724. != LSA_UNKNOWN_INDEX) {
  2725. OutputCompletelyUnmappedCount--;
  2726. }
  2727. }
  2728. } else {
  2729. for ( Index = (ULONG) 0; Index < WorkList->Count; Index++ ) {
  2730. if (WorkList->LookupNamesParams.TranslatedSids->Sids[ Index].Use
  2731. != SidTypeUnknown) {
  2732. OutputMappedCount++;
  2733. OutputCompletelyUnmappedCount--;
  2734. } else if (WorkList->LookupNamesParams.TranslatedSids->Sids[ Index].DomainIndex
  2735. != LSA_UNKNOWN_INDEX) {
  2736. OutputCompletelyUnmappedCount--;
  2737. }
  2738. }
  2739. }
  2740. *WorkList->MappedCount = OutputMappedCount;
  2741. *WorkList->CompletelyUnmappedCount = OutputCompletelyUnmappedCount;
  2742. UpdateMappedCountsWorkListFinish:
  2743. if (AcquiredWorkQueueLock) {
  2744. LsapDbLookupReleaseWorkQueueLock();
  2745. AcquiredWorkQueueLock = FALSE;
  2746. }
  2747. return;
  2748. UpdateMappedCountsWorkListError:
  2749. goto UpdateMappedCountsWorkListFinish;
  2750. }
  2751. NTSTATUS
  2752. LsapDbLookupSignalCompletionWorkList(
  2753. IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
  2754. )
  2755. /*++
  2756. Routine Description:
  2757. This function signals the completion or termination of work on
  2758. a Work List.
  2759. Arguments:
  2760. WorkList - Pointer to a Work List structure describing a Lookup Sids
  2761. or Lookup Names operation.
  2762. Return Value:
  2763. NTSTATUS - Standard Nt Result Code
  2764. --*/
  2765. {
  2766. NTSTATUS Status = STATUS_SUCCESS;
  2767. BOOLEAN AcquiredWorkQueueLock = FALSE;
  2768. //
  2769. // Verify that all work on the Work List is either complete or
  2770. // the Work List has been terminated.
  2771. //
  2772. Status = LsapDbLookupAcquireWorkQueueLock();
  2773. if (!NT_SUCCESS(Status)) {
  2774. goto LookupSignalCompletionWorkListError;
  2775. }
  2776. AcquiredWorkQueueLock = TRUE;
  2777. if (NT_SUCCESS(WorkList->Status)) {
  2778. Status = STATUS_INTERNAL_DB_ERROR;
  2779. if (WorkList->CompletedWorkItemCount != WorkList->WorkItemCount) {
  2780. goto LookupSignalCompletionWorkListError;
  2781. }
  2782. }
  2783. //
  2784. // Signal the event that indicates that a Work List has been processed.
  2785. //
  2786. Status = NtSetEvent( WorkList->LookupCompleteEvent, NULL );
  2787. if (!NT_SUCCESS(Status)) {
  2788. LsapDiagPrint( DB_LOOKUP_WORK_LIST,
  2789. ("LSA DB: LsapDbLookupSignalCompletion.. NtSetEvent failed 0x%lx\n",Status));
  2790. goto LookupSignalCompletionWorkListError;
  2791. }
  2792. LookupSignalCompletionWorkListFinish:
  2793. LsapDiagPrint( DB_LOOKUP_WORK_LIST,
  2794. ("LSA DB: Lookup completion event signalled. (Status: 0x%lx)\n"
  2795. " WorkList: 0x%lx\n", Status, WorkList) );
  2796. //
  2797. // If necessary, release the Lookup Work Queue Lock.
  2798. //
  2799. if (AcquiredWorkQueueLock) {
  2800. LsapDbLookupReleaseWorkQueueLock();
  2801. AcquiredWorkQueueLock = FALSE;
  2802. }
  2803. return(Status);
  2804. LookupSignalCompletionWorkListError:
  2805. goto LookupSignalCompletionWorkListFinish;
  2806. }
  2807. NTSTATUS
  2808. LsapDbLookupAwaitCompletionWorkList(
  2809. IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
  2810. )
  2811. /*++
  2812. Routine Description:
  2813. This function awaits the completion or termination of work on a
  2814. specified Work List.
  2815. NOTE: This routine expects the specified pointer to a Work List to be
  2816. valid. A Work List pointer always remains valid until its
  2817. Primary thread detects completion of the Work List via this
  2818. routine and then deletes it via LsapDbLookupDeleteWorkList().
  2819. For this reason, the Lookup Work Queue lock does not have to
  2820. be held while this routine executes.
  2821. Arguments:
  2822. WorkList - Pointer to a Work List structure describing a Lookup Sids
  2823. or Lookup Names operation.
  2824. Return Value:
  2825. NTSTATUS - Standard Nt Result Code
  2826. --*/
  2827. {
  2828. NTSTATUS Status = STATUS_SUCCESS;
  2829. LSAP_DB_LOOKUP_WORK_LIST_STATE WorkListState;
  2830. BOOLEAN AcquiredWorkQueueLock = FALSE;
  2831. //
  2832. // Loop, waiting for completion events to occur. When one does,
  2833. // check the status of the specified Work List.
  2834. //
  2835. for (;;) {
  2836. //
  2837. // Check for completed Work List. Since someone else may be
  2838. // setting the state, we need to read it while holding the lock.
  2839. //
  2840. Status = LsapDbLookupAcquireWorkQueueLock();
  2841. if (!NT_SUCCESS(Status)) {
  2842. break;
  2843. }
  2844. AcquiredWorkQueueLock = TRUE;
  2845. WorkListState = WorkList->State;
  2846. LsapDbLookupReleaseWorkQueueLock();
  2847. AcquiredWorkQueueLock = FALSE;
  2848. if (WorkListState == CompletedWorkList) {
  2849. break;
  2850. }
  2851. //
  2852. // Wait for Work List completed event to be signalled.
  2853. //
  2854. LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("Lsa Db: Waiting on worklist completion event\n") );
  2855. Status = NtWaitForSingleObject( WorkList->LookupCompleteEvent, TRUE, NULL);
  2856. LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LsapDb: Wait on worklist completion event Done\n Status: 0x%lx\n", Status) );
  2857. if (!NT_SUCCESS(Status)) {
  2858. break;
  2859. }
  2860. }
  2861. if (!NT_SUCCESS(Status)) {
  2862. goto LookupAwaitCompletionWorkListError;
  2863. }
  2864. LookupAwaitCompletionWorkListFinish:
  2865. //
  2866. // If necessary, release the Lookup Work Queue Lock.
  2867. //
  2868. if (AcquiredWorkQueueLock) {
  2869. LsapDbLookupReleaseWorkQueueLock();
  2870. AcquiredWorkQueueLock = FALSE;
  2871. }
  2872. return(Status);
  2873. LookupAwaitCompletionWorkListError:
  2874. LsapDiagPrint( DB_LOOKUP_WORK_LIST,
  2875. ("Lsa Db: LookupAwaitWorklist error. (Status: 0x%lx)\n", Status) );
  2876. goto LookupAwaitCompletionWorkListFinish;
  2877. }
  2878. NTSTATUS
  2879. LsapDbAddWorkItemToWorkList(
  2880. IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
  2881. IN PLSAP_DB_LOOKUP_WORK_ITEM WorkItem
  2882. )
  2883. /*++
  2884. Routine Decsription:
  2885. This function adds a Work Item to a Work List. The specified
  2886. Work Item must exist in non-volatile memory (e.g a heap block).
  2887. Arguments:
  2888. WorkList - Pointer to a Work List structure describing a Lookup Sids
  2889. or Lookup Names operation.
  2890. WorkItem - Pointer to a Work Item structure describing a list of
  2891. Sids or Names and a domain in which they are to be looked up.
  2892. Return Value:
  2893. NTSTATUS - Standard Nt Result Code
  2894. --*/
  2895. {
  2896. NTSTATUS Status = STATUS_SUCCESS;
  2897. BOOLEAN AcquiredWorkQueueLock = FALSE;
  2898. //
  2899. // Acquire the Lookup Work Queue Lock.
  2900. //
  2901. Status = LsapDbLookupAcquireWorkQueueLock();
  2902. if (!NT_SUCCESS(Status)) {
  2903. goto LookupAddWorkItemToWorkListError;
  2904. }
  2905. AcquiredWorkQueueLock = TRUE;
  2906. //
  2907. // Mark the Work Item as assignable.
  2908. //
  2909. WorkItem->State = AssignableWorkItem;
  2910. //
  2911. // Link the Work Item onto the end of the Work List and increment the
  2912. // Work Item Count.
  2913. //
  2914. WorkItem->Links.Flink = (PLIST_ENTRY) WorkList->AnchorWorkItem;
  2915. WorkItem->Links.Blink = (PLIST_ENTRY) WorkList->AnchorWorkItem->Links.Blink;
  2916. WorkItem->Links.Flink->Blink = (PLIST_ENTRY) WorkItem;
  2917. WorkItem->Links.Blink->Flink = (PLIST_ENTRY) WorkItem;
  2918. WorkList->WorkItemCount++;
  2919. LookupAddWorkItemToWorkListFinish:
  2920. //
  2921. // If necessary, release the Lookup Work Queue Lock.
  2922. //
  2923. if (AcquiredWorkQueueLock) {
  2924. LsapDbLookupReleaseWorkQueueLock();
  2925. AcquiredWorkQueueLock = FALSE;
  2926. }
  2927. return(Status);
  2928. LookupAddWorkItemToWorkListError:
  2929. goto LookupAddWorkItemToWorkListFinish;
  2930. }
  2931. VOID
  2932. LsapDbLookupWorkerThreadStart(
  2933. )
  2934. /*++
  2935. Routine Description:
  2936. This routine initiates a child Worker Thread for a Lookup operation.
  2937. Arguments:
  2938. None.
  2939. Return Value:
  2940. None.
  2941. --*/
  2942. {
  2943. //
  2944. // Start the thread's work processing loop, specifying that this
  2945. // thread is a child thread rather than the main thread.
  2946. //
  2947. LsapDbLookupWorkerThread( FALSE );
  2948. }
  2949. VOID
  2950. LsapDbLookupWorkerThread(
  2951. IN BOOLEAN PrimaryThread
  2952. )
  2953. /*++
  2954. Routine Description:
  2955. This function is executed by each worker thread for a Lookup operation.
  2956. Each worker thread loops, requesting work items from the Lookup
  2957. Work Queue. Work Items assigned may belong to any current lookup.
  2958. Arguments:
  2959. PrimaryThread - TRUE if thread is the main thread of the Lookup
  2960. operation, FALSE if the thread is a child thread created by
  2961. the Lookup operation. The main thread of the Lookup operation
  2962. also processes work items, but is also responsible for collating
  2963. the results of the Lookup operation. It is not counted in the
  2964. active thread count and is not returnable to the thread pool.
  2965. Return Value:
  2966. None.
  2967. --*/
  2968. {
  2969. NTSTATUS Status = STATUS_SUCCESS;
  2970. PLSAP_DB_LOOKUP_WORK_LIST WorkList = NULL;
  2971. PLSAP_DB_LOOKUP_WORK_ITEM WorkItem = NULL;
  2972. BOOLEAN AcquiredWorkQueueLock = FALSE;
  2973. //
  2974. // If this thread is a child worker thread, increment count of active
  2975. // child threads.
  2976. //
  2977. if (!PrimaryThread) {
  2978. Status = LsapDbLookupAcquireWorkQueueLock();
  2979. if (!NT_SUCCESS(Status)) {
  2980. goto LookupWorkerThreadError;
  2981. }
  2982. AcquiredWorkQueueLock = TRUE;
  2983. LookupWorkQueue.ActiveChildThreadCount++;
  2984. LsapDbLookupReleaseWorkQueueLock();
  2985. AcquiredWorkQueueLock = FALSE;
  2986. }
  2987. //
  2988. // Loop while there is work to do.
  2989. //
  2990. for (;;) {
  2991. //
  2992. // Obtain work packet
  2993. //
  2994. Status = LsapDbLookupObtainWorkItem(&WorkList, &WorkItem);
  2995. if (NT_SUCCESS(Status)) {
  2996. Status = LsapDbLookupProcessWorkItem(WorkList, WorkItem);
  2997. if (NT_SUCCESS(Status)) {
  2998. continue;
  2999. }
  3000. //
  3001. // An error has occurred. Stop this lookup.
  3002. //
  3003. Status = LsapDbLookupStopProcessingWorkList(WorkList, Status);
  3004. //
  3005. // NOTE: Intentionally ignore the status.
  3006. //
  3007. Status = STATUS_SUCCESS;
  3008. }
  3009. //
  3010. // If an error occurred other than there being no more work to do,
  3011. // quit.
  3012. //
  3013. if (Status != STATUS_NO_MORE_ENTRIES) {
  3014. break;
  3015. }
  3016. Status = STATUS_SUCCESS;
  3017. //
  3018. // There is no more work to do. If this thread is a child worker
  3019. // thread, either return thread to pool and wait for more work, or
  3020. // terminate if enough threads have already been retained. If this
  3021. // thread is the main thread of a Lookup operation, just return
  3022. // in order to collate results.
  3023. //
  3024. if (!PrimaryThread) {
  3025. Status = LsapDbLookupAcquireWorkQueueLock();
  3026. if (!NT_SUCCESS(Status)) {
  3027. break;
  3028. }
  3029. AcquiredWorkQueueLock = TRUE;
  3030. if (LookupWorkQueue.ActiveChildThreadCount <= LookupWorkQueue.MaximumRetainedChildThreadCount) {
  3031. LsapDbLookupReleaseWorkQueueLock();
  3032. AcquiredWorkQueueLock = FALSE;
  3033. //
  3034. // Wait forever for more work.
  3035. //
  3036. Status = NtWaitForSingleObject( LsapDbLookupStartedEvent, TRUE, NULL);
  3037. if (NT_SUCCESS(Status)) {
  3038. continue;
  3039. }
  3040. //
  3041. // An error occurred in the wait routine. Exit the thread.
  3042. //
  3043. Status = LsapDbLookupAcquireWorkQueueLock();
  3044. if (!NT_SUCCESS(Status)) {
  3045. break;
  3046. }
  3047. AcquiredWorkQueueLock = TRUE;
  3048. }
  3049. //
  3050. // We already have enough active threads or an error has occurred.
  3051. // Mark this one inactive and terminate it.
  3052. //
  3053. LookupWorkQueue.ActiveChildThreadCount--;
  3054. LsapDbLookupReleaseWorkQueueLock();
  3055. AcquiredWorkQueueLock = FALSE;
  3056. //
  3057. // Terminate the thread.
  3058. //
  3059. ExitThread((DWORD) Status);
  3060. }
  3061. //
  3062. // We're the Primary Thread of some Lookup operation and there is
  3063. // no more work to do. Break out so we can return to caller.
  3064. //
  3065. break;
  3066. }
  3067. LookupWorkerThreadFinish:
  3068. //
  3069. // If necessary, release the Lookup Work Queue Lock.
  3070. //
  3071. if (AcquiredWorkQueueLock) {
  3072. LsapDbLookupReleaseWorkQueueLock();
  3073. AcquiredWorkQueueLock = FALSE;
  3074. }
  3075. return;
  3076. LookupWorkerThreadError:
  3077. goto LookupWorkerThreadFinish;
  3078. }
  3079. NTSTATUS
  3080. LsapDbLookupObtainWorkItem(
  3081. OUT PLSAP_DB_LOOKUP_WORK_LIST *WorkList,
  3082. OUT PLSAP_DB_LOOKUP_WORK_ITEM *WorkItem
  3083. )
  3084. /*++
  3085. Routine Description:
  3086. This function is called by a worker thread to obtain a Work Item. This
  3087. Work Item may belong to any current lookup operation.
  3088. Arguments:
  3089. WorkList - Receives a pointer to a Work List structure describing a
  3090. Lookup Sids or Lookup Names operation.
  3091. WorkItem - Receives a pointer to a Work Item structure describing a
  3092. list of Sids or Names and a domain in which they are to be looked up.
  3093. Return Value:
  3094. NTSTATUS - Standard Nt Result Code
  3095. STATUS_NO_MORE_ENTRIES - No more work items available.
  3096. --*/
  3097. {
  3098. NTSTATUS Status;
  3099. BOOLEAN AcquiredWorkQueueLock = FALSE;
  3100. *WorkList = NULL;
  3101. *WorkItem = NULL;
  3102. //
  3103. // Acquire the Lookup Work Queue Lock.
  3104. //
  3105. Status = LsapDbLookupAcquireWorkQueueLock();
  3106. if (!NT_SUCCESS(Status)) {
  3107. goto LookupObtainWorkItemError;
  3108. }
  3109. AcquiredWorkQueueLock = TRUE;
  3110. //
  3111. // Return an error if there are no more Work Items.
  3112. //
  3113. Status = STATUS_NO_MORE_ENTRIES;
  3114. if (LookupWorkQueue.CurrentAssignableWorkList == NULL) {
  3115. goto LookupObtainWorkItemError;
  3116. }
  3117. //
  3118. // Verify that the Current Assignable Work List does not have
  3119. // a termination error. This should never happen, because the
  3120. // pointers should be updated if the Lookup corresponding to the Current
  3121. // Assignable Work List is terminated.
  3122. //
  3123. ASSERT(NT_SUCCESS(LookupWorkQueue.CurrentAssignableWorkList->Status));
  3124. //
  3125. // There are work items available. Check the next one out.
  3126. //
  3127. ASSERT(LookupWorkQueue.CurrentAssignableWorkItem->State == AssignableWorkItem);
  3128. LookupWorkQueue.CurrentAssignableWorkItem->State = AssignedWorkItem;
  3129. *WorkList = LookupWorkQueue.CurrentAssignableWorkList;
  3130. *WorkItem = LookupWorkQueue.CurrentAssignableWorkItem;
  3131. //
  3132. // Update pointers to next item (if any) in the current Work List
  3133. // where work is being given out.
  3134. //
  3135. Status = LsapDbLookupUpdateAssignableWorkItem(FALSE);
  3136. if (!NT_SUCCESS(Status)) {
  3137. goto LookupObtainWorkItemError;
  3138. }
  3139. LookupObtainWorkItemFinish:
  3140. //
  3141. // If we acquired the Lookup Work Queue Lock, release it.
  3142. //
  3143. if (AcquiredWorkQueueLock) {
  3144. LsapDbLookupReleaseWorkQueueLock();
  3145. AcquiredWorkQueueLock = FALSE;
  3146. }
  3147. return(Status);
  3148. LookupObtainWorkItemError:
  3149. goto LookupObtainWorkItemFinish;
  3150. }
  3151. NTSTATUS
  3152. LsapDbLookupProcessWorkItem(
  3153. IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
  3154. IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem
  3155. )
  3156. /*++
  3157. Routine Description:
  3158. This function processes a Work Item for a Lookup operation. The Work
  3159. Item specifies a number of Sids or Names to be looked up in a given
  3160. domain.
  3161. Arguments:
  3162. WorkList - Pointer to a Work List structure describing a
  3163. Lookup Sids or Lookup Names operation.
  3164. WorkItem - Pointer to a Work Item structure describing a
  3165. list of Sids or Names and a domain in which they are to be looked up.
  3166. Return Value:
  3167. NTSTATUS - Standard Nt Result Code
  3168. --*/
  3169. {
  3170. NTSTATUS Status = STATUS_SUCCESS;
  3171. NTSTATUS SecondaryStatus = STATUS_SUCCESS;
  3172. ULONG NextLevelCount;
  3173. ULONG NextLevelMappedCount;
  3174. PLSAP_BINDING_CACHE_ENTRY ControllerPolicyEntry = NULL;
  3175. ULONG NextLevelIndex;
  3176. PLSAPR_REFERENCED_DOMAIN_LIST NextLevelReferencedDomains = NULL;
  3177. PSID *NextLevelSids = NULL;
  3178. PUNICODE_STRING NextLevelNames = NULL;
  3179. PLSAPR_REFERENCED_DOMAIN_LIST OutputReferencedDomains = NULL;
  3180. PLSAPR_TRANSLATED_SID_EX2 NextLevelTranslatedSids = NULL;
  3181. PLSA_TRANSLATED_NAME_EX NextLevelTranslatedNames = NULL;
  3182. ULONG Index;
  3183. LPWSTR ServerPrincipalName = NULL;
  3184. LPWSTR ServerName = NULL;
  3185. PVOID ClientContext = NULL;
  3186. ULONG AuthnLevel = 0;
  3187. NL_OS_VERSION ServerOsVersion;
  3188. LSAP_LOOKUP_LEVEL LookupLevel = WorkList->LookupLevel;
  3189. PUNICODE_STRING TargetDomainName = NULL;
  3190. LSAPR_TRUST_INFORMATION_EX TrustInfoEx;
  3191. BOOLEAN *NextLevelNamesMorphed = NULL;
  3192. TargetDomainName = (PUNICODE_STRING) &WorkItem->TrustInformation.Name;
  3193. RtlZeroMemory(&TrustInfoEx, sizeof(TrustInfoEx));
  3194. TrustInfoEx.FlatName = WorkItem->TrustInformation.Name;
  3195. TrustInfoEx.Sid = WorkItem->TrustInformation.Sid;
  3196. LsapDiagPrint( DB_LOOKUP_WORK_LIST,
  3197. ("LSA DB: Processing work item. (0x%lx, 0x%lx)\n", WorkList, WorkItem));
  3198. ASSERT( (WorkList->LookupLevel == LsapLookupTDL)
  3199. || (WorkList->LookupLevel == LsapLookupXForestResolve) );
  3200. //
  3201. // Branch according to lookup type.
  3202. //
  3203. NextLevelCount = WorkItem->UsedCount;
  3204. if (WorkList->LookupType == LookupSids) {
  3205. //
  3206. // Allocate an array for the Sids to be looked up at a Domain
  3207. // Controller for the specified Trusted Domain.
  3208. //
  3209. NextLevelSids = MIDL_user_allocate( sizeof(PSID) * NextLevelCount );
  3210. if (NextLevelSids == NULL) {
  3211. Status = STATUS_INSUFFICIENT_RESOURCES;
  3212. goto LookupProcessWorkItemError;
  3213. }
  3214. //
  3215. // Copy in the Sids to be looked up from the Work List. The Work
  3216. // Item contains their indices relative to the Sid array in the
  3217. // Work List.
  3218. //
  3219. for (NextLevelIndex = 0;
  3220. NextLevelIndex < NextLevelCount;
  3221. NextLevelIndex++) {
  3222. Index = WorkItem->Indices[ NextLevelIndex ];
  3223. NextLevelSids[NextLevelIndex] = WorkList->LookupSidsParams.Sids[Index];
  3224. }
  3225. NextLevelMappedCount = (ULONG) 0;
  3226. //
  3227. // Lookup the Sids at the DC.
  3228. //
  3229. Status = LsaDbLookupSidChainRequest(&TrustInfoEx,
  3230. NextLevelCount,
  3231. NextLevelSids,
  3232. (PLSA_REFERENCED_DOMAIN_LIST *) &NextLevelReferencedDomains,
  3233. &NextLevelTranslatedNames,
  3234. WorkList->LookupLevel,
  3235. &NextLevelMappedCount,
  3236. NULL
  3237. );
  3238. LsapDiagPrint( DB_LOOKUP_WORK_LIST,
  3239. ("LSA DB: Sid Lookup.\n"
  3240. " Item: (0x%lx, 0x%lx)\n"
  3241. " Count: 0x%lx\n", WorkList, WorkItem, NextLevelCount));
  3242. //
  3243. // If the callout to LsaLookupSids() was unsuccessful, disregard
  3244. // the error and set the domain name for any Sids having this
  3245. // domain Sid as prefix sid.
  3246. //
  3247. if (!NT_SUCCESS(Status) && Status != STATUS_NONE_MAPPED) {
  3248. SecondaryStatus = Status;
  3249. Status = STATUS_SUCCESS;
  3250. goto LookupProcessWorkItemFinish;
  3251. }
  3252. //
  3253. // The callout to LsaICLookupSids() was successful. Update the
  3254. // TranslatedNames information in the Work List as appropriate
  3255. // using the TranslatedNames information returned from the callout.
  3256. //
  3257. Status = LsapDbLookupSidsUpdateTranslatedNames(
  3258. WorkList,
  3259. WorkItem,
  3260. NextLevelTranslatedNames,
  3261. NextLevelReferencedDomains
  3262. );
  3263. if (!NT_SUCCESS(Status)) {
  3264. goto LookupProcessWorkItemError;
  3265. }
  3266. } else if (WorkList->LookupType == LookupNames) {
  3267. //
  3268. // Allocate an array of UNICODE_STRING structures for the
  3269. // names to be looked up at the Domain Controller.
  3270. //
  3271. NextLevelNames = MIDL_user_allocate(sizeof(UNICODE_STRING) * NextLevelCount);
  3272. if (NextLevelNames == NULL) {
  3273. Status = STATUS_INSUFFICIENT_RESOURCES;
  3274. goto LookupProcessWorkItemError;
  3275. }
  3276. //
  3277. // Allocate space to remember which names are morphed in place
  3278. // from a UPN to SamAccountName format.
  3279. //
  3280. NextLevelNamesMorphed = MIDL_user_allocate(sizeof(BOOLEAN) * NextLevelCount);
  3281. if (NextLevelNamesMorphed == NULL) {
  3282. Status = STATUS_INSUFFICIENT_RESOURCES;
  3283. goto LookupProcessWorkItemError;
  3284. }
  3285. //
  3286. // Copy in the Names to be looked up from the Work List. The Work
  3287. // Item contains their indices relative to the Names array in the
  3288. // Work List.
  3289. //
  3290. for (NextLevelIndex = 0;
  3291. NextLevelIndex < NextLevelCount;
  3292. NextLevelIndex++) {
  3293. Index = WorkItem->Indices[ NextLevelIndex ];
  3294. NextLevelNames[NextLevelIndex] =
  3295. *((PUNICODE_STRING) &WorkList->LookupNamesParams.Names[Index]);
  3296. if ( (WorkList->LookupLevel == LsapLookupTDL)
  3297. && LsapLookupIsUPN(&NextLevelNames[NextLevelIndex])) {
  3298. //
  3299. // We are performing a TDL level lookup. The server side
  3300. // of the TDL lookup's don't know how to translate UPN's
  3301. // so morph username@domainname to domainname\username.
  3302. // Remember which names we morph so that we can morph
  3303. // them back.
  3304. //
  3305. // N.B. A name would only be here if format of the UPN
  3306. // was username@domainname, where domainname is the name
  3307. // of the trusted domain.
  3308. //
  3309. LsapLookupUPNToSamAccountName(&NextLevelNames[NextLevelIndex]);
  3310. NextLevelNamesMorphed[NextLevelIndex] = TRUE;
  3311. } else {
  3312. NextLevelNamesMorphed[NextLevelIndex] = FALSE;
  3313. }
  3314. }
  3315. NextLevelMappedCount = (ULONG) 0;
  3316. //
  3317. // Lookup the Names at the DC.
  3318. //
  3319. Status = LsapDbLookupNameChainRequest(&TrustInfoEx,
  3320. NextLevelCount,
  3321. NextLevelNames,
  3322. (PLSA_REFERENCED_DOMAIN_LIST *)&NextLevelReferencedDomains,
  3323. (PLSA_TRANSLATED_SID_EX2 * )&NextLevelTranslatedSids,
  3324. WorkList->LookupLevel,
  3325. &NextLevelMappedCount,
  3326. NULL
  3327. );
  3328. //
  3329. // Upmorph any names
  3330. //
  3331. for (NextLevelIndex = 0; NextLevelIndex < NextLevelCount; NextLevelIndex++) {
  3332. if (NextLevelNamesMorphed[NextLevelIndex]) {
  3333. //
  3334. // Morph name back to original state
  3335. //
  3336. LsapLookupSamAccountNameToUPN(&NextLevelNames[NextLevelIndex]);
  3337. }
  3338. }
  3339. //
  3340. // If the callout to LsaLookupNames() was unsuccessful, disregard
  3341. // the error and set the domain name for any Sids having this
  3342. // domain Sid as prefix sid.
  3343. //
  3344. if (!NT_SUCCESS(Status) && Status != STATUS_NONE_MAPPED) {
  3345. SecondaryStatus = Status;
  3346. Status = STATUS_SUCCESS;
  3347. goto LookupProcessWorkItemError;
  3348. }
  3349. //
  3350. // The callout to LsaICLookupNames() was successful. Update the
  3351. // TranslatedSids information in the Work List as appropriate
  3352. // using the TranslatedSids information returned from the callout.
  3353. //
  3354. Status = LsapDbLookupNamesUpdateTranslatedSids(
  3355. WorkList,
  3356. WorkItem,
  3357. NextLevelTranslatedSids,
  3358. NextLevelReferencedDomains
  3359. );
  3360. if (!NT_SUCCESS(Status)) {
  3361. goto LookupProcessWorkItemError;
  3362. }
  3363. } else {
  3364. Status = STATUS_INVALID_PARAMETER;
  3365. goto LookupProcessWorkItemError;
  3366. }
  3367. LookupProcessWorkItemFinish:
  3368. //
  3369. // If we are unable to connect to the Trusted Domain via any DC,
  3370. // suppress the error so that the Lookup can continue to try and
  3371. // translate other Sids/Names.
  3372. //
  3373. // But record what the error was in case no sids are translated
  3374. if (!NT_SUCCESS(SecondaryStatus)) {
  3375. NTSTATUS st;
  3376. st = LsapDbLookupAcquireWorkQueueLock();
  3377. ASSERT( NT_SUCCESS( st ) );
  3378. if ( NT_SUCCESS( st ) ) {
  3379. if ( NT_SUCCESS(WorkList->NonFatalStatus) ) {
  3380. //
  3381. // Treat any error to open the open domain
  3382. // as a trust problem
  3383. //
  3384. WorkList->NonFatalStatus = STATUS_TRUSTED_DOMAIN_FAILURE;
  3385. }
  3386. LsapDbLookupReleaseWorkQueueLock();
  3387. }
  3388. }
  3389. //
  3390. // Change the state of the work item to "Completed"
  3391. //
  3392. WorkItem->State = CompletedWorkItem;
  3393. //
  3394. // Update the Mapped Counts
  3395. //
  3396. LsapDbUpdateMappedCountsWorkList( WorkList );
  3397. //
  3398. // Protect WorkList operations
  3399. //
  3400. Status = LsapDbLookupAcquireWorkQueueLock();
  3401. if (!NT_SUCCESS(Status)) {
  3402. goto LookupProcessWorkItemError;
  3403. }
  3404. //
  3405. // Increment the count of completed Work Items whether or not this
  3406. // one was completed without error. If the Work List has just been
  3407. // completed, change its state to "CompletedWorkList" and signal
  3408. // the Lookup operation completed event. Allow re-entry into
  3409. // this section if an error is returned.
  3410. //
  3411. WorkList->CompletedWorkItemCount++;
  3412. LsapDiagPrint( DB_LOOKUP_WORK_LIST,
  3413. ("LSA DB: Process Work Item Completed.\n"
  3414. " Item: (0x%lx, 0x%lx)\n"
  3415. " Completed Count: %ld\n", WorkList, WorkItem, WorkList->CompletedWorkItemCount));
  3416. if (WorkList->State != CompletedWorkList) {
  3417. if (WorkList->CompletedWorkItemCount == WorkList->WorkItemCount) {
  3418. WorkList->State = CompletedWorkList;
  3419. SecondaryStatus = LsapDbLookupSignalCompletionWorkList( WorkList );
  3420. }
  3421. }
  3422. //
  3423. // Done making work list changes
  3424. //
  3425. LsapDbLookupReleaseWorkQueueLock();
  3426. //
  3427. // If necessary, free the array of Sids looked up at the next level.
  3428. //
  3429. if (NextLevelSids != NULL) {
  3430. MIDL_user_free( NextLevelSids );
  3431. NextLevelSids = NULL;
  3432. }
  3433. //
  3434. // If necessary, free the array of Names looked up at the next level.
  3435. //
  3436. if (NextLevelNames != NULL) {
  3437. MIDL_user_free( NextLevelNames );
  3438. NextLevelNames = NULL;
  3439. }
  3440. if (NextLevelReferencedDomains != NULL) {
  3441. MIDL_user_free( NextLevelReferencedDomains );
  3442. NextLevelReferencedDomains = NULL;
  3443. }
  3444. if (NextLevelTranslatedNames != NULL) {
  3445. MIDL_user_free( NextLevelTranslatedNames );
  3446. NextLevelTranslatedNames = NULL;
  3447. }
  3448. if (NextLevelTranslatedSids != NULL) {
  3449. MIDL_user_free( NextLevelTranslatedSids );
  3450. NextLevelTranslatedSids = NULL;
  3451. }
  3452. if (NextLevelNamesMorphed != NULL) {
  3453. MIDL_user_free( NextLevelNamesMorphed );
  3454. }
  3455. return(Status);
  3456. LookupProcessWorkItemError:
  3457. goto LookupProcessWorkItemFinish;
  3458. }
  3459. NTSTATUS
  3460. LsapDbLookupSidsUpdateTranslatedNames(
  3461. IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
  3462. IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem,
  3463. IN PLSA_TRANSLATED_NAME_EX TranslatedNames,
  3464. IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains
  3465. )
  3466. /*++
  3467. Routine Description:
  3468. This function is called during the processing of a Work Item to update
  3469. the output TranslatedNames and ReferencedDomains parameters of a
  3470. Lookup operation's Work List with the results of a callout to
  3471. LsaICLookupNames. Zero or more Sids may have been translated. Note
  3472. that, unlike the translation of Names, Sid translation occurs within
  3473. a single Work Item only.
  3474. Arguments:
  3475. WorkList - Pointer to a Work List
  3476. WorkItem - Pointer to a Work Item.
  3477. TranslatedNames - Translated Sids information returned from
  3478. LsaICLookupSids().
  3479. Return Values:
  3480. NTSTATUS - Standard Nt Result Code
  3481. --*/
  3482. {
  3483. NTSTATUS Status = STATUS_SUCCESS;
  3484. ULONG Index, WorkItemIndex;
  3485. BOOLEAN AcquiredWorkQueueLock = FALSE;
  3486. PLSAPR_TRANSLATED_NAME_EX WorkListTranslatedNames =
  3487. WorkList->LookupSidsParams.TranslatedNames->Names;
  3488. //
  3489. // Acquire the Work Queue Lock.
  3490. //
  3491. Status = LsapDbLookupAcquireWorkQueueLock();
  3492. if (!NT_SUCCESS(Status)) {
  3493. goto LookupSidsUpdateTranslatedNamesError;
  3494. }
  3495. AcquiredWorkQueueLock = TRUE;
  3496. for( WorkItemIndex = 0;
  3497. WorkItemIndex < WorkItem->UsedCount;
  3498. WorkItemIndex++) {
  3499. //
  3500. // If this Sid has been fully translated, copy information to output.
  3501. // Note that the Sid is partially translated during the building
  3502. // phase where its Domain is identified.
  3503. //
  3504. if (TranslatedNames[WorkItemIndex].Use != SidTypeUnknown) {
  3505. ULONG LocalDomainIndex;
  3506. Index = WorkItem->Indices[WorkItemIndex];
  3507. if (TranslatedNames[WorkItemIndex].DomainIndex != LSA_UNKNOWN_INDEX) {
  3508. //
  3509. // Make sure this is in the referenced domains list
  3510. //
  3511. Status = LsapDbLookupAddListReferencedDomains(
  3512. WorkList->ReferencedDomains,
  3513. &ReferencedDomains->Domains[TranslatedNames[WorkItemIndex].DomainIndex],
  3514. (PLONG) &LocalDomainIndex
  3515. );
  3516. if (!NT_SUCCESS(Status)) {
  3517. break;
  3518. }
  3519. } else {
  3520. LocalDomainIndex = TranslatedNames[WorkItemIndex].DomainIndex;
  3521. }
  3522. WorkListTranslatedNames[Index].Use
  3523. = TranslatedNames[WorkItemIndex].Use;
  3524. WorkListTranslatedNames[Index].DomainIndex = LocalDomainIndex;
  3525. Status = LsapRpcCopyUnicodeString(
  3526. NULL,
  3527. (PUNICODE_STRING) &WorkListTranslatedNames[Index].Name,
  3528. (PUNICODE_STRING) &TranslatedNames[WorkItemIndex].Name
  3529. );
  3530. if (!NT_SUCCESS(Status)) {
  3531. break;
  3532. }
  3533. }
  3534. }
  3535. if (!NT_SUCCESS(Status)) {
  3536. goto LookupSidsUpdateTranslatedNamesError;
  3537. }
  3538. LookupSidsUpdateTranslatedNamesFinish:
  3539. //
  3540. // If necessary, release the Lookup Work Queue Lock.
  3541. //
  3542. if (AcquiredWorkQueueLock) {
  3543. LsapDbLookupReleaseWorkQueueLock();
  3544. AcquiredWorkQueueLock = FALSE;
  3545. }
  3546. return(Status);
  3547. LookupSidsUpdateTranslatedNamesError:
  3548. goto LookupSidsUpdateTranslatedNamesFinish;
  3549. }
  3550. NTSTATUS
  3551. LsapDbLookupNamesUpdateTranslatedSids(
  3552. IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList,
  3553. IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem,
  3554. IN PLSAPR_TRANSLATED_SID_EX2 TranslatedSids,
  3555. IN PLSAPR_REFERENCED_DOMAIN_LIST ReferencedDomains
  3556. )
  3557. /*++
  3558. Routine Description:
  3559. This function is called during the processing of a Work Item to update
  3560. the output TranslatedSids and ReferencedDomains parameters of a
  3561. Lookup operation's Work List with the results of a callout to
  3562. LsaICLookupNames. Zero or more Names may have been translated, and
  3563. there is the additional complication of multiple translations of
  3564. Isolated Names as a result of their presence in more than one
  3565. Work Item. The following rules apply:
  3566. If the Name is a Qualified Name, it only belongs to the specified
  3567. Work Item, so it suffices to check that it has been mapped to a Sid.
  3568. If the Name is an Isolated Name, it belongs to all other Work Items,
  3569. so it may already have been translated during the processing of some
  3570. other Work Item. If the Name has previously been translated, the prior
  3571. translation stands and the present translation is discarded. If the
  3572. Name has not previously been translated, the Domain for this Work Item
  3573. is added to the Referenced Domain List and the newly obtained translation
  3574. is stored in the output TranslatedSids array in the Work List.
  3575. Arguments:
  3576. WorkList - Pointer to a Work List
  3577. WorkItem - Pointer to a Work Item. The DomainIndex field will be
  3578. updated if the Domain specified by this Work Item is added to
  3579. the Referenced Domain List by this routine.
  3580. TranslatedSids - Translated Sids information returned from
  3581. LsaICLookupNames().
  3582. Return Values:
  3583. NTSTATUS - Standard Nt Result Code
  3584. --*/
  3585. {
  3586. NTSTATUS Status = STATUS_SUCCESS;
  3587. ULONG WorkItemIndex;
  3588. ULONG LocalDomainIndex;
  3589. ULONG Index;
  3590. PLSAPR_TRANSLATED_SID_EX2 WorkListTranslatedSids =
  3591. WorkList->LookupNamesParams.TranslatedSids->Sids;
  3592. BOOLEAN AcquiredWorkQueueLock = FALSE;
  3593. BOOLEAN AcquiredTrustedDomainLock = FALSE;
  3594. //
  3595. // Acquire the trusted domain list lock (so that LsapSidOnFtInfo can be called)
  3596. //
  3597. Status = LsapDbAcquireReadLockTrustedDomainList();
  3598. if (!NT_SUCCESS(Status)) {
  3599. goto LookupNamesUpdateTranslatedSidsError;
  3600. }
  3601. AcquiredTrustedDomainLock = TRUE;
  3602. //
  3603. // Acquire the Work Queue Lock.
  3604. //
  3605. Status = LsapDbLookupAcquireWorkQueueLock();
  3606. if (!NT_SUCCESS(Status)) {
  3607. goto LookupNamesUpdateTranslatedSidsError;
  3608. }
  3609. AcquiredWorkQueueLock = TRUE;
  3610. for( WorkItemIndex = 0;
  3611. WorkItemIndex < WorkItem->UsedCount;
  3612. WorkItemIndex++) {
  3613. //
  3614. // If this Name has not been translated at all during processing of
  3615. // this Work Item, skip to the next.
  3616. //
  3617. if (LsapDbCompletelyUnmappedSid(&TranslatedSids[WorkItemIndex])) {
  3618. continue;
  3619. }
  3620. //
  3621. // We partially or fully translated the Name during processing of
  3622. // this Work Item. If this Name has previously been fully translated
  3623. // during the processing of another Work Item, discard the new
  3624. // translation and skip to the next Name. Note that Qualified
  3625. // Names are always partially translated during the building
  3626. // of the Work List. Isolated Names are fully translated during
  3627. // the building phase if they are Domain Names.
  3628. //
  3629. Index = WorkItem->Indices[WorkItemIndex];
  3630. if ( WorkListTranslatedSids[ Index ].Use != SidTypeUnknown ) {
  3631. continue;
  3632. }
  3633. //
  3634. // If the SID does not pass the filter test, ignore
  3635. //
  3636. if ( (WorkItem->Properties & LSAP_DB_LOOKUP_WORK_ITEM_XFOREST)
  3637. && TranslatedSids[WorkItemIndex].Sid ) {
  3638. NTSTATUS Status2;
  3639. Status2 = LsapSidOnFtInfo((PUNICODE_STRING)&WorkItem->TrustInformation.Name,
  3640. TranslatedSids[WorkItemIndex].Sid );
  3641. if (!NT_SUCCESS(Status2)) {
  3642. //
  3643. // This SID did not pass the test
  3644. //
  3645. BOOL fSuccess;
  3646. LPWSTR StringSid = NULL, TargetForest = NULL, AccountName = NULL;
  3647. LsapDiagPrint( DB_LOOKUP_WORK_LIST,
  3648. ("LsapSidOnFtInfo returned 0x%x\n",Status2));
  3649. //
  3650. // This should be rare -- event log for troubleshooting
  3651. // purposes
  3652. //
  3653. fSuccess = ConvertSidToStringSidW(TranslatedSids[WorkItemIndex].Sid,
  3654. &StringSid);
  3655. TargetForest = LocalAlloc(LMEM_ZEROINIT, WorkItem->TrustInformation.Name.Length + sizeof(WCHAR));
  3656. if (TargetForest) {
  3657. RtlCopyMemory(TargetForest,
  3658. WorkItem->TrustInformation.Name.Buffer,
  3659. WorkItem->TrustInformation.Name.Length);
  3660. }
  3661. AccountName = LocalAlloc(LMEM_ZEROINIT, WorkList->LookupNamesParams.Names[Index].Length + sizeof(WCHAR));
  3662. if (AccountName) {
  3663. RtlCopyMemory(AccountName,
  3664. WorkList->LookupNamesParams.Names[Index].Buffer,
  3665. WorkList->LookupNamesParams.Names[Index].Length);
  3666. }
  3667. if ( fSuccess
  3668. && TargetForest
  3669. && AccountName) {
  3670. LsapDbLookupReportEvent3( 1,
  3671. EVENTLOG_WARNING_TYPE,
  3672. LSAEVENT_LOOKUP_SID_FILTERED,
  3673. sizeof( ULONG ),
  3674. &Status2,
  3675. AccountName,
  3676. StringSid,
  3677. TargetForest );
  3678. }
  3679. if (StringSid) {
  3680. LocalFree(StringSid);
  3681. }
  3682. if (TargetForest) {
  3683. LocalFree(TargetForest);
  3684. }
  3685. if (AccountName) {
  3686. LocalFree(AccountName);
  3687. }
  3688. continue;
  3689. }
  3690. }
  3691. //
  3692. // Name has been translated for the first time during the processing
  3693. // of this Work Item. If this Work Item does not specify a Domain
  3694. // Index, we need to add its Domain to the Referenced Domains List.
  3695. //
  3696. if (TranslatedSids[WorkItemIndex].DomainIndex != LSA_UNKNOWN_INDEX) {
  3697. //
  3698. // Make sure this is in the referenced domains list
  3699. //
  3700. Status = LsapDbLookupAddListReferencedDomains(
  3701. WorkList->ReferencedDomains,
  3702. &ReferencedDomains->Domains[TranslatedSids[WorkItemIndex].DomainIndex],
  3703. (PLONG) &LocalDomainIndex
  3704. );
  3705. if (!NT_SUCCESS(Status)) {
  3706. break;
  3707. }
  3708. } else {
  3709. LocalDomainIndex = TranslatedSids[WorkItemIndex].DomainIndex;
  3710. }
  3711. //
  3712. // Now update the TranslatedSids array in the Work List.
  3713. WorkListTranslatedSids[Index] = TranslatedSids[WorkItemIndex];
  3714. WorkListTranslatedSids[Index].DomainIndex = LocalDomainIndex;
  3715. Status = LsapRpcCopySid(NULL,
  3716. &WorkListTranslatedSids[Index].Sid,
  3717. TranslatedSids[WorkItemIndex].Sid);
  3718. if (!NT_SUCCESS(Status)) {
  3719. break;;
  3720. }
  3721. }
  3722. if (!NT_SUCCESS(Status)) {
  3723. goto LookupNamesUpdateTranslatedSidsError;
  3724. }
  3725. LookupNamesUpdateTranslatedSidsFinish:
  3726. //
  3727. // If necessary, release the Lookup Work Queue Lock.
  3728. //
  3729. if (AcquiredWorkQueueLock) {
  3730. LsapDbLookupReleaseWorkQueueLock();
  3731. AcquiredWorkQueueLock = FALSE;
  3732. }
  3733. if ( AcquiredTrustedDomainLock ) {
  3734. LsapDbReleaseLockTrustedDomainList();
  3735. AcquiredTrustedDomainLock = FALSE;
  3736. }
  3737. return(Status);
  3738. LookupNamesUpdateTranslatedSidsError:
  3739. goto LookupNamesUpdateTranslatedSidsFinish;
  3740. }
  3741. NTSTATUS
  3742. LsapDbLookupCreateWorkItem(
  3743. IN PLSAPR_TRUST_INFORMATION TrustInformation,
  3744. IN LONG DomainIndex,
  3745. IN ULONG MaximumEntryCount,
  3746. OUT PLSAP_DB_LOOKUP_WORK_ITEM *WorkItem
  3747. )
  3748. /*++
  3749. Routine Description:
  3750. This function creates a new Work Item for a name Lookup operation.
  3751. Arguments:
  3752. TrustInformation - Specifies the Name of the Trusted Domain
  3753. to which the Work Item relates. The Sid field may be NULL or
  3754. set to the corresponding Sid. The Trust Information is expected
  3755. to be in heap or global data.
  3756. DomainIndex - Specifies the Domain Index of this domain relative to
  3757. the Referenced Domain List for the Lookup operation specified
  3758. by the Work List.
  3759. MaximumEntryCount - Specifies the maximum number of entries that
  3760. this Work Item will initialiiy be able to contain.
  3761. WorkItem - Receives a pointer to an empty Work Item structure.
  3762. Return Value:
  3763. NTSTATUS - Standard Nt Result Code
  3764. --*/
  3765. {
  3766. NTSTATUS Status = STATUS_SUCCESS;
  3767. PLSAP_DB_LOOKUP_WORK_ITEM OutputWorkItem = NULL;
  3768. PULONG OutputIndices = NULL;
  3769. ULONG InitialEntryCount;
  3770. //
  3771. // Allocate memory for the Work Item Header.
  3772. //
  3773. Status = STATUS_INSUFFICIENT_RESOURCES;
  3774. OutputWorkItem = MIDL_user_allocate(sizeof(LSAP_DB_LOOKUP_WORK_ITEM));
  3775. if (OutputWorkItem == NULL) {
  3776. goto LookupCreateWorkItemError;
  3777. }
  3778. RtlZeroMemory(
  3779. OutputWorkItem,
  3780. sizeof(LSAP_DB_LOOKUP_WORK_ITEM)
  3781. );
  3782. //
  3783. // Initialize the fixed fields in the Work Item.
  3784. //
  3785. Status = LsapDbLookupInitializeWorkItem(OutputWorkItem);
  3786. if (!NT_SUCCESS(Status)) {
  3787. goto LookupCreateWorkItemError;
  3788. }
  3789. //
  3790. // Initialize other fields from parameters.
  3791. //
  3792. //
  3793. // Copy the trusted domain information into the work item. The
  3794. // trust information may be NULL if this is the isolated names
  3795. // work item.
  3796. //
  3797. if (TrustInformation != NULL) {
  3798. if ( TrustInformation->Sid ) {
  3799. OutputWorkItem->TrustInformation.Sid =
  3800. MIDL_user_allocate( RtlLengthSid(TrustInformation->Sid) );
  3801. if (OutputWorkItem->TrustInformation.Sid == NULL) {
  3802. Status = STATUS_INSUFFICIENT_RESOURCES;
  3803. goto LookupCreateWorkItemError;
  3804. }
  3805. RtlCopyMemory(
  3806. OutputWorkItem->TrustInformation.Sid,
  3807. TrustInformation->Sid,
  3808. RtlLengthSid(TrustInformation->Sid)
  3809. );
  3810. }
  3811. OutputWorkItem->TrustInformation.Name.MaximumLength = TrustInformation->Name.Length + sizeof(WCHAR);
  3812. OutputWorkItem->TrustInformation.Name.Buffer =
  3813. MIDL_user_allocate(TrustInformation->Name.Length + sizeof(WCHAR));
  3814. if (OutputWorkItem->TrustInformation.Name.Buffer == NULL ) {
  3815. Status = STATUS_INSUFFICIENT_RESOURCES;
  3816. goto LookupCreateWorkItemError;
  3817. }
  3818. RtlCopyUnicodeString(
  3819. (PUNICODE_STRING) &OutputWorkItem->TrustInformation.Name,
  3820. (PUNICODE_STRING) &TrustInformation->Name
  3821. );
  3822. }
  3823. //
  3824. // Create the Indices array in the Work Item.
  3825. //
  3826. InitialEntryCount = (MaximumEntryCount +
  3827. LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY) &
  3828. (~LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY);
  3829. Status = STATUS_INSUFFICIENT_RESOURCES;
  3830. OutputIndices = MIDL_user_allocate( InitialEntryCount * sizeof(ULONG) );
  3831. if (OutputIndices == NULL) {
  3832. goto LookupCreateWorkItemError;
  3833. }
  3834. Status = STATUS_SUCCESS;
  3835. OutputWorkItem->UsedCount = (ULONG) 0;
  3836. OutputWorkItem->MaximumCount = InitialEntryCount;
  3837. OutputWorkItem->Indices = OutputIndices;
  3838. OutputWorkItem->DomainIndex = DomainIndex;
  3839. LookupCreateWorkItemFinish:
  3840. //
  3841. // Return pointer to newly created Work Item or NULL.
  3842. //
  3843. *WorkItem = OutputWorkItem;
  3844. return(Status);
  3845. LookupCreateWorkItemError:
  3846. //
  3847. // Free memory allocated for Indices array.
  3848. //
  3849. if (OutputIndices != NULL) {
  3850. MIDL_user_free( OutputIndices );
  3851. OutputIndices = NULL;
  3852. }
  3853. //
  3854. // Free any memory allocated for the Work Item header.
  3855. //
  3856. if (OutputWorkItem != NULL) {
  3857. if (OutputWorkItem->TrustInformation.Sid != NULL) {
  3858. MIDL_user_free( OutputWorkItem->TrustInformation.Sid );
  3859. }
  3860. if (OutputWorkItem->TrustInformation.Name.Buffer != NULL) {
  3861. MIDL_user_free( OutputWorkItem->TrustInformation.Name.Buffer );
  3862. }
  3863. MIDL_user_free(OutputWorkItem);
  3864. OutputWorkItem = NULL;
  3865. }
  3866. goto LookupCreateWorkItemFinish;
  3867. }
  3868. NTSTATUS
  3869. LsapDbLookupAddIndicesToWorkItem(
  3870. IN OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem,
  3871. IN ULONG Count,
  3872. IN PULONG Indices
  3873. )
  3874. /*++
  3875. Routine Description:
  3876. This function adds an array of Sid or Name indices to a Work Item.
  3877. The indices specify Sids or Names within the Sids or Names arrays in
  3878. the WorkList. If there is sufficient room in the Work Item's
  3879. existing indices array, the indices will be copied to that array.
  3880. Otherwise, a larger array will be allocated and the existing one
  3881. will be freed after copying the existing indices.
  3882. NOTE: The Work Item must NOT belong to a Work List that is currently
  3883. on the Work Queue. The Lookup Work Queue Lock will not be
  3884. taken.
  3885. Arguments:
  3886. WorkItem - Pointer to a Work Item structure describing a
  3887. list of Sids or Names and a domain in which they are to be looked up.
  3888. Count - Specifies the number of indices to be added.
  3889. Indices - Specifies the array of indices to be added to the WorkItem.
  3890. Return Value:
  3891. NTSTATUS - Standard Nt Result Code
  3892. --*/
  3893. {
  3894. NTSTATUS Status = STATUS_SUCCESS;
  3895. PULONG OutputIndices = NULL;
  3896. ULONG NewMaximumCount;
  3897. //
  3898. // Check room available in the work item. If there is enough
  3899. // room, just copy the indices in.
  3900. //
  3901. if (WorkItem->MaximumCount - WorkItem->UsedCount >= Count) {
  3902. RtlCopyMemory(
  3903. &WorkItem->Indices[WorkItem->UsedCount],
  3904. Indices,
  3905. Count * sizeof(ULONG)
  3906. );
  3907. WorkItem->UsedCount += Count;
  3908. goto AddIndicesToWorkItemFinish;
  3909. }
  3910. //
  3911. // Allocate array of sufficient size to accommodate the existing
  3912. // and new indices. Round up number of entries to some granularity
  3913. // to avoid frequent reallocations.
  3914. //
  3915. Status = STATUS_INSUFFICIENT_RESOURCES;
  3916. NewMaximumCount = ((WorkItem->UsedCount + Count) +
  3917. LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY) &
  3918. (~LSAP_DB_LOOKUP_WORK_ITEM_GRANULARITY);
  3919. OutputIndices = MIDL_user_allocate( NewMaximumCount * sizeof(ULONG) );
  3920. if (OutputIndices == NULL) {
  3921. goto AddIndicesToWorkItemError;
  3922. }
  3923. Status = STATUS_SUCCESS;
  3924. //
  3925. // Copy in the existing and new indices.
  3926. //
  3927. RtlCopyMemory(
  3928. OutputIndices,
  3929. WorkItem->Indices,
  3930. WorkItem->UsedCount * sizeof(ULONG)
  3931. );
  3932. RtlCopyMemory(
  3933. &OutputIndices[WorkItem->UsedCount],
  3934. Indices,
  3935. Count * sizeof(ULONG)
  3936. );
  3937. //
  3938. // Free the existing indices. Set pointer to the updated indices array
  3939. // and update the used and maximum counts.
  3940. //
  3941. MIDL_user_free( WorkItem->Indices );
  3942. WorkItem->Indices = OutputIndices;
  3943. WorkItem->UsedCount += Count;
  3944. WorkItem->MaximumCount = NewMaximumCount;
  3945. AddIndicesToWorkItemFinish:
  3946. return(Status);
  3947. AddIndicesToWorkItemError:
  3948. //
  3949. // Free any memory allocated for the Output Indices array.
  3950. //
  3951. if (OutputIndices != NULL) {
  3952. MIDL_user_free( OutputIndices );
  3953. OutputIndices = NULL;
  3954. }
  3955. goto AddIndicesToWorkItemFinish;
  3956. }
  3957. NTSTATUS
  3958. LsapDbLookupComputeAdvisoryChildThreadCount(
  3959. IN OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
  3960. )
  3961. /*++
  3962. Routine Description:
  3963. This function computes the advisory thread count for a Lookup
  3964. operation. This count is an estimate of the optimal number of
  3965. worker threads (in addition to the main thread) needed to process the
  3966. Work List.
  3967. Arguments:
  3968. WorkList - Pointer to a Work List structure describing a
  3969. Lookup Sids or Lookup Names operation.
  3970. Return Value:
  3971. NTSTATUS - Standard Nt Result Code
  3972. --*/
  3973. {
  3974. NTSTATUS Status = STATUS_SUCCESS;
  3975. ASSERT(WorkList->WorkItemCount != (ULONG) 0);
  3976. WorkList->AdvisoryChildThreadCount = (WorkList->WorkItemCount - (ULONG) 1);
  3977. return(Status);
  3978. }
  3979. NTSTATUS
  3980. LsapDbLookupUpdateAssignableWorkItem(
  3981. IN BOOLEAN MoveToNextWorkList
  3982. )
  3983. /*++
  3984. Routine Description:
  3985. This function updates the next assignable Work Item pointers.
  3986. Arguments:
  3987. MoveToNextWorkList - If TRUE, skip the rest of the current Work List. If
  3988. FALSE, point at the next item in the current Work List.
  3989. Return Value:
  3990. NTSTATUS - Standard Nt Result Code.
  3991. --*/
  3992. {
  3993. NTSTATUS Status = STATUS_SUCCESS;
  3994. PLSAP_DB_LOOKUP_WORK_ITEM CandAssignableWorkItem = NULL;
  3995. PLSAP_DB_LOOKUP_WORK_LIST CandAssignableWorkList = NULL;
  3996. BOOLEAN AcquiredWorkQueueLock = FALSE;
  3997. //
  3998. // Acquire the LookupWork Queue Lock.
  3999. //
  4000. Status = LsapDbLookupAcquireWorkQueueLock();
  4001. if (!NT_SUCCESS(Status)) {
  4002. goto LookupUpdateAssignableWorkItemError;
  4003. }
  4004. AcquiredWorkQueueLock = TRUE;
  4005. //
  4006. // If there is no Currently Assignable Work List, just exit.
  4007. //
  4008. if (LookupWorkQueue.CurrentAssignableWorkList == NULL) {
  4009. goto LookupUpdateAssignableWorkItemFinish;
  4010. }
  4011. //
  4012. // There is a Currently Assignable Work List. Unless requested to
  4013. // skip this Work List, examine it.
  4014. //
  4015. if (!MoveToNextWorkList) {
  4016. ASSERT( LookupWorkQueue.CurrentAssignableWorkItem != NULL);
  4017. //
  4018. // Select the next Work Item in the list as candidate for the
  4019. // next Assignable Work Item. If we have not returned to the First
  4020. // Work Item, selection is complete.
  4021. //
  4022. CandAssignableWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM)
  4023. LookupWorkQueue.CurrentAssignableWorkItem->Links.Flink;
  4024. if (CandAssignableWorkItem !=
  4025. LookupWorkQueue.CurrentAssignableWorkList->AnchorWorkItem) {
  4026. ASSERT( CandAssignableWorkItem->State == AssignableWorkItem);
  4027. LookupWorkQueue.CurrentAssignableWorkItem = CandAssignableWorkItem;
  4028. goto LookupUpdateAssignableWorkItemFinish;
  4029. }
  4030. }
  4031. //
  4032. // There are no more work items in this Work List or we're to skip the
  4033. // rest of it. See if there is another Work List.
  4034. //
  4035. CandAssignableWorkList = (PLSAP_DB_LOOKUP_WORK_LIST)
  4036. LookupWorkQueue.CurrentAssignableWorkList->WorkLists.Flink;
  4037. if (CandAssignableWorkList != LookupWorkQueue.AnchorWorkList) {
  4038. //
  4039. // There is another Work List. Select the first Work Item in the
  4040. // list following the anchor.
  4041. //
  4042. CandAssignableWorkItem = (PLSAP_DB_LOOKUP_WORK_ITEM)
  4043. CandAssignableWorkList->AnchorWorkItem->Links.Flink;
  4044. //
  4045. // Verify that the list does not just contain the Anchor Work Item.
  4046. // Work Lists on the Work Queue should never be empty.
  4047. //
  4048. ASSERT (CandAssignableWorkItem != CandAssignableWorkList->AnchorWorkItem);
  4049. LookupWorkQueue.CurrentAssignableWorkList = CandAssignableWorkList;
  4050. LookupWorkQueue.CurrentAssignableWorkItem = CandAssignableWorkItem;
  4051. goto LookupUpdateAssignableWorkItemFinish;
  4052. }
  4053. //
  4054. // All work has been assigned. Set pointers to NULL.
  4055. //
  4056. LookupWorkQueue.CurrentAssignableWorkList = NULL;
  4057. LookupWorkQueue.CurrentAssignableWorkItem = NULL;
  4058. LookupUpdateAssignableWorkItemFinish:
  4059. //
  4060. // If necessary, release the Lookup Work Queue Lock.
  4061. //
  4062. if (AcquiredWorkQueueLock) {
  4063. LsapDbLookupReleaseWorkQueueLock();
  4064. AcquiredWorkQueueLock = FALSE;
  4065. }
  4066. return(Status);
  4067. LookupUpdateAssignableWorkItemError:
  4068. goto LookupUpdateAssignableWorkItemFinish;
  4069. }
  4070. NTSTATUS
  4071. LsapDbLookupStopProcessingWorkList(
  4072. IN PLSAP_DB_LOOKUP_WORK_LIST WorkList,
  4073. IN NTSTATUS TerminationStatus
  4074. )
  4075. /*++
  4076. Routine Description:
  4077. This function stops further work on a lookup operation at a given
  4078. level and stores an error code.
  4079. Arguments:
  4080. WorkList - Pointer to a Work List structure describing a
  4081. Lookup Sids or Lookup Names operation.
  4082. TerminationStatus - Specifies the Nt Result Code to be returned
  4083. by LsarLookupnames or LsarLookupSids.
  4084. Return Value:
  4085. NTSTATUS - Standard Nt Result Code
  4086. --*/
  4087. {
  4088. NTSTATUS Status = STATUS_SUCCESS;
  4089. BOOLEAN AcquiredWorkQueueLock = FALSE;
  4090. //
  4091. // Acquire the LookupWork Queue Lock.
  4092. //
  4093. Status = LsapDbLookupAcquireWorkQueueLock();
  4094. if (!NT_SUCCESS(Status)) {
  4095. goto LookupStopProcessingWorkListError;
  4096. }
  4097. AcquiredWorkQueueLock = TRUE;
  4098. //
  4099. // Store the termination status in the appropriate WorkList.
  4100. //
  4101. WorkList->Status = TerminationStatus;
  4102. //
  4103. // If this WorkList happens to be the one in which Work Items are being
  4104. // given out, we need to prevent any further Work Items from being given
  4105. // out. Update the next assignable Work Item pointers, specifying that
  4106. // we should skip to the next Work List (if any).
  4107. //
  4108. if (WorkList == LookupWorkQueue.CurrentAssignableWorkList) {
  4109. Status = LsapDbLookupUpdateAssignableWorkItem(TRUE);
  4110. }
  4111. if (!NT_SUCCESS(Status)) {
  4112. goto LookupStopProcessingWorkListError;
  4113. }
  4114. LookupStopProcessingWorkListFinish:
  4115. //
  4116. // If necessary, release the Lookup Work Queue Lock.
  4117. //
  4118. if (AcquiredWorkQueueLock) {
  4119. LsapDbLookupReleaseWorkQueueLock();
  4120. AcquiredWorkQueueLock = FALSE;
  4121. }
  4122. return(Status);
  4123. LookupStopProcessingWorkListError:
  4124. goto LookupStopProcessingWorkListFinish;
  4125. }
  4126. NTSTATUS
  4127. LsapRtlExtractDomainSid(
  4128. IN PSID Sid,
  4129. OUT PSID *DomainSid
  4130. )
  4131. /*++
  4132. Routine Description:
  4133. This function extracts a Domain Sid from a Sid.
  4134. Arguments:
  4135. Sid - Pointer to Sid whose Domain Prefix Sid is to be extracted.
  4136. DomainSid - Receives pointer to Domain Sid. This Sid will be
  4137. allocated memory by MIDL_User_allocate() and should be freed
  4138. via MIDL_user_free when no longer required.
  4139. Return Value:
  4140. NTSTATUS - Standard Nt Result Code
  4141. --*/
  4142. {
  4143. PSID OutputDomainSid;
  4144. ULONG DomainSidLength = RtlLengthSid(Sid) - sizeof(ULONG);
  4145. OutputDomainSid = MIDL_user_allocate( DomainSidLength );
  4146. if (OutputDomainSid == NULL) {
  4147. return(STATUS_INSUFFICIENT_RESOURCES);
  4148. }
  4149. RtlCopyMemory( OutputDomainSid, Sid, DomainSidLength);
  4150. (*(RtlSubAuthorityCountSid(OutputDomainSid)))--;
  4151. *DomainSid = OutputDomainSid;
  4152. return(STATUS_SUCCESS);
  4153. }
  4154. NTSTATUS
  4155. LsapDbLookupReadRegistrySettings(
  4156. PVOID Ignored OPTIONAL
  4157. )
  4158. /*++
  4159. Routine Description:
  4160. This routine is called via LsaIRegisterNotification whenever the LSA's
  4161. registry settings change.
  4162. Arguments:
  4163. Ignored -- a callback parameter that is not used.
  4164. Return Value:
  4165. STATUS_SUCCCESS;
  4166. --*/
  4167. {
  4168. DWORD err;
  4169. HKEY hKey;
  4170. DWORD dwType;
  4171. DWORD dwValue;
  4172. DWORD dwValueSize;
  4173. err = RegOpenKeyExW(HKEY_LOCAL_MACHINE,
  4174. L"SYSTEM\\CurrentControlSet\\Control\\Lsa",
  4175. 0, // reserved
  4176. KEY_QUERY_VALUE,
  4177. &hKey );
  4178. if ( ERROR_SUCCESS == err ) {
  4179. dwValueSize = sizeof(dwValue);
  4180. err = RegQueryValueExW( hKey,
  4181. L"AllowExtendedDownlevelLookup",
  4182. NULL, //reserved,
  4183. &dwType,
  4184. (PBYTE)&dwValue,
  4185. &dwValueSize );
  4186. if ( (ERROR_SUCCESS == err)
  4187. && (dwValue != 0) ) {
  4188. LsapAllowExtendedDownlevelLookup = TRUE;
  4189. } else {
  4190. // Reset the value
  4191. LsapAllowExtendedDownlevelLookup = FALSE;
  4192. }
  4193. dwValueSize = sizeof(dwValue);
  4194. err = RegQueryValueExW( hKey,
  4195. L"LookupLogLevel",
  4196. NULL, //reserved,
  4197. &dwType,
  4198. (PBYTE)&dwValue,
  4199. &dwValueSize );
  4200. if ( ERROR_SUCCESS == err) {
  4201. LsapLookupLogLevel = dwValue;
  4202. } else {
  4203. // default value
  4204. LsapLookupLogLevel = 0;
  4205. }
  4206. #if DBG
  4207. if (LsapLookupLogLevel > 0) {
  4208. LsapGlobalFlag |= LSAP_DIAG_DB_LOOKUP_WORK_LIST;
  4209. } else {
  4210. LsapGlobalFlag &= ~LSAP_DIAG_DB_LOOKUP_WORK_LIST;
  4211. }
  4212. #endif
  4213. dwValueSize = sizeof(DWORD);
  4214. dwValue = 0;
  4215. err = RegQueryValueExW( hKey,
  4216. L"LsaLookupReturnSidTypeDeleted",
  4217. NULL, //reserved,
  4218. &dwType,
  4219. (PBYTE)&dwValue,
  4220. &dwValueSize );
  4221. if ( (ERROR_SUCCESS == err)
  4222. && (dwType == REG_DWORD)
  4223. && (dwValue != 0) ) {
  4224. LsapReturnSidTypeDeleted = TRUE;
  4225. } else {
  4226. LsapReturnSidTypeDeleted = FALSE;
  4227. }
  4228. dwValueSize = sizeof(DWORD);
  4229. dwValue = 0;
  4230. err = RegQueryValueExW( hKey,
  4231. L"LsaLookupRestrictIsolatedNameLevel",
  4232. NULL, //reserved,
  4233. &dwType,
  4234. (PBYTE)&dwValue,
  4235. &dwValueSize );
  4236. if ( (ERROR_SUCCESS == err)
  4237. && (dwType == REG_DWORD)
  4238. && (dwValue != 0) ) {
  4239. LsapLookupRestrictIsolatedNameLevel = TRUE;
  4240. } else {
  4241. LsapLookupRestrictIsolatedNameLevel = FALSE;
  4242. }
  4243. LsapSidCacheReadParameters(hKey);
  4244. RegCloseKey( hKey );
  4245. }
  4246. return STATUS_SUCCESS;
  4247. UNREFERENCED_PARAMETER(Ignored);
  4248. }
  4249. NTSTATUS
  4250. LsapDbLookupInitialize(
  4251. )
  4252. /*++
  4253. Routine Description:
  4254. This function performs initialization of the data structures
  4255. used by Lookup operations. These structures are as follows:
  4256. LookupWorkQueue - This is a doubly-linked list of Lookup Work Lists.
  4257. There is one Work List for each Lookup operation in progress
  4258. on a DC. Each Lookup Work List contains a doubly-linked list
  4259. of Lookup Work Items. Each Lookup Work Item specifies a
  4260. Trusted Domain and array of Sids or Names to be looked up in that
  4261. domain. Access to this queue is controlled via the Lookup
  4262. Work Queue Lock.
  4263. Trusted Domain List - This is a doubly-linked list which contains
  4264. the Trust Information (i.e. Domain Sid and Domain Name) of
  4265. each Trusted Domain. The purpose of this list is to enable
  4266. fast identification of Trusted Domain Sids and Names, without
  4267. having recourse to open or enumerate Trusted Domain objects.
  4268. This list is initialized when the system is loaded, and is
  4269. updated directly when a Trusted Domain object is created or
  4270. deleted.
  4271. Arguments:
  4272. None
  4273. Return Value:
  4274. NTSTATUS - Standard Nt Result Code.
  4275. --*/
  4276. {
  4277. NTSTATUS Status = STATUS_SUCCESS;
  4278. Status = LsapDbLookupInitPolicyCache();
  4279. if (!NT_SUCCESS(Status)) {
  4280. return Status;
  4281. }
  4282. //
  4283. // Arrange to be notified when the parameter settings change
  4284. //
  4285. LsaIRegisterNotification( LsapDbLookupReadRegistrySettings,
  4286. 0,
  4287. NOTIFIER_TYPE_NOTIFY_EVENT,
  4288. NOTIFY_CLASS_REGISTRY_CHANGE,
  4289. 0,
  4290. 0,
  4291. 0 );
  4292. Status = LsapDbLookupReadRegistrySettings(NULL);
  4293. if (!NT_SUCCESS(Status)) {
  4294. return Status;
  4295. }
  4296. //
  4297. // Perform initialization specific to DC's
  4298. //
  4299. if (LsapProductType != NtProductLanManNt) {
  4300. goto LookupInitializeFinish;
  4301. }
  4302. //
  4303. // Create the Lookup Work List initiated event.
  4304. //
  4305. Status = NtCreateEvent(
  4306. &LsapDbLookupStartedEvent,
  4307. EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
  4308. NULL,
  4309. SynchronizationEvent,
  4310. FALSE
  4311. );
  4312. if (!NT_SUCCESS(Status)) {
  4313. goto LookupInitializeError;
  4314. }
  4315. //
  4316. // Initialize the Lookup Work Queue
  4317. //
  4318. Status = LsapDbLookupInitializeWorkQueue();
  4319. if (!NT_SUCCESS(Status)) {
  4320. goto LookupInitializeError;
  4321. }
  4322. LookupInitializeFinish:
  4323. return(Status);
  4324. LookupInitializeError:
  4325. goto LookupInitializeFinish;
  4326. }
  4327. NTSTATUS
  4328. LsapDbLookupInitializeWorkQueue(
  4329. )
  4330. /*++
  4331. Routine Description:
  4332. This function initializes the Lookup Work Queue. It is only
  4333. called for DC's.
  4334. Arguments:
  4335. None
  4336. Return Value:
  4337. NTSTATUS - Standard Nt Result Code.
  4338. --*/
  4339. {
  4340. NTSTATUS Status = STATUS_SUCCESS;
  4341. PLSAP_DB_LOOKUP_WORK_LIST AnchorWorkList = NULL;
  4342. //
  4343. // Initialize the Work Queue Lock.
  4344. //
  4345. Status = SafeInitializeCriticalSection(&LookupWorkQueue.Lock, ( DWORD )LOOKUP_WORK_QUEUE_LOCK_ENUM );
  4346. if (!NT_SUCCESS(Status)) {
  4347. LsapLogError(
  4348. "LsapDbLookupInitialize: RtlInit..CritSec returned 0x%lx\n",
  4349. Status
  4350. );
  4351. return Status;
  4352. }
  4353. //
  4354. // Initialize the Work Queue to comprise the Anchor Work List
  4355. // doubly-linked to itself.
  4356. //
  4357. LookupWorkQueue.AnchorWorkList = &LookupWorkQueue.DummyAnchorWorkList;
  4358. AnchorWorkList = &LookupWorkQueue.DummyAnchorWorkList;
  4359. //
  4360. // Set the currently assignable Work List and Work Item pointers to
  4361. // NULL to indicate that there is no work to to.
  4362. //
  4363. LookupWorkQueue.CurrentAssignableWorkList = NULL;
  4364. LookupWorkQueue.CurrentAssignableWorkItem = NULL;
  4365. //
  4366. // Initialize the Anchor Work List.
  4367. //
  4368. Status = LsapDbLookupInitializeWorkList(AnchorWorkList);
  4369. if (!NT_SUCCESS(Status)) {
  4370. goto LookupInitializeWorkQueueError;
  4371. }
  4372. AnchorWorkList->WorkLists.Flink = (PLIST_ENTRY) AnchorWorkList;
  4373. AnchorWorkList->WorkLists.Blink = (PLIST_ENTRY) AnchorWorkList;
  4374. //
  4375. // Set the thread counts.
  4376. //
  4377. LookupWorkQueue.ActiveChildThreadCount = (ULONG) 0;
  4378. LookupWorkQueue.MaximumChildThreadCount = LSAP_DB_LOOKUP_MAX_THREAD_COUNT;
  4379. LookupWorkQueue.MaximumRetainedChildThreadCount = LSAP_DB_LOOKUP_MAX_RET_THREAD_COUNT;
  4380. LookupInitializeWorkQueueFinish:
  4381. return(Status);
  4382. LookupInitializeWorkQueueError:
  4383. goto LookupInitializeWorkQueueFinish;
  4384. }
  4385. NTSTATUS
  4386. LsapDbLookupInitializeWorkList(
  4387. OUT PLSAP_DB_LOOKUP_WORK_LIST WorkList
  4388. )
  4389. /*++
  4390. Routine Description:
  4391. This function initializes an empty Work List structure. The Work List
  4392. link fields are not set by this function.
  4393. Arguments:
  4394. WorkList - Points to Work List structure to be initialized.
  4395. Return Value:
  4396. NTSTATUS - Standard Nt Result Code.
  4397. --*/
  4398. {
  4399. NTSTATUS Status = STATUS_SUCCESS;
  4400. PLSAP_DB_LOOKUP_WORK_ITEM AnchorWorkItem = NULL;
  4401. //
  4402. // Initialize miscellaneous fields in the Work List header.
  4403. //
  4404. WorkList->WorkLists.Flink = NULL;
  4405. WorkList->WorkLists.Blink = NULL;
  4406. WorkList->WorkItemCount = (ULONG) 0;
  4407. WorkList->CompletedWorkItemCount = (ULONG) 0;
  4408. WorkList->State = InactiveWorkList;
  4409. WorkList->Status = STATUS_SUCCESS;
  4410. WorkList->NonFatalStatus = STATUS_SUCCESS;
  4411. //
  4412. // Init the completion event
  4413. //
  4414. Status = NtCreateEvent(
  4415. &WorkList->LookupCompleteEvent,
  4416. EVENT_QUERY_STATE | EVENT_MODIFY_STATE | SYNCHRONIZE,
  4417. NULL,
  4418. SynchronizationEvent,
  4419. FALSE
  4420. );
  4421. if (!NT_SUCCESS(Status)) {
  4422. goto LookupInitializeWorkListError;
  4423. }
  4424. //
  4425. // Initialize the Work List's list of Work Items to comprise the
  4426. // Anchor Work Item doubly-linked to itself.
  4427. //
  4428. WorkList->AnchorWorkItem = &WorkList->DummyAnchorWorkItem;
  4429. AnchorWorkItem = WorkList->AnchorWorkItem;
  4430. AnchorWorkItem->Links.Flink = (PLIST_ENTRY) AnchorWorkItem;
  4431. AnchorWorkItem->Links.Blink = (PLIST_ENTRY) AnchorWorkItem;
  4432. //
  4433. // Initialize the Anchor Work Item.
  4434. //
  4435. Status = LsapDbLookupInitializeWorkItem(AnchorWorkItem);
  4436. if (!NT_SUCCESS(Status)) {
  4437. goto LookupInitializeWorkListError;
  4438. }
  4439. LookupInitializeWorkListFinish:
  4440. return(Status);
  4441. LookupInitializeWorkListError:
  4442. goto LookupInitializeWorkListFinish;
  4443. }
  4444. NTSTATUS
  4445. LsapDbLookupInitializeWorkItem(
  4446. OUT PLSAP_DB_LOOKUP_WORK_ITEM WorkItem
  4447. )
  4448. /*++
  4449. Routine Description:
  4450. This function initializes an empty Work Item structure. The Work Item
  4451. link fields are not set by this function.
  4452. Arguments:
  4453. WorkItem - Points to Work Item structure to be initialized.
  4454. Return Value:
  4455. NTSTATUS - Standard Nt Result Code.
  4456. --*/
  4457. {
  4458. NTSTATUS Status = STATUS_SUCCESS;
  4459. WorkItem->UsedCount = (ULONG) 0;
  4460. WorkItem->MaximumCount = (ULONG) 0;
  4461. WorkItem->State = NonAssignableWorkItem;
  4462. WorkItem->Properties = (ULONG) 0;
  4463. return(Status);
  4464. }
  4465. NTSTATUS
  4466. LsapDbLookupLocalDomains(
  4467. OUT PLSAPR_TRUST_INFORMATION BuiltInDomainTrustInformation,
  4468. OUT PLSAPR_TRUST_INFORMATION_EX AccountDomainTrustInformation,
  4469. OUT PLSAPR_TRUST_INFORMATION_EX PrimaryDomainTrustInformation
  4470. )
  4471. /*++
  4472. Routine Description:
  4473. This function returns Trust Information for the Local Domains.
  4474. Arguments:
  4475. BuiltInDomainTrustInformation - Pointer to structure that will
  4476. receive the Name and Sid of the Built-In Domain. Unlike
  4477. the other two parameters, the Name and Sid buffers for the
  4478. Built-in Domain are NOT freed after use, because they are
  4479. global data constants.
  4480. AccountDomainTrustInformation - Pointer to structure that will
  4481. receive the Name and Sid of the Accounts Domain. The Name and
  4482. Sid buffers must be freed after use via MIDL_user_free.
  4483. PrimaryDomainTrustInformation - Pointer to structure that will
  4484. receive the Name and Sid of the Accounts Domain. The Name and
  4485. Sid buffers must be freed after use via MIDL_user_free.
  4486. --*/
  4487. {
  4488. NTSTATUS Status = STATUS_SUCCESS;
  4489. ULONG WellKnownSidIndex;
  4490. PLSAPR_POLICY_ACCOUNT_DOM_INFO PolicyAccountDomainInfo = NULL;
  4491. PLSAPR_POLICY_DNS_DOMAIN_INFO PolicyDnsDomainInfo = NULL;
  4492. LPWSTR NameBuffer = NULL;
  4493. LPWSTR DnsDomainNameBuffer = NULL;
  4494. BOOLEAN fFreeNameBuffer = FALSE;
  4495. BOOLEAN fFreeDnsDomainNameBuffer = FALSE;
  4496. RtlZeroMemory( AccountDomainTrustInformation, sizeof( LSAPR_TRUST_INFORMATION_EX ) );
  4497. RtlZeroMemory( PrimaryDomainTrustInformation, sizeof( LSAPR_TRUST_INFORMATION_EX ) );
  4498. //
  4499. // Obtain the Name and Sid of the Built-in Domain
  4500. //
  4501. BuiltInDomainTrustInformation->Sid = LsapBuiltInDomainSid;
  4502. Status = STATUS_INTERNAL_DB_ERROR;
  4503. if (!LsapDbLookupIndexWellKnownSid(
  4504. LsapBuiltInDomainSid,
  4505. (PLSAP_WELL_KNOWN_SID_INDEX) &WellKnownSidIndex
  4506. )) {
  4507. goto LookupLocalDomainsError;
  4508. }
  4509. Status = STATUS_SUCCESS;
  4510. //
  4511. // Obtain the name of the Built In Domain from the table of
  4512. // Well Known Sids. It suffices to copy the Unicode structures
  4513. // since we do not need a separate copy of the name buffer.
  4514. //
  4515. BuiltInDomainTrustInformation->Name = *((PLSAPR_UNICODE_STRING)
  4516. LsapDbWellKnownSidName(WellKnownSidIndex));
  4517. //
  4518. // Now obtain the Name and Sid of the Account Domain.
  4519. // The Sid and Name of the Account Domain are both configurable, and
  4520. // we need to obtain them from the Policy Object. Now obtain the
  4521. // Account Domain Sid and Name by querying the appropriate
  4522. // Policy Information Class.
  4523. //
  4524. Status = LsapDbLookupGetDomainInfo((POLICY_ACCOUNT_DOMAIN_INFO **)&PolicyAccountDomainInfo,
  4525. (POLICY_DNS_DOMAIN_INFO **)&PolicyDnsDomainInfo);
  4526. if (!NT_SUCCESS(Status)) {
  4527. goto LookupLocalDomainsError;
  4528. }
  4529. //
  4530. // Set up the Trust Information structure for the Account Domain.
  4531. //
  4532. AccountDomainTrustInformation->Sid = PolicyAccountDomainInfo->DomainSid;
  4533. RtlCopyMemory(
  4534. &AccountDomainTrustInformation->FlatName,
  4535. &PolicyAccountDomainInfo->DomainName,
  4536. sizeof (UNICODE_STRING)
  4537. );
  4538. //
  4539. // If the account domain is the same as the Dns domain info, return the
  4540. // dns domain name as the account domain name
  4541. //
  4542. if ( PolicyDnsDomainInfo->Sid &&
  4543. PolicyAccountDomainInfo->DomainSid &&
  4544. RtlEqualSid( PolicyDnsDomainInfo->Sid,
  4545. PolicyAccountDomainInfo->DomainSid ) &&
  4546. RtlEqualUnicodeString( (PUNICODE_STRING)&PolicyDnsDomainInfo->Name,
  4547. (PUNICODE_STRING)&PolicyAccountDomainInfo->DomainName,
  4548. TRUE) ) {
  4549. AccountDomainTrustInformation->DomainName = PolicyDnsDomainInfo->DnsDomainName;
  4550. AccountDomainTrustInformation->DomainNamesDiffer = TRUE;
  4551. } else {
  4552. AccountDomainTrustInformation->DomainName = AccountDomainTrustInformation->FlatName;
  4553. }
  4554. //
  4555. // Now obtain the Name and Sid of the Primary Domain (if any)
  4556. // The Sid and Name of the Primary Domain are both configurable, and
  4557. // we need to obtain them from the Policy Object. Now obtain the
  4558. // Account Domain Sid and Name by querying the appropriate
  4559. // Policy Information Class.
  4560. //
  4561. if ( NT_SUCCESS( Status ) ) {
  4562. //
  4563. // Set up the Trust Information structure for the Primary Domain.
  4564. //
  4565. PrimaryDomainTrustInformation->Sid = PolicyDnsDomainInfo->Sid;
  4566. RtlCopyMemory( &PrimaryDomainTrustInformation->FlatName,
  4567. &PolicyDnsDomainInfo->Name,
  4568. sizeof (UNICODE_STRING) );
  4569. RtlCopyMemory( &PrimaryDomainTrustInformation->DomainName,
  4570. &PolicyDnsDomainInfo->DnsDomainName,
  4571. sizeof( UNICODE_STRING ) );
  4572. Status = LsapNullTerminateUnicodeString(
  4573. (PUNICODE_STRING)&PolicyDnsDomainInfo->DnsDomainName,
  4574. &DnsDomainNameBuffer,
  4575. &fFreeDnsDomainNameBuffer
  4576. );
  4577. if (NT_SUCCESS(Status)) {
  4578. Status = LsapNullTerminateUnicodeString(
  4579. (PUNICODE_STRING)&PolicyDnsDomainInfo->Name,
  4580. &NameBuffer,
  4581. &fFreeNameBuffer
  4582. );
  4583. }
  4584. if (NT_SUCCESS(Status)) {
  4585. if ( !DnsNameCompare_W( DnsDomainNameBuffer,
  4586. NameBuffer)
  4587. ) {
  4588. PrimaryDomainTrustInformation->DomainNamesDiffer = TRUE;
  4589. }
  4590. }
  4591. if ( fFreeDnsDomainNameBuffer ) {
  4592. midl_user_free( DnsDomainNameBuffer );
  4593. }
  4594. if ( fFreeNameBuffer ) {
  4595. midl_user_free( NameBuffer );
  4596. }
  4597. }
  4598. LookupLocalDomainsFinish:
  4599. return(Status);
  4600. LookupLocalDomainsError:
  4601. goto LookupLocalDomainsFinish;
  4602. }
  4603. BOOLEAN
  4604. LsapIsBuiltinDomain(
  4605. IN PSID Sid
  4606. )
  4607. {
  4608. return RtlEqualSid( Sid, LsapBuiltInDomainSid );
  4609. }
  4610. BOOLEAN
  4611. LsapIsDsDomainByNetbiosName(
  4612. WCHAR *NetbiosName
  4613. )
  4614. /*++
  4615. Routine Description
  4616. This routine determines if the (domain) netbios name passed in is an
  4617. accounts domain that is reprssented in the DS (ie is at least an nt5 domain).
  4618. Parameters:
  4619. NetbiosName -- a valid string
  4620. --*/
  4621. {
  4622. NTSTATUS NtStatus;
  4623. PDSNAME dsname;
  4624. ULONG len;
  4625. ASSERT( Sid );
  4626. // Ask the DS if it has heard of this name
  4627. NtStatus = MatchCrossRefByNetbiosName( NetbiosName,
  4628. NULL,
  4629. &len );
  4630. if ( NT_SUCCESS(NtStatus) ) {
  4631. //
  4632. // The domain was found in the ds
  4633. //
  4634. return TRUE;
  4635. }
  4636. return FALSE;
  4637. }
  4638. NTSTATUS
  4639. LsapGetDomainNameBySid(
  4640. IN PSID Sid,
  4641. OUT PUNICODE_STRING DomainName
  4642. )
  4643. /*++
  4644. Routine Description
  4645. Given a sid, this routine will return the netbios name of the domain,
  4646. if the domain is stored in the ds
  4647. Parameters:
  4648. Sid -- domain sid
  4649. Domain Name -- domain name allocated from MIDL
  4650. --*/
  4651. {
  4652. NTSTATUS NtStatus = STATUS_SUCCESS;
  4653. LPWSTR Name = NULL;
  4654. DSNAME dsname = {0};
  4655. ULONG len = 0;
  4656. ASSERT( Sid );
  4657. ASSERT( DomainName );
  4658. dsname.structLen = DSNameSizeFromLen(0);
  4659. len = min( sizeof( NT4SID ), RtlLengthSid( Sid ) );
  4660. memcpy( &dsname.Sid, Sid, len );
  4661. dsname.SidLen = len;
  4662. NtStatus = FindNetbiosDomainName(
  4663. &dsname,
  4664. NULL,
  4665. &len );
  4666. if ( NT_SUCCESS( NtStatus ) ) {
  4667. Name = MIDL_user_allocate( len );
  4668. if ( !Name ) {
  4669. NtStatus = STATUS_NO_MEMORY;
  4670. goto Cleanup;
  4671. }
  4672. NtStatus = FindNetbiosDomainName(
  4673. &dsname,
  4674. Name,
  4675. &len );
  4676. if ( NT_SUCCESS( NtStatus ) ) {
  4677. RtlInitUnicodeString( DomainName, Name );
  4678. } else {
  4679. MIDL_user_free( Name );
  4680. }
  4681. }
  4682. if ( !NT_SUCCESS( NtStatus ) ) {
  4683. //
  4684. // Something failed? Assume not a nt5 domain
  4685. //
  4686. NtStatus = STATUS_NO_SUCH_DOMAIN;
  4687. }
  4688. Cleanup:
  4689. return NtStatus;
  4690. }
  4691. NTSTATUS
  4692. LsapGetDomainSidByNetbiosName(
  4693. IN LPWSTR NetbiosName,
  4694. OUT PSID *Sid
  4695. )
  4696. /*++
  4697. Routine Description
  4698. Given a netbios name, this routine will return the sid of the domain, if
  4699. it exists.
  4700. Parameters:
  4701. NetbiosName -- the name of the domain
  4702. Sid -- domain sid allocated from MIDL
  4703. --*/
  4704. {
  4705. NTSTATUS NtStatus = STATUS_SUCCESS;
  4706. DSNAME *dsname = NULL;
  4707. ULONG len = 0;
  4708. ASSERT( NetbiosName );
  4709. ASSERT( Sid );
  4710. // Init the out param
  4711. *Sid = NULL;
  4712. // Check with the DS
  4713. NtStatus = MatchDomainDnByNetbiosName( NetbiosName,
  4714. NULL,
  4715. &len );
  4716. if ( NT_SUCCESS( NtStatus ) ) {
  4717. SafeAllocaAllocate( dsname, len );
  4718. if ( dsname == NULL ) {
  4719. NtStatus = STATUS_NO_MEMORY;
  4720. goto Cleanup;
  4721. }
  4722. NtStatus = MatchDomainDnByNetbiosName( NetbiosName,
  4723. dsname,
  4724. &len );
  4725. if ( NT_SUCCESS( NtStatus )
  4726. && (dsname->SidLen > 0) ) {
  4727. len = RtlLengthSid( &dsname->Sid );
  4728. *Sid = midl_user_allocate( len );
  4729. if ( !(*Sid) ) {
  4730. SafeAllocaFree( dsname );
  4731. NtStatus = STATUS_NO_MEMORY;
  4732. goto Cleanup;
  4733. }
  4734. RtlCopySid( len, *Sid, &dsname->Sid );
  4735. }
  4736. SafeAllocaFree( dsname );
  4737. }
  4738. if ( !(*Sid) ) {
  4739. //
  4740. // Something failed? Assume not a nt5 domain
  4741. //
  4742. NtStatus = STATUS_NO_SUCH_DOMAIN;
  4743. }
  4744. Cleanup:
  4745. return NtStatus;
  4746. }
  4747. NTSTATUS
  4748. LsapGetDomainSidByDnsName(
  4749. IN LPWSTR DnsName,
  4750. OUT PSID *Sid
  4751. )
  4752. /*++
  4753. Routine Description
  4754. Given a dns name, this routine will return the sid of the domain, if
  4755. it exists.
  4756. Parameters:
  4757. DnsName -- the name of the domain
  4758. Sid -- domain sid allocated from MIDL
  4759. --*/
  4760. {
  4761. NTSTATUS NtStatus = STATUS_SUCCESS;
  4762. DSNAME *dsname = NULL;
  4763. ULONG len = 0;
  4764. ASSERT( DnsName );
  4765. ASSERT( Sid );
  4766. // Init the out param
  4767. *Sid = NULL;
  4768. // Check with the DS
  4769. NtStatus = MatchDomainDnByDnsName( DnsName,
  4770. NULL,
  4771. &len );
  4772. if ( NT_SUCCESS( NtStatus ) ) {
  4773. SafeAllocaAllocate( dsname, len );
  4774. if ( dsname == NULL ) {
  4775. NtStatus = STATUS_NO_MEMORY;
  4776. goto Cleanup;
  4777. }
  4778. NtStatus = MatchDomainDnByDnsName( DnsName,
  4779. dsname,
  4780. &len );
  4781. if ( NT_SUCCESS( NtStatus )
  4782. && (dsname->SidLen > 0) ) {
  4783. len = RtlLengthSid( &dsname->Sid );
  4784. *Sid = midl_user_allocate( len );
  4785. if ( !(*Sid) ) {
  4786. SafeAllocaFree( dsname );
  4787. NtStatus = STATUS_NO_MEMORY;
  4788. goto Cleanup;
  4789. }
  4790. RtlCopySid( len, *Sid, &dsname->Sid );
  4791. }
  4792. SafeAllocaFree( dsname );
  4793. }
  4794. if ( !(*Sid) ) {
  4795. //
  4796. // Something failed? Assume not a nt5 domain
  4797. //
  4798. NtStatus = STATUS_NO_SUCH_DOMAIN;
  4799. }
  4800. Cleanup:
  4801. return NtStatus;
  4802. }
  4803. VOID
  4804. LsapConvertExTrustToOriginal(
  4805. IN OUT PLSAPR_TRUST_INFORMATION TrustInformation,
  4806. IN PLSAPR_TRUST_INFORMATION_EX TrustInformationEx
  4807. )
  4808. {
  4809. RtlZeroMemory( TrustInformation, sizeof(LSAPR_TRUST_INFORMATION) );
  4810. RtlCopyMemory( &TrustInformation->Name, &TrustInformationEx->FlatName, sizeof(UNICODE_STRING) );
  4811. TrustInformation->Sid = TrustInformationEx->Sid;
  4812. return;
  4813. }
  4814. VOID
  4815. LsapConvertTrustToEx(
  4816. IN OUT PLSAPR_TRUST_INFORMATION_EX TrustInformationEx,
  4817. IN PLSAPR_TRUST_INFORMATION TrustInformation
  4818. )
  4819. {
  4820. RtlZeroMemory( TrustInformationEx, sizeof(LSAPR_TRUST_INFORMATION_EX) );
  4821. RtlCopyMemory( &TrustInformationEx->FlatName, &TrustInformation->Name, sizeof(UNICODE_STRING) );
  4822. TrustInformationEx->Sid = TrustInformation->Sid;
  4823. }
  4824. NTSTATUS
  4825. LsapDbOpenPolicyGc (
  4826. OUT HANDLE *LsaPolicyHandle
  4827. )
  4828. {
  4829. NTSTATUS Status = STATUS_SUCCESS;
  4830. DWORD WinError = ERROR_SUCCESS;
  4831. PDOMAIN_CONTROLLER_INFO DcInfo = NULL;
  4832. OBJECT_ATTRIBUTES ObjectAttributes;
  4833. UNICODE_STRING DcName;
  4834. ASSERT( LsaPolicyHandle );
  4835. InitializeObjectAttributes(
  4836. &ObjectAttributes,
  4837. NULL,
  4838. 0L,
  4839. NULL,
  4840. NULL
  4841. );
  4842. //
  4843. // Find a GC in our forest
  4844. //
  4845. WinError = DsGetDcName(
  4846. NULL, // Local computer
  4847. NULL, // Local domain
  4848. NULL, // no domain guid
  4849. NULL, // site doesn't matter
  4850. (DS_GC_SERVER_REQUIRED |
  4851. DS_RETURN_DNS_NAME |
  4852. DS_DIRECTORY_SERVICE_REQUIRED),
  4853. &DcInfo );
  4854. if ( ERROR_SUCCESS != WinError ) {
  4855. //
  4856. // No GC?
  4857. //
  4858. Status = STATUS_DS_GC_NOT_AVAILABLE;
  4859. goto Finish;
  4860. }
  4861. //
  4862. // Open it
  4863. //
  4864. RtlInitUnicodeString( &DcName, DcInfo->DomainControllerName );
  4865. Status = LsaOpenPolicy( &DcName,
  4866. &ObjectAttributes,
  4867. POLICY_LOOKUP_NAMES,
  4868. LsaPolicyHandle );
  4869. if ( !NT_SUCCESS( Status ) ) {
  4870. //
  4871. // A problem binding means that we can't access the GC for what ever
  4872. // reason so return that code
  4873. //
  4874. Status = STATUS_DS_GC_NOT_AVAILABLE;
  4875. goto Finish;
  4876. }
  4877. Finish:
  4878. if ( DcInfo ) {
  4879. NetApiBufferFree( DcInfo );
  4880. }
  4881. return Status;
  4882. }
  4883. BOOLEAN
  4884. LsapRevisionCanHandleNewErrorCodes (
  4885. IN ULONG Revision
  4886. )
  4887. {
  4888. return (Revision != LSA_CLIENT_PRE_NT5);
  4889. }
  4890. //
  4891. // The LSA Lookup Policy Cache
  4892. //
  4893. //
  4894. // During lookup operations the code frequently needs to know the current
  4895. // Account and DNS domain policy information. Since this information is
  4896. // largely static, it is suited for caching.
  4897. //
  4898. // LsapDbPolicyCache contains the cached information. When the policy changes,
  4899. // a callback function is called (LsapDbLookupDomainCacheNotify) that NULL's
  4900. // out this value. The existing global value is freed in one hour. The next
  4901. // time LsapDbLookupGetDomainInfo is called, new values are placed in the cache.
  4902. // Note that this scheme does not require any locks.
  4903. //
  4904. //
  4905. // This typedef describes the format of the cache
  4906. //
  4907. typedef struct _LSAP_DB_DOMAIN_CACHE_TYPE
  4908. {
  4909. PPOLICY_ACCOUNT_DOMAIN_INFO Account;
  4910. PPOLICY_DNS_DOMAIN_INFO Dns;
  4911. }LSAP_DB_DOMAIN_CACHE_TYPE, *PLSAP_DB_DOMAIN_CACHE_TYPE;
  4912. //
  4913. // This is global cache value, gaurded in code by InterlockedExchangePointer
  4914. //
  4915. PLSAP_DB_DOMAIN_CACHE_TYPE LsapDbPolicyCache = NULL;
  4916. NTSTATUS
  4917. LsapDbLookupFreeDomainCache(
  4918. PVOID p
  4919. )
  4920. /*++
  4921. Routine Description:
  4922. This routine frees a cached copy of the LSA Lookup Policy Cache.
  4923. Arguments:
  4924. p -- a valid pointer to LSAP_DB_DOMAIN_CACHE_TYPE
  4925. Return Values:
  4926. STATUS_SUCCESS
  4927. --*/
  4928. {
  4929. ASSERT(p);
  4930. if (p) {
  4931. PLSAP_DB_DOMAIN_CACHE_TYPE Cache = (PLSAP_DB_DOMAIN_CACHE_TYPE)p;
  4932. if (Cache->Account) {
  4933. LsaIFree_LSAPR_POLICY_INFORMATION(PolicyAccountDomainInformation,
  4934. (PLSAPR_POLICY_INFORMATION) Cache->Account);
  4935. }
  4936. if (Cache->Dns) {
  4937. LsaIFree_LSAPR_POLICY_INFORMATION(PolicyDnsDomainInformation,
  4938. (PLSAPR_POLICY_INFORMATION) Cache->Dns);
  4939. }
  4940. LocalFree(p);
  4941. }
  4942. return STATUS_SUCCESS;
  4943. }
  4944. NTSTATUS
  4945. LsapDbLookupBuildDomainCache(
  4946. OUT PLSAP_DB_DOMAIN_CACHE_TYPE *pCache OPTIONAL
  4947. )
  4948. /*++
  4949. Routine Description:
  4950. This routine queries the LSA to find out the current policy settings
  4951. (Account and DNS domain) and then places the information in the LSA
  4952. Lookup Policy cache.
  4953. Note that the current values are freed after 1 hour.
  4954. Arguments:
  4955. pCache -- the value of the new cache created by this routine; caller should
  4956. not free
  4957. Return Values:
  4958. STATUS_SUCCESS, or a resource error
  4959. --*/
  4960. {
  4961. NTSTATUS Status = STATUS_SUCCESS;
  4962. PPOLICY_ACCOUNT_DOMAIN_INFO LocalAccountDomainInfo = NULL;
  4963. PPOLICY_DNS_DOMAIN_INFO LocalDnsDomainInfo = NULL;
  4964. PLSAP_DB_DOMAIN_CACHE_TYPE NewCache = NULL, OldCache = NULL;
  4965. NewCache = LocalAlloc(LMEM_ZEROINIT, sizeof(*NewCache));
  4966. if (NULL == NewCache) {
  4967. return STATUS_NO_MEMORY;
  4968. }
  4969. Status = LsaIQueryInformationPolicyTrusted(PolicyAccountDomainInformation,
  4970. (PLSAPR_POLICY_INFORMATION *) &LocalAccountDomainInfo);
  4971. if (NT_SUCCESS(Status)) {
  4972. Status = LsaIQueryInformationPolicyTrusted(PolicyDnsDomainInformation,
  4973. (PLSAPR_POLICY_INFORMATION *) &LocalDnsDomainInfo);
  4974. }
  4975. if (NT_SUCCESS(Status)) {
  4976. ASSERT(NULL != LocalAccountDomainInfo);
  4977. ASSERT(NULL != LocalDnsDomainInfo);
  4978. NewCache->Account = LocalAccountDomainInfo;
  4979. LocalAccountDomainInfo = NULL;
  4980. NewCache->Dns = LocalDnsDomainInfo;
  4981. LocalDnsDomainInfo = NULL;
  4982. //
  4983. // Return the new cache to caller
  4984. //
  4985. if (pCache) {
  4986. *pCache = NewCache;
  4987. }
  4988. //
  4989. // Carefully move new cache to global pointer
  4990. //
  4991. OldCache = InterlockedExchangePointer(&LsapDbPolicyCache, NewCache);
  4992. //
  4993. // Don't free the NewCache since it is now in the global space
  4994. //
  4995. NewCache = NULL;
  4996. if (OldCache) {
  4997. LsaIRegisterNotification(LsapDbLookupFreeDomainCache,
  4998. OldCache,
  4999. NOTIFIER_TYPE_INTERVAL,
  5000. 0, // no class
  5001. NOTIFIER_FLAG_ONE_SHOT,
  5002. 60 * 60, // free in one hour
  5003. NULL); // no handle
  5004. //
  5005. // N.B Memory leak of OldCache if failed to register task
  5006. //
  5007. }
  5008. }
  5009. if (LocalAccountDomainInfo) {
  5010. LsaIFree_LSAPR_POLICY_INFORMATION(PolicyAccountDomainInformation,
  5011. (PLSAPR_POLICY_INFORMATION) LocalAccountDomainInfo);
  5012. }
  5013. if (LocalDnsDomainInfo) {
  5014. LsaIFree_LSAPR_POLICY_INFORMATION(PolicyDnsDomainInformation,
  5015. (PLSAPR_POLICY_INFORMATION) LocalDnsDomainInfo);
  5016. }
  5017. if (NewCache) {
  5018. LocalFree(NewCache);
  5019. }
  5020. return Status;
  5021. }
  5022. NTSTATUS
  5023. LsapDbLookupGetDomainInfo(
  5024. OUT PPOLICY_ACCOUNT_DOMAIN_INFO *AccountDomainInfo OPTIONAL,
  5025. OUT PPOLICY_DNS_DOMAIN_INFO *DnsDomainInfo OPTIONAL
  5026. )
  5027. /*++
  5028. Routine Description:
  5029. This routine returns to the caller a reference to the global copy
  5030. of the cached Account or DNS domain policy. Caller must not free.
  5031. Arguments:
  5032. p -- a valid pointer to LSAP_DB_DOMAIN_CACHE_TYPE
  5033. Return Values:
  5034. STATUS_SUCCESS
  5035. --*/
  5036. {
  5037. NTSTATUS NtStatus = STATUS_SUCCESS;
  5038. //
  5039. // Get a copy of the global cache
  5040. //
  5041. PLSAP_DB_DOMAIN_CACHE_TYPE LocalPolicyCache = LsapDbPolicyCache;
  5042. //
  5043. // If the cache is empty, fill it
  5044. //
  5045. if (NULL == LocalPolicyCache) {
  5046. //
  5047. // This will only fail on resource error
  5048. //
  5049. NtStatus = LsapDbLookupBuildDomainCache(&LocalPolicyCache);
  5050. if (!NT_SUCCESS(NtStatus)) {
  5051. return NtStatus;
  5052. }
  5053. }
  5054. //
  5055. // We must have valid values
  5056. //
  5057. ASSERT(NULL != LocalPolicyCache);
  5058. ASSERT(NULL != LocalPolicyCache->Account);
  5059. ASSERT(NULL != LocalPolicyCache->Dns);
  5060. if (AccountDomainInfo) {
  5061. *AccountDomainInfo = LocalPolicyCache->Account;
  5062. }
  5063. if (DnsDomainInfo) {
  5064. *DnsDomainInfo = LocalPolicyCache->Dns;
  5065. }
  5066. return NtStatus;
  5067. }
  5068. NTSTATUS
  5069. LsapDbLookupDomainCacheNotify(
  5070. PVOID p
  5071. )
  5072. /*++
  5073. Routine Description:
  5074. This routine is called when a change occurs to the system's policy. This
  5075. routine rebuilds the LSA policy cache.
  5076. Arguments:
  5077. p -- ignored.
  5078. Return Values:
  5079. STATUS_SUCCESS, or a resource error
  5080. --*/
  5081. {
  5082. PLSAP_DB_DOMAIN_CACHE_TYPE OldCache;
  5083. //
  5084. // Invalidate the current cache
  5085. //
  5086. OldCache = InterlockedExchangePointer(&LsapDbPolicyCache,
  5087. NULL);
  5088. if (OldCache) {
  5089. //
  5090. // Free the memory in an hour
  5091. //
  5092. LsaIRegisterNotification(LsapDbLookupFreeDomainCache,
  5093. OldCache,
  5094. NOTIFIER_TYPE_INTERVAL,
  5095. 0, // no class
  5096. NOTIFIER_FLAG_ONE_SHOT,
  5097. 60 * 60, // free in one hour
  5098. NULL); // no handle
  5099. //
  5100. // N.B Memory leak of OldCache if failed to register task
  5101. //
  5102. }
  5103. return STATUS_SUCCESS;
  5104. UNREFERENCED_PARAMETER(p);
  5105. }
  5106. NTSTATUS
  5107. LsapDbLookupInitPolicyCache(
  5108. VOID
  5109. )
  5110. /*++
  5111. Routine Description:
  5112. This routine initializes the LSA Lookup Policy Cache.
  5113. Arguments:
  5114. None.
  5115. Return Values:
  5116. STATUS_SUCCESS, or a resources error
  5117. --*/
  5118. {
  5119. NTSTATUS Status = STATUS_SUCCESS;
  5120. HANDLE hEvent = NULL;
  5121. PVOID fItem = NULL;
  5122. //
  5123. // Create event to be used to notify us of changes to the domain
  5124. // policy
  5125. //
  5126. hEvent = CreateEvent(NULL, // use default access control
  5127. FALSE, // reset automatically
  5128. FALSE, // start off non-signalled
  5129. NULL );
  5130. if (NULL == hEvent) {
  5131. return STATUS_INSUFFICIENT_RESOURCES;
  5132. }
  5133. //
  5134. // Set the function to be called on change
  5135. //
  5136. fItem = LsaIRegisterNotification(LsapDbLookupDomainCacheNotify,
  5137. NULL,
  5138. NOTIFIER_TYPE_HANDLE_WAIT,
  5139. 0, // no class,
  5140. 0,
  5141. 0,
  5142. hEvent);
  5143. if (NULL == fItem) {
  5144. Status = STATUS_INSUFFICIENT_RESOURCES;
  5145. goto Cleanup;
  5146. }
  5147. //
  5148. // Set the event to be set on change
  5149. //
  5150. Status = LsaRegisterPolicyChangeNotification(PolicyNotifyAccountDomainInformation,
  5151. hEvent);
  5152. if (!NT_SUCCESS(Status)) {
  5153. goto Cleanup;
  5154. }
  5155. Status = LsaRegisterPolicyChangeNotification(PolicyNotifyDnsDomainInformation,
  5156. hEvent);
  5157. if (!NT_SUCCESS(Status)) {
  5158. goto Cleanup;
  5159. }
  5160. Status = LsapDbLookupBuildDomainCache(NULL);
  5161. if (!NT_SUCCESS(Status)) {
  5162. goto Cleanup;
  5163. }
  5164. //
  5165. // Success!
  5166. //
  5167. fItem = NULL;
  5168. hEvent = NULL;
  5169. Cleanup:
  5170. if (fItem) {
  5171. LsaICancelNotification(fItem);
  5172. }
  5173. if (hEvent) {
  5174. CloseHandle(hEvent);
  5175. }
  5176. return Status;
  5177. }
  5178. BOOLEAN
  5179. LsapDbIsStatusConnectionFailure(
  5180. NTSTATUS st
  5181. )
  5182. /*++
  5183. Routine Description:
  5184. This routine returns TRUE if the status provided indicates a trust
  5185. or connection error that prevented the lookup from succeeding.
  5186. Arguments:
  5187. None.
  5188. Return Values:
  5189. TRUE, FALSE
  5190. --*/
  5191. {
  5192. switch (st) {
  5193. case STATUS_TRUSTED_DOMAIN_FAILURE:
  5194. case STATUS_TRUSTED_RELATIONSHIP_FAILURE:
  5195. case STATUS_DS_GC_NOT_AVAILABLE:
  5196. return TRUE;
  5197. }
  5198. return FALSE;
  5199. }
  5200. NTSTATUS
  5201. LsapDbLookupAccessCheck(
  5202. IN LSAPR_HANDLE PolicyHandle
  5203. )
  5204. /*++
  5205. Routine Description:
  5206. This routine performs an access on PolicyHandle to see if the handle
  5207. has the right to perform a lookup.
  5208. Arguments:
  5209. PolicyHanlde -- an RPC context handle
  5210. Return Values:
  5211. STATUS_SUCCESS, STATUS_ACCESS_DENIED, other resource errors
  5212. --*/
  5213. {
  5214. NTSTATUS Status = STATUS_SUCCESS;
  5215. if (PolicyHandle) {
  5216. //
  5217. // Acquire the Lsa Database lock. Verify that the connection handle is
  5218. // valid, is of the expected type and has all of the desired accesses
  5219. // granted. Reference he handle.
  5220. //
  5221. Status = LsapDbReferenceObject(
  5222. PolicyHandle,
  5223. POLICY_LOOKUP_NAMES,
  5224. PolicyObject,
  5225. NullObject,
  5226. LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION
  5227. );
  5228. if (NT_SUCCESS(Status)) {
  5229. //
  5230. // We can dereference the original PolicyHandle and release the lock on
  5231. // the LSA Database; if we need to access the database again, we'll
  5232. // use the trusted Lsa handle and the appropriate API will take
  5233. // the lock as required.
  5234. //
  5235. Status = LsapDbDereferenceObject(
  5236. &PolicyHandle,
  5237. PolicyObject,
  5238. NullObject,
  5239. LSAP_DB_READ_ONLY_TRANSACTION | LSAP_DB_NO_DS_OP_TRANSACTION,
  5240. (SECURITY_DB_DELTA_TYPE) 0,
  5241. Status
  5242. );
  5243. }
  5244. } else {
  5245. //
  5246. // Only NETLOGON can call without a policy handle
  5247. //
  5248. ULONG RpcErr;
  5249. ULONG AuthnLevel = 0;
  5250. ULONG AuthnSvc = 0;
  5251. RpcErr = RpcBindingInqAuthClient(
  5252. NULL,
  5253. NULL, // no privileges
  5254. NULL, // no server principal name
  5255. &AuthnLevel,
  5256. &AuthnSvc,
  5257. NULL // no authorization level
  5258. );
  5259. //
  5260. // If it as authenticated at level packet integrity or better
  5261. // and is done with the netlogon package, allow it through
  5262. //
  5263. if ((RpcErr == ERROR_SUCCESS) &&
  5264. (AuthnLevel >= RPC_C_AUTHN_LEVEL_PKT_INTEGRITY) &&
  5265. (AuthnSvc == RPC_C_AUTHN_NETLOGON )) {
  5266. Status = STATUS_SUCCESS;
  5267. } else {
  5268. Status = STATUS_ACCESS_DENIED;
  5269. }
  5270. }
  5271. return Status;
  5272. }
  5273. NTSTATUS
  5274. LsapDbLookupGetServerConnection(
  5275. IN LSAPR_TRUST_INFORMATION_EX *TrustInfo,
  5276. IN DWORD Flags,
  5277. IN LSAP_LOOKUP_LEVEL LookupLevel,
  5278. IN PLARGE_INTEGER FailedSessionSetupTime, OPTIONAL
  5279. OUT LPWSTR *ServerName,
  5280. OUT NL_OS_VERSION *ServerOsVersion,
  5281. OUT LPWSTR *ServerPrincipalName,
  5282. OUT PVOID *ClientContext,
  5283. OUT ULONG *AuthnLevel,
  5284. OUT LSA_HANDLE *PolicyHandle,
  5285. OUT PLSAP_BINDING_CACHE_ENTRY * CachedPolicyEntry,
  5286. OUT PLARGE_INTEGER SessionSetupTime
  5287. )
  5288. /*++
  5289. Routine Description:
  5290. This routines returns connection information to a domain controller in the
  5291. domain specified by TrustInfo, if one can be found.
  5292. Primarily, this routine uses I_NetLogonGetAuthData to obtain the secure
  5293. channel DC.
  5294. Once a DC is found, if the DC is Whistler and we have a client context,
  5295. then we can exit since that is what is needed by the Whister protocol.
  5296. Otherwise, the call falls back to obtaining an LSA policy handle.
  5297. OUT Parameters:
  5298. --------------
  5299. OUT parameters are only allocated on success.
  5300. ServerName, ServerOsVersion are returned on all successes.
  5301. ServerPrincipalNames, ClientContext, AuthLevel are returned when the
  5302. target DC of the sucure channel is .NET or greater. This is to allow
  5303. an RPC call to use the authentication information directly. Otherwise, if
  5304. the DC is not Whistler or greater, these OUT parameters are NULL.
  5305. PolicyHandle is returned when the target DC is a revision less than .NET
  5306. and the "handle cache" is not in effect. The "handle cache" is used only
  5307. for secure channels to trusted domains (ie DC to DC communication). Otherwise
  5308. this parameter is set to NULL on return
  5309. CachedPolicyEntry is returned when the target DC is a revision less than
  5310. .NET and the "handle cache" is in effect. Otherwise, this parameter is
  5311. set to NULL on return.
  5312. SessionSetupTime is the time NETLOGON established the session time. This
  5313. is used to know whether to reset the connection, or not.
  5314. Retry Logic:
  5315. -----------
  5316. Since NETLOGON maintains a cache of security connections to trusted domain
  5317. DC's, it is possible that the cache becomes out of date. In this case,
  5318. the ClientContext returned won't work as an authenticated blob against
  5319. the target DC and RPC calls will fail with STATUS_ACCESS_DENIED or (
  5320. STATUS_RPC_SERVER_UNAVAILABLE if the target server is not alive). So,
  5321. the lookup code will take whatever ClientContext is returned from NETLOGON
  5322. (via I_NetLogonGetAuthData) and attempt to use it for an RPC call. If
  5323. the RPC calls fails with access denied or server unavailable then
  5324. I_NetLogonGetAuthData is called again with the FailedSessionSetupTime. This
  5325. tells NETLOGON that the client context returned was stale. NETLOGON will
  5326. then attempt to reset the secure channel to the target domain in order
  5327. to obtain a new ClientContext for the lookup code to use.
  5328. Also, when talking to an .NET or greater server, the RPC call to lookup
  5329. the names is outside this function thus must communicate via the IN
  5330. parameter FailedSessionSetupTime as to whether the previously given context
  5331. was good or not.
  5332. Here are the algorithms. Each one is ultimately what happens when the
  5333. target domain is the particular OS in the title. So the algorithm under
  5334. NT4 is what this code does when talking to a NT4 domain, etc.
  5335. NT4 (or above when signing and sealing is turned off)
  5336. ------------------------------------------------------
  5337. FailedSessionSetupTime = NULL;
  5338. RetryNetLogon:
  5339. Call I_NetLogonGetAuthData(FailedSessionSetupTime,
  5340. &ServerName,
  5341. &SessionSetupTime)
  5342. if err
  5343. return
  5344. Look in LSA policy handle cache for a match on target domain
  5345. if found
  5346. call LsaQueryInformationPolicy to verify handle
  5347. if !err
  5348. return success
  5349. else
  5350. remove handle from cache and continue
  5351. endif
  5352. endif
  5353. call LsaOpenPolicy(ServerName, POLICY_LOOKUP)
  5354. if err
  5355. if (FailedSessionSetupTime == NULL)
  5356. FailedSessionSetupTime = &SessionSetupTime
  5357. goto RetryNetLogon;
  5358. else
  5359. return failure
  5360. endif
  5361. else
  5362. add handle to cache
  5363. return success
  5364. endif
  5365. Window2000
  5366. ----------
  5367. FailedSessionSetupTime = NULL
  5368. RetryNetLogon:
  5369. Call I_NetLogonGetAuthData(FailedSessionSetupTime,
  5370. &ServerName,
  5371. &SessionSetupTime,
  5372. &ClientContext)
  5373. if err
  5374. return
  5375. Look in LSA policy handle cache for a match on target domain
  5376. if found
  5377. call LsaQueryInformationPolicy(ServerName) to verify handle
  5378. if !err
  5379. return success
  5380. else
  5381. remove handle from cache
  5382. endif
  5383. endif
  5384. // The reasoning for the PolicyAccess is that at one point the process may
  5385. // go off machine as Anonymous and may not have access to POLICY_LOOKUP so
  5386. // don't ask for it.
  5387. PolicyAccess = VIEW_LOCAL_INFORMATION;
  5388. RetryLsaOpenPolicy:
  5389. call LsaOpenPolicy(ServerName, PolicyAccess)
  5390. if server unavailable or access denied
  5391. // if we've already retried, bail
  5392. if FailedSessionSetupTime != NULL return failure
  5393. // retry, indicating the bad context
  5394. FailedSessionSetupTime = &SessionSetupTime;
  5395. goto RetryNetLogon
  5396. else if success
  5397. Set ClientContext on RPC binding
  5398. Call LsaQueryInformationPolicy
  5399. if access denied or authentication service unknown
  5400. if PolicyAccess == POLICY_LOOKUP
  5401. // The ClientContext isn't working, retry
  5402. FailedSessionSetupTime = &SessionSetupTime;
  5403. goto RetryNetLogon
  5404. else
  5405. // The reasoning here is that the target server may not have
  5406. // understood the NETLOGON authentication package so retry
  5407. // asking for POLICY_LOOKUP
  5408. PolicyAccess = POLICY_LOOKUP
  5409. goto RetryLsaOpenPolicy
  5410. endif
  5411. else if success
  5412. put handle in cache and return
  5413. else
  5414. return failure
  5415. endif
  5416. else
  5417. return failure
  5418. endif
  5419. .NET
  5420. ----
  5421. Call I_NetLogonGetAuthData(FailedSessionSetupTime,
  5422. &ServerName,
  5423. &SessionSetupTime,
  5424. &ClientContext)
  5425. if !err
  5426. Call LsaILookupNames/SidsWithCreds(ServerName, ClientContext)
  5427. if err is access denied or server unavailable
  5428. FailedSessionSetupTime = &SessionSetupTime;
  5429. Call I_NetLogonGetAuthData(FailedSessionSetupTime,
  5430. &ServerName,
  5431. &SessionSetupTime,
  5432. &ClientContext)
  5433. if !err
  5434. LsaILookupNames/SidsWithCreds(ServerName, ClientContext)
  5435. Arguments:
  5436. TrustInfo -- contains the destination domain; at least one of DomainName
  5437. or FlatName must be present; Sid is optional.
  5438. Flags -- None.
  5439. LookupLevel -- the kind of chaining being performed
  5440. FailedSessionSetupTime -- the SessionSetupTime of information returned from
  5441. NETLOGON that didn't work.
  5442. Server -- out, the destination server name, NULL terminated
  5443. ServerOsVersion -- out, the OS version
  5444. ServerPrincipalName,
  5445. ClientContext,
  5446. AuthnLevel -- out, goo needed for RpcSetAuthInfo
  5447. PolicyHandle -- out, a policy to the destination DC.
  5448. CachedPolicyEntry -- out, a binding handle cache (only for TDL's)
  5449. SessionSetupTime -- a time stamp of the information returned by NETLOGON
  5450. Return Values:
  5451. STATUS_SUCCESS, or fatal resource or network related error.
  5452. --*/
  5453. {
  5454. NTSTATUS Status = STATUS_SUCCESS;
  5455. LPWSTR TargetDomain = NULL;
  5456. PUNICODE_STRING TargetDomainU;
  5457. ULONG Size;
  5458. ULONG NlFlags = 0;
  5459. LPWSTR NetlogonServerName = NULL;
  5460. PLARGE_INTEGER LocalFailedSessionSetupTime = FailedSessionSetupTime;
  5461. //
  5462. // Init the out parameters
  5463. //
  5464. *ServerName = NULL;
  5465. *ServerPrincipalName = NULL;
  5466. *ClientContext = NULL;
  5467. *PolicyHandle = NULL;
  5468. *CachedPolicyEntry = NULL;
  5469. if (TrustInfo->DomainName.Length > 0) {
  5470. TargetDomainU = (PUNICODE_STRING) &TrustInfo->DomainName;
  5471. } else {
  5472. TargetDomainU = (PUNICODE_STRING) &TrustInfo->FlatName;
  5473. }
  5474. ASSERT(TargetDomainU->Length > 0);
  5475. //
  5476. // Make the domain name null terminated
  5477. //
  5478. Size = TargetDomainU->Length + sizeof(WCHAR);
  5479. SafeAllocaAllocate( TargetDomain, Size );
  5480. if ( TargetDomain == NULL ) {
  5481. Status = STATUS_NO_MEMORY;
  5482. goto Cleanup;
  5483. }
  5484. RtlZeroMemory(TargetDomain, Size);
  5485. RtlCopyMemory(TargetDomain, TargetDomainU->Buffer, TargetDomainU->Length);
  5486. //
  5487. // Determine the type of secure channel we need
  5488. //
  5489. if (LookupLevel == LsapLookupXForestReferral) {
  5490. NlFlags |= NL_RETURN_CLOSEST_HOP;
  5491. } else {
  5492. NlFlags |= NL_DIRECT_TRUST_REQUIRED;
  5493. }
  5494. while (TRUE) {
  5495. //
  5496. // Try to obtain a secure channel
  5497. //
  5498. Status = I_NetLogonGetAuthDataEx(NULL, // local domain
  5499. TargetDomain,
  5500. NlFlags,
  5501. LocalFailedSessionSetupTime,
  5502. ServerPrincipalName,
  5503. ClientContext,
  5504. &NetlogonServerName,
  5505. ServerOsVersion,
  5506. AuthnLevel,
  5507. SessionSetupTime
  5508. );
  5509. if (NT_SUCCESS(Status)) {
  5510. //
  5511. // Realloc the ServerName
  5512. //
  5513. Size = (wcslen(NetlogonServerName) + 1) * sizeof(WCHAR);
  5514. *ServerName = LocalAlloc(LMEM_ZEROINIT, Size);
  5515. if (NULL == *ServerName) {
  5516. Status = STATUS_NO_MEMORY;
  5517. goto Cleanup;
  5518. }
  5519. wcscpy(*ServerName, NetlogonServerName);
  5520. I_NetLogonFree( NetlogonServerName );
  5521. NetlogonServerName = NULL;
  5522. }
  5523. if (!NT_SUCCESS(Status)) {
  5524. //
  5525. // This is a fatal error for this batch of names
  5526. //
  5527. LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: I_NetLogonGetAuthDataEx to %ws failed (0x%x)\n", TargetDomain, Status));
  5528. LsapDbLookupReportEvent2( 1,
  5529. EVENTLOG_WARNING_TYPE,
  5530. LSAEVENT_LOOKUP_SC_FAILED,
  5531. sizeof( ULONG ),
  5532. &Status,
  5533. TargetDomain,
  5534. TargetDomain );
  5535. goto Cleanup;
  5536. }
  5537. ASSERT(NULL != *ServerName);
  5538. //
  5539. // We have a destination DC
  5540. //
  5541. if ( (*ServerOsVersion < NlWhistler)
  5542. || ((*ServerOsVersion >= NlWhistler) && (*ClientContext == NULL)) ) {
  5543. //
  5544. // Get a policy handle because either
  5545. //
  5546. // 1. Secure channel DC is not at least Whistler
  5547. // 2. Secure channel is configured to not use auth blob (no sign or seal)
  5548. //
  5549. LSAPR_TRUST_INFORMATION TrustInformation;
  5550. UNICODE_STRING DomainControllerName;
  5551. RtlInitUnicodeString(&DomainControllerName, *ServerName);
  5552. //
  5553. // We use the flat name here since part of the validate routine
  5554. // compares the name against the AccountDomainName of the target
  5555. // domain controller.
  5556. //
  5557. RtlZeroMemory(&TrustInformation, sizeof(TrustInformation));
  5558. TrustInformation.Name = TrustInfo->FlatName;
  5559. if (LookupLevel == LsapLookupTDL) {
  5560. //
  5561. // Use the caching scheme; note that this routine will take
  5562. // ownership of ServerName, ServerPrincipalName, and ClientContext
  5563. // on success (and will hence set them to NULL)
  5564. //
  5565. Status = LsapDbGetCachedHandleTrustedDomain(&TrustInformation,
  5566. POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION,
  5567. ServerName,
  5568. ServerPrincipalName,
  5569. ClientContext,
  5570. CachedPolicyEntry);
  5571. } else {
  5572. Status = LsapRtlValidateControllerTrustedDomain( (PLSAPR_UNICODE_STRING)&DomainControllerName,
  5573. &TrustInformation,
  5574. POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION,
  5575. *ServerPrincipalName,
  5576. *ClientContext,
  5577. PolicyHandle );
  5578. }
  5579. if ( ((STATUS_ACCESS_DENIED == Status)
  5580. || (RPC_NT_SERVER_UNAVAILABLE == Status))
  5581. && (NULL == LocalFailedSessionSetupTime)) {
  5582. //
  5583. // Try again, telling NETLOGON this blob is no good
  5584. //
  5585. if (*ServerPrincipalName != NULL) {
  5586. I_NetLogonFree(*ServerPrincipalName);
  5587. *ServerPrincipalName = NULL;
  5588. }
  5589. if (*ServerName) {
  5590. LocalFree(*ServerName);
  5591. *ServerName = NULL;
  5592. }
  5593. if (*ClientContext) {
  5594. I_NetLogonFree(*ClientContext);
  5595. *ClientContext = NULL;
  5596. }
  5597. LocalFailedSessionSetupTime = SessionSetupTime;
  5598. continue;
  5599. }
  5600. if (!NT_SUCCESS(Status)) {
  5601. //
  5602. // This is a fatal error for this batch of names
  5603. //
  5604. LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: Failed to open a policy handle to %ws (0x%x)\n", *ServerName, Status));
  5605. LsapDbLookupReportEvent2( 1,
  5606. EVENTLOG_WARNING_TYPE,
  5607. LSAEVENT_LOOKUP_SC_HANDLE_FAILED,
  5608. sizeof( ULONG ),
  5609. &Status,
  5610. *ServerName,
  5611. *ServerName );
  5612. goto Cleanup;
  5613. }
  5614. }
  5615. //
  5616. // Exit from the loop
  5617. //
  5618. break;
  5619. }
  5620. Cleanup:
  5621. if (!NT_SUCCESS(Status)) {
  5622. if (*PolicyHandle != NULL) {
  5623. LsaClose( *PolicyHandle );
  5624. *PolicyHandle = NULL;
  5625. }
  5626. if (NetlogonServerName != NULL) {
  5627. I_NetLogonFree(NetlogonServerName);
  5628. }
  5629. if (*ServerPrincipalName != NULL) {
  5630. I_NetLogonFree(*ServerPrincipalName);
  5631. *ServerPrincipalName = NULL;
  5632. }
  5633. if (*ClientContext != NULL) {
  5634. I_NetLogonFree(*ClientContext);
  5635. *ClientContext = NULL;
  5636. }
  5637. if (*ServerName) {
  5638. LocalFree(*ServerName);
  5639. *ServerName = NULL;
  5640. }
  5641. if (*CachedPolicyEntry) {
  5642. LsapDereferenceBindingCacheEntry(*CachedPolicyEntry);
  5643. *CachedPolicyEntry = NULL;
  5644. }
  5645. } else {
  5646. //
  5647. // Either a auth info, a policy handle, or a cached policy handle
  5648. // must be returned
  5649. //
  5650. ASSERT( (*PolicyHandle != NULL)
  5651. || (*ClientContext != NULL)
  5652. || (*CachedPolicyEntry != NULL));
  5653. }
  5654. SafeAllocaFree( TargetDomain );
  5655. return Status;
  5656. }
  5657. NTSTATUS
  5658. LsapDbLookupNameChainRequest(
  5659. IN LSAPR_TRUST_INFORMATION_EX *TrustInfo,
  5660. IN ULONG Count,
  5661. IN PUNICODE_STRING Names,
  5662. OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains,
  5663. OUT PLSA_TRANSLATED_SID_EX2 *Sids,
  5664. IN LSAP_LOOKUP_LEVEL LookupLevel,
  5665. OUT PULONG MappedCount,
  5666. OUT PULONG ServerRevision
  5667. )
  5668. /*++
  5669. Routine Description:
  5670. This routine is the general purpose routine to be used when ever
  5671. a name request needs to be chained (be resolved off machine). This includes
  5672. member -> DC
  5673. DC -> trusted domain DC
  5674. DC -> trusted forest DC
  5675. Arguments:
  5676. TrustInfo -- contains the destination domain; at least one of DomainName
  5677. or FlatName must be present; Sid is optional.
  5678. Count -- the number of Names to be resovled
  5679. Names -- the names to be resolved
  5680. ReferencedDomains -- out, the resolved domain referenced
  5681. Sids -- out, the resolved SID's
  5682. LookupLevel -- the type of chaining requested
  5683. MappedCount -- out, the number of Names fully resolved
  5684. ServerRevision -- the LSA lookup revision of the target
  5685. Return Values:
  5686. STATUS_SUCCESS
  5687. STATUS_NONE_MAPPED
  5688. STATUS_SOME_NOT_MAPPED
  5689. other fatal resource errors
  5690. --*/
  5691. {
  5692. NTSTATUS Status = STATUS_SUCCESS;
  5693. LPWSTR ServerPrincipalName = NULL;
  5694. LPWSTR ServerName = NULL;
  5695. PVOID ClientContext = NULL;
  5696. ULONG AuthnLevel;
  5697. NL_OS_VERSION ServerOsVersion;
  5698. LSA_HANDLE ControllerPolicyHandle = NULL;
  5699. BOOLEAN fLookupCallFailed = FALSE;
  5700. PUNICODE_STRING DestinationDomain;
  5701. PLSAP_BINDING_CACHE_ENTRY ControllerPolicyEntry = NULL;
  5702. LPWSTR TargetServerName = NULL;
  5703. LARGE_INTEGER SessionSetupTime = {0};
  5704. PLARGE_INTEGER FailedSessionSetupTime = NULL;
  5705. if (TrustInfo->DomainName.Length > 0) {
  5706. DestinationDomain = (PUNICODE_STRING)&TrustInfo->DomainName;
  5707. } else {
  5708. DestinationDomain = (PUNICODE_STRING)&TrustInfo->FlatName;
  5709. }
  5710. ASSERT(DestinationDomain->Length > 0);
  5711. LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: Chaining a name request to %wZ of type %ws\n", DestinationDomain, LsapDbLookupGetLevel(LookupLevel)) );
  5712. while (TRUE) {
  5713. Status = LsapDbLookupGetServerConnection(TrustInfo,
  5714. 0,
  5715. LookupLevel,
  5716. FailedSessionSetupTime,
  5717. &ServerName,
  5718. &ServerOsVersion,
  5719. &ServerPrincipalName,
  5720. &ClientContext,
  5721. &AuthnLevel,
  5722. &ControllerPolicyHandle,
  5723. &ControllerPolicyEntry,
  5724. &SessionSetupTime
  5725. );
  5726. if (!NT_SUCCESS(Status)) {
  5727. //
  5728. // This is a fatal error for this batch of names
  5729. //
  5730. LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: Can't get server connection to %wZ (0x%x)\n", DestinationDomain, Status));
  5731. goto Cleanup;
  5732. }
  5733. //
  5734. // We have the secure channel
  5735. //
  5736. if ( (ServerOsVersion >= NlWhistler)
  5737. && (ClientContext != NULL) ) {
  5738. //
  5739. // Use new version
  5740. //
  5741. Status = LsaICLookupNamesWithCreds(ServerName,
  5742. ServerPrincipalName,
  5743. AuthnLevel,
  5744. RPC_C_AUTHN_NETLOGON,
  5745. ClientContext,
  5746. RPC_C_AUTHZ_NONE,
  5747. Count,
  5748. (PUNICODE_STRING)Names,
  5749. (PLSA_REFERENCED_DOMAIN_LIST *)ReferencedDomains,
  5750. (PLSA_TRANSLATED_SID_EX2 * )Sids,
  5751. LookupLevel,
  5752. MappedCount);
  5753. if (((Status == STATUS_ACCESS_DENIED)
  5754. || (Status == RPC_NT_SERVER_UNAVAILABLE))
  5755. && (FailedSessionSetupTime == NULL) ) {
  5756. //
  5757. // NETLOGON's auth data has gone stale
  5758. //
  5759. if (ServerName != NULL) {
  5760. LocalFree(ServerName);
  5761. ServerName = NULL;
  5762. }
  5763. if (ServerPrincipalName != NULL) {
  5764. I_NetLogonFree(ServerPrincipalName);
  5765. ServerPrincipalName = NULL;
  5766. }
  5767. if (ClientContext != NULL) {
  5768. I_NetLogonFree(ClientContext);
  5769. ClientContext = NULL;
  5770. }
  5771. FailedSessionSetupTime = &SessionSetupTime;
  5772. ASSERT( NULL == ControllerPolicyHandle);
  5773. ASSERT( NULL == ControllerPolicyEntry);
  5774. continue;
  5775. }
  5776. if (ServerRevision) {
  5777. *ServerRevision = LSA_CLIENT_LATEST;
  5778. }
  5779. TargetServerName = ServerName;
  5780. if (!NT_SUCCESS(Status)
  5781. && Status != STATUS_NONE_MAPPED ) {
  5782. //
  5783. // This is a fatal error for this batch of names
  5784. //
  5785. LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: LsaICLookupNamesWithCreds to %ws failed (0x%x)\n", ServerName, Status));
  5786. fLookupCallFailed = TRUE;
  5787. goto Cleanup;
  5788. }
  5789. LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: LsaICLookupNamesWithCreds to %ws succeeded\n", ServerName));
  5790. } else {
  5791. LSA_HANDLE TargetHandle;
  5792. ULONG LsaICLookupFlags = 0;
  5793. LsaICLookupFlags = LsapLookupGetChainingFlags(ServerOsVersion);
  5794. if (ControllerPolicyEntry) {
  5795. TargetHandle = ControllerPolicyEntry->PolicyHandle;
  5796. TargetServerName = ControllerPolicyEntry->ServerName;
  5797. ASSERT( NULL == ControllerPolicyHandle);
  5798. } else {
  5799. TargetHandle = ControllerPolicyHandle;
  5800. TargetServerName = ServerName;
  5801. }
  5802. ASSERT(NULL != TargetHandle);
  5803. Status = LsaICLookupNames(
  5804. TargetHandle,
  5805. 0, // no flags necessary
  5806. Count,
  5807. (PUNICODE_STRING) Names,
  5808. (PLSA_REFERENCED_DOMAIN_LIST *) ReferencedDomains,
  5809. (PLSA_TRANSLATED_SID_EX2 *) Sids,
  5810. LookupLevel,
  5811. LsaICLookupFlags,
  5812. MappedCount,
  5813. ServerRevision
  5814. );
  5815. if (!NT_SUCCESS(Status)
  5816. && Status != STATUS_NONE_MAPPED ) {
  5817. //
  5818. // This is a fatal error for this batch of names
  5819. //
  5820. LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: LsaICLookupNames to %ws failed (0x%x)\n", TargetServerName, Status));
  5821. fLookupCallFailed = TRUE;
  5822. goto Cleanup;
  5823. }
  5824. LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: LsaICLookupNames to %ws succeeded\n", TargetServerName));
  5825. }
  5826. break;
  5827. }
  5828. Cleanup:
  5829. if (fLookupCallFailed) {
  5830. ASSERT(NULL != TargetServerName);
  5831. LsapDbLookupReportEvent2( 1,
  5832. EVENTLOG_WARNING_TYPE,
  5833. LSAEVENT_LOOKUP_SC_LOOKUP_FAILED,
  5834. sizeof( ULONG ),
  5835. &Status,
  5836. TargetServerName,
  5837. TargetServerName );
  5838. }
  5839. if ( !NT_SUCCESS(Status)
  5840. && (Status != STATUS_NONE_MAPPED)
  5841. && !LsapDbIsStatusConnectionFailure(Status)) {
  5842. //
  5843. // An error occurred not one that our callers will understand.
  5844. // The specific error has already been logged, so return a general one.
  5845. //
  5846. if (LookupLevel == LsapLookupPDC) {
  5847. Status = STATUS_TRUSTED_RELATIONSHIP_FAILURE;
  5848. } else {
  5849. Status = STATUS_TRUSTED_DOMAIN_FAILURE;
  5850. }
  5851. }
  5852. if (ControllerPolicyHandle != NULL) {
  5853. LsaClose( ControllerPolicyHandle );
  5854. }
  5855. if (ServerName != NULL) {
  5856. LocalFree(ServerName);
  5857. }
  5858. if (ServerPrincipalName != NULL) {
  5859. I_NetLogonFree(ServerPrincipalName);
  5860. }
  5861. if (ClientContext != NULL) {
  5862. I_NetLogonFree(ClientContext);
  5863. }
  5864. if (ControllerPolicyEntry) {
  5865. LsapDereferenceBindingCacheEntry( ControllerPolicyEntry );
  5866. }
  5867. return Status;
  5868. }
  5869. NTSTATUS
  5870. LsaDbLookupSidChainRequest(
  5871. IN LSAPR_TRUST_INFORMATION_EX *TrustInfo,
  5872. IN ULONG Count,
  5873. IN PSID *Sids,
  5874. OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains,
  5875. OUT PLSA_TRANSLATED_NAME_EX *Names,
  5876. IN LSAP_LOOKUP_LEVEL LookupLevel,
  5877. IN OUT PULONG MappedCount,
  5878. OUT PULONG ServerRevision OPTIONAL
  5879. )
  5880. /*++
  5881. Routine Description:
  5882. This routine is the general purpose routine to be used when ever
  5883. a SID request needs to be chained (be resolved off machine). This includes
  5884. member -> DC
  5885. DC -> trusted domain DC
  5886. DC -> trusted forest DC
  5887. Arguments:
  5888. TrustInfo -- contains the destination domain; at least one of DomainName
  5889. or FlatName must be present; Sid is optional.
  5890. Count -- the number of SIDs to be resovled
  5891. Sids -- the Sids to be resolved
  5892. ReferencedDomains -- out, the resolved domain referenced
  5893. Names -- out, the resolved names's
  5894. LookupLevel -- the type of chaining requested
  5895. MappedCount -- out, the number of Names fully resolved
  5896. ServerRevision -- the LSA lookup revision of the target
  5897. Return Values:
  5898. STATUS_SUCCESS
  5899. STATUS_NONE_MAPPED
  5900. STATUS_SOME_NOT_MAPPED
  5901. STATUS_TRUSTED_DOMAIN_FAILURE
  5902. STATUS_TRUSTED_RELATIONSHIP_FAILURE
  5903. other fatal resource errors
  5904. --*/
  5905. {
  5906. NTSTATUS Status = STATUS_SUCCESS;
  5907. LPWSTR ServerName = NULL;
  5908. NL_OS_VERSION ServerOsVersion;
  5909. LPWSTR ServerPrincipalName = NULL;
  5910. PVOID ClientContext = NULL;
  5911. ULONG AuthnLevel;
  5912. LSA_HANDLE ControllerPolicyHandle = NULL;
  5913. BOOLEAN fLookupCallFailed = FALSE;
  5914. PUNICODE_STRING DestinationDomain;
  5915. PLSAP_BINDING_CACHE_ENTRY ControllerPolicyEntry = NULL;
  5916. LPWSTR TargetServerName = NULL;
  5917. LARGE_INTEGER SessionSetupTime = {0};
  5918. PLARGE_INTEGER FailedSessionSetupTime = NULL;
  5919. if (TrustInfo->DomainName.Length > 0) {
  5920. DestinationDomain = (PUNICODE_STRING)&TrustInfo->DomainName;
  5921. } else {
  5922. DestinationDomain = (PUNICODE_STRING)&TrustInfo->FlatName;
  5923. }
  5924. ASSERT(DestinationDomain->Length > 0);
  5925. LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: Chaining a SID request to %wZ of type %ws\n", DestinationDomain, LsapDbLookupGetLevel(LookupLevel)) );
  5926. while (TRUE) {
  5927. Status = LsapDbLookupGetServerConnection(TrustInfo,
  5928. 0,
  5929. LookupLevel,
  5930. FailedSessionSetupTime,
  5931. &ServerName,
  5932. &ServerOsVersion,
  5933. &ServerPrincipalName,
  5934. &ClientContext,
  5935. &AuthnLevel,
  5936. &ControllerPolicyHandle,
  5937. &ControllerPolicyEntry,
  5938. &SessionSetupTime
  5939. );
  5940. if (!NT_SUCCESS(Status)) {
  5941. //
  5942. // This is a fatal error for this batch of names
  5943. //
  5944. LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: Can't get server connection to %wZ failed (0x%x)\n", DestinationDomain, Status));
  5945. goto Cleanup;
  5946. }
  5947. //
  5948. // We have the secure channel
  5949. //
  5950. if ( (ServerOsVersion >= NlWhistler)
  5951. && (ClientContext != NULL)) {
  5952. //
  5953. // Use new version
  5954. //
  5955. Status = LsaICLookupSidsWithCreds(ServerName,
  5956. ServerPrincipalName,
  5957. AuthnLevel,
  5958. RPC_C_AUTHN_NETLOGON,
  5959. ClientContext,
  5960. RPC_C_AUTHZ_NONE,
  5961. Count,
  5962. (PSID*)Sids,
  5963. (PLSA_REFERENCED_DOMAIN_LIST *)ReferencedDomains,
  5964. (PLSA_TRANSLATED_NAME_EX * )Names,
  5965. LookupLevel,
  5966. MappedCount);
  5967. if (((Status == STATUS_ACCESS_DENIED)
  5968. || (Status == RPC_NT_SERVER_UNAVAILABLE))
  5969. && (FailedSessionSetupTime == NULL)) {
  5970. //
  5971. // NETLOGON's auth data has gone stale
  5972. //
  5973. if (ServerName != NULL) {
  5974. LocalFree(ServerName);
  5975. ServerName = NULL;
  5976. }
  5977. if (ServerPrincipalName != NULL) {
  5978. I_NetLogonFree(ServerPrincipalName);
  5979. ServerPrincipalName = NULL;
  5980. }
  5981. if (ClientContext != NULL) {
  5982. I_NetLogonFree(ClientContext);
  5983. ClientContext = NULL;
  5984. }
  5985. FailedSessionSetupTime = &SessionSetupTime;
  5986. ASSERT( NULL == ControllerPolicyHandle);
  5987. ASSERT( NULL == ControllerPolicyEntry);
  5988. continue;
  5989. }
  5990. if (ServerRevision) {
  5991. *ServerRevision = LSA_CLIENT_LATEST;
  5992. }
  5993. TargetServerName = ServerName;
  5994. if (!NT_SUCCESS(Status)
  5995. && Status != STATUS_NONE_MAPPED ) {
  5996. //
  5997. // This is a fatal error for this batch of names
  5998. //
  5999. LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: LsaICLookupSidsWithCreds to %ws failed 0x%x\n", ServerName, Status));
  6000. fLookupCallFailed = TRUE;
  6001. goto Cleanup;
  6002. } else {
  6003. LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: LsaICLookupSidsWithCreds to %ws succeeded\n", ServerName));
  6004. }
  6005. } else {
  6006. //
  6007. // Must use downlevel API
  6008. //
  6009. LSA_HANDLE TargetHandle;
  6010. ULONG LsaICLookupFlags = 0;
  6011. LsaICLookupFlags = LsapLookupGetChainingFlags(ServerOsVersion);
  6012. if (ControllerPolicyEntry) {
  6013. TargetHandle = ControllerPolicyEntry->PolicyHandle;
  6014. TargetServerName = ControllerPolicyEntry->ServerName;
  6015. ASSERT( NULL == ControllerPolicyHandle);
  6016. } else {
  6017. TargetHandle = ControllerPolicyHandle;
  6018. TargetServerName = ServerName;
  6019. }
  6020. ASSERT(NULL != TargetHandle);
  6021. Status = LsaICLookupSids(
  6022. TargetHandle,
  6023. Count,
  6024. (PSID*) Sids,
  6025. (PLSA_REFERENCED_DOMAIN_LIST *) ReferencedDomains,
  6026. (PLSA_TRANSLATED_NAME_EX *) Names,
  6027. LookupLevel,
  6028. LsaICLookupFlags,
  6029. MappedCount,
  6030. ServerRevision
  6031. );
  6032. if (!NT_SUCCESS(Status)
  6033. && Status != STATUS_NONE_MAPPED ) {
  6034. //
  6035. // This is a fatal error for this batch of names
  6036. //
  6037. LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: LsaICLookupNames to %ws failed 0x%x\n", TargetServerName, Status));
  6038. fLookupCallFailed = TRUE;
  6039. goto Cleanup;
  6040. } else {
  6041. LsapDiagPrint( DB_LOOKUP_WORK_LIST, ("LSA: LsaICLookupNames to %ws succeeded\n", TargetServerName));
  6042. }
  6043. }
  6044. break;
  6045. }
  6046. Cleanup:
  6047. if (fLookupCallFailed) {
  6048. ASSERT(NULL != TargetServerName);
  6049. LsapDbLookupReportEvent2( 1,
  6050. EVENTLOG_WARNING_TYPE,
  6051. LSAEVENT_LOOKUP_SC_LOOKUP_FAILED,
  6052. sizeof( ULONG ),
  6053. &Status,
  6054. TargetServerName,
  6055. TargetServerName );
  6056. }
  6057. if ( !NT_SUCCESS(Status)
  6058. && (Status != STATUS_NONE_MAPPED)
  6059. && !LsapDbIsStatusConnectionFailure(Status)) {
  6060. //
  6061. // An error occurred not one that our callers will understand.
  6062. // The specific error has already been logged, so return a general one.
  6063. //
  6064. if (LookupLevel == LsapLookupPDC) {
  6065. Status = STATUS_TRUSTED_RELATIONSHIP_FAILURE;
  6066. } else {
  6067. Status = STATUS_TRUSTED_DOMAIN_FAILURE;
  6068. }
  6069. }
  6070. if (ServerName != NULL) {
  6071. LocalFree(ServerName);
  6072. }
  6073. if (ServerPrincipalName != NULL) {
  6074. I_NetLogonFree(ServerPrincipalName);
  6075. }
  6076. if (ClientContext != NULL) {
  6077. I_NetLogonFree(ClientContext);
  6078. }
  6079. if (ControllerPolicyHandle != NULL) {
  6080. LsaClose( ControllerPolicyHandle );
  6081. }
  6082. if (ControllerPolicyEntry) {
  6083. LsapDereferenceBindingCacheEntry( ControllerPolicyEntry );
  6084. }
  6085. return Status;
  6086. }
  6087. NTSTATUS
  6088. LsapLookupReallocateTranslations(
  6089. IN OUT PLSA_REFERENCED_DOMAIN_LIST *ReferencedDomains,
  6090. IN ULONG Count,
  6091. IN OUT PLSA_TRANSLATED_NAME_EX * Names, OPTIONAL
  6092. IN OUT PLSA_TRANSLATED_SID_EX2 * Sids OPTIONAL
  6093. )
  6094. /*++
  6095. Routine Description:
  6096. This routine reallocates ReferencedDomains, Names, and Sids, from
  6097. allocate_all_nodes, to !allocate_all_nodes. This is so that values
  6098. returned from chaining calls can be returned to the caller.
  6099. Arguments:
  6100. ReferencedDomains -- the referenced domains to reallocate, if any
  6101. Count -- the number of entries in either Names or Sids
  6102. Names -- the names to reallocate, if any
  6103. Sids -- the sids to reallocate, if any
  6104. Return Values:
  6105. STATUS_SUCCESS, STATUS_NO_MEMORY
  6106. --*/
  6107. {
  6108. NTSTATUS Status = STATUS_SUCCESS;
  6109. PLSA_REFERENCED_DOMAIN_LIST LocalReferencedDomains = NULL;
  6110. PLSA_TRANSLATED_NAME_EX LocalNames = NULL;
  6111. PLSA_TRANSLATED_SID_EX2 LocalSids = NULL;
  6112. ULONG Length, i;
  6113. PVOID Src = NULL, Dst = NULL;
  6114. // Only one is allowed
  6115. ASSERT(!((Names && *Names) && (Sids && *Sids)));
  6116. if (*ReferencedDomains) {
  6117. LocalReferencedDomains = midl_user_allocate(sizeof(LSA_REFERENCED_DOMAIN_LIST));
  6118. if (NULL == LocalReferencedDomains) {
  6119. goto MemoryFailure;
  6120. }
  6121. Length = sizeof(LSA_TRUST_INFORMATION) * (*ReferencedDomains)->Entries;
  6122. LocalReferencedDomains->Domains = midl_user_allocate(Length);
  6123. if (NULL == LocalReferencedDomains->Domains) {
  6124. goto MemoryFailure;
  6125. }
  6126. RtlZeroMemory(LocalReferencedDomains->Domains, Length);
  6127. LocalReferencedDomains->Entries = (*ReferencedDomains)->Entries;
  6128. for (i = 0; i < LocalReferencedDomains->Entries; i++) {
  6129. Src= (*ReferencedDomains)->Domains[i].Name.Buffer;
  6130. if (Src) {
  6131. Length = (*ReferencedDomains)->Domains[i].Name.Length;
  6132. Dst = midl_user_allocate(Length);
  6133. if (NULL == Dst) {
  6134. goto MemoryFailure;
  6135. }
  6136. RtlCopyMemory(Dst, Src, Length);
  6137. LocalReferencedDomains->Domains[i].Name.Length = (USHORT)Length;
  6138. LocalReferencedDomains->Domains[i].Name.MaximumLength = (USHORT)Length;
  6139. LocalReferencedDomains->Domains[i].Name.Buffer = Dst;
  6140. Dst = NULL;
  6141. }
  6142. Src = (*ReferencedDomains)->Domains[i].Sid;
  6143. if (Src) {
  6144. Length = GetLengthSid(Src);
  6145. Dst = midl_user_allocate(Length);
  6146. if (NULL == Dst) {
  6147. goto MemoryFailure;
  6148. }
  6149. CopySid(Length, Dst, Src);
  6150. LocalReferencedDomains->Domains[i].Sid = Dst;
  6151. Dst = NULL;
  6152. }
  6153. }
  6154. }
  6155. if (Names && *Names) {
  6156. Length = sizeof(LSA_TRANSLATED_NAME_EX) * Count;
  6157. LocalNames = midl_user_allocate(Length);
  6158. if (NULL == LocalNames) {
  6159. goto MemoryFailure;
  6160. }
  6161. RtlZeroMemory(LocalNames, Length);
  6162. for (i = 0; i < Count; i++) {
  6163. LocalNames[i] = (*Names)[i];
  6164. RtlInitUnicodeString(&LocalNames[i].Name, NULL);
  6165. Src = (*Names)[i].Name.Buffer;
  6166. if (Src) {
  6167. Length = (*Names)[i].Name.Length;
  6168. Dst = midl_user_allocate(Length);
  6169. if (NULL == Dst) {
  6170. goto MemoryFailure;
  6171. }
  6172. RtlCopyMemory(Dst, Src, Length);
  6173. LocalNames[i].Name.Length = (USHORT)Length;
  6174. LocalNames[i].Name.MaximumLength = (USHORT)Length;
  6175. LocalNames[i].Name.Buffer = Dst;
  6176. Dst = NULL;
  6177. }
  6178. }
  6179. }
  6180. if (Sids && *Sids) {
  6181. Length = sizeof(LSA_TRANSLATED_SID_EX2) * Count;
  6182. LocalSids = midl_user_allocate(Length);
  6183. if (NULL == LocalSids) {
  6184. goto MemoryFailure;
  6185. }
  6186. RtlZeroMemory(LocalSids, Length);
  6187. for (i = 0; i < Count; i++) {
  6188. LocalSids[i] = (*Sids)[i];
  6189. LocalSids[i].Sid = NULL;
  6190. Src = (*Sids)[i].Sid;
  6191. if (Src) {
  6192. Length = GetLengthSid(Src);
  6193. Dst = midl_user_allocate(Length);
  6194. if (NULL == Dst) {
  6195. goto MemoryFailure;
  6196. }
  6197. CopySid(Length, Dst, Src);
  6198. LocalSids[i].Sid = Dst;
  6199. Dst = NULL;
  6200. }
  6201. }
  6202. }
  6203. if (*ReferencedDomains) {
  6204. midl_user_free(*ReferencedDomains);
  6205. *ReferencedDomains = LocalReferencedDomains;
  6206. LocalReferencedDomains = NULL;
  6207. }
  6208. if (Names && *Names) {
  6209. midl_user_free(*Names);
  6210. *Names = LocalNames;
  6211. LocalNames = NULL;
  6212. }
  6213. if (Sids && *Sids) {
  6214. midl_user_free(*Sids);
  6215. *Sids = LocalSids;
  6216. LocalSids = NULL;
  6217. }
  6218. Exit:
  6219. if (LocalReferencedDomains) {
  6220. if (LocalReferencedDomains->Domains) {
  6221. for (i = 0; i < LocalReferencedDomains->Entries; i++) {
  6222. if (LocalReferencedDomains->Domains[i].Name.Buffer) {
  6223. midl_user_free(LocalReferencedDomains->Domains[i].Name.Buffer);
  6224. }
  6225. if (LocalReferencedDomains->Domains[i].Sid) {
  6226. midl_user_free(LocalReferencedDomains->Domains[i].Sid);
  6227. }
  6228. }
  6229. midl_user_free(LocalReferencedDomains->Domains);
  6230. }
  6231. midl_user_free(LocalReferencedDomains);
  6232. }
  6233. if (LocalNames) {
  6234. for (i = 0; i < Count; i++) {
  6235. if (LocalNames[i].Name.Buffer) {
  6236. midl_user_free(LocalNames[i].Name.Buffer);
  6237. }
  6238. }
  6239. midl_user_free(LocalNames);
  6240. }
  6241. if (LocalSids) {
  6242. for (i = 0; i < Count; i++) {
  6243. if (LocalSids[i].Sid) {
  6244. midl_user_free(LocalSids[i].Sid);
  6245. }
  6246. }
  6247. midl_user_free(LocalSids);
  6248. }
  6249. return Status;
  6250. MemoryFailure:
  6251. Status = STATUS_NO_MEMORY;
  6252. goto Exit;
  6253. }
  6254. #if DBG
  6255. LPWSTR
  6256. LsapDbLookupGetLevel(
  6257. IN LSAP_LOOKUP_LEVEL LookupLevel
  6258. )
  6259. //
  6260. // Simple debug helper routine
  6261. //
  6262. {
  6263. switch (LookupLevel) {
  6264. case LsapLookupWksta:
  6265. return L"LsapLookupWksta";
  6266. case LsapLookupPDC:
  6267. return L"LsapLookupPDC";
  6268. case LsapLookupTDL:
  6269. return L"LsapLookupTDL";
  6270. case LsapLookupGC:
  6271. return L"LsapLookupGC";
  6272. case LsapLookupXForestReferral:
  6273. return L"LsapLookupXForestReferral";
  6274. case LsapLookupXForestResolve:
  6275. return L"LsapLookupXForestResolve";
  6276. default:
  6277. return L"Unknown Lookup Level";
  6278. }
  6279. }
  6280. #endif
  6281. NTSTATUS
  6282. LsapDomainHasDomainTrust(
  6283. IN ULONG Flags,
  6284. IN PUNICODE_STRING DomainName, OPTIONAL
  6285. IN PSID DomainSid, OPTIONAL
  6286. IN OUT BOOLEAN *fTDLLock, OPTIONAL
  6287. OUT PLSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY *TrustEntryOut OPTIONAL
  6288. )
  6289. /*++
  6290. Routine Description:
  6291. This routine determines if DomainName/DomainSid refers to a domain that
  6292. the current DC trusts. This is a worker routine for the
  6293. LsaLookupNames/Sids API and as such is only interested in trust
  6294. entries with an associated SID. Therefore, only outbound Windows
  6295. trusts will be considered.
  6296. If requested, the TrustEntry for the domain will be returned.
  6297. Note that the Trusted Domain List lock is required for this. Therefore
  6298. fTDLock is a required parameter when passing in TrustEntryOut.
  6299. Arguments:
  6300. Flags -- LSAP_LOOKUP_DOMAIN_TRUST_DIRECT_EXTERNAL
  6301. LSAP_LOOKUP_DOMAIN_TRUST_DIRECT_INTRA
  6302. LSAP_LOOKUP_DOMAIN_TRUST_FOREST
  6303. DomainName -- The name of the target domain. DomainSid must be NULL if
  6304. DomainName is non-NULL.
  6305. DomainSid -- The sid of the target domain. DomainName must be NULL if
  6306. DomainSid is non-NULL.
  6307. fTDLLock -- An IN/OUT parameter indicating whether the trusted domain
  6308. lock is held or not. NULL implies that this routine
  6309. should grab and release the lock. fTDLLock and TrustEntryOut
  6310. must either both be present, or both be equal to NULL.
  6311. TrustEntryOut -- An OUT parameter receiving the TrustEntry of
  6312. DomainName/DomainSid if found and if the trust satisfies
  6313. above criteria. fTDLLock and TrustEntryOut must either
  6314. both be present, or both be equal to NULL.
  6315. Return Values:
  6316. STATUS_SUCCESS -- the domain fits the criteria above
  6317. STATUS_NO_SUCH_DOMAIN -- otherwise
  6318. --*/
  6319. {
  6320. NTSTATUS Status = STATUS_SUCCESS;
  6321. BOOLEAN fLock = FALSE;
  6322. PLSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY TrustEntry = NULL;
  6323. UNICODE_STRING DomainNameFound;
  6324. BOOLEAN fForestTrust = FALSE;
  6325. BOOLEAN fDomainTrust = FALSE;
  6326. BOOLEAN fIntraForestDomainTrust = FALSE;
  6327. //
  6328. // Only and exactly one IN parameter is permitted
  6329. //
  6330. ASSERT( (DomainName != NULL) || (DomainSid != NULL) );
  6331. ASSERT( (DomainName == NULL) || (DomainSid == NULL) );
  6332. //
  6333. // Both or none of the these parameters should be sent in
  6334. //
  6335. ASSERT( ((fTDLLock == NULL) && (TrustEntryOut == NULL))
  6336. || ((fTDLLock != NULL) && (TrustEntryOut != NULL)) );
  6337. //
  6338. // At least one variation must be present currently
  6339. //
  6340. ASSERT( 0 != Flags );
  6341. //
  6342. // Init the out parameter
  6343. //
  6344. if (TrustEntryOut) {
  6345. *TrustEntryOut = NULL;
  6346. }
  6347. RtlInitUnicodeString(&DomainNameFound, NULL);
  6348. //
  6349. // Attempt to find the domain in our trusted domain list
  6350. //
  6351. if ( (fTDLLock && (*fTDLLock == FALSE))
  6352. || fTDLLock == NULL ) {
  6353. Status = LsapDbAcquireReadLockTrustedDomainList();
  6354. if (!NT_SUCCESS(Status)) {
  6355. goto Cleanup;
  6356. }
  6357. fLock = TRUE;
  6358. }
  6359. if (DomainName) {
  6360. Status = LsapDbLookupNameTrustedDomainListEx(
  6361. (PLSAPR_UNICODE_STRING) DomainName,
  6362. &TrustEntry
  6363. );
  6364. } else {
  6365. Status = LsapDbLookupSidTrustedDomainListEx(
  6366. DomainSid,
  6367. &TrustEntry
  6368. );
  6369. }
  6370. //
  6371. // Did we find it?
  6372. //
  6373. if (!NT_SUCCESS(Status)) {
  6374. Status = STATUS_NO_SUCH_DOMAIN;
  6375. goto Cleanup;
  6376. }
  6377. //
  6378. // We only consider trusted domains which have SIDs associated with them.
  6379. // Only outbound Windows trusts are guaranteed to have SIDs (per CliffV).
  6380. //
  6381. if ( !(TrustEntry->TrustInfoEx.TrustDirection & TRUST_DIRECTION_OUTBOUND)) {
  6382. Status = STATUS_NO_SUCH_DOMAIN;
  6383. goto Cleanup;
  6384. }
  6385. if ( TrustEntry->TrustInfoEx.TrustType != TRUST_TYPE_UPLEVEL &&
  6386. TrustEntry->TrustInfoEx.TrustType != TRUST_TYPE_DOWNLEVEL ) {
  6387. Status = STATUS_NO_SUCH_DOMAIN;
  6388. goto Cleanup;
  6389. }
  6390. //
  6391. // All outbound Windows trusts should have a SID
  6392. //
  6393. ASSERT( NULL != TrustEntry->TrustInfoEx.Sid );
  6394. if ( LsapOutboundTrustedForest(TrustEntry) ) {
  6395. fForestTrust = TRUE;
  6396. }
  6397. if ( LsapOutboundTrustedDomain(TrustEntry) ) {
  6398. fDomainTrust = TRUE;
  6399. }
  6400. //
  6401. // Only check domain trusts for intra-forest trusts
  6402. //
  6403. if ( fDomainTrust ) {
  6404. Status = LsapGetDomainNameBySid(TrustEntry->TrustInfoEx.Sid,
  6405. &DomainNameFound);
  6406. if (NT_SUCCESS(Status)) {
  6407. fIntraForestDomainTrust = TRUE;
  6408. } else if (Status != STATUS_NO_SUCH_DOMAIN) {
  6409. // Unhandled error
  6410. goto Cleanup;
  6411. }
  6412. }
  6413. //
  6414. // Logic to determine if the desired type of trust was found
  6415. //
  6416. if (
  6417. //
  6418. // Forest trust
  6419. //
  6420. ( FLAG_ON(Flags, LSAP_LOOKUP_DOMAIN_TRUST_FOREST)
  6421. && fForestTrust)
  6422. //
  6423. // Direct external trust
  6424. //
  6425. || ( FLAG_ON(Flags, LSAP_LOOKUP_DOMAIN_TRUST_DIRECT_EXTERNAL)
  6426. && fDomainTrust
  6427. && !fIntraForestDomainTrust)
  6428. //
  6429. // Direct, internal trust (intra forest)
  6430. //
  6431. || ( FLAG_ON(Flags, LSAP_LOOKUP_DOMAIN_TRUST_DIRECT_INTRA)
  6432. && fDomainTrust
  6433. && fIntraForestDomainTrust)
  6434. ) {
  6435. //
  6436. // Success!
  6437. //
  6438. Status = STATUS_SUCCESS;
  6439. if (TrustEntryOut) {
  6440. *TrustEntryOut = TrustEntry;
  6441. }
  6442. } else {
  6443. Status = STATUS_NO_SUCH_DOMAIN;
  6444. }
  6445. Cleanup:
  6446. if (fLock) {
  6447. if (fTDLLock == NULL) {
  6448. LsapDbReleaseLockTrustedDomainList();
  6449. } else {
  6450. *fTDLLock = TRUE;
  6451. }
  6452. }
  6453. if (DomainNameFound.Buffer) {
  6454. midl_user_free(DomainNameFound.Buffer);
  6455. }
  6456. return Status;
  6457. }
  6458. NTSTATUS
  6459. LsapDomainHasForestTrust(
  6460. IN PUNICODE_STRING DomainName, OPTIONAL
  6461. IN PSID DomainSid, OPTIONAL
  6462. IN OUT BOOLEAN *fTDLLock, OPTIONAL
  6463. OUT PLSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY *TrustEntryOut OPTIONAL
  6464. )
  6465. //
  6466. // See LsapDomainHasDomainTrust
  6467. //
  6468. {
  6469. return LsapDomainHasDomainTrust(LSAP_LOOKUP_DOMAIN_TRUST_FOREST,
  6470. DomainName,
  6471. DomainSid,
  6472. fTDLLock,
  6473. TrustEntryOut);
  6474. }
  6475. NTSTATUS
  6476. LsapDomainHasDirectTrust(
  6477. IN PUNICODE_STRING DomainName, OPTIONAL
  6478. IN PSID DomainSid, OPTIONAL
  6479. IN OUT BOOLEAN *fTDLLock, OPTIONAL
  6480. OUT PLSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY *TrustEntryOut OPTIONAL
  6481. )
  6482. {
  6483. return LsapDomainHasDomainTrust(LSAP_LOOKUP_DOMAIN_TRUST_DIRECT_INTRA|
  6484. LSAP_LOOKUP_DOMAIN_TRUST_DIRECT_EXTERNAL,
  6485. DomainName,
  6486. DomainSid,
  6487. fTDLLock,
  6488. TrustEntryOut);
  6489. }
  6490. NTSTATUS
  6491. LsapDomainHasDirectExternalTrust(
  6492. IN PUNICODE_STRING DomainName, OPTIONAL
  6493. IN PSID DomainSid, OPTIONAL
  6494. IN OUT BOOLEAN *fTDLLock, OPTIONAL
  6495. OUT PLSAP_DB_TRUSTED_DOMAIN_LIST_ENTRY *TrustEntryOut OPTIONAL
  6496. )
  6497. //
  6498. // See LsapDomainHasDomainTrust
  6499. //
  6500. {
  6501. return LsapDomainHasDomainTrust(LSAP_LOOKUP_DOMAIN_TRUST_DIRECT_EXTERNAL,
  6502. DomainName,
  6503. DomainSid,
  6504. fTDLLock,
  6505. TrustEntryOut);
  6506. }
  6507. NTSTATUS
  6508. LsapDomainHasTransitiveTrust(
  6509. IN PUNICODE_STRING DomainName, OPTIONAL
  6510. IN PSID DomainSid, OPTIONAL
  6511. OUT LSA_TRUST_INFORMATION *TrustInfo OPTIONAL
  6512. )
  6513. /*++
  6514. Routine Description:
  6515. This routine determines if the domain information (DomainName or
  6516. DomainSid) belongs to a domain in the current forest.
  6517. Arguments:
  6518. DomainName -- the name of the target domain
  6519. DomainSid -- the sid of the target domain
  6520. TrustInfo -- the domain SID and netbios name of the domain, if found is
  6521. returned. The embedded values (the SID and unicode string
  6522. must be freed by the caller)
  6523. Return Values:
  6524. STATUS_SUCCESS -- the domain fits the criteria above
  6525. STATUS_NO_SUCH_DOMAIN -- otherwise
  6526. --*/
  6527. {
  6528. NTSTATUS Status = STATUS_SUCCESS;
  6529. PSID LocalDomainSid = NULL;
  6530. UNICODE_STRING LocalDomainName;
  6531. // Precisely one is allowed and expected
  6532. ASSERT(!(DomainName && DomainSid));
  6533. ASSERT(!((DomainName == NULL) && (DomainSid == NULL)));
  6534. RtlInitUnicodeString(&LocalDomainName, NULL);
  6535. if (DomainName) {
  6536. //
  6537. // Try to match by name
  6538. //
  6539. LPWSTR Name;
  6540. //
  6541. // Make a NULL terminated string
  6542. //
  6543. Name = (WCHAR*)midl_user_allocate(DomainName->Length + sizeof(WCHAR));
  6544. if (NULL == Name) {
  6545. Status = STATUS_NO_MEMORY;
  6546. goto Exit;
  6547. }
  6548. RtlCopyMemory(Name, DomainName->Buffer, DomainName->Length);
  6549. Name[DomainName->Length / sizeof(WCHAR)] = UNICODE_NULL;
  6550. //
  6551. // Try Netbios
  6552. //
  6553. Status = LsapGetDomainSidByNetbiosName( Name,
  6554. &LocalDomainSid );
  6555. if ( STATUS_NO_SUCH_DOMAIN == Status ) {
  6556. //
  6557. // Try DNS
  6558. //
  6559. Status = LsapGetDomainSidByDnsName( Name,
  6560. &LocalDomainSid );
  6561. }
  6562. //
  6563. // Get the flat name in all cases
  6564. //
  6565. if ( NT_SUCCESS( Status ) ) {
  6566. Status = LsapGetDomainNameBySid( LocalDomainSid,
  6567. &LocalDomainName );
  6568. }
  6569. midl_user_free(Name);
  6570. } else {
  6571. //
  6572. // Try to match by SID
  6573. //
  6574. Status = LsapGetDomainNameBySid( DomainSid,
  6575. &LocalDomainName );
  6576. if (NT_SUCCESS(Status)) {
  6577. LocalDomainSid = midl_user_allocate(GetLengthSid(DomainSid));
  6578. if (NULL == LocalDomainSid) {
  6579. Status = STATUS_NO_MEMORY;
  6580. goto Exit;
  6581. }
  6582. if (NULL != LocalDomainSid) {
  6583. CopySid(GetLengthSid(DomainSid), LocalDomainSid, DomainSid);
  6584. }
  6585. }
  6586. }
  6587. if (NT_SUCCESS(Status)) {
  6588. ASSERT(NULL != LocalDomainSid);
  6589. ASSERT(NULL != LocalDomainName.Buffer);
  6590. if (TrustInfo) {
  6591. RtlZeroMemory(TrustInfo, sizeof(*TrustInfo));
  6592. TrustInfo->Sid = LocalDomainSid;
  6593. TrustInfo->Name = LocalDomainName;
  6594. LocalDomainSid = NULL;
  6595. RtlInitUnicodeString(&LocalDomainName, NULL);
  6596. }
  6597. }
  6598. Exit:
  6599. if (LocalDomainSid) {
  6600. midl_user_free(LocalDomainSid);
  6601. }
  6602. if (LocalDomainName.Buffer) {
  6603. midl_user_free(LocalDomainName.Buffer);
  6604. }
  6605. return Status;
  6606. }
  6607. //
  6608. // The character to search for if the name format is assumed to be
  6609. // a UPN
  6610. //
  6611. #define LSAP_LOOKUP_UPN_DELIMITER L'@'
  6612. //
  6613. // The character to search for if the name format is assumed to a
  6614. // SamAccountName format name
  6615. //
  6616. #define LSAP_LOOKUP_SAM_ACCOUNT_DELIMITER L'\\'
  6617. BOOL
  6618. LsapLookupNameContainsDelimiter(
  6619. IN PUNICODE_STRING Name,
  6620. IN WCHAR Delimiter,
  6621. OUT ULONG *Position OPTIONAL
  6622. )
  6623. /*++
  6624. Routine Description:
  6625. This routine finds Delimiter in Name and returns the position of
  6626. the delimiter. Name must be at least 3 characters long and the delimiter
  6627. cannot be in the first or last position.
  6628. N.B. This routines looks for the left-most delimiter
  6629. Arguments:
  6630. Name -- the name to be converted (in place)
  6631. Delimiter -- LSAP_LOOKUP_SAM_ACCOUNT_DELIMITER
  6632. LSAP_LOOKUP_UPN_DELIMITER
  6633. Position -- the array index of Delimiter in Name->Buffer
  6634. Return Values:
  6635. TRUE if delimiter found; FALSE otherwise
  6636. --*/
  6637. {
  6638. ULONG StringLength = Name->Length / sizeof(WCHAR);
  6639. ULONG DelimiterPosition;
  6640. ULONG i;
  6641. //
  6642. // Find the last Delimiter; return if not found, or in first or last position
  6643. //
  6644. DelimiterPosition = 0;
  6645. for (i = StringLength; i > 0; i--) {
  6646. if (Name->Buffer[i-1] == Delimiter) {
  6647. DelimiterPosition = i-1;
  6648. break;
  6649. }
  6650. }
  6651. if ((DelimiterPosition == 0) || (DelimiterPosition == (StringLength - 1))) {
  6652. return FALSE;
  6653. }
  6654. if (Position) {
  6655. *Position = DelimiterPosition;
  6656. }
  6657. return TRUE;
  6658. }
  6659. VOID
  6660. LsapLookupConvertNameFormat(
  6661. IN OUT PUNICODE_STRING Name,
  6662. IN WCHAR OldDelimiter,
  6663. IN WCHAR NewDelimiter
  6664. )
  6665. /*++
  6666. Routine Description:
  6667. This routine takes a Name and converts the name format between
  6668. UPN and SamAccountName style. For example:
  6669. billg@microsoft.com to microsoft.com\billg
  6670. and
  6671. microsoft.com\billg to billg@microsoft.com
  6672. If the expected delimiter is not found in the name, the string is
  6673. untouched.
  6674. Arguments:
  6675. Name -- the name to be converted (in place)
  6676. OldDelimiter -- LSAP_LOOKUP_SAM_ACCOUNT_DELIMITER
  6677. LSAP_LOOKUP_UPN_DELIMITER
  6678. NewDelimiter -- LSAP_LOOKUP_SAM_ACCOUNT_DELIMITER
  6679. LSAP_LOOKUP_UPN_DELIMITER
  6680. Return Values:
  6681. None.
  6682. --*/
  6683. {
  6684. ULONG StringLength = Name->Length / sizeof(WCHAR);
  6685. ULONG Delimiter;
  6686. ULONG i;
  6687. ULONG Length1, Length2;
  6688. WCHAR *Buffer = Name->Buffer;
  6689. ULONG RotationFactor;
  6690. ULONG LastStartingPoint, MovedCount, CurrentPosition, NextPosition;
  6691. WCHAR Temp1, Temp2;
  6692. //
  6693. // The function's behavior is not defined in this case.
  6694. //
  6695. ASSERT(OldDelimiter != NewDelimiter);
  6696. //
  6697. // Find the last Delimiter; return if not found, or in first or last position
  6698. //
  6699. if (!LsapLookupNameContainsDelimiter(Name, OldDelimiter, &Delimiter)) {
  6700. return;
  6701. }
  6702. //
  6703. // Make the delimiter part of the first segment
  6704. // Ie. billg@ microsoft.com
  6705. // microsoft.com\ billg
  6706. //
  6707. Length1 = Delimiter + 1;
  6708. Length2 = StringLength - Length1;
  6709. //
  6710. // Rotate the string
  6711. //
  6712. RotationFactor = Length2;
  6713. MovedCount = 0;
  6714. CurrentPosition = 0;
  6715. LastStartingPoint = 0;
  6716. Temp1 = Buffer[0];
  6717. while (MovedCount < StringLength) {
  6718. NextPosition = CurrentPosition + RotationFactor;
  6719. NextPosition %= StringLength;
  6720. Temp2 = Buffer[NextPosition];
  6721. Buffer[NextPosition] = Temp1;
  6722. Temp1 = Temp2;
  6723. CurrentPosition = NextPosition;
  6724. if (CurrentPosition == LastStartingPoint) {
  6725. CurrentPosition++;
  6726. LastStartingPoint = CurrentPosition;
  6727. Temp1 = Buffer[CurrentPosition];
  6728. }
  6729. MovedCount++;
  6730. }
  6731. //
  6732. // The string now looks like
  6733. // microsoft.combillg@
  6734. // billgmicrosoft.com\
  6735. //
  6736. // Move down and add new limiter
  6737. //
  6738. Temp1 = Buffer[Length2];
  6739. for (i = Length2+1; i < StringLength; i++) {
  6740. Temp2 = Buffer[i];
  6741. Buffer[i] = Temp1;
  6742. Temp1 = Temp2;
  6743. }
  6744. Buffer[Length2] = NewDelimiter;
  6745. //
  6746. // Final form:
  6747. // microsoft.com\billg
  6748. // [email protected]
  6749. //
  6750. return;
  6751. }
  6752. VOID
  6753. LsapLookupCrackName(
  6754. IN PUNICODE_STRING Prefix,
  6755. IN PUNICODE_STRING Suffix,
  6756. OUT PUNICODE_STRING SamAccountName,
  6757. OUT PUNICODE_STRING DomainName
  6758. )
  6759. /*++
  6760. Routine Description:
  6761. This routine takes Prefix and Suffix, which represent a name of the
  6762. form Prefix\Suffix, and determines what the domain and username
  6763. portion are.
  6764. Arguments:
  6765. Prefix -- the left side of the \ of the originally requested name
  6766. Suffix -- the right side of the \ of the originally requested name
  6767. SamAccountName -- the sam account name embedded in Prefix\Suffix
  6768. DomainName -- the domain name embedded in Prefix\Suffix
  6769. Return Values:
  6770. None.
  6771. --*/
  6772. {
  6773. ULONG Position;
  6774. if ( (Prefix->Length == 0)
  6775. && LsapLookupNameContainsDelimiter(Suffix,
  6776. LSAP_LOOKUP_UPN_DELIMITER,
  6777. &Position)) {
  6778. //
  6779. // This is an isolated name (no explicit domain portion) that contains
  6780. // the UPN delimiter -- crack as UPN.
  6781. //
  6782. ULONG StringLength = Suffix->Length / sizeof(WCHAR);
  6783. SamAccountName->Buffer = Suffix->Buffer;
  6784. SamAccountName->Length = (USHORT)Position * sizeof(WCHAR);
  6785. SamAccountName->MaximumLength = SamAccountName->Length;
  6786. DomainName->Buffer = Suffix->Buffer + Position + 1;
  6787. DomainName->Length = (USHORT) (StringLength - Position - 1) * sizeof(WCHAR);
  6788. DomainName->MaximumLength = DomainName->Length;
  6789. } else {
  6790. //
  6791. // Simple case, domain is specified as prefix
  6792. //
  6793. *SamAccountName = *Suffix;
  6794. *DomainName = *Prefix;
  6795. }
  6796. }
  6797. VOID
  6798. LsapLookupSamAccountNameToUPN(
  6799. IN OUT PUNICODE_STRING Name
  6800. )
  6801. //
  6802. // Converts, in place, domainname\username to username@domainname
  6803. //
  6804. {
  6805. LsapLookupConvertNameFormat(Name,
  6806. LSAP_LOOKUP_SAM_ACCOUNT_DELIMITER,
  6807. LSAP_LOOKUP_UPN_DELIMITER);
  6808. }
  6809. VOID
  6810. LsapLookupUPNToSamAccountName(
  6811. IN OUT PUNICODE_STRING Name
  6812. )
  6813. //
  6814. // Converts, in place, username@domainname to domainname\username
  6815. //
  6816. {
  6817. LsapLookupConvertNameFormat(Name,
  6818. LSAP_LOOKUP_UPN_DELIMITER,
  6819. LSAP_LOOKUP_SAM_ACCOUNT_DELIMITER);
  6820. }
  6821. BOOL
  6822. LsapLookupIsUPN(
  6823. OUT PUNICODE_STRING Name
  6824. )
  6825. //
  6826. // Returns TRUE if Name is syntactically determined to be a UPN
  6827. //
  6828. {
  6829. return LsapLookupNameContainsDelimiter(Name,
  6830. LSAP_LOOKUP_UPN_DELIMITER,
  6831. NULL);
  6832. }
  6833. ULONG
  6834. LsapGetDomainLookupScope(
  6835. IN LSAP_LOOKUP_LEVEL LookupLevel,
  6836. IN ULONG ClientRevision
  6837. )
  6838. /*++
  6839. Routine Description:
  6840. This routine returns what scope is appropriate when looking up domain
  6841. names and SID's.
  6842. Arguments:
  6843. LookupLevel -- the level requested by the caller
  6844. fTransitiveTrustSupport -- is the client aware of transitive trust
  6845. relations.
  6846. Return Values:
  6847. A bitmask containing, possibly
  6848. LSAP_LOOKUP_TRUSTED_DOMAIN_DIRECT
  6849. LSAP_LOOKUP_TRUSTED_DOMAIN_TRANSITIVE
  6850. LSAP_LOOKUP_TRUSTED_FOREST
  6851. LSAP_LOOKUP_TRUSTED_FOREST_ROOT
  6852. --*/
  6853. {
  6854. ULONG Scope = 0;
  6855. if (
  6856. //
  6857. // Workstation request
  6858. //
  6859. (LookupLevel == LsapLookupPDC)
  6860. //
  6861. // Local lookup on DC
  6862. //
  6863. || ((LookupLevel == LsapLookupWksta)
  6864. && (LsapProductType == NtProductLanManNt))
  6865. ) {
  6866. //
  6867. // Include directly trusted domains
  6868. //
  6869. Scope |= LSAP_LOOKUP_TRUSTED_DOMAIN_DIRECT;
  6870. //
  6871. // Determine if newer features apply
  6872. //
  6873. if ( (ClientRevision > LSA_CLIENT_PRE_NT5)
  6874. || (LsapSamOpened && !SamIMixedDomain( LsapAccountDomainHandle ))
  6875. || LsapAllowExtendedDownlevelLookup ) {
  6876. //
  6877. // Allow DNS support
  6878. //
  6879. Scope |= LSAP_LOOKUP_DNS_SUPPORT;
  6880. //
  6881. // Include transitivly trusted domains
  6882. //
  6883. Scope |= LSAP_LOOKUP_TRUSTED_DOMAIN_TRANSITIVE;
  6884. //
  6885. // Include forest trusts
  6886. //
  6887. Scope |= LSAP_LOOKUP_TRUSTED_FOREST;
  6888. if (LsapDbDcInRootDomain()) {
  6889. //
  6890. // If we are in the root domain, also check for
  6891. // trusts to directly trusted forests for isolated
  6892. // domain names or domain SID's
  6893. //
  6894. Scope |= LSAP_LOOKUP_TRUSTED_FOREST_ROOT;
  6895. }
  6896. }
  6897. } else if ((LookupLevel == LsapLookupXForestResolve)
  6898. || (LookupLevel == LsapLookupGC) ) {
  6899. //
  6900. // Only consider transitively trusted domains
  6901. //
  6902. Scope |= LSAP_LOOKUP_TRUSTED_DOMAIN_TRANSITIVE;
  6903. }
  6904. return Scope;
  6905. }
  6906. NTSTATUS
  6907. LsapNullTerminateUnicodeString(
  6908. IN PUNICODE_STRING String,
  6909. OUT LPWSTR *pBuffer,
  6910. OUT BOOLEAN *fFreeBuffer
  6911. )
  6912. /*++
  6913. Routine Description:
  6914. This routine accepts a UNICODE_STRING and returns its internal buffer,
  6915. ensuring that it is NULL terminated.
  6916. If the buffer is NULL terminated it will be returned in pBuffer.
  6917. If the buffer isn't NULL terminated it will be reallocated, NULL terminated,
  6918. and returned in pBuffer. fFreeBuffer will be set to TRUE indicating the
  6919. caller is responsible for deallocating pBuffer.
  6920. If an error occurs then pBuffer will be NULL, fFreeBuffer will be FALSE, and
  6921. no memory will be allocated.
  6922. Arguments:
  6923. String - Pointer to a UNICODE_STRING
  6924. pBuffer - Pointer to a pointer to return the buffer
  6925. fFreeBuffer - Pointer to a BOOLEAN to indicate whether the caller needs to
  6926. deallocate pBuffer or not.
  6927. Return Values:
  6928. STATUS_SUCCESS - *pBuffer points to a NULL terminated version of String's
  6929. internal buffer. Check *fFreeBuffer to determine if
  6930. pBuffer must be freed by the caller.
  6931. STATUS_NO_MEMORY - The routine failed to NULL terminate String's internal
  6932. buffer. *pBuffer is NULL and *fFreeBuffer is FALSE.
  6933. --*/
  6934. {
  6935. BOOLEAN fNullTerminated;
  6936. NTSTATUS Status = STATUS_SUCCESS;
  6937. ASSERT(pBuffer);
  6938. ASSERT(fFreeBuffer);
  6939. //
  6940. // Initialize input parameters
  6941. //
  6942. *pBuffer = NULL;
  6943. *fFreeBuffer = FALSE;
  6944. //
  6945. // Short circuit for strings that are already NULL terminated.
  6946. //
  6947. fNullTerminated = (String->MaximumLength > String->Length &&
  6948. String->Buffer[String->Length / sizeof(WCHAR)] == UNICODE_NULL);
  6949. if (!fNullTerminated) {
  6950. //
  6951. // Allocate enough memory to include a terminating NULL character
  6952. //
  6953. *pBuffer = (WCHAR*)midl_user_allocate(String->Length + sizeof(WCHAR));
  6954. if ( NULL == *pBuffer ) {
  6955. Status = STATUS_NO_MEMORY;
  6956. }
  6957. else
  6958. {
  6959. //
  6960. // Copy the buffer into pBuffer and NULL terminate it.
  6961. //
  6962. *fFreeBuffer = TRUE;
  6963. RtlCopyMemory(*pBuffer, String->Buffer, String->Length);
  6964. (*pBuffer)[String->Length / sizeof(WCHAR)] = UNICODE_NULL;
  6965. }
  6966. }
  6967. else
  6968. {
  6969. //
  6970. // String's internal buffer is already NULL terminated, return it.
  6971. //
  6972. *pBuffer = String->Buffer;
  6973. }
  6974. return Status;
  6975. }
  6976. BOOLEAN
  6977. LsapCompareDomainNames(
  6978. IN PUNICODE_STRING String,
  6979. IN PUNICODE_STRING AmbiguousName,
  6980. IN PUNICODE_STRING FlatName OPTIONAL
  6981. )
  6982. /*++
  6983. Routine Description:
  6984. This routine performs a case insensitive comparison between a string
  6985. and a domain name. If both the NetBIOS and Dns name forms are known then
  6986. the caller must pass the NetBIOS domain name as FlatName and the Dns domain
  6987. name as AmbiguousName. A non-NULL value for FlatName indicates the caller
  6988. knows both name forms and will result in a more optimal comparison. If the
  6989. caller has only one name form and it is ambiguous, that is it may be NetBIOS
  6990. or Dns, then the caller must pass NULL for FlatName. The routine will try
  6991. both a NetBIOS comparison using RtlEqualDomainName and a Dns comparison
  6992. using DnsNameCompare_W. If either comparison results in equality the TRUE
  6993. is returned, otherwise FALSE.
  6994. This routine provides centralized logic for robust domain name comparison
  6995. consistent with domain name semantics in Lsa data structures. Lsa trust
  6996. information structures are interpreted in the following way.
  6997. LSAPR_TRUST_INFORMATION.Name - Either NetBIOS or Dns
  6998. The following structures have both a FlatName and DomainName (or Name)
  6999. field. In this case they are interpreted as follows:
  7000. LSAPR_TRUST_INFORMATION_EX
  7001. LSAPR_TRUSTED_DOMAIN_INFORMATION_EX
  7002. LSAPR_TRUSTED_DOMAIN_INFORMATION_EX2
  7003. If the FlatName field is NULL then the other name field is ambiguous.
  7004. If the FlatName field is non NULL, then the other name field is Dns.
  7005. NetBIOS comparison is performed using RtlEqualDomainName which enforces the
  7006. proper OEM character equivelencies. DNS name comparison is performed using
  7007. DnsNameCompare_W to ensure proper handling of trailing dots and character
  7008. equivelencies.
  7009. Arguments:
  7010. String -- Potentially ambiguous domain name to compare against
  7011. AmbiguousName, and FlatName if non-NULL.
  7012. AmbiguousName -- Is treated as an ambiguous name form unless FlatName
  7013. is also specified. If FlatName is non-NULL then
  7014. AmbiguousName is treated as a Dns domain name.
  7015. FlatName -- This parameter is optional. If present it must be the
  7016. flat name of the domain. Furthermore, passing this
  7017. parameter indicates that AmbiguousName is in fact a
  7018. Dns domain name.
  7019. Return Values:
  7020. TRUE - String is equivelent to one of FlatDomainName or DnsDomainName
  7021. FALSE - String is not equivelent to either domain name
  7022. If any parameter isn't a valid UNICODE_STRING then FALSE is returned.
  7023. Notes:
  7024. The number of comparisons required to determine equivelency will depend
  7025. on the ammount of information passed in by the caller. If both the
  7026. NetBIOS and Dns domain names are known, pass them both to ensure the minimal
  7027. number of comparisons.
  7028. --*/
  7029. {
  7030. NTSTATUS Status;
  7031. BOOLEAN fEquivalent = FALSE;
  7032. LPWSTR StringBuffer = NULL;
  7033. LPWSTR AmbiguousNameBuffer = NULL;
  7034. BOOLEAN fFreeStringBuffer = FALSE;
  7035. BOOLEAN fFreeAmbiguousBuffer = FALSE;
  7036. //
  7037. // Validate input strings
  7038. //
  7039. ASSERT(String);
  7040. ASSERT(AmbiguousName);
  7041. ASSERT(NT_SUCCESS(RtlValidateUnicodeString( 0, String )));
  7042. ASSERT(NT_SUCCESS(RtlValidateUnicodeString( 0, AmbiguousName )));
  7043. //
  7044. // Ensure the UNICODE_STRING data buffers are NULL terminated before
  7045. // passing them to DnsNameCompare_W
  7046. //
  7047. Status = LsapNullTerminateUnicodeString( String,
  7048. &StringBuffer,
  7049. &fFreeStringBuffer
  7050. );
  7051. if (NT_SUCCESS(Status)) {
  7052. Status = LsapNullTerminateUnicodeString( AmbiguousName,
  7053. &AmbiguousNameBuffer,
  7054. &fFreeAmbiguousBuffer
  7055. );
  7056. }
  7057. if (NT_SUCCESS(Status)) {
  7058. if ( NULL == FlatName ) {
  7059. //
  7060. // AmbiguousName is truly ambiguous, we must perform both
  7061. // types of comparison between String
  7062. //
  7063. fEquivalent = ( RtlEqualDomainName( String, AmbiguousName ) ||
  7064. DnsNameCompare_W( StringBuffer,
  7065. AmbiguousNameBuffer )
  7066. );
  7067. }
  7068. else
  7069. {
  7070. ASSERT(NT_SUCCESS(RtlValidateUnicodeString( 0, FlatName )));
  7071. //
  7072. // We are sure of the name forms so lets just use the
  7073. // appropriate comparison routines on each.
  7074. //
  7075. fEquivalent = ( RtlEqualDomainName( String, FlatName ) ||
  7076. DnsNameCompare_W( StringBuffer,
  7077. AmbiguousNameBuffer )
  7078. );
  7079. }
  7080. }
  7081. if ( fFreeStringBuffer ) {
  7082. midl_user_free( StringBuffer );
  7083. }
  7084. if ( fFreeAmbiguousBuffer ) {
  7085. midl_user_free( AmbiguousNameBuffer );
  7086. }
  7087. return fEquivalent;
  7088. }