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.

814 lines
20 KiB

  1. /*++
  2. Copyright (c) 2001 Microsoft Corporation
  3. Module Name:
  4. domain.cxx
  5. Abstract:
  6. Routines implementing a cache of domains and the DC for that domain.
  7. Author:
  8. Cliff Van Dyke (cliffv) 29-June-2001
  9. --*/
  10. #include "pch.hxx"
  11. #include <dsgetdc.h>
  12. #include <lmcons.h>
  13. #include <lmapibuf.h>
  14. #include <windns.h>
  15. //
  16. // Structure definitions
  17. //
  18. // A cached domain
  19. //
  20. typedef struct _AZP_DOMAIN {
  21. //
  22. // Link to the next domain for this AzAuthorizationStore
  23. // Access is serialized by the AzAuthorizationStore->DomainCritSect
  24. //
  25. LIST_ENTRY Next;
  26. //
  27. // Reference count in this structure
  28. // Access is serialized by Interlocked increment/decrement
  29. //
  30. LONG ReferenceCount;
  31. //
  32. // Name of the domain represented by this structure
  33. // Access is serialized by the AZP_DOMAIN->DomainCritSect
  34. // In the case of InitializeFromName we do not have the DNS domain name.
  35. // The boolean is used to distinguish the two cases.
  36. //
  37. AZP_STRING DomainName;
  38. BOOLEAN IsDnsDomainName;
  39. //
  40. // A DCs in that domain
  41. // Access is serialized by the AZP_DOMAIN->DomainCritSect
  42. //
  43. PAZP_DC Dc;
  44. //
  45. // A boolean indicating that the domain is down and the time when it went down.
  46. // Access is serialized by the AZP_DOMAIN->DomainCritSect
  47. //
  48. BOOLEAN DomainIsDown;
  49. LARGE_INTEGER DomainDownStartTime;
  50. SAFE_CRITICAL_SECTION DomainCritSect;
  51. } AZP_DOMAIN, *PAZP_DOMAIN;
  52. PVOID
  53. AzpReferenceDomain(
  54. IN PAZP_AZSTORE AzAuthorizationStore,
  55. IN LPWSTR DomainName,
  56. IN BOOLEAN IsDnsDomainName
  57. )
  58. /*++
  59. Routine Description:
  60. This routine finds the domain structure for a domain. If none exists, it is allocated.
  61. Arguments:
  62. AzAuthorizationStore - The AzAuthorizationStore object for the authz policy database.
  63. DomainName - Name of the domain being referenced.
  64. IsDnsDomainName - Whether this is really a DNS Domain name or NetBios Name.
  65. When we have a token avalaible we have the DNS Domain name. If not, in
  66. the case in which we have initialized the context from Name we only
  67. have a NetBios name.
  68. Return Value:
  69. Returns a pointer to the referenced domain object. The pointer must be dereference by
  70. calling AzpDereferenceDomain.
  71. NULL: memory could not be allocated for the domain.
  72. --*/
  73. {
  74. NTSTATUS Status;
  75. PAZP_DOMAIN Domain = NULL;
  76. PLIST_ENTRY ListEntry;
  77. AZP_STRING DomainNameString;
  78. //
  79. // Initialization
  80. //
  81. ASSERT( AzAuthorizationStore->GenericObject.ObjectType == OBJECT_TYPE_AZAUTHSTORE );
  82. ASSERT( AzAuthorizationStore->DomainCritSectInitialized );
  83. AzpInitString( &DomainNameString, DomainName );
  84. SafeEnterCriticalSection( &AzAuthorizationStore->DomainCritSect );
  85. //
  86. // Walk the list finding an existing domain.
  87. //
  88. for ( ListEntry = AzAuthorizationStore->Domains.Flink ;
  89. ListEntry != &AzAuthorizationStore->Domains ;
  90. ListEntry = ListEntry->Flink) {
  91. Domain = CONTAINING_RECORD( ListEntry,
  92. AZP_DOMAIN,
  93. Next );
  94. if ( AzpEqualStrings( &DomainNameString, &Domain->DomainName )) {
  95. //
  96. // Reference the domain
  97. //
  98. InterlockedIncrement( &Domain->ReferenceCount );
  99. AzPrint(( AZD_DOMREF, "0x%lx %ws (%ld): Domain ref\n", Domain, Domain->DomainName.String, Domain->ReferenceCount ));
  100. //
  101. // Move it to the front of the list
  102. //
  103. RemoveEntryList( &Domain->Next );
  104. InsertHeadList( &AzAuthorizationStore->Domains, &Domain->Next );
  105. break;
  106. }
  107. Domain = NULL;
  108. }
  109. //
  110. // If we didn't find one,
  111. // allocate one.
  112. //
  113. if ( Domain == NULL ) {
  114. Domain = (PAZP_DOMAIN) AzpAllocateHeap( sizeof(AZP_DOMAIN) + DomainNameString.StringSize, "DMDOM" );
  115. if ( Domain != NULL ) {
  116. //
  117. // Initialize the domain
  118. //
  119. RtlZeroMemory( Domain, sizeof(*Domain) );
  120. //
  121. // Initialize the client context critical section
  122. //
  123. Status = SafeInitializeCriticalSection( &Domain->DomainCritSect, SAFE_DOMAIN );
  124. if ( !NT_SUCCESS( Status )) {
  125. AzpFreeHeap( Domain );
  126. Domain = NULL;
  127. } else {
  128. RtlCopyMemory( (Domain+1),
  129. DomainNameString.String,
  130. DomainNameString.StringSize );
  131. Domain->DomainName = DomainNameString;
  132. Domain->DomainName.String = (LPWSTR)(Domain+1);
  133. Domain->IsDnsDomainName = IsDnsDomainName;
  134. // One to return to the caller.
  135. // One for being in the linked list
  136. Domain->ReferenceCount = 2;
  137. InsertHeadList( &AzAuthorizationStore->Domains, &Domain->Next );
  138. }
  139. }
  140. }
  141. SafeLeaveCriticalSection( &AzAuthorizationStore->DomainCritSect );
  142. return (PVOID) Domain;
  143. }
  144. VOID
  145. AzpDereferenceDomain(
  146. IN PVOID DomainHandle
  147. )
  148. /*++
  149. Routine Description:
  150. This routine decrements the reference count on the domain object.
  151. If the reference count reaches zero, the object is deleted.
  152. Arguments:
  153. DomainHandle - Handle to the domain to dereference
  154. Return Value:
  155. None.
  156. --*/
  157. {
  158. PAZP_DOMAIN Domain = (PAZP_DOMAIN) DomainHandle;
  159. ULONG RefCount;
  160. //
  161. // Decrement the reference count
  162. //
  163. RefCount = InterlockedDecrement( &Domain->ReferenceCount );
  164. AzPrint(( AZD_DOMREF, "0x%lx %ws (%ld): Domain deref\n", Domain, Domain->DomainName.String, Domain->ReferenceCount ));
  165. //
  166. // If the object is no longer referenced,
  167. // delete it.
  168. //
  169. if ( RefCount == 0 ) {
  170. if ( Domain->Dc != NULL ) {
  171. AzpDereferenceDc( Domain->Dc );
  172. }
  173. SafeDeleteCriticalSection( &Domain->DomainCritSect );
  174. AzpFreeHeap( Domain );
  175. }
  176. }
  177. VOID
  178. AzpUnlinkDomains(
  179. IN PAZP_AZSTORE AzAuthorizationStore
  180. )
  181. /*++
  182. Routine Description:
  183. This routine unlinks all of the domains from the AzAuthorizationStore.
  184. Arguments:
  185. AzAuthorizationStore - The AzAuthorizationStore object for the authz policy database.
  186. Return Value:
  187. None.
  188. --*/
  189. {
  190. //
  191. // Initialization
  192. //
  193. ASSERT( AzAuthorizationStore->GenericObject.ObjectType == OBJECT_TYPE_AZAUTHSTORE );
  194. ASSERT( AzAuthorizationStore->DomainCritSectInitialized );
  195. SafeEnterCriticalSection( &AzAuthorizationStore->DomainCritSect );
  196. //
  197. // Free the list of domains
  198. //
  199. while ( !IsListEmpty( &AzAuthorizationStore->Domains ) ) {
  200. PLIST_ENTRY ListEntry;
  201. PAZP_DOMAIN Domain;
  202. //
  203. // Remove the entry from the list
  204. //
  205. ListEntry = RemoveHeadList( &AzAuthorizationStore->Domains );
  206. Domain = CONTAINING_RECORD( ListEntry,
  207. AZP_DOMAIN,
  208. Next );
  209. ASSERT( Domain->ReferenceCount == 1 );
  210. AzpDereferenceDomain( Domain );
  211. }
  212. SafeLeaveCriticalSection( &AzAuthorizationStore->DomainCritSect );
  213. }
  214. DWORD
  215. AzpLdapErrorToWin32Error(
  216. IN ULONG LdapStatus
  217. )
  218. /*++
  219. Routine Description:
  220. Map Ldap Error to Win 32 error.
  221. Be a little bit more specific than LdapMapErrorToWin32
  222. Arguments:
  223. LdapStatus - LdapStatus code to map.
  224. Return Value:
  225. Corresponding win 32 status code
  226. --*/
  227. {
  228. //
  229. // Return a consistent status code for DC down
  230. //
  231. switch ( LdapStatus ) {
  232. case LDAP_SERVER_DOWN :
  233. case LDAP_UNAVAILABLE :
  234. case LDAP_BUSY:
  235. return ERROR_NO_SUCH_DOMAIN;
  236. default:
  237. return LdapMapErrorToWin32( LdapStatus );
  238. }
  239. }
  240. DWORD
  241. AzpAllocateDc(
  242. IN PAZP_STRING DcName,
  243. OUT PAZP_DC *RetDc
  244. )
  245. /*++
  246. Routine Description:
  247. This routine allocates a PAZP_DC structure and binds to the DC
  248. Arguments:
  249. DcName - Name of the Dc to allocate the structure for
  250. RetDc - Returns a pointer to a structure representing a DC.
  251. The caller should dereference this structure by calling AzpDereferenceDc
  252. Return Value:
  253. Status of the operation:
  254. NO_ERROR: a DcName has been returned. The caller should try the named DC. If the DC is
  255. expected to be down, the caller should call this routine again.
  256. ERROR_NO_SUCH_DOMAIN: No DC could be found.
  257. Others: resource errors. etc.
  258. --*/
  259. {
  260. DWORD WinStatus;
  261. PAZP_DC Dc = NULL;
  262. ULONG LdapStatus;
  263. LDAP *LdapHandle = NULL;
  264. //
  265. // Initialize the LDAP connection
  266. //
  267. LdapHandle = ldap_init( DcName->String, LDAP_PORT );
  268. if ( LdapHandle == NULL ) {
  269. LdapStatus = LdapGetLastError();
  270. AzPrint(( AZD_ACCESS,
  271. "AzpAllocateDc: ldap_init failed on %ws: %ld: %s\n",
  272. DcName->String,
  273. LdapStatus,
  274. ldap_err2stringA( LdapStatus )));
  275. WinStatus = AzpLdapErrorToWin32Error( LdapStatus );
  276. goto Cleanup;
  277. }
  278. //
  279. // Set our default options
  280. //
  281. WinStatus = AzpADSetDefaultLdapOptions( LdapHandle, DcName->String );
  282. if (WinStatus != NO_ERROR)
  283. {
  284. AzPrint(( AZD_AD,
  285. "AzpAllocateDc: AzpADSetDefaultLdapOptions failed on %ws: %ld\n",
  286. DcName->String,
  287. WinStatus
  288. ));
  289. goto Cleanup;
  290. }
  291. //
  292. // Bind to the DC
  293. //
  294. LdapStatus = ldap_bind_s( LdapHandle,
  295. NULL, // No DN of account to authenticate as
  296. NULL, // Default credentials
  297. LDAP_AUTH_NEGOTIATE );
  298. if ( LdapStatus != LDAP_SUCCESS ) {
  299. AzPrint(( AZD_ACCESS,
  300. "AzpAllocateDc: ldap_bind failed on %ws: %ld: %s\n",
  301. DcName->String,
  302. LdapStatus,
  303. ldap_err2stringA( LdapStatus )));
  304. WinStatus = AzpLdapErrorToWin32Error(LdapStatus);
  305. goto Cleanup;
  306. }
  307. //
  308. // Allocate the structure
  309. //
  310. Dc = (PAZP_DC) AzpAllocateHeap( sizeof(AZP_DC) + DcName->StringSize, "DMDC" );
  311. if ( Dc == NULL ) {
  312. WinStatus = ERROR_NOT_ENOUGH_MEMORY;
  313. goto Cleanup;
  314. }
  315. //
  316. // Fill it in
  317. //
  318. RtlCopyMemory( (Dc+1),
  319. DcName->String,
  320. DcName->StringSize );
  321. Dc->DcName = *DcName;
  322. Dc->DcName.String = (LPWSTR)(Dc+1);
  323. Dc->LdapHandle = LdapHandle;
  324. LdapHandle = NULL;
  325. // One to return to the caller.
  326. Dc->ReferenceCount = 1;
  327. AzPrint(( AZD_DOMREF, "0x%lx %ws (%ld): DC Allocaote\n", Dc, Dc->DcName.String, Dc->ReferenceCount ));
  328. //
  329. // Return it to the caller
  330. //
  331. *RetDc = Dc;
  332. Dc = NULL;
  333. WinStatus = NO_ERROR;
  334. Cleanup:
  335. if ( LdapHandle != NULL ) {
  336. LdapStatus = ldap_unbind( LdapHandle );
  337. ASSERT( LdapStatus == LDAP_SUCCESS );
  338. }
  339. return WinStatus;
  340. }
  341. DWORD
  342. AzpGetDc(
  343. IN PAZP_AZSTORE AzAuthorizationStore,
  344. IN PVOID DomainHandle,
  345. IN OUT PULONG Context,
  346. OUT PAZP_DC *RetDc
  347. )
  348. /*++
  349. Routine Description:
  350. This routine returns the DC for the domain represented by DomainHandle.
  351. Arguments:
  352. AzAuthorizationStore - The AzAuthorizationStore object for the authz policy database.
  353. DomainHandle - A handle to the domain to find a DC for
  354. Context - Specifies the context indicating how hard this routine has tried to find
  355. a DC. The caller should call the routine in a loop. On the first call, the caller
  356. should pass in a pointer DWORD set to zero. On subsequent calls, the caller should
  357. pass in the value returned on the previous pass.
  358. RetDc - Returns a pointer to a structure representing a DC.
  359. The caller should dereference this structure by calling AzpDereferenceDc
  360. Return Value:
  361. Status of the operation:
  362. NO_ERROR: a DcName has been returned. The caller should try the named DC. If the DC is
  363. expected to be down, the caller should call this routine again.
  364. ERROR_NO_SUCH_DOMAIN: No DC could be found.
  365. Others: resource errors. etc.
  366. --*/
  367. {
  368. DWORD WinStatus;
  369. PAZP_DOMAIN Domain = (PAZP_DOMAIN) DomainHandle;
  370. ULONG DsGetDcFlags;
  371. PDOMAIN_CONTROLLER_INFO DomainControllerInfo = NULL;
  372. AZP_STRING CapturedString;
  373. PAZP_DC Dc = NULL;
  374. PAZP_DC TempDc;
  375. //
  376. // Initialization
  377. //
  378. ASSERT( AzAuthorizationStore->GenericObject.ObjectType == OBJECT_TYPE_AZAUTHSTORE );
  379. ASSERT( AzAuthorizationStore->DomainCritSectInitialized );
  380. AzpInitString( &CapturedString, NULL );
  381. SafeEnterCriticalSection( &Domain->DomainCritSect );
  382. *RetDc = NULL;
  383. //
  384. // If the domain is down,
  385. // check to see if it is time to try again.
  386. //
  387. if ( Domain->DomainIsDown ) {
  388. //
  389. // If we haven't waited long enough,
  390. // fail immediately.
  391. //
  392. if ( !AzpTimeHasElapsed( &Domain->DomainDownStartTime,
  393. AzAuthorizationStore->DomainTimeout ) ) {
  394. WinStatus = ERROR_NO_SUCH_DOMAIN;
  395. goto Cleanup;
  396. }
  397. //
  398. // If we have waited long enough,
  399. // try again to find a DC.
  400. //
  401. if ( Domain->Dc != NULL ) {
  402. AzpDereferenceDc( Domain->Dc ); // No use trying this stale DC
  403. Domain->Dc = NULL;
  404. }
  405. Domain->DomainIsDown = FALSE;
  406. }
  407. //
  408. // Loop through the various states internally so our caller doesn't need to
  409. //
  410. for ( ; *Context <= 2 ; (*Context)++ ) {
  411. //
  412. // If this is the first call,
  413. // return the cached DC name.
  414. //
  415. if ( *Context == 0 ) {
  416. //
  417. // If there is no cached value,
  418. // continue the loop to look harder.
  419. //
  420. if ( Domain->Dc == NULL ) {
  421. continue;
  422. }
  423. //
  424. // On the second call, use DsGetDcName.
  425. // On the third call, use DsGetDcName withforce.
  426. //
  427. } else {
  428. if (Domain->IsDnsDomainName) {
  429. DsGetDcFlags = DS_IS_DNS_NAME | DS_IP_REQUIRED;
  430. } else {;
  431. //
  432. // Make sure that this is NT5 domain or higher.
  433. //
  434. DsGetDcFlags = DS_IS_FLAT_NAME | DS_IP_REQUIRED |
  435. DS_DIRECTORY_SERVICE_REQUIRED | DS_RETURN_DNS_NAME;
  436. }
  437. if ( *Context == 2 ) {
  438. DsGetDcFlags |= DS_FORCE_REDISCOVERY;
  439. }
  440. //
  441. // Free the values from the previous iteration
  442. //
  443. if ( DomainControllerInfo != NULL ) {
  444. NetApiBufferFree( DomainControllerInfo );
  445. DomainControllerInfo = NULL;
  446. }
  447. if ( Dc != NULL ) {
  448. AzpDereferenceDc( Dc );
  449. Dc = NULL;
  450. }
  451. AzpFreeString( &CapturedString );
  452. //
  453. // Find a DC in the user's account domain
  454. //
  455. WinStatus = DsGetDcName(
  456. NULL,
  457. Domain->DomainName.String,
  458. NULL, // No guid
  459. NULL, // No site,
  460. DsGetDcFlags,
  461. &DomainControllerInfo );
  462. if ( WinStatus != NO_ERROR ) {
  463. //
  464. // If the DC is down,
  465. // DsGetDcName never lies,
  466. // but map obnoxious status codes to a more likeable one
  467. //
  468. if ( WinStatus == WSAEHOSTUNREACH ||
  469. WinStatus == ERROR_NO_SUCH_DOMAIN ) {
  470. break;
  471. }
  472. goto Cleanup;
  473. }
  474. //
  475. // Capture the DC name into a string structure.
  476. //
  477. AzpFreeString( &CapturedString );
  478. WinStatus = AzpCaptureString( &CapturedString,
  479. DomainControllerInfo->DomainControllerName + 2,
  480. DNS_MAX_NAME_LENGTH,
  481. FALSE ); // Null not OK
  482. if ( WinStatus != NO_ERROR ) {
  483. goto Cleanup;
  484. }
  485. //
  486. // If the DC name is the same as the previous name,
  487. // continue looping to try harder to find a DC.
  488. //
  489. if ( Domain->Dc != NULL &&
  490. AzpEqualStrings( &CapturedString, &Domain->Dc->DcName) ) {
  491. WinStatus = ERROR_NO_SUCH_DOMAIN;
  492. continue;
  493. }
  494. //
  495. // Allocate a structure representing the DC
  496. //
  497. WinStatus = AzpAllocateDc( &CapturedString,
  498. &Dc );
  499. if ( WinStatus != NO_ERROR ) {
  500. if ( WinStatus == ERROR_NO_SUCH_DOMAIN ) {
  501. continue;
  502. }
  503. goto Cleanup;
  504. }
  505. //
  506. // Swap the old/new Dcs
  507. //
  508. TempDc = Domain->Dc;
  509. Domain->Dc = Dc;
  510. Dc = TempDc;
  511. }
  512. //
  513. // Return the found DC to the caller.
  514. //
  515. ASSERT( Domain->Dc != NULL );
  516. ASSERT( Domain->Dc->DcName.StringSize != 0 );
  517. InterlockedIncrement( &Domain->Dc->ReferenceCount );
  518. AzPrint(( AZD_DOMREF, "0x%lx %ws (%ld): DC ref\n", Domain->Dc, Domain->Dc->DcName.String, Domain->Dc->ReferenceCount ));
  519. *RetDc = Domain->Dc;
  520. WinStatus = NO_ERROR;
  521. goto Cleanup;
  522. }
  523. //
  524. // No DC can be found via any mechanism
  525. // Set the negative cache to indicate so.
  526. //
  527. Domain->DomainIsDown = TRUE;
  528. GetSystemTimeAsFileTime( (PFILETIME)&Domain->DomainDownStartTime );
  529. WinStatus = ERROR_NO_SUCH_DOMAIN;
  530. Cleanup:
  531. if ( DomainControllerInfo != NULL ) {
  532. NetApiBufferFree( DomainControllerInfo );
  533. }
  534. AzpFreeString( &CapturedString );
  535. if ( Dc != NULL ) {
  536. AzpDereferenceDc( Dc );
  537. Dc = NULL;
  538. }
  539. SafeLeaveCriticalSection( &Domain->DomainCritSect );
  540. return WinStatus;
  541. }
  542. VOID
  543. AzpDereferenceDc(
  544. IN PAZP_DC Dc
  545. )
  546. /*++
  547. Routine Description:
  548. This routine decrements the reference count on the DC object.
  549. If the reference count reaches zero, the object is deleted.
  550. Arguments:
  551. Dc - Pointer to the DC to dereference
  552. Return Value:
  553. None.
  554. --*/
  555. {
  556. ULONG RefCount;
  557. //
  558. // Decrement the reference count
  559. //
  560. RefCount = InterlockedDecrement( &Dc->ReferenceCount );
  561. AzPrint(( AZD_DOMREF, "0x%lx %ws (%ld): DC deref\n", Dc, Dc->DcName.String, Dc->ReferenceCount ));
  562. //
  563. // If the object is no longer referenced,
  564. // delete it.
  565. //
  566. if ( RefCount == 0 ) {
  567. if ( Dc->LdapHandle != NULL ) {
  568. ULONG LdapStatus;
  569. LdapStatus = ldap_unbind( Dc->LdapHandle );
  570. ASSERT( LdapStatus == LDAP_SUCCESS );
  571. }
  572. AzpFreeHeap( Dc );
  573. }
  574. }